Fix deadlock when pending key release sessions are deallocated
[ Merge from http://go/wvgerrit/27261/ ] Sessions created to release keys are periodically cleaned up if the key release operation does not complete within a specific amount of time. If other sessions are open, they will be released through the timer thread. This would result in deadlock as a mutex was taken twice. Test: Verified by cdm_extended_duration_test (AutomatedOfflineSessionReleaseOnOpenSession and AutomatedOfflineSessionReleaseOnTimerEvent tests) b/37546078 Change-Id: I7d45f939bdce77e5db461a401364da4f42c1c034
This commit is contained in:
@@ -1443,35 +1443,37 @@ void CdmEngine::OnTimerEvent() {
|
|||||||
bool is_initial_usage_update = false;
|
bool is_initial_usage_update = false;
|
||||||
bool is_usage_update_needed = false;
|
bool is_usage_update_needed = false;
|
||||||
|
|
||||||
AutoLock lock(session_list_lock_);
|
{
|
||||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
AutoLock lock(session_list_lock_);
|
||||||
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;
|
|
||||||
for (CdmSessionMap::iterator iter = sessions_.begin();
|
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||||
iter != sessions_.end(); ++iter) {
|
iter != sessions_.end(); ++iter) {
|
||||||
iter->second->reset_usage_flags();
|
is_initial_usage_update =
|
||||||
if (iter->second->get_usage_support_type() == kUsageEntrySupport)
|
is_initial_usage_update || iter->second->is_initial_usage_update();
|
||||||
iter->second->UpdateUsageEntryInformation();
|
is_usage_update_needed =
|
||||||
|
is_usage_update_needed || iter->second->is_usage_update_needed();
|
||||||
|
|
||||||
if (!has_usage_been_updated) {
|
iter->second->OnTimerEvent(usage_update_period_expired);
|
||||||
// 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) {
|
if (is_usage_update_needed &&
|
||||||
CdmResponseType status = iter->second->UpdateUsageTableInformation();
|
(usage_update_period_expired || is_initial_usage_update)) {
|
||||||
if (NO_ERROR != status) {
|
bool has_usage_been_updated = false;
|
||||||
LOGW("Update usage information failed: %d", status);
|
for (CdmSessionMap::iterator iter = sessions_.begin();
|
||||||
} else {
|
iter != sessions_.end(); ++iter) {
|
||||||
has_usage_been_updated = true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
// This test verifies that sessions allocated internally during
|
||||||
// key release message generation are deallocated after their
|
// key release message generation are deallocated after their
|
||||||
// time to live period expires.
|
// time to live period expires.
|
||||||
|
|||||||
Reference in New Issue
Block a user