From 7cb83eb02e92abb41349acf1e89d0d31a35f531f Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Thu, 7 Apr 2016 15:58:45 -0700 Subject: [PATCH] Load keys before sending OnKeyStatusChange notifications [ merge of http://go/wvgerrit/17454 ] When processing a license or renewal, calls to Set/UpdateLicense update the policy information. A side effect was introduced whereby updating the policy may cause (expiration, session key state) notifications to be sent to the listener. Due to the ordering, the notifications would be sent before the keys were loaded/refreshed, which caused issues when the notifications were immediately acted upon. This has now been corrected. b/27842970 Change-Id: Id81a71ff48edfa9ca0baafc43267995d5a3e80a6 --- libwvdrmengine/cdm/core/src/license.cpp | 7 +-- .../cdm/test/request_license_test.cpp | 60 ++++++++++++++++++- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 20591e5f..da15a28f 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -549,8 +549,6 @@ CdmResponseType CdmLicense::HandleKeyResponse( server_url_ = license.policy().renewal_server_url(); } - policy_engine_->SetLicense(license); - if (license.policy().has_renew_with_client_id()) { renew_with_client_id_ = license.policy().renew_with_client_id(); } @@ -565,6 +563,7 @@ CdmResponseType CdmLicense::HandleKeyResponse( it != key_array.end(); ++it) { loaded_keys_.insert(it->key_id()); } + policy_engine_->SetLicense(license); } return resp; } @@ -626,8 +625,6 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( return LICENSE_ID_NOT_FOUND; } - policy_engine_->UpdateLicense(license); - if (license.policy().has_renew_with_client_id()) { renew_with_client_id_ = license.policy().renew_with_client_id(); } @@ -651,6 +648,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse( if (session_->RefreshKeys(signed_response.msg(), signed_response.signature(), key_array.size(), &key_array[0])) { + policy_engine_->UpdateLicense(license); + return KEY_ADDED; } else { return REFRESH_KEYS_ERROR; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 68a070ab..602b6aa5 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -30,6 +30,7 @@ using ::testing::AllOf; using ::testing::AtLeast; using ::testing::Contains; using ::testing::Each; +using ::testing::Invoke; using ::testing::IsEmpty; using ::testing::Not; using ::testing::Pair; @@ -811,7 +812,6 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { class TestWvCdmEventListener : public WvCdmEventListener { public: TestWvCdmEventListener() : WvCdmEventListener() {} - MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id)); MOCK_METHOD3(OnSessionKeysChange, void(const CdmSessionId& session_id, const CdmKeyStatusMap& keys_status, @@ -820,6 +820,36 @@ class TestWvCdmEventListener : public WvCdmEventListener { int64_t new_expiry_time_seconds)); }; +class DecryptCallbackTester { + public: + DecryptCallbackTester(wvcdm::WvContentDecryptionModule* decryptor, + SubSampleInfo* sub_sample_info) + : decryptor_(decryptor), + sub_sample_info_(sub_sample_info) {} + + void Decrypt(const CdmSessionId& session_id, + const CdmKeyStatusMap& /* keys_status */, + bool /* has_new_usable_key */) { + EXPECT_TRUE(decryptor_ != NULL); + EXPECT_TRUE(sub_sample_info_ != NULL); + + std::vector decrypt_buffer(sub_sample_info_->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &sub_sample_info_->key_id, &sub_sample_info_->encrypt_data.front(), + sub_sample_info_->encrypt_data.size(), &sub_sample_info_->iv, + sub_sample_info_->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = sub_sample_info_->is_encrypted; + decryption_parameters.is_secure = sub_sample_info_->is_secure; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id, + sub_sample_info_->validate_key_id, + decryption_parameters)); + + } + private: + wvcdm::WvContentDecryptionModule* decryptor_; + SubSampleInfo* sub_sample_info_; +}; + class TestWvCdmHlsEventListener : public WvCdmEventListener { public: TestWvCdmHlsEventListener() : WvCdmEventListener() {} @@ -2644,6 +2674,7 @@ TEST_F(WvCdmRequestLicenseTest, QueryStatusL3) { EXPECT_TRUE(ss.eof()); EXPECT_LE(10u, api_version); } + TEST_F(WvCdmRequestLicenseTest, QueryKeyControlInfo) { Unprovision(); Provision(kLevelDefault); @@ -3106,6 +3137,33 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) { + StrictMock listener; + DecryptCallbackTester decrypt_callback( + &decryptor_, + &single_encrypted_sub_sample_short_expiry); + decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, &listener, + &session_id_); + EXPECT_CALL( + listener, + OnSessionKeysChange( + session_id_, + AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true)) + .WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt)); +; + EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _)); + + const std::string kCpKeyId = a2bs_hex( + "000000347073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id + "0801121030313233343536373839616263646566"); // pssh data + + GenerateKeyRequest(kCpKeyId, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + decryptor_.CloseSession(session_id_); +} + class WvCdmDecryptionTest : public WvCdmRequestLicenseTest, public ::testing::WithParamInterface {};