Unified State-Changing API for LicenseKeyStatus
(This is a merge of http://go/wvgerrit/31040) Because the Policy Engine was only consulting the result of the Max-Res Decode check when it was in kLicenseStateCanPlay and not in other states that imply kKeyStatusUsable, like kLicenseStateWaitingLicenseUpdate, the Max-Res Decode results would not be honored during the interval between requesting a renewal and receiving the result. (Or until the key expired.) This was particularly problematic for keys with renewal delays less than ten seconds long, which would freeze the Max-Res state before it had a chance to update for the first time, effectively disabling Max-Res Decode until renewal was received. Fixing this required changing how the Policy Engine and the LicenseKeyStatus objects communicate about the changing usability state of the LicenseKeyStatus objects. Before, a call to ApplyConstraints() might calculate a Max-Res failure, but this failure would be pending until the Policy Engine deigned to call ApplyStatusChange() again. Without a call to ApplyStatusChange(), it could pend forever. This put a burden on the PolicyEngine to poll the LicenseKeys with redundant ApplyStatusChange() calls using the same CdmKeyStatus that the keys were already in, just in case Max-Res had changed since the last necessary call to ApplyStatusChange(). If the Policy Engine got the timing of these calls wrong, it would result in Max-Res results being ignored. (as in the linked bug) If it ever polled with the wrong CdmKeyStatus, it would update the LicenseKeys' status when it did not mean to. It would be preferable if this polling were not needed, so that the Policy Engine couldn't get it wrong. This patch changes the API between these classes so that when Max-Res fails, the state change can be reported immediately instead of pending until ApplyStatusChange() is called, eliminating the need for polling. All state changes to the LicenseKeyStatus objects go through a unified ApplyStatusChange() method that can update the CdmKeyStatus, resolution, and/or HDCP level and report any resulting usability changes immediately. This patch updates the unit tests to exercise this new API instead of the old API. Previously, the linked bug slipped past our unit tests because we only test unrenewable, streaming licenses against Max-Res. This patch adds several more variants to policy_engine_constraints_unittest so that it tests six kinds of license to provide better coverage. Bug: 62393949 Test: build_and_run_all_unit_tests Change-Id: I0dfdbf6b8ea39abb446089aef5f6ea0502e9b4c6
This commit is contained in:
@@ -17,7 +17,6 @@ using video_widevine::License;
|
||||
namespace {
|
||||
|
||||
const int64_t kHdcpCheckInterval = 10;
|
||||
const uint32_t kNoResolution = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -71,7 +70,15 @@ void PolicyEngine::CheckDevice(int64_t current_time) {
|
||||
if (!crypto_session_->GetHdcpCapabilities(¤t_hdcp_level, &ignored)) {
|
||||
current_hdcp_level = HDCP_NONE;
|
||||
}
|
||||
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
|
||||
|
||||
bool new_usable_keys = false;
|
||||
bool keys_changed =
|
||||
license_keys_->ApplyStatusChange(NULL, // new_status
|
||||
¤t_resolution_,
|
||||
¤t_hdcp_level,
|
||||
&new_usable_keys);
|
||||
NotifyIfKeysChanged(keys_changed, new_usable_keys);
|
||||
|
||||
next_device_check_ = current_time + kHdcpCheckInterval;
|
||||
}
|
||||
}
|
||||
@@ -89,21 +96,19 @@ void PolicyEngine::OnTimerEvent() {
|
||||
if (HasLicenseOrPlaybackDurationExpired(current_time) &&
|
||||
license_state_ != kLicenseStateExpired) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
UpdateKeyStatus(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check device conditions that affect playability (HDCP, resolution)
|
||||
CheckDevice(current_time);
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
bool renewal_needed = false;
|
||||
|
||||
// Test to determine if renewal should be attempted.
|
||||
switch (license_state_) {
|
||||
case kLicenseStateCanPlay: {
|
||||
if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
|
||||
// HDCP may change, so force a check.
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -120,7 +125,7 @@ void PolicyEngine::OnTimerEvent() {
|
||||
case kLicenseStatePending: {
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
UpdateKeyStatus(kKeyStatusUsable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -132,7 +137,7 @@ void PolicyEngine::OnTimerEvent() {
|
||||
|
||||
default: {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusInternalError);
|
||||
UpdateKeyStatus(kKeyStatusInternalError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +162,7 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
|
||||
policy_.Clear();
|
||||
|
||||
// Expire any old keys.
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
UpdateKeyStatus(kKeyStatusExpired);
|
||||
UpdateLicense(license);
|
||||
}
|
||||
|
||||
@@ -190,17 +195,17 @@ void PolicyEngine::UpdateLicense(const License& license) {
|
||||
if (!policy_.can_play() ||
|
||||
HasLicenseOrPlaybackDurationExpired(current_time)) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
UpdateKeyStatus(kKeyStatusExpired);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update state
|
||||
if (current_time >= license_start_time_) {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
NotifyKeysChange(kKeyStatusUsable);
|
||||
UpdateKeyStatus(kKeyStatusUsable);
|
||||
} else {
|
||||
license_state_ = kLicenseStatePending;
|
||||
NotifyKeysChange(kKeyStatusPending);
|
||||
UpdateKeyStatus(kKeyStatusPending);
|
||||
}
|
||||
NotifyExpirationUpdate(current_time);
|
||||
}
|
||||
@@ -240,7 +245,7 @@ void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
|
||||
void PolicyEngine::NotifySessionExpiration() {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
NotifyKeysChange(kKeyStatusExpired);
|
||||
UpdateKeyStatus(kKeyStatusExpired);
|
||||
}
|
||||
|
||||
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
|
||||
@@ -429,16 +434,23 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
|
||||
next_renewal_time_ <= current_time;
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
|
||||
bool keys_changed;
|
||||
bool has_new_usable_key = false;
|
||||
keys_changed = license_keys_->ApplyStatusChange(new_status,
|
||||
&has_new_usable_key);
|
||||
void PolicyEngine::UpdateKeyStatus(CdmKeyStatus new_status) {
|
||||
bool new_usable_keys = false;
|
||||
bool keys_changed =
|
||||
license_keys_->ApplyStatusChange(&new_status,
|
||||
NULL, // new_resolution
|
||||
NULL, // new_hdcp_level
|
||||
&new_usable_keys);
|
||||
NotifyIfKeysChanged(keys_changed, new_usable_keys);
|
||||
}
|
||||
|
||||
void PolicyEngine::NotifyIfKeysChanged(bool keys_changed,
|
||||
bool new_usable_keys) {
|
||||
if (event_listener_ && keys_changed) {
|
||||
CdmKeyStatusMap content_keys;
|
||||
license_keys_->ExtractKeyStatuses(&content_keys);
|
||||
event_listener_->OnSessionKeysChange(session_id_, content_keys,
|
||||
has_new_usable_key);
|
||||
new_usable_keys);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user