Allow license renewals after expiry
Licenses could be renewed uptil the point of expiry. After that point we expected that the session would have to be closed and a new one opened with a new license loaded. Clank requested that we support renewal of sessions past expiry. In addition, the error returned on decryption, if OEMCrypto determines that the KCB duration has expired, is NEED_KEY rather than KEY_ERROR. Merge of https://widevine-internal-review.googlesource.com/#/c/8240 from the widevine cdm repo. b/11390539 Change-Id: I023320f3f25514cd07b368701a92100429ce1c04
This commit is contained in:
@@ -53,6 +53,9 @@ class PolicyEngine {
|
|||||||
return license_id_;
|
return license_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsLicenseDurationExpired(int64_t current_time);
|
||||||
|
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kLicenseStateInitial,
|
kLicenseStateInitial,
|
||||||
@@ -65,8 +68,6 @@ class PolicyEngine {
|
|||||||
|
|
||||||
void Init(Clock* clock);
|
void Init(Clock* clock);
|
||||||
|
|
||||||
bool IsLicenseDurationExpired(int64_t current_time);
|
|
||||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
|
||||||
bool IsRenewalDelayExpired(int64_t current_time);
|
bool IsRenewalDelayExpired(int64_t current_time);
|
||||||
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
||||||
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
||||||
|
|||||||
@@ -272,7 +272,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
|||||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
return crypto_session_->Decrypt(params);
|
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||||
|
// TODO(rfrias): Remove after support for OEMCrypto_ERROR_KEY_EXPIRED is in
|
||||||
|
if (UNKNOWN_ERROR == status) {
|
||||||
|
Clock clock;
|
||||||
|
int64_t current_time = clock.GetCurrentTime();
|
||||||
|
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
|
||||||
|
policy_engine_.IsPlaybackDurationExpired(current_time)) {
|
||||||
|
return NEED_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// License renewal
|
// License renewal
|
||||||
|
|||||||
9
libwvdrmengine/cdm/core/src/crypto_session.cpp
Executable file → Normal file
9
libwvdrmengine/cdm/core/src/crypto_session.cpp
Executable file → Normal file
@@ -588,9 +588,14 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
|||||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||||
&buffer_descriptor, params.subsample_flags);
|
&buffer_descriptor, params.subsample_flags);
|
||||||
|
|
||||||
if (OEMCrypto_ERROR_INSUFFICIENT_RESOURCES == sts) {
|
switch (sts) {
|
||||||
|
case OEMCrypto_SUCCESS:
|
||||||
|
break;
|
||||||
|
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
||||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||||
} else if (OEMCrypto_SUCCESS != sts) {
|
case OEMCrypto_ERROR_KEY_EXPIRED:
|
||||||
|
return NEED_KEY;
|
||||||
|
default:
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
|
|||||||
@@ -106,21 +106,11 @@ void PolicyEngine::SetLicense(
|
|||||||
|
|
||||||
void PolicyEngine::UpdateLicense(
|
void PolicyEngine::UpdateLicense(
|
||||||
const video_widevine_server::sdk::License& license) {
|
const video_widevine_server::sdk::License& license) {
|
||||||
if (!license.has_policy() || kLicenseStateExpired == license_state_)
|
if (!license.has_policy())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
policy_.MergeFrom(license.policy());
|
policy_.MergeFrom(license.policy());
|
||||||
|
|
||||||
switch (license_state_) {
|
|
||||||
case kLicenseStateExpired:
|
|
||||||
// Ignore policy updates.
|
|
||||||
return;
|
|
||||||
|
|
||||||
case kLicenseStateInitial:
|
|
||||||
case kLicenseStateInitialPendingUsage:
|
|
||||||
case kLicenseStateCanPlay:
|
|
||||||
case kLicenseStateNeedRenewal:
|
|
||||||
case kLicenseStateWaitingLicenseUpdate:
|
|
||||||
if (!policy_.can_play()) {
|
if (!policy_.can_play()) {
|
||||||
license_state_ = kLicenseStateExpired;
|
license_state_ = kLicenseStateExpired;
|
||||||
return;
|
return;
|
||||||
@@ -193,9 +183,6 @@ void PolicyEngine::UpdateLicense(
|
|||||||
can_decrypt_ = true;
|
can_decrypt_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PolicyEngine::BeginDecryption() {
|
void PolicyEngine::BeginDecryption() {
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
|
|||||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
|
||||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
.WillOnce(Return(license_start_time_ + 1))
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
@@ -496,7 +496,83 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
|||||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) {
|
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
|
||||||
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
|
playback_duration_ + 1))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 30))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 40));
|
||||||
|
|
||||||
|
policy_engine_->SetLicense(license_);
|
||||||
|
|
||||||
|
policy_engine_->BeginDecryption();
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
bool event_occurred;
|
||||||
|
CdmEventType event;
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
license_.set_license_start_time(license_start_time_ +
|
||||||
|
license_duration_ + 20);
|
||||||
|
LicenseIdentification* id = license_.mutable_id();
|
||||||
|
id->set_version(2);
|
||||||
|
License_Policy* policy = license_.mutable_policy();
|
||||||
|
policy = license_.mutable_policy();
|
||||||
|
policy->set_playback_duration_seconds(playback_duration_ + 100);
|
||||||
|
policy->set_license_duration_seconds(license_duration_ + 100);
|
||||||
|
|
||||||
|
policy_engine_->UpdateLicense(license_);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
|
||||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
.WillOnce(Return(license_start_time_ + 1))
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
|
|||||||
Reference in New Issue
Block a user