diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 4b7e2971..e761c9c8 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1443,35 +1443,37 @@ void CdmEngine::OnTimerEvent() { bool is_initial_usage_update = false; bool is_usage_update_needed = false; - AutoLock lock(session_list_lock_); - for (CdmSessionMap::iterator iter = sessions_.begin(); - iter != sessions_.end(); ++iter) { - is_initial_usage_update = - is_initial_usage_update || iter->second->is_initial_usage_update(); - is_usage_update_needed = - is_usage_update_needed || iter->second->is_usage_update_needed(); - - iter->second->OnTimerEvent(usage_update_period_expired); - } - - if (is_usage_update_needed && - (usage_update_period_expired || is_initial_usage_update)) { - bool has_usage_been_updated = false; + { + AutoLock lock(session_list_lock_); for (CdmSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - iter->second->reset_usage_flags(); - if (iter->second->get_usage_support_type() == kUsageEntrySupport) - iter->second->UpdateUsageEntryInformation(); + is_initial_usage_update = + is_initial_usage_update || iter->second->is_initial_usage_update(); + is_usage_update_needed = + is_usage_update_needed || iter->second->is_usage_update_needed(); - if (!has_usage_been_updated) { - // usage is updated for all sessions so this needs to be - // called only once per update usage information period - if (iter->second->get_usage_support_type() == kUsageTableSupport) { - CdmResponseType status = iter->second->UpdateUsageTableInformation(); - if (NO_ERROR != status) { - LOGW("Update usage information failed: %d", status); - } else { - has_usage_been_updated = true; + iter->second->OnTimerEvent(usage_update_period_expired); + } + + if (is_usage_update_needed && + (usage_update_period_expired || is_initial_usage_update)) { + bool has_usage_been_updated = false; + for (CdmSessionMap::iterator iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + iter->second->reset_usage_flags(); + if (iter->second->get_usage_support_type() == kUsageEntrySupport) + iter->second->UpdateUsageEntryInformation(); + + if (!has_usage_been_updated) { + // usage is updated for all sessions so this needs to be + // called only once per update usage information period + if (iter->second->get_usage_support_type() == kUsageTableSupport) { + CdmResponseType status = iter->second->UpdateUsageTableInformation(); + if (NO_ERROR != status) { + LOGW("Update usage information failed: %d", status); + } else { + has_usage_been_updated = true; + } } } } diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index c8c1b7e2..5f668317 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -988,6 +988,142 @@ TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) { } } +// This test verifies that sessions allocated internally during key release +// message generation are deallocated after their time to live period expires +// by timer events (if other sessions are open). +TEST_F(WvCdmExtendedDurationTest, AutomatedOfflineSessionReleaseOnTimerEvent) { + Unprovision(); + Provision(); + + // Leave session open to run the CDM timer + CdmSessionId streaming_session_id; + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &streaming_session_id); + + // override default settings unless configured through the command line + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); + + uint32_t initial_open_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); + + uint32_t max_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS); + + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline); + VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); + + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + CdmKeySetId key_set_id = key_set_id_; + + session_id_.clear(); + key_set_id_.clear(); + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + GenerateKeyRelease(key_set_id); + + uint32_t open_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); + + EXPECT_GT(open_sessions, initial_open_sessions); + + sleep(kMinute + kClockTolerance); + + open_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); + + EXPECT_EQ(open_sessions, initial_open_sessions); + + session_id_.clear(); + GenerateKeyRelease(key_set_id); + key_set_id_ = key_set_id; + VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); + decryptor_.CloseSession(streaming_session_id); +} + +// This test verifies that sessions allocated internally during key release +// message generation are deallocated after their time to live period expires +// when a new session is opened. +TEST_F(WvCdmExtendedDurationTest, AutomatedOfflineSessionReleaseOnOpenSession) { + Unprovision(); + Provision(); + + // override default settings unless configured through the command line + std::string key_id; + std::string client_auth; + GetOfflineConfiguration(&key_id, &client_auth); + + uint32_t initial_open_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS); + + uint32_t max_sessions = + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_MAX_NUMBER_OF_SESSIONS); + + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline); + VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); + + EXPECT_FALSE(key_set_id_.empty()); + decryptor_.CloseSession(session_id_); + CdmKeySetId key_set_id = key_set_id_; + + session_id_.clear(); + key_set_id_.clear(); + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id)); + decryptor_.CloseSession(session_id_); + + session_id_.clear(); + GenerateKeyRelease(key_set_id); + + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + + EXPECT_GT( + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS), + initial_open_sessions); + + decryptor_.CloseSession(session_id_); + + EXPECT_GT( + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS), + initial_open_sessions); + + sleep(kMinute + kClockTolerance); + + EXPECT_GT( + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS), + initial_open_sessions); + + decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL, + &session_id_); + + EXPECT_GT( + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS), + initial_open_sessions); + + decryptor_.CloseSession(session_id_); + + EXPECT_EQ( + QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS), + initial_open_sessions); + + session_id_.clear(); + GenerateKeyRelease(key_set_id); + key_set_id_ = key_set_id; + VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false); +} + // This test verifies that sessions allocated internally during // key release message generation are deallocated after their // time to live period expires.