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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user