diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 64457a65..be383e68 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1286,6 +1286,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, // else we must be level 1 direct and we don't need to return a buffer. } + AutoLock lock(session_list_lock_); CdmSessionMap::iterator session_iter = sessions_.end(); if (session_id.empty()) { // Loop through the sessions to find the session containing the key_id diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index fad8e394..c8c1b7e2 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -22,6 +22,7 @@ #include "string_conversions.h" #include "test_base.h" #include "url_request.h" +#include #include "wv_cdm_constants.h" #include "wv_content_decryption_module.h" @@ -678,6 +679,43 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase { return security_level; } + class CloseSessionThread : public android::Thread { + public: + CloseSessionThread() : + Thread(false), + wv_content_decryption_module_(NULL) {} + ~CloseSessionThread() {} + + bool Start(WvContentDecryptionModule* decryptor, + const CdmSessionId& session_id, + uint32_t time_in_msecs) { + wv_content_decryption_module_ = decryptor; + sess_id_ = session_id; + delay_.tv_sec = time_in_msecs / 1000; + delay_.tv_nsec = (time_in_msecs % 1000) * 10000000ll; + return run("CloseSessionThread") == android::NO_ERROR; + } + + void Stop() { requestExitAndWait(); } + + private: + virtual bool threadLoop() { + struct timespec delay_remaining; + int result = nanosleep(&delay_, &delay_remaining); + while (result < 0 && + (delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) { + result = nanosleep(&delay_remaining, &delay_remaining); + } + wv_content_decryption_module_->CloseSession(sess_id_); + return false; + } + + WvContentDecryptionModule* wv_content_decryption_module_; + CdmSessionId sess_id_; + struct timespec delay_; + CORE_DISALLOW_COPY_AND_ASSIGN(CloseSessionThread); + }; + WvContentDecryptionModule decryptor_; CdmKeyMessage key_msg_; CdmKeyResponse key_response_; @@ -831,6 +869,74 @@ TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) { decryptor_.CloseSession(session_id_); } +TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) { + Unprovision(); + Provision(); + + // Leave session open to avoid CDM termination + CdmSessionId session_id; + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id); + + // Retrieve offline license + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline); + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + + EXPECT_FALSE(key_set_id_.empty()); + + decryptor_.CloseSession(session_id_); + + for (uint32_t j = 0; j < 500; ++j) { + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + EXPECT_EQ(KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id_)); + + CdmResponseType status = NO_ERROR; + struct timespec decrypt_delay; + decrypt_delay.tv_sec = 0; + decrypt_delay.tv_nsec = 10000000ll; // 10 ms + + CloseSessionThread* thread = new CloseSessionThread(); + thread->Start(&decryptor_, session_id_, 500 /* 500 ms */); + thread = NULL; + + while (status == NO_ERROR) { + struct timespec delay_remaining; + int result = nanosleep(&decrypt_delay, &delay_remaining); + while (result < 0 && + (delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) { + result = nanosleep(&delay_remaining, &delay_remaining); + } + SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; + for (size_t i = 0; i < data->num_of_subsamples; i++) { + std::vector decrypt_buffer((data + i)->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters( + &(data + i)->key_id, &(data + i)->encrypt_data.front(), + (data + i)->encrypt_data.size(), &(data + i)->iv, + (data + i)->block_offset, &decrypt_buffer[0]); + decryption_parameters.is_encrypted = (data + i)->is_encrypted; + decryption_parameters.is_secure = (data + i)->is_secure; + decryption_parameters.subsample_flags = (data + i)->subsample_flags; + status = decryptor_.Decrypt(session_id_, (data + i)->validate_key_id, + decryption_parameters); + + switch (status) { + case SESSION_NOT_FOUND_FOR_DECRYPT: + break; + case NO_ERROR: + EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer); + break; + default: + EXPECT_TRUE(false); + } + } + } + } + decryptor_.CloseSession(session_id); +} + TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) { Provision(); SubSampleInfo* data = &kEncryptedStreamingClip5SubSample;