diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 22c70283..ce2a2714 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -69,6 +69,8 @@ static const std::string QUERY_KEY_LICENSE_DURATION_REMAINING = "LicenseDurationRemaining"; // non-negative integer denoting seconds static const std::string QUERY_KEY_PLAYBACK_DURATION_REMAINING = "PlaybackDurationRemaining"; // non-negative integer denoting seconds +static const std::string QUERY_KEY_RENTAL_DURATION_REMAINING = + "RentalDurationRemaining"; // non-negative integer denoting seconds static const std::string QUERY_KEY_RENEWAL_SERVER_URL = "RenewalServerUrl"; // url static const std::string QUERY_KEY_OEMCRYPTO_SESSION_ID = diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 9194f5be..f85cbfe6 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -864,13 +864,13 @@ CdmResponseType CdmLicense::RestoreOfflineLicense( break; case CryptoSession::kUsageDurationsValid: { int64_t current_time = clock_->GetCurrentTime(); - if (current_time - seconds_since_started > 0) - playback_start_time = current_time - seconds_since_started; - if (current_time - last_playback_time > 0) - last_playback_time = current_time - seconds_since_last_played; + playback_start_time = current_time - seconds_since_started; + last_playback_time = current_time - seconds_since_last_played; break; } default: + // Use playback_start_time and last_playback_time from + // persistently stored license data break; } } diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index c2be3dd6..019383dc 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -307,6 +307,8 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { : QUERY_VALUE_FALSE; (*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = std::to_string( policy_timers_->GetLicenseOrRentalDurationRemaining(current_time)); + (*query_response)[QUERY_KEY_RENTAL_DURATION_REMAINING] = std::to_string( + policy_timers_->GetLicenseOrRentalDurationRemaining(current_time)); (*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = std::to_string( policy_timers_->GetPlaybackDurationRemaining(current_time)); (*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index c60349b4..b35cafda 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -5053,6 +5053,13 @@ TEST_F(WvCdmRequestLicenseTest, QueryKeyStatus) { ss >> remaining_time; ASSERT_FALSE(ss.fail()); EXPECT_LT(0, remaining_time); + itr = query_info.find(wvcdm::QUERY_KEY_RENTAL_DURATION_REMAINING); + ASSERT_TRUE(itr != query_info.end()); + ss.clear(); + ss.str(itr->second); + ss >> remaining_time; + ASSERT_FALSE(ss.fail()); + EXPECT_LT(0, remaining_time); itr = query_info.find(wvcdm::QUERY_KEY_PLAYBACK_DURATION_REMAINING); ASSERT_TRUE(itr != query_info.end()); ss.clear(); @@ -6598,6 +6605,69 @@ TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackBeforeRestoreKey) { ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); } +// The difference between this test and Offline_RollbackBeforeRestoreKey is +// that this test has a 15 second expiration window. +// Offline_RollbackBeforeRestoreKey has a 5 second window. The rollback +// before restore causes the expiration timer to run from the rollback time. +// The license in Offline_RollbackBeforeRestoreKey is expired by the time +// the rollback is undone, while this one has not yet expired. +TEST_F(WvCdmRequestLicenseRollbackTest, + Offline_LongerDurationRollbackBeforeRestoreKey) { + Unprovision(); + Provision(); + + // The default offline asset is "offline_clip2". Substitute '2' for '8'. + // "offline_clip8" has a 15 second expiration. + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); + key_id[key_id.size() - 1] = '8'; + + ASSERT_EQ(NO_ERROR, decryptor_->OpenSession(config_.key_system(), nullptr, + kDefaultCdmIdentifier, nullptr, + &session_id_)); + GenerateKeyRequest(key_id, kLicenseTypeOffline); + VerifyKeyRequestResponse(config_.license_server(), client_auth); + + // Verify that we can decrypt a subsample to begin with. + EXPECT_EQ(NO_ERROR, Decrypt(session_id_)); + + CdmKeySetId key_set_id = key_set_id_; + EXPECT_FALSE(key_set_id_.empty()); + decryptor_->CloseSession(session_id_); + + // This number must be > the time between GenerateKeyRequest and this call. + RollbackSystemTime(10 * 1000); + + session_id_.clear(); + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_); + + decryptor_->RestoreKey(session_id_, key_set_id); + + // Verify we can't decrypt. The license start time is in the future. + EXPECT_EQ(DECRYPT_NOT_READY, Decrypt(session_id_)); + + RestoreSystemTime(); + + // Sleep for a little bit to account for the execution time of OpenSession and + // RestoreKey. + std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationWindowMs_)); + + // Verify we can decrypt. + EXPECT_EQ(NO_ERROR, Decrypt(session_id_)); + + // Sleep for a while more. + std::this_thread::sleep_for(std::chrono::milliseconds(kExpirationTimeMs_)); + + // Rollback time + kExpirationWindowMs_ + kExpirationTimeMs_ (~17s) will have + // elapsed. This is greater than the expiration window for longer duration + // licenses (15s). The license should have expired. + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + + ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); +} + TEST_F(WvCdmRequestLicenseRollbackTest, Offline_RollbackAndExpireAfterRestoreKey) { Unprovision(); @@ -6636,6 +6706,8 @@ TEST_F(WvCdmRequestLicenseRollbackTest, RestoreSystemTime(); + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + ASSERT_EQ(NO_ERROR, decryptor_->CloseSession(session_id_)); } @@ -6670,9 +6742,11 @@ TEST_F(WvCdmRequestLicenseRollbackTest, std::this_thread::sleep_for( std::chrono::milliseconds(kExpirationWithWindowMs_)); + // Verify that we can no longer decrypt a subsample due to key expiration. + EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); + RollbackSystemTime(kExpirationWithWindowMs_); - // Verify that we can no longer decrypt a subsample due to key expiration. EXPECT_EQ(NEED_KEY, Decrypt(session_id_)); RestoreSystemTime();