diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index 2dbeee70..650b5553 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -53,6 +53,9 @@ class PolicyEngine { return license_id_; } + bool IsLicenseDurationExpired(int64_t current_time); + bool IsPlaybackDurationExpired(int64_t current_time); + private: typedef enum { kLicenseStateInitial, @@ -65,8 +68,6 @@ class PolicyEngine { void Init(Clock* clock); - bool IsLicenseDurationExpired(int64_t current_time); - bool IsPlaybackDurationExpired(int64_t current_time); bool IsRenewalDelayExpired(int64_t current_time); bool IsRenewalRecoveryDurationExpired(int64_t current_time); bool IsRenewalRetryIntervalExpired(int64_t current_time); diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 61b254ae..645d035b 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -272,7 +272,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) 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 diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp old mode 100755 new mode 100644 index fa23002f..b14d5f0e --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -588,10 +588,15 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { params.is_encrypted, &(*params.iv).front(), params.block_offset, &buffer_descriptor, params.subsample_flags); - if (OEMCrypto_ERROR_INSUFFICIENT_RESOURCES == sts) { - return INSUFFICIENT_CRYPTO_RESOURCES; - } else if (OEMCrypto_SUCCESS != sts) { - return UNKNOWN_ERROR; + switch (sts) { + case OEMCrypto_SUCCESS: + break; + case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES: + return INSUFFICIENT_CRYPTO_RESOURCES; + case OEMCrypto_ERROR_KEY_EXPIRED: + return NEED_KEY; + default: + return UNKNOWN_ERROR; } return NO_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 5d4ef60d..8b1a9e40 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -106,95 +106,82 @@ void PolicyEngine::SetLicense( void PolicyEngine::UpdateLicense( const video_widevine_server::sdk::License& license) { - if (!license.has_policy() || kLicenseStateExpired == license_state_) + if (!license.has_policy()) return; policy_.MergeFrom(license.policy()); - switch (license_state_) { - case kLicenseStateExpired: - // Ignore policy updates. + if (!policy_.can_play()) { + license_state_ = kLicenseStateExpired; + return; + } + + // some basic license validation + if (license_state_ == kLicenseStateInitial) { + // license start time needs to be present in the initial response + if (!license.has_license_start_time()) return; + } + else { + // TODO(edwingwong, rfrias): Check back with Thomas and see if + // we need to enforce that all duration windows are absent if + // license_start_time is not present. This is a TBD. - case kLicenseStateInitial: - case kLicenseStateInitialPendingUsage: - case kLicenseStateCanPlay: - case kLicenseStateNeedRenewal: - case kLicenseStateWaitingLicenseUpdate: - if (!policy_.can_play()) { - license_state_ = kLicenseStateExpired; - return; - } + // if renewal, discard license if version has not been updated + if (license.id().version() > license_id_.version()) + license_id_.CopyFrom(license.id()); + else + return; + } - // some basic license validation - if (license_state_ == kLicenseStateInitial) { - // license start time needs to be present in the initial response - if (!license.has_license_start_time()) - return; - } - else { - // TODO(edwingwong, rfrias): Check back with Thomas and see if - // we need to enforce that all duration windows are absent if - // license_start_time is not present. This is a TBD. + // Update time information + int64_t current_time = clock_->GetCurrentTime(); + // TODO(edwingwong, rfrias): Check back with Thomas and see if + // we need to enforce that all duration windows are absent if + // license_start_time is not present. This is a TBD. + if (license.has_license_start_time()) + license_start_time_ = license.license_start_time(); + license_received_time_ = current_time; + next_renewal_time_ = current_time + + policy_.renewal_delay_seconds(); - // if renewal, discard license if version has not been updated - if (license.id().version() > license_id_.version()) - license_id_.CopyFrom(license.id()); - else - return; - } + // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ + // will be set to the minimum of the following policies : + // rental_duration_seconds and license_duration_seconds. + // The value is used to determine when the license expires. + policy_max_duration_seconds_ = 0; - // Update time information - int64_t current_time = clock_->GetCurrentTime(); - // TODO(edwingwong, rfrias): Check back with Thomas and see if - // we need to enforce that all duration windows are absent if - // license_start_time is not present. This is a TBD. - if (license.has_license_start_time()) - license_start_time_ = license.license_start_time(); - license_received_time_ = current_time; - next_renewal_time_ = current_time + - policy_.renewal_delay_seconds(); + if (policy_.has_rental_duration_seconds()) + policy_max_duration_seconds_ = policy_.rental_duration_seconds(); - // Calculate policy_max_duration_seconds_. policy_max_duration_seconds_ - // will be set to the minimum of the following policies : - // rental_duration_seconds and license_duration_seconds. - // The value is used to determine when the license expires. - policy_max_duration_seconds_ = 0; + if ((policy_.license_duration_seconds() > 0) && + ((policy_.license_duration_seconds() < + policy_max_duration_seconds_) || + policy_max_duration_seconds_ == 0)) { + policy_max_duration_seconds_ = policy_.license_duration_seconds(); + } - if (policy_.has_rental_duration_seconds()) - policy_max_duration_seconds_ = policy_.rental_duration_seconds(); + if (Properties::begin_license_usage_when_received()) + playback_start_time_ = current_time; - if ((policy_.license_duration_seconds() > 0) && - ((policy_.license_duration_seconds() < - policy_max_duration_seconds_) || - policy_max_duration_seconds_ == 0)) { - policy_max_duration_seconds_ = policy_.license_duration_seconds(); - } - - if (Properties::begin_license_usage_when_received()) - playback_start_time_ = current_time; - - // Update state - if (Properties::begin_license_usage_when_received()) { - if (policy_.renew_with_usage()) { - license_state_ = kLicenseStateNeedRenewal; - } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } - } - else { - if (license_state_ == kLicenseStateInitial) { - license_state_ = kLicenseStateInitialPendingUsage; - } - else { - license_state_ = kLicenseStateCanPlay; - can_decrypt_ = true; - } - } - - break; + // Update state + if (Properties::begin_license_usage_when_received()) { + if (policy_.renew_with_usage()) { + license_state_ = kLicenseStateNeedRenewal; + } + else { + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; + } + } + else { + if (license_state_ == kLicenseStateInitial) { + license_state_ = kLicenseStateInitialPendingUsage; + } + else { + license_state_ = kLicenseStateCanPlay; + can_decrypt_ = true; + } } } diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 7d3afc48..e76e5cd1 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -438,7 +438,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) { EXPECT_FALSE(policy_engine_->can_decrypt()); } -TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) { +TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ - @@ -496,7 +496,83 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) { 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()) .WillOnce(Return(license_start_time_ + 1)) .WillOnce(Return(license_start_time_ + license_duration_ -