Delay license state evaluation for offline licenses am: 8543b4c903

Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/12646162

Change-Id: I7b0f8239720e3aab7324dce8e2956e8135d0a65c
This commit is contained in:
Rahul Frias
2020-09-23 02:46:20 +00:00
committed by Automerger Merge Worker
7 changed files with 248 additions and 171 deletions

View File

@@ -58,9 +58,9 @@ class CdmLicense {
CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url);
virtual CdmResponseType HandleKeyResponse(
const CdmKeyResponse& license_response);
bool is_restore, const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response);
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response);
virtual CdmResponseType HandleEmbeddedKeyData(
const InitializationData& init_data);
@@ -109,7 +109,7 @@ class CdmLicense {
video_widevine::LicenseRequest* license_request);
CdmResponseType HandleContentKeyResponse(
const std::string& msg, const std::string& core_message,
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license);
@@ -118,7 +118,7 @@ class CdmLicense {
// the crypto session. In addition, it also extracts content keys from
// |wrapped_keys_| and loads them for use.
CdmResponseType HandleEntitlementKeyResponse(
const std::string& msg, const std::string& core_message,
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license);

View File

@@ -56,10 +56,13 @@ class PolicyEngine {
// SetLicense is used in handling the initial license response. It stores
// an exact copy of the policy information stored in the license.
// The license state transitions to kLicenseStateCanPlay if the license
// permits playback.
// License state transitions and notifications may occur if
// |defer_license_state_update| is not set. If set, the license is likely
// being restored and transitions and notifications will be deferred until
// stored playback times are restored.
virtual void SetLicense(const video_widevine::License& license,
bool supports_core_messages);
bool supports_core_messages,
bool defer_license_state_update);
// Used to update the currently loaded entitled content keys.
virtual void SetEntitledLicenseKeys(
@@ -78,9 +81,17 @@ class PolicyEngine {
// UpdateLicense is used in handling a license response for a renewal request.
// The response may only contain any policy fields that have changed. In this
// case an exact copy is not what we want to happen. We also will receive an
// updated license_start_time from the server. The license will transition to
// kLicenseStateCanPlay if the license permits playback.
virtual void UpdateLicense(const video_widevine::License& license);
// updated license_start_time from the server.
// License state transitions and notifications may occur if
// |defer_license_state_update| is not set. If set, the license is likely
// being restored and transitions and notifications will be deferred until
// stored playback times are restored.
virtual void UpdateLicense(const video_widevine::License& license,
bool defer_license_state_update);
// This method updates license state and sends appropriate notifications
// to event listeners.
virtual void UpdateLicenseState(int64_t current_time);
// Used for notifying the Policy Engine of resolution changes
virtual void NotifyResolution(uint32_t width, uint32_t height);
@@ -101,6 +112,9 @@ class PolicyEngine {
int64_t GetPlaybackStartTime();
int64_t GetLastPlaybackTime();
int64_t GetGracePeriodEndTime();
// This method will also update license state and sends appropriate
// notifications to event listeners.
void RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time,
int64_t grace_period_end_time);
@@ -153,6 +167,7 @@ class PolicyEngine {
void SetSecurityLevelForTest(CdmSecurityLevel security_level);
LicenseState license_state_;
int64_t license_state_update_deadline_;
// This is the license id field from server response. This data gets passed
// back to the server in each renewal request. When we get a renewal response

View File

@@ -592,7 +592,8 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
if (sts != NO_ERROR) return sts;
}
}
sts = license_parser_->HandleKeyResponse(key_response);
sts = license_parser_->HandleKeyResponse(/* is restore */ false,
key_response);
// Update the license sdk and service versions.
const VersionInfo& version_info = license_parser_->GetServiceVersion();
@@ -789,8 +790,9 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
LOGE("CDM session not initialized");
return NOT_INITIALIZED_ERROR;
}
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(true, key_response);
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
/* is renewal */ true,
/* is restore */ false, key_response);
// Record the timing on success.
UpdateRequestLatencyTiming(sts);
@@ -854,8 +856,9 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
LOGE("CDM session not initialized");
return NOT_INITIALIZED_ERROR;
}
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(false, key_response);
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(
/* is renewal */ false,
/* is restore */ false, key_response);
// Record the timing on success.
UpdateRequestLatencyTiming(sts);

View File

@@ -530,7 +530,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
}
CdmResponseType CdmLicense::HandleKeyResponse(
const CdmKeyResponse& license_response) {
bool is_restore, const CdmKeyResponse& license_response) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_2;
@@ -683,18 +683,19 @@ CdmResponseType CdmLicense::HandleKeyResponse(
CdmResponseType resp = NO_CONTENT_KEY;
if (kLicenseKeyTypeEntitlement == key_type) {
resp =
HandleEntitlementKeyResponse(signed_message, core_message, signature,
mac_key_iv, mac_keys, key_array, license);
resp = HandleEntitlementKeyResponse(is_restore, signed_message,
core_message, signature, mac_key_iv,
mac_keys, key_array, license);
} else if (kLicenseKeyTypeContent == key_type) {
resp = HandleContentKeyResponse(signed_message, core_message, signature,
mac_key_iv, mac_keys, key_array, license);
resp = HandleContentKeyResponse(is_restore, signed_message, core_message,
signature, mac_key_iv, mac_keys, key_array,
license);
}
return resp;
}
CdmResponseType CdmLicense::HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response) {
bool is_renewal, bool is_restore, const CdmKeyResponse& license_response) {
if (!initialized_) {
LOGE("CdmLicense not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_3;
@@ -781,7 +782,7 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
}
if (status == KEY_ADDED) {
policy_engine_->UpdateLicense(license);
policy_engine_->UpdateLicense(license, is_restore);
}
return status;
@@ -836,14 +837,14 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
license_nonce_ = original_license_request.key_control_nonce();
}
CdmResponseType sts = HandleKeyResponse(license_response);
CdmResponseType sts = HandleKeyResponse(true, license_response);
if (sts != KEY_ADDED) return sts;
if (!license_renewal_response.empty()) {
sts = PrepareKeyUpdateReload(cdm_session);
if (sts != KEY_MESSAGE) return sts;
sts = HandleKeyUpdateResponse(true, license_renewal_response);
if (sts != KEY_MESSAGE && sts != NO_ERROR) return sts;
sts = HandleKeyUpdateResponse(true, true, license_renewal_response);
if (sts != KEY_ADDED) return sts;
}
@@ -968,7 +969,7 @@ CdmResponseType CdmLicense::RestoreLicenseForRelease(
}
if (!license.id().has_provider_session_token()) {
CdmResponseType result = HandleKeyResponse(license_response);
CdmResponseType result = HandleKeyResponse(false, license_response);
return result == KEY_ADDED ? NO_ERROR : result;
}
@@ -1116,7 +1117,7 @@ CdmResponseType CdmLicense::PrepareContentId(
}
CdmResponseType CdmLicense::HandleContentKeyResponse(
const std::string& msg, const std::string& core_message,
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
@@ -1140,13 +1141,13 @@ CdmResponseType CdmLicense::HandleContentKeyResponse(
it != key_array.end(); ++it) {
loaded_keys_.insert(it->key_id());
}
policy_engine_->SetLicense(license, supports_core_messages());
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
}
return resp;
}
CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
const std::string& msg, const std::string& core_message,
bool is_restore, const std::string& msg, const std::string& core_message,
const std::string& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license) {
@@ -1170,7 +1171,7 @@ CdmResponseType CdmLicense::HandleEntitlementKeyResponse(
// Save the entitlement keys for future use to handle key changes.
entitlement_keys_.CopyFrom(license.key());
policy_engine_->SetLicense(license, supports_core_messages());
policy_engine_->SetLicense(license, supports_core_messages(), is_restore);
return HandleNewEntitledKeys(wrapped_keys_);
}

View File

@@ -20,7 +20,8 @@ using video_widevine::License;
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
const int kClockSkewDelta = 5; // seconds
const int kClockSkewDelta = 5; // seconds
const int64_t kLicenseStateUpdateDelay = 20; // seconds
} // namespace
@@ -30,6 +31,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
WvCdmEventListener* event_listener,
CryptoSession* crypto_session)
: license_state_(kLicenseStateInitial),
license_state_update_deadline_(0),
last_recorded_current_time_(0),
session_id_(session_id),
event_listener_(event_listener),
@@ -90,6 +92,17 @@ void PolicyEngine::OnTimerEvent() {
last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds;
const int64_t current_time = GetCurrentTime();
// This conditional should not succeed but being cautious in case license
// state was not updated after the license was processed. Licenses and
// renewals from the license service should update state on
// |Set/UpdateLicense|. Offline licenses, when restored, should update license
// state when |RestorePlaybackTimes| is called.
if (license_state_update_deadline_ != 0 &&
license_state_update_deadline_ < current_time) {
LOGW("License state was not updated after a license was loaded/renewed");
UpdateLicenseState(current_time);
}
// If we have passed the grace period, the expiration will update.
if (policy_timers_->HasPassedGracePeriod(current_time)) {
NotifyExpirationUpdate(current_time);
@@ -159,12 +172,13 @@ void PolicyEngine::OnTimerEvent() {
}
void PolicyEngine::SetLicense(const License& license,
bool supports_core_messages) {
bool supports_core_messages,
bool defer_license_state_update) {
if (supports_core_messages) policy_timers_.reset(new PolicyTimersV16());
license_id_.CopyFrom(license.id());
license_keys_->SetFromLicense(license);
policy_timers_->SetLicense(license);
UpdateLicense(license);
UpdateLicense(license, defer_license_state_update);
}
void PolicyEngine::SetEntitledLicenseKeys(
@@ -180,10 +194,11 @@ void PolicyEngine::SetLicenseForRelease(const License& license,
// Expire any old keys.
NotifyKeysChange(kKeyStatusExpired);
policy_timers_->SetLicense(license);
UpdateLicense(license);
UpdateLicense(license, false);
}
void PolicyEngine::UpdateLicense(const License& license) {
void PolicyEngine::UpdateLicense(const License& license,
bool defer_license_state_update) {
if (!license.has_policy()) return;
if (kLicenseStateExpired == license_state_) {
@@ -204,8 +219,15 @@ void PolicyEngine::UpdateLicense(const License& license) {
const int64_t current_time = GetCurrentTime();
policy_timers_->UpdateLicense(current_time, license);
if (defer_license_state_update)
license_state_update_deadline_ = current_time + kLicenseStateUpdateDelay;
else
UpdateLicenseState(current_time);
}
void PolicyEngine::UpdateLicenseState(int64_t current_time) {
// Update time information
license_state_update_deadline_ = 0;
if (!policy_timers_->get_policy().can_play() ||
policy_timers_->HasLicenseOrRentalOrPlaybackDurationExpired(
current_time)) {
@@ -349,7 +371,7 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
last_playback_time,
grace_period_end_time);
NotifyExpirationUpdate(current_time);
UpdateLicenseState(current_time);
}
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {

View File

@@ -238,7 +238,7 @@ TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(GET_HDCP_CAPABILITY_FAILED)));
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -270,7 +270,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
.WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), Return(NO_ERROR)));
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -334,7 +334,7 @@ TEST_F(PolicyEngineConstraintsTest,
}
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->OnTimerEvent();
policy_engine_->OnTimerEvent();
policy_engine_->OnTimerEvent();
@@ -367,7 +367,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_V2), Return(NO_ERROR)));
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -409,7 +409,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NONE), Return(NO_ERROR)));
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent();
@@ -453,7 +453,7 @@ TEST_F(PolicyEngineConstraintsTest, UsesDefaultHdcpWhenResolutionNotSet) {
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(GET_HDCP_CAPABILITY_FAILED)));
policy_engine_->SetLicense(license_, false);
policy_engine_->SetLicense(license_, false, false);
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));

File diff suppressed because it is too large Load Diff