Delay license state evaluation for offline licenses am: 8543b4c903 am: 9f2363e076

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

Change-Id: I0158e6b1dc2560ff28ff9379533a428f2970efeb
This commit is contained in:
Rahul Frias
2020-09-23 03:03:08 +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, CdmSession* cdm_session, CdmKeyMessage* signed_request,
std::string* server_url); std::string* server_url);
virtual CdmResponseType HandleKeyResponse( virtual CdmResponseType HandleKeyResponse(
const CdmKeyResponse& license_response); bool is_restore, const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse( virtual CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response); bool is_renewal, bool is_restore, const CdmKeyResponse& license_response);
virtual CdmResponseType HandleEmbeddedKeyData( virtual CdmResponseType HandleEmbeddedKeyData(
const InitializationData& init_data); const InitializationData& init_data);
@@ -109,7 +109,7 @@ class CdmLicense {
video_widevine::LicenseRequest* license_request); video_widevine::LicenseRequest* license_request);
CdmResponseType HandleContentKeyResponse( 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& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array, const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license); const video_widevine::License& license);
@@ -118,7 +118,7 @@ class CdmLicense {
// the crypto session. In addition, it also extracts content keys from // the crypto session. In addition, it also extracts content keys from
// |wrapped_keys_| and loads them for use. // |wrapped_keys_| and loads them for use.
CdmResponseType HandleEntitlementKeyResponse( 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& signature, const std::string& mac_key_iv,
const std::string& mac_key, const std::vector<CryptoKey>& key_array, const std::string& mac_key, const std::vector<CryptoKey>& key_array,
const video_widevine::License& license); const video_widevine::License& license);

View File

@@ -56,10 +56,13 @@ class PolicyEngine {
// SetLicense is used in handling the initial license response. It stores // SetLicense is used in handling the initial license response. It stores
// an exact copy of the policy information stored in the license. // an exact copy of the policy information stored in the license.
// The license state transitions to kLicenseStateCanPlay if the license // License state transitions and notifications may occur if
// permits playback. // |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, 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. // Used to update the currently loaded entitled content keys.
virtual void SetEntitledLicenseKeys( virtual void SetEntitledLicenseKeys(
@@ -78,9 +81,17 @@ class PolicyEngine {
// UpdateLicense is used in handling a license response for a renewal request. // 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 // 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 // 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 // updated license_start_time from the server.
// kLicenseStateCanPlay if the license permits playback. // License state transitions and notifications may occur if
virtual void UpdateLicense(const video_widevine::License& license); // |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 // Used for notifying the Policy Engine of resolution changes
virtual void NotifyResolution(uint32_t width, uint32_t height); virtual void NotifyResolution(uint32_t width, uint32_t height);
@@ -101,6 +112,9 @@ class PolicyEngine {
int64_t GetPlaybackStartTime(); int64_t GetPlaybackStartTime();
int64_t GetLastPlaybackTime(); int64_t GetLastPlaybackTime();
int64_t GetGracePeriodEndTime(); int64_t GetGracePeriodEndTime();
// This method will also update license state and sends appropriate
// notifications to event listeners.
void RestorePlaybackTimes(int64_t playback_start_time, void RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time, int64_t last_playback_time,
int64_t grace_period_end_time); int64_t grace_period_end_time);
@@ -153,6 +167,7 @@ class PolicyEngine {
void SetSecurityLevelForTest(CdmSecurityLevel security_level); void SetSecurityLevelForTest(CdmSecurityLevel security_level);
LicenseState license_state_; LicenseState license_state_;
int64_t license_state_update_deadline_;
// This is the license id field from server response. This data gets passed // 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 // back to the server in each renewal request. When we get a renewal response

View File

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

View File

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

View File

@@ -21,6 +21,7 @@ namespace {
const int kCdmPolicyTimerDurationSeconds = 1; const int kCdmPolicyTimerDurationSeconds = 1;
const int kClockSkewDelta = 5; // seconds const int kClockSkewDelta = 5; // seconds
const int64_t kLicenseStateUpdateDelay = 20; // seconds
} // namespace } // namespace
@@ -30,6 +31,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
WvCdmEventListener* event_listener, WvCdmEventListener* event_listener,
CryptoSession* crypto_session) CryptoSession* crypto_session)
: license_state_(kLicenseStateInitial), : license_state_(kLicenseStateInitial),
license_state_update_deadline_(0),
last_recorded_current_time_(0), last_recorded_current_time_(0),
session_id_(session_id), session_id_(session_id),
event_listener_(event_listener), event_listener_(event_listener),
@@ -90,6 +92,17 @@ void PolicyEngine::OnTimerEvent() {
last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds; last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds;
const int64_t current_time = GetCurrentTime(); 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 we have passed the grace period, the expiration will update.
if (policy_timers_->HasPassedGracePeriod(current_time)) { if (policy_timers_->HasPassedGracePeriod(current_time)) {
NotifyExpirationUpdate(current_time); NotifyExpirationUpdate(current_time);
@@ -159,12 +172,13 @@ void PolicyEngine::OnTimerEvent() {
} }
void PolicyEngine::SetLicense(const License& license, 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()); if (supports_core_messages) policy_timers_.reset(new PolicyTimersV16());
license_id_.CopyFrom(license.id()); license_id_.CopyFrom(license.id());
license_keys_->SetFromLicense(license); license_keys_->SetFromLicense(license);
policy_timers_->SetLicense(license); policy_timers_->SetLicense(license);
UpdateLicense(license); UpdateLicense(license, defer_license_state_update);
} }
void PolicyEngine::SetEntitledLicenseKeys( void PolicyEngine::SetEntitledLicenseKeys(
@@ -180,10 +194,11 @@ void PolicyEngine::SetLicenseForRelease(const License& license,
// Expire any old keys. // Expire any old keys.
NotifyKeysChange(kKeyStatusExpired); NotifyKeysChange(kKeyStatusExpired);
policy_timers_->SetLicense(license); 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 (!license.has_policy()) return;
if (kLicenseStateExpired == license_state_) { if (kLicenseStateExpired == license_state_) {
@@ -204,8 +219,15 @@ void PolicyEngine::UpdateLicense(const License& license) {
const int64_t current_time = GetCurrentTime(); const int64_t current_time = GetCurrentTime();
policy_timers_->UpdateLicense(current_time, license); 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 // Update time information
license_state_update_deadline_ = 0;
if (!policy_timers_->get_policy().can_play() || if (!policy_timers_->get_policy().can_play() ||
policy_timers_->HasLicenseOrRentalOrPlaybackDurationExpired( policy_timers_->HasLicenseOrRentalOrPlaybackDurationExpired(
current_time)) { current_time)) {
@@ -349,7 +371,7 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
last_playback_time, last_playback_time,
grace_period_end_time); grace_period_end_time);
NotifyExpirationUpdate(current_time); UpdateLicenseState(current_time);
} }
void PolicyEngine::UpdateRenewalRequest(int64_t 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), .WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(GET_HDCP_CAPABILITY_FAILED))); Return(GET_HDCP_CAPABILITY_FAILED)));
policy_engine_->SetLicense(license_, false); policy_engine_->SetLicense(license_, false, false);
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1)); EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -270,7 +270,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
.WillRepeatedly( .WillRepeatedly(
DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), Return(NO_ERROR))); 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_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1)); EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -334,7 +334,7 @@ TEST_F(PolicyEngineConstraintsTest,
} }
policy_engine_->NotifyResolution(1, kTargetRes1); 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(); policy_engine_->OnTimerEvent();
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
@@ -367,7 +367,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_V2), Return(NO_ERROR))); .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_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1)); EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
@@ -409,7 +409,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)) EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NONE), Return(NO_ERROR))); .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_->NotifyResolution(1, kTargetRes1);
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
@@ -453,7 +453,7 @@ TEST_F(PolicyEngineConstraintsTest, UsesDefaultHdcpWhenResolutionNotSet) {
.WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), .WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT),
Return(GET_HDCP_CAPABILITY_FAILED))); Return(GET_HDCP_CAPABILITY_FAILED)));
policy_engine_->SetLicense(license_, false); policy_engine_->SetLicense(license_, false, false);
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1)); EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId1));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId2));

File diff suppressed because it is too large Load Diff