From 669fc9c7afee8234d20aa9be5b5054737fd7f051 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 8 May 2023 16:41:13 -0700 Subject: [PATCH 1/2] Fixes for renew on license load [ Merge of http://go/wvgerrit/173290 ] * Renew timer offset from when license is loaded verifies that the rental duration has not expired and begins decryption. * Renew timer offset from first decrypt bugfix * Feature is enabled based on oemcrypto v18 presence * Renewal logic verifies that |can_renew| is enabled * Unit tests were added to reflect use cases from duration and renewal documentation Bug: 278751387 Test: policy unittests, CdmUseCase tests, wvts tests Change-Id: I3070b3f31b316e150c28ebe38d0440ab1eeb89b9 --- .../cdm/core/include/policy_timers.h | 3 +- .../cdm/core/include/policy_timers_v18.h | 1 + libwvdrmengine/cdm/core/src/policy_engine.cpp | 27 +- .../cdm/core/src/policy_timers_v18.cpp | 17 +- .../cdm/core/test/policy_engine_unittest.cpp | 2401 ++++++++++++++++- 5 files changed, 2436 insertions(+), 13 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/policy_timers.h b/libwvdrmengine/cdm/core/include/policy_timers.h index 564e3b38..b5bc0f83 100644 --- a/libwvdrmengine/cdm/core/include/policy_timers.h +++ b/libwvdrmengine/cdm/core/include/policy_timers.h @@ -119,7 +119,7 @@ class PolicyTimers { // started playing. This takes into account GetHardLicenseExpiryTime. virtual int64_t GetExpiryTime(int64_t current_time, bool ignore_soft_enforce_playback_duration); - + virtual int64_t GetRentalExpiryTime(int64_t current_time); virtual int64_t GetRenewalStartTime() { return renewal_start_time_; } // This is the current policy information for this license. This gets updated @@ -139,7 +139,6 @@ class PolicyTimers { private: // Gets the clock time that the rental duration or playback will expire. - virtual int64_t GetRentalExpiryTime(int64_t current_time); virtual int64_t GetPlaybackExpiryTime( int64_t current_time, bool ignore_soft_enforce_playback_duration); bool HasRentalOrPlaybackDurationExpired(int64_t current_time); diff --git a/libwvdrmengine/cdm/core/include/policy_timers_v18.h b/libwvdrmengine/cdm/core/include/policy_timers_v18.h index 5eead2ac..e8048522 100644 --- a/libwvdrmengine/cdm/core/include/policy_timers_v18.h +++ b/libwvdrmengine/cdm/core/include/policy_timers_v18.h @@ -47,6 +47,7 @@ class PolicyTimersV18 : public PolicyTimers { // Indicates whether this is an initial license or a renewal bool license_renewal_ = false; bool renew_on_first_decrypt_ = false; + bool can_renew_ = false; CORE_DISALLOW_COPY_AND_ASSIGN(PolicyTimersV18); }; diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index bfde62a8..6ee78c58 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -9,6 +9,7 @@ #include "clock.h" #include "log.h" #include "policy_timers_v16.h" +#include "policy_timers_v18.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -21,7 +22,7 @@ namespace { const int kCdmPolicyTimerDurationSeconds = 1; const int kClockSkewDelta = 5; // seconds const int64_t kLicenseStateUpdateDelay = 20; // seconds - +const uint32_t kMinOemCryptoApiVersionSupportsRenewalDelayBase = 18; } // namespace namespace wvcdm { @@ -32,8 +33,17 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, : session_id_(session_id), event_listener_(event_listener), license_keys_(new LicenseKeys(crypto_session->GetSecurityLevel())), - policy_timers_(new PolicyTimersV16()), clock_(new wvutil::Clock()) { + uint32_t version = 0; + if (crypto_session->GetApiVersion(&version)) { + if (version >= kMinOemCryptoApiVersionSupportsRenewalDelayBase) { + policy_timers_.reset(new PolicyTimersV18()); + } + } + + if (policy_timers_ == nullptr) { + policy_timers_.reset(new PolicyTimersV16()); + } InitDevice(crypto_session); } @@ -121,7 +131,8 @@ void PolicyEngine::OnTimerEvent() { // Test to determine if renewal should be attempted. switch (license_state_) { case kLicenseStateCanPlay: { - if (policy_timers_->HasRenewalDelayExpired(current_time)) { + if (policy_timers_->HasRenewalDelayExpired(current_time) && + policy_timers_->get_policy().can_renew()) { renewal_needed = true; } // HDCP may change, so force a check. @@ -130,12 +141,15 @@ void PolicyEngine::OnTimerEvent() { } case kLicenseStateNeedRenewal: { - renewal_needed = true; + if (policy_timers_->get_policy().can_renew()) { + renewal_needed = true; + } break; } case kLicenseStateWaitingLicenseUpdate: { - if (policy_timers_->HasRenewalRetryIntervalExpired(current_time)) { + if (policy_timers_->HasRenewalRetryIntervalExpired(current_time) && + policy_timers_->get_policy().can_renew()) { renewal_needed = true; } break; @@ -249,7 +263,8 @@ bool PolicyEngine::BeginDecryption() { case kLicenseStateWaitingLicenseUpdate: policy_timers_->BeginDecryption(current_time); - if (policy_timers_->get_policy().renew_with_usage()) { + if (policy_timers_->get_policy().renew_with_usage() && + policy_timers_->get_policy().can_renew()) { license_state_ = kLicenseStateNeedRenewal; } NotifyExpirationUpdate(current_time); diff --git a/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp b/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp index 79b73c27..e112faf2 100644 --- a/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp +++ b/libwvdrmengine/cdm/core/src/policy_timers_v18.cpp @@ -18,6 +18,7 @@ bool PolicyTimersV18::UpdateLicense(int64_t current_time, if (!license.has_policy()) return false; policy_.MergeFrom(license.policy()); + can_renew_ = policy_.can_renew(); // some basic license validation // license start time needs to be specified in the initial response @@ -36,10 +37,18 @@ bool PolicyTimersV18::UpdateLicense(int64_t current_time, case video_widevine::License_Policy_TimerDelayBase_LICENSE_START: renewal_start_time_ = license.license_start_time(); break; - case video_widevine::License_Policy_TimerDelayBase_LICENSE_LOAD: - renewal_start_time_ = current_time; - break; + case video_widevine::License_Policy_TimerDelayBase_LICENSE_LOAD: { + const int64_t rental_expiry_time = GetRentalExpiryTime(current_time); + if ((rental_expiry_time == NEVER_EXPIRES) || + (rental_expiry_time >= current_time)) { + renewal_start_time_ = current_time; + BeginDecryption(current_time); + } else { + can_renew_ = false; + } + } break; case video_widevine::License_Policy_TimerDelayBase_FIRST_DECRYPT: + renew_on_first_decrypt_ = true; break; default: LOGE("Unrecognized initial_renewal_delay_base value = %d", @@ -79,7 +88,7 @@ void PolicyTimersV18::BeginDecryption(int64_t current_time) { bool PolicyTimersV18::HasRenewalDelayExpired(int64_t current_time) { if (renew_on_first_decrypt_ && playback_start_time_ == 0) return false; - return policy_.can_renew() && + return can_renew_ && (policy_.renewal_delay_seconds() > 0) && (renewal_start_time_ + policy_.renewal_delay_seconds() <= current_time); } diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 2f9b17f8..5ec542cf 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -36,6 +36,31 @@ const int64_t kLicenseRenewalRecoveryDuration = 30; // 30 seconds const int64_t kLowDuration = 300; // 5 minutes const int64_t kHighDuration = std::max(std::max(kRentalDuration, kPlaybackDuration), kLicenseDuration); +const int64_t kOneDay = 24 * 60 * 60; +const int64_t kTwoDays = 2 * 24 * 60 * 60; +const int64_t kSevenDays = 7 * 24 * 60 * 60; +const int64_t kFiveSeconds = 5; +const int64_t kTenSeconds = 10; +const int64_t kThirtySeconds = 30; +const int64_t kFortySeconds = 30; +const int64_t kFiveMinutes = 5 * 60; +const int64_t kFifteenMinutes = 15 * 60; +const int64_t kTwoHours = 2 * 60 * 60; +const int64_t kThreeHours = 3 * 60 * 60; +const int64_t kDurationUnlimited = 0; +const video_widevine::License::Policy::TimerDelayBase + kTimerDelayBaseUnspecified = video_widevine:: + License_Policy_TimerDelayBase_TIMER_DELAY_BASE_UNSPECIFIED; +const video_widevine::License::Policy::TimerDelayBase + kTimerDelayBaseLicenseStart = + video_widevine::License_Policy_TimerDelayBase_LICENSE_START; +const video_widevine::License::Policy::TimerDelayBase + kTimerDelayBaseLicenseLoad = + video_widevine::License_Policy_TimerDelayBase_LICENSE_LOAD; +const video_widevine::License::Policy::TimerDelayBase + kTimerDelayBaseLicenseFirstDecrypt = + video_widevine::License_Policy_TimerDelayBase_FIRST_DECRYPT; + const char* kRenewalServerUrl = "https://test.google.com/license/GetCencLicense"; const KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee"; @@ -45,6 +70,9 @@ const KeyId kSomeRandomKeyId = "some_random_key_id"; const KeyId kUnknownKeyId = "some_random_unknown_key_id"; const CdmSessionId kSessionId = "mock_session_id"; +const uint32_t kOemCryptoV16 = 16; +const uint32_t kOemCryptoV18 = 18; + int64_t GetLicenseRenewalDelay(int64_t license_duration) { return license_duration > kLicenseRenewalPeriod ? license_duration - kLicenseRenewalPeriod @@ -156,6 +184,12 @@ class PolicyEngineTest : public WvCdmTestBase { policy_engine_->set_clock(mock_clock_); } + void ResetPolicyEngine(MockClock* mock_clock) { + policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_, + crypto_session_.get())); + policy_engine_->set_clock(mock_clock); + } + void ExpectSessionKeysChange(CdmKeyStatus expected_key_status, bool expected_has_new_usable_key) { EXPECT_CALL( @@ -165,6 +199,17 @@ class PolicyEngineTest : public WvCdmTestBase { expected_has_new_usable_key)); } + void ExpectSessionKeysChange(CdmKeyStatus expected_key_status, + bool expected_has_new_usable_key, + int repetitions) { + EXPECT_CALL( + mock_event_listener_, + OnSessionKeysChange( + kSessionId, UnorderedElementsAre(Pair(kKeyId, expected_key_status)), + expected_has_new_usable_key)) + .Times(repetitions); + } + void ExpectSessionKeysChange(CdmKeyStatus expected_key_status, bool expected_has_new_usable_key, KeyId expected_keyid) { @@ -205,7 +250,23 @@ class PolicyEngineTestV16 : public PolicyEngineTest { PolicyEngineTestV16() {} protected: - void SetUp() override { PolicyEngineTest::SetUp(); } + void SetUp() override { + EXPECT_CALL(*crypto_session_, GetApiVersion(_)) + .WillOnce(DoAll(SetArgPointee<0>(kOemCryptoV16), Return(true))); + PolicyEngineTest::SetUp(); + } +}; + +class PolicyEngineTestV18 : public PolicyEngineTest { + public: + PolicyEngineTestV18() {} + + protected: + void SetUp() override { + EXPECT_CALL(*crypto_session_, GetApiVersion(_)) + .WillRepeatedly(DoAll(SetArgPointee<0>(kOemCryptoV18), Return(true))); + PolicyEngineTest::SetUp(); + } }; TEST_F(PolicyEngineTest, NoLicense) { @@ -2767,4 +2828,2342 @@ TEST_F(PolicyEngineTestV16, PlaybackOk_RestoreWithoutPlaybackTimes) { EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); } +// These tests exercise license policy when OEMCrypto supports v18. +// The following scenarios are from the duration-and-renewal doc. +// Verifies correct reporting of events, OnSessionRenewalNeeded, +// OnExpirationUpdate, OnSessionKeysChange. Actual license expiration +// is managed/tested by OEMCrypto. + +// Scenario: Streaming +// Case: 1, 2, 3 and 4 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. Expiration is unaffected when playback begins +// 3. After rental window +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, Streaming_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kThreeHours; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Streaming +// Case: 5 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window expires +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, Streaming_5) { + const int64_t rental_duration = kThreeHours; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Streaming Quick Start +// Case: 1, 2 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is correctly set based on playback duration +// 3. After playback window +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, StreamingQuickStart_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kThirtySeconds; + const int64_t playback_duration = kThreeHours; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Streaming Quick Start +// Case: 3 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is correctly set based on playback duration +// 3. After license is restored +// a. Expiration is correctly set based on playback duration +// b. Usable keys are returned +// 4. After license is restored +// a. Expiration is correctly set based on playback duration +// b. Usable keys are returned +// 5. After playback window is passed +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, StreamingQuickStart_3) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kThirtySeconds; + const int64_t playback_duration = kThreeHours; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 10)); + + uint32_t license_last_decrypt_time = license_decrypt_start_time + 10; + + ExpectSessionKeysChange(kKeyStatusUsable, true, 3); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)) + .Times(3); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + MockClock* mock_clock1(new MockClock()); + EXPECT_CALL(*mock_clock1, GetCurrentTime()) + .WillOnce(Return(license_decrypt_start_time + 600)) + .WillOnce(Return(license_decrypt_start_time + 610)) + .WillOnce(Return(license_decrypt_start_time + 620)); + ResetPolicyEngine(mock_clock1); + + policy_engine_->SetLicense(license_, true); + policy_engine_->RestorePlaybackTimes(license_decrypt_start_time, + license_last_decrypt_time, 0); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + license_last_decrypt_time = license_decrypt_start_time + 620; + + MockClock* mock_clock2(new MockClock()); + EXPECT_CALL(*mock_clock2, GetCurrentTime()) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 30)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 20)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + ResetPolicyEngine(mock_clock2); + + policy_engine_->SetLicense(license_, true); + policy_engine_->RestorePlaybackTimes(license_decrypt_start_time, + license_last_decrypt_time, 0); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Streaming Quick Start +// Case: 4 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window expires +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, StreamingQuickStart_4) { + const int64_t rental_duration = kThirtySeconds; + const int64_t playback_duration = kThreeHours; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Hard Rental Expiry (Seven Hard Two Hard) +// Case: 1, 2, 3, 4 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is correctly set based on playback duration +// 3. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenHardTwoHard_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Hard Rental Expiry (Seven Hard Two Hard) +// Case: 5, 6, 7, 8 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins close to rental window expiration +// a. Expiration is unchanged +// 3. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired +// 4. Before playback window is past +// a. Keys remain expired + +TEST_F(PolicyEngineTestV18, SevenHardTwoHard_5) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = + kLicenseStartTime + rental_duration - playback_duration + kOneDay; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Hard Rental Expiry (Seven Hard Two Hard) +// Case: 9 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenHardTwoHard_9) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Playback Expiry (Seven Hard Two Soft) +// Case: 1, 2, 3 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is unchanged +// 3. After playback window is past +// a. Playback is allowed +// 4. After rental window is past +// a. Keys are expired correctly based on rental duration +// a. Playback is disallowed + +TEST_F(PolicyEngineTestV18, SevenHardTwoSoft_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Playback Expiry (Seven Hard Two Soft) +// Case: 5, 6, 7, 8 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins close to rental window expiration +// a. Expiration is unchanged +// 3. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired +// 4. Before and after playback window is past +// a. Keys remain expired + +TEST_F(PolicyEngineTestV18, SevenHardTwoSoft_5) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = + kLicenseStartTime + rental_duration - playback_duration + kOneDay; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Playback Expiry (Seven Hard Two Soft) +// Case: 9 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenHardTwoSoft_9) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Rental Expiry (Seven Soft Two Hard) +// Case: 1, 2, 3, 4 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is correctly set based on playback duration +// 3. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenSoftTwoHard_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Rental Expiry (Seven Soft Two Hard) +// Case: 5, 6, 7, 8 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins close to rental window expiration +// a. Expiration is correctly set based on playback duration +// 3. After rental window is past +// a. Expiration is unaffected +// b. Keys are not reported as expired +// 3. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired +// 4. Before playback window is past +// a. Keys remain expired + +TEST_F(PolicyEngineTestV18, SevenSoftTwoHard_5) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = + kLicenseStartTime + rental_duration - playback_duration + kOneDay; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Rental Expiry (Seven Soft Two Hard) +// Case: 9 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenSoftTwoHard_9) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Expiry (Seven Soft Two Soft) +// Case: 1, 2, 3 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is set to unlimited +// 3. After playback window is past +// a. Playback is allowed +// 4. After rental window is past +// a. Playback is allowed + +TEST_F(PolicyEngineTestV18, SevenSoftTwoSoft_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Expiry (Seven Soft Two Soft) +// Case: 5, 6, 7, 8 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. When playback begins close to rental window expiration +// a. Expiration is correctly set to unlimited +// 3. After rental window is past +// a. Expiration is unaffected +// b. Keys are not reported as expired +// 3. After playback window is past +// a. Expiration is unaffected +// b. Keys are not reported as expired + +TEST_F(PolicyEngineTestV18, SevenSoftTwoSoft_5) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = + kLicenseStartTime + rental_duration - playback_duration + kOneDay; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration - 10)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: Seven Day/Two Day Soft Expiry (Seven Soft Two Soft) +// Case: 9 +// +// Verifies +// 1. When license is loaded +// a. Expiration is correctly set based on rental duration +// b. Usable keys are returned +// 2. After rental window is past +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, SevenSoftTwoSoft_9) { + const int64_t rental_duration = kSevenDays; + const int64_t playback_duration = kTwoDays; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(false); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(true); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + policy_engine_->SetLicense(license_, false); + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +// Scenario: License with Renewal +// Case: 1, 2, 5, 6 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to unlimited as playback has not begun +// b. Usable keys are returned +// 2. When playback begins Expiration is correctly set based on +// playback duration +// 3. When renewal_delay is exceeded a session renewal event is returned +// 4. When license is renewed, durations have been adjusted but windows +// have not been changed so no event is sent +// 5. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, LicenseWithRenewal_1) { + const int64_t playback_duration = kTwoDays; + const int64_t renewal_delay = kFiveMinutes; + const int64_t renewal_recovery_duration = kTenSeconds; + const int64_t license_decrypt_start_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseStart); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 20)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->BeginDecryption(); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_, false); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + +// Scenario: License with Renewal +// Case: 3, 4 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to unlimited as playback has not begun +// b. Usable keys are returned +// 2. When playback begins Expiration is correctly set based on +// playback duration +// 3. When renewal_delay is exceeded a session renewal event is returned +// 4. When license is renewed, durations have been adjusted but windows +// have not been changed so no event is sent. Keys are still available + +TEST_F(PolicyEngineTestV18, LicenseWithRenewal_3) { + const int64_t playback_duration = kTwoDays; + const int64_t renewal_delay = kFiveMinutes; + const int64_t renewal_recovery_duration = kTenSeconds; + const int64_t license_decrypt_start_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseStart); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + renewal_delay - 5)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 5)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + + renewal_recovery_duration + 5)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + + renewal_recovery_duration + 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + + renewal_recovery_duration + 20)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->BeginDecryption(); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(5); + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_, false); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + +// Scenario: Limited duration license (playback starts in rental duration) +// Case: 1, 4, 5 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When playback begins +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned +// 3. When renewal_delay is exceeded a session renewal event is returned +// 4. When license is renewed, durations have been adjusted but windows +// have not been changed so no event is sent +// 5. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, LimitedDurationLicense_1) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 20; + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseStart); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 2)) + .WillOnce(Return(license_decrypt_start_time + 10)) + .WillOnce(Return(license_decrypt_start_time + 20)) + .WillOnce(Return(license_decrypt_start_time + 30)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + EXPECT_CALL(check_, Call(8)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(9)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->BeginDecryption(); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(9); +} + +// Scenario: Limited duration license (playback starts after rental duration) +// Case: 2 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When playback begins after rental duration expires +// a. License is correctly expired based on rental duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, LimitedDurationLicense_2) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseStart); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(kLicenseStartTime + rental_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(check_, Call(2)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(3)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); +} + +// Scenario: Limited duration license (playback starts after renewal) +// Case: 6 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal is received durations remain the same and no events are +// returned +// 3. When playback begins +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned + +TEST_F(PolicyEngineTestV18, LimitedDurationLicense_6) { + const int64_t license_load_time = kLicenseStartTime + 10; + const int64_t license_decrypt_start_time = kLicenseStartTime + 60; + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseStart); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(_, _)) + .WillRepeatedly(DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType( + CdmResponseType(GET_HDCP_CAPABILITY_FAILED))))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(3); + + policy_engine_->BeginDecryption(); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(5); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 1S +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. When encrypted playback begins +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_1S) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = license_load_time + 20; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 20)) + .WillOnce(Return(license_load_time + playback_duration - 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->BeginDecryption(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 1M +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. When encrypted playback begins after rental duration +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_1M) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = license_load_time + 35; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 5)) + .WillOnce(Return(license_load_time + playback_duration - 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->BeginDecryption(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 1L +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. When encrypted playback begins after rental duration +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_1L) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = + license_load_time + renewal_delay + renewal_recovery_duration + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_load_time + renewal_delay + + renewal_recovery_duration - 5)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 5)) + .WillOnce(Return(license_load_time + playback_duration - 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(5); + + policy_engine_->BeginDecryption(); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + +// Scenario: Limited duration license with clear lead +// Case: 2 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. When encrypted playback begins after rental duration +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_2) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration + 30; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + rental_duration)); + EXPECT_CALL(check_, Call(3)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); +} + +// Scenario: Limited duration license with clear lead +// Case: 3S +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. Encrypted playback begins just inside the rental window +// a. The expiration time is unaffected +// 4. No renewal license is loaded + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_3S) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = license_load_time + 20; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 20)) + .WillOnce(Return(license_decrypt_start_time + 30)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->BeginDecryption(); + check_.Call(5); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 3M +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. No renewal license is loaded +// 3. Encrypted playback begins just outside the rental window +// a. The expiration time is unaffected +// 4. No renewal license is loaded + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_3M) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = license_load_time + 35; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 5)) + .WillOnce(Return(license_decrypt_start_time + 15)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->BeginDecryption(); + + check_.Call(5); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 3L +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. No renewal license is loaded +// 4. Encrypted playback begins outsdide the rental duration + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_3L) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = + license_load_time + renewal_delay + renewal_recovery_duration + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + renewal_delay + 10)) + .WillOnce(Return(license_load_time + renewal_delay + + renewal_recovery_duration - 5)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 5)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(5); + + policy_engine_->BeginDecryption(); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + check_.Call(7); +} + +// Scenario: Limited duration license with clear lead +// Case: 4, 5 +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to rental duration as playback has not begun +// b. Usable keys are returned +// 2. When renewal_delay is exceeded a session renewal event is returned +// 3. When encrypted playback begins after rental duration +// a. Expiration is correctly set based on playback duration +// b. A session renewal event is returned +// 4. When playback duration is exceeded +// a. Keys are expired + +TEST_F(PolicyEngineTestV18, LimitedDurationLicenseWithClearLead_4) { + const int64_t rental_duration = kFifteenMinutes; + const int64_t playback_duration = kTwoHours; + const int64_t renewal_delay = kFiveSeconds; + const int64_t renewal_recovery_duration = kFortySeconds; + const int64_t license_load_time = kLicenseStartTime + rental_duration - 30; + const int64_t license_decrypt_start_time = license_load_time + 30; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_renew_with_usage(true); + policy->set_rental_duration_seconds(rental_duration); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(true); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseLoad); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(license_load_time)) + .WillOnce(Return(license_load_time + 3)) + .WillOnce(Return(license_load_time + 10)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(license_decrypt_start_time + 10)) + .WillOnce(Return(license_load_time + playback_duration - 10)) + .WillOnce(Return(license_load_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, license_load_time + playback_duration)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(check_, Call(7)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->BeginDecryption(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy->set_can_renew(false); + policy->set_renewal_delay_seconds(kTwoHours); + policy_engine_->UpdateLicense(license_, false); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + +// Scenario: License with Renewal +// Case: Timer Base unspecified. +// +// This should be the same bevhavior as license start +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to unlimited as playback has not begun +// b. Usable keys are returned +// 2. When playback begins Expiration is correctly set based on +// playback duration +// 3. When renewal_delay is exceeded a session renewal event is returned +// 4. When license is renewed, durations have been adjusted but windows +// have not been changed so no event is sent +// 5. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, LicensWithRenewal_TimerBaseUnspecified) { + const int64_t playback_duration = kTwoDays; + const int64_t renewal_delay = kFiveMinutes; + const int64_t renewal_recovery_duration = kTenSeconds; + const int64_t license_decrypt_start_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseUnspecified); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 20)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->BeginDecryption(); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_, false); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + +// Scenario: License with Renewal +// Case: Timer Base First Decrypt +// +// Verifies +// 1. When license is loaded +// a. Expiration is is set to unlimited as playback has not begun +// b. Usable keys are returned +// 2. When playback begins Expiration is correctly set based on +// playback duration +// 3. When renewal_delay is exceeded a session renewal event is returned +// 4. When license is renewed, durations have been adjusted but windows +// have not been changed so no event is sent +// 5. After playback window is past +// a. License is correctly expired based on playback duration +// b. Keys are reported as expired + +TEST_F(PolicyEngineTestV18, LicensWithRenewal_TimerBaseFirstDecrypt) { + const int64_t playback_duration = kTwoDays; + const int64_t renewal_delay = kFiveMinutes; + const int64_t renewal_recovery_duration = kTenSeconds; + const int64_t license_decrypt_start_time = kLicenseStartTime + 10; + + License_Policy* policy = license_.mutable_policy(); + policy->set_can_renew(true); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(playback_duration); + policy->set_renewal_delay_seconds(renewal_delay); + policy->set_renewal_recovery_duration_seconds(renewal_recovery_duration); + policy->set_soft_enforce_rental_duration(false); + policy->set_soft_enforce_playback_duration(false); + policy->set_initial_renewal_delay_base(kTimerDelayBaseLicenseFirstDecrypt); + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime)) + .WillOnce(Return(license_decrypt_start_time)) + .WillOnce(Return(kLicenseStartTime + renewal_delay - 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 10)) + .WillOnce(Return(kLicenseStartTime + renewal_delay + 20)) + .WillOnce(Return(license_decrypt_start_time + playback_duration - 10)) + .WillOnce(Return(license_decrypt_start_time + playback_duration + 10)); + + EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(HDCP_NO_DIGITAL_OUTPUT), + Return(CdmResponseType(GET_HDCP_CAPABILITY_FAILED)))); + InSequence s; + EXPECT_CALL(check_, Call(1)); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + EXPECT_CALL(check_, Call(2)); + EXPECT_CALL( + mock_event_listener_, + OnExpirationUpdate(_, license_decrypt_start_time + playback_duration)); + EXPECT_CALL(check_, Call(3)); + EXPECT_CALL(check_, Call(4)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(5)); + EXPECT_CALL(check_, Call(6)); + EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_)); + EXPECT_CALL(check_, Call(7)); + ExpectSessionKeysChange(kKeyStatusExpired, false); + EXPECT_CALL(check_, Call(8)); + + check_.Call(1); + policy_engine_->SetLicense(license_, false); + check_.Call(2); + policy_engine_->BeginDecryption(); + check_.Call(3); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(4); + + policy_engine_->OnTimerEvent(); + check_.Call(5); + + LicenseIdentification* id = license_.mutable_id(); + id->set_version(2); + policy_engine_->UpdateLicense(license_, false); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(6); + + policy_engine_->OnTimerEvent(); + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(7); + + policy_engine_->OnTimerEvent(); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + check_.Call(8); +} + } // namespace wvcdm From 0c288c1cda04b161102bfa46b1154cc1cbf36f4a Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 8 May 2023 17:38:01 -0700 Subject: [PATCH 2/2] Indicate support for initial_renewal_delay_base [ Merge of http://go/wvgerrit/174555 ] This is only announced if OEMCrypto is v18+ Bug: 278751387 Test: Duration use case tests, wvts tests Change-Id: I5cbfcc733ed2af2c940fde381b40a5be850e7e88 --- libwvdrmengine/cdm/core/include/wv_cdm_constants.h | 2 ++ libwvdrmengine/cdm/core/src/client_identification.cpp | 5 +++++ libwvdrmengine/cdm/core/src/license_protocol.proto | 3 +++ 3 files changed, 10 insertions(+) diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 800c55b1..3c49ff5d 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -52,6 +52,8 @@ constexpr uint32_t RESOURCE_RATING_TIER_MAX = RESOURCE_RATING_TIER_VERY_HIGH; // OEMCrypto features by version constexpr uint32_t OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER = 15; constexpr uint32_t OEM_CRYPTO_API_VERSION_SUPPORTS_PROV40_CORE_MESSAGE = 18; +constexpr uint32_t OEM_CRYPTO_API_VERSION_SUPPORTS_INITIAL_RENEWAL_DELAY_BASE = + 18; constexpr char SESSION_ID_PREFIX[] = "sid"; constexpr char ATSC_KEY_SET_ID_PREFIX[] = "atscksid"; diff --git a/libwvdrmengine/cdm/core/src/client_identification.cpp b/libwvdrmengine/cdm/core/src/client_identification.cpp index a9a363ee..0bc07bf4 100644 --- a/libwvdrmengine/cdm/core/src/client_identification.cpp +++ b/libwvdrmengine/cdm/core/src/client_identification.cpp @@ -364,6 +364,11 @@ CdmResponseType ClientIdentification::Prepare( } } + if (api_version >= + OEM_CRYPTO_API_VERSION_SUPPORTS_INITIAL_RENEWAL_DELAY_BASE) { + client_capabilities->set_initial_renewal_delay_base(true); + } + return CdmResponseType(NO_ERROR); } diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index 1d0ec510..25ede138 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -1149,6 +1149,9 @@ message ClientIdentification { // Support is optional. // Value is only required to be set for license requests. optional WatermarkingSupport watermarking_support = 13; + // Indicate whether or not `initial_renewal_delay_base` is supported by the + // client. + optional bool initial_renewal_delay_base = 14 [default = false]; } message ClientCredentials {