Release offline release sessions -- DO NOT MERGE

am: 9a55ca3249

Change-Id: If92d87a6d1cf98a786c72070122d7db68444be4f
This commit is contained in:
Rahul Frias
2016-12-05 18:53:29 +00:00
committed by android-build-merger
4 changed files with 247 additions and 17 deletions

View File

@@ -6,6 +6,7 @@
#include <string> #include <string>
#include "certificate_provisioning.h" #include "certificate_provisioning.h"
#include "clock.h"
#include "crypto_session.h" #include "crypto_session.h"
#include "initialization_data.h" #include "initialization_data.h"
#include "lock.h" #include "lock.h"
@@ -22,7 +23,10 @@ class UsagePropertySet;
class WvCdmEventListener; class WvCdmEventListener;
typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap; typedef std::map<CdmSessionId, CdmSession*> CdmSessionMap;
typedef std::map<CdmKeySetId, CdmSessionId> CdmReleaseKeySetMap; typedef std::map<
CdmKeySetId,
std::pair<CdmSessionId, int64_t /* expiration time in seconds */> >
CdmReleaseKeySetMap;
class CdmEngine { class CdmEngine {
public: public:
@@ -185,11 +189,14 @@ class CdmEngine {
std::string MapHdcpVersion(CryptoSession::HdcpCapability version); std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
void CloseExpiredReleaseSessions();
// instance variables // instance variables
CdmSessionMap sessions_; CdmSessionMap sessions_;
CdmReleaseKeySetMap release_key_sets_; CdmReleaseKeySetMap release_key_sets_;
scoped_ptr<CertificateProvisioning> cert_provisioning_; scoped_ptr<CertificateProvisioning> cert_provisioning_;
SecurityLevel cert_provisioning_requested_security_level_; SecurityLevel cert_provisioning_requested_security_level_;
Clock clock_;
static bool seeded_; static bool seeded_;
@@ -206,6 +213,8 @@ class CdmEngine {
// occur simultaneously with OpenSession or CloseSession. // occur simultaneously with OpenSession or CloseSession.
Lock session_list_lock_; Lock session_list_lock_;
Lock release_key_sets_lock_;
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine); CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
}; };

View File

@@ -21,6 +21,7 @@
#include "wv_cdm_event_listener.h" #include "wv_cdm_event_listener.h"
namespace { namespace {
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
const size_t kUsageReportsPerRequest = 1; const size_t kUsageReportsPerRequest = 1;
} // namespace } // namespace
@@ -62,8 +63,7 @@ CdmEngine::CdmEngine()
last_usage_information_update_time_(0) { last_usage_information_update_time_(0) {
Properties::Init(); Properties::Init();
if (!seeded_) { if (!seeded_) {
Clock clock; srand(clock_.GetCurrentTime());
srand(clock.GetCurrentTime());
seeded_ = true; seeded_ = true;
} }
} }
@@ -119,6 +119,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
} }
} }
CloseExpiredReleaseSessions();
scoped_ptr<CdmSession> new_session(new CdmSession(origin)); scoped_ptr<CdmSession> new_session(new CdmSession(origin));
CdmResponseType sts = new_session->Init(property_set, forced_session_id, CdmResponseType sts = new_session->Init(property_set, forced_session_id,
event_listener); event_listener);
@@ -149,6 +151,14 @@ CdmResponseType CdmEngine::OpenKeySetSession(
return EMPTY_KEYSET_ID_ENG_1; return EMPTY_KEYSET_ID_ENG_1;
} }
bool exists = false;
{
AutoLock lock(release_key_sets_lock_);
exists = release_key_sets_.find(key_set_id) != release_key_sets_.end();
}
if (exists)
CloseKeySetSession(key_set_id);
CdmSessionId session_id; CdmSessionId session_id;
CdmResponseType sts = CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, origin, event_listener, OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
@@ -156,7 +166,10 @@ CdmResponseType CdmEngine::OpenKeySetSession(
if (sts != NO_ERROR) return sts; if (sts != NO_ERROR) return sts;
release_key_sets_[key_set_id] = session_id; AutoLock lock(release_key_sets_lock_);
release_key_sets_[key_set_id] = std::make_pair(session_id,
clock_.GetCurrentTime() + kReleaseSessionTimeToLive);
return NO_ERROR; return NO_ERROR;
} }
@@ -177,19 +190,30 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) { CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
LOGI("CdmEngine::CloseKeySetSession"); LOGI("CdmEngine::CloseKeySetSession");
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); CdmSessionId session_id;
if (iter == release_key_sets_.end()) { {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s", AutoLock lock(release_key_sets_lock_);
key_set_id.c_str()); CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
return KEYSET_ID_NOT_FOUND_1; if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::CloseKeySetSession: key set id not found = %s",
key_set_id.c_str());
return KEYSET_ID_NOT_FOUND_1;
}
session_id = iter->second.first;
} }
CdmResponseType sts = CloseSession(iter->second); CdmResponseType sts = CloseSession(session_id);
release_key_sets_.erase(iter);
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter != release_key_sets_.end()) {
release_key_sets_.erase(iter);
}
return sts; return sts;
} }
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) { bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
AutoLock lock(session_list_lock_);
CdmSessionMap::iterator iter = sessions_.find(session_id); CdmSessionMap::iterator iter = sessions_.find(session_id);
return iter != sessions_.end(); return iter != sessions_.end();
} }
@@ -218,6 +242,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return INVALID_SESSION_ID; return INVALID_SESSION_ID;
} }
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id); CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
if (iter == release_key_sets_.end()) { if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s", LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
@@ -225,7 +250,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
return KEYSET_ID_NOT_FOUND_2; return KEYSET_ID_NOT_FOUND_2;
} }
id = iter->second; id = iter->second.first;
} }
CdmSessionMap::iterator iter = sessions_.find(id); CdmSessionMap::iterator iter = sessions_.find(id);
@@ -291,13 +316,14 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
return EMPTY_KEYSET_ID_ENG_3; return EMPTY_KEYSET_ID_ENG_3;
} }
AutoLock lock(release_key_sets_lock_);
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id); CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
if (iter == release_key_sets_.end()) { if (iter == release_key_sets_.end()) {
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str()); LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
return KEYSET_ID_NOT_FOUND_3; return KEYSET_ID_NOT_FOUND_3;
} }
id = iter->second; id = iter->second.first;
} }
CdmSessionMap::iterator iter = sessions_.find(id); CdmSessionMap::iterator iter = sessions_.find(id);
@@ -1159,6 +1185,8 @@ void CdmEngine::OnTimerEvent() {
} }
} }
} }
CloseExpiredReleaseSessions();
} }
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
@@ -1188,6 +1216,29 @@ std::string CdmEngine::MapHdcpVersion(
return ""; return "";
} }
void CdmEngine::CloseExpiredReleaseSessions() {
int64_t current_time = clock_.GetCurrentTime();
std::set<CdmSessionId> close_session_set;
{
AutoLock lock(release_key_sets_lock_);
for (CdmReleaseKeySetMap::iterator iter = release_key_sets_.begin();
iter != release_key_sets_.end();) {
if (iter->second.second < current_time) {
close_session_set.insert(iter->second.first);
release_key_sets_.erase(iter++);
} else {
++iter;
}
}
}
for (std::set<CdmSessionId>::iterator iter = close_session_set.begin();
iter != close_session_set.end(); ++iter) {
CloseSession(*iter);
}
}
void CdmEngine::DeleteAllUsageReportsUponFactoryReset() { void CdmEngine::DeleteAllUsageReportsUponFactoryReset() {
std::string device_base_path_level1 = ""; std::string device_base_path_level1 = "";
std::string device_base_path_level3 = ""; std::string device_base_path_level3 = "";

View File

@@ -166,6 +166,14 @@ std::string kOfflineClip2PstInitData = wvcdm::a2bs_hex(
"08011a0d7769646576696e655f74657374220d6f" // pssh data "08011a0d7769646576696e655f74657374220d6f" // pssh data
"66666c696e655f636c697032"); "66666c696e655f636c697032");
std::string kOfflineClip4 = wvcdm::a2bs_hex(
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
"08011a0d7769646576696e655f74657374220d6f" // pssh data
"66666c696e655f636c697034");
std::string kUatLicenseServer = "https://proxy.uat.widevine.com/proxy";
bool StringToInt64(const std::string& input, int64_t* output) { bool StringToInt64(const std::string& input, int64_t* output) {
std::istringstream ss(input); std::istringstream ss(input);
ss >> *output; ss >> *output;
@@ -286,19 +294,26 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
EXPECT_NE(0u, key_request.url.size()); EXPECT_NE(0u, key_request.url.size());
} }
void GenerateKeyRelease(CdmKeySetId key_set_id) { void GenerateKeyRelease(CdmKeySetId key_set_id,
CdmResponseType expected_response) {
CdmSessionId session_id; CdmSessionId session_id;
CdmInitData init_data; CdmInitData init_data;
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmKeyRequest key_request; CdmKeyRequest key_request;
EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest( EXPECT_EQ(expected_response, decryptor_.GenerateKeyRequest(
session_id, key_set_id, "video/mp4", init_data, session_id, key_set_id, "video/mp4", init_data,
kLicenseTypeRelease, app_parameters, NULL, kLicenseTypeRelease, app_parameters, NULL,
EMPTY_ORIGIN, &key_request)); EMPTY_ORIGIN, &key_request));
key_msg_ = key_request.message; if (expected_response == KEY_MESSAGE) {
EXPECT_EQ(kKeyRequestTypeRelease, key_request.type); key_msg_ = key_request.message;
EXPECT_EQ(kKeyRequestTypeRelease, key_request.type);
}
}
void GenerateKeyRelease(CdmKeySetId key_set_id) {
GenerateKeyRelease(key_set_id, KEY_MESSAGE);
} }
void LogResponseError(const std::string& message, int http_status_code) { void LogResponseError(const std::string& message, int http_status_code) {
@@ -612,6 +627,19 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
EXPECT_TRUE(StringToInt64(query_info[key], playback_duration_remaining)); EXPECT_TRUE(StringToInt64(query_info[key], playback_duration_remaining));
} }
uint32_t QueryStatus(SecurityLevel security_level, const std::string& key) {
std::string str;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.QueryStatus(security_level, key, &str));
std::istringstream ss(str);
uint32_t value;
ss >> value;
EXPECT_FALSE(ss.fail());
EXPECT_TRUE(ss.eof());
return value;
}
std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) { std::string GetSecurityLevel(TestWvCdmClientPropertySet* property_set) {
decryptor_.OpenSession(g_key_system, property_set, EMPTY_ORIGIN, NULL, decryptor_.OpenSession(g_key_system, property_set, EMPTY_ORIGIN, NULL,
&session_id_); &session_id_);
@@ -837,6 +865,81 @@ 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.
// TODO: Disabled till b/32617908 is addressed
TEST_F(WvCdmExtendedDurationTest, DISABLED_AutomatedOfflineSessionReleaseTest) {
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);
uint32_t num_key_set_ids = max_sessions - initial_open_sessions;
if (num_key_set_ids > kMaxUsageTableSize)
num_key_set_ids = kMaxUsageTableSize;
std::set<std::string> key_set_id_map;
for (uint32_t i = 0; i < num_key_set_ids; ++i) {
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
&session_id_);
GenerateKeyRequest(kOfflineClip4, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
key_set_id_map.insert(key_set_id_);
}
std::set<std::string>::iterator iter;
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
key_set_id_.clear();
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL,
&session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, *iter));
decryptor_.CloseSession(session_id_);
}
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
GenerateKeyRelease(*iter);
}
uint32_t open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_EQ(open_sessions, key_set_id_map.size() + initial_open_sessions);
sleep(kMinute + kClockTolerance);
iter = key_set_id_map.begin();
session_id_.clear();
GenerateKeyRelease(*iter);
open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
EXPECT_GE(open_sessions, initial_open_sessions);
EXPECT_LE(open_sessions - initial_open_sessions, key_set_id_map.size());
for (iter = key_set_id_map.begin(); iter != key_set_id_map.end(); ++iter) {
session_id_.clear();
GenerateKeyRelease(*iter);
key_set_id_ = *iter;
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
}
}
class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest, class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest,
public ::testing::WithParamInterface<size_t> {}; public ::testing::WithParamInterface<size_t> {};

View File

@@ -1211,6 +1211,19 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
return security_level; return security_level;
} }
uint32_t QueryStatus(SecurityLevel security_level, const std::string& key) {
std::string str;
EXPECT_EQ(wvcdm::NO_ERROR,
decryptor_.QueryStatus(security_level, key, &str));
std::istringstream ss(str);
uint32_t value;
ss >> value;
EXPECT_FALSE(ss.fail());
EXPECT_TRUE(ss.eof());
return value;
}
wvcdm::WvContentDecryptionModule decryptor_; wvcdm::WvContentDecryptionModule decryptor_;
CdmKeyMessage key_msg_; CdmKeyMessage key_msg_;
CdmSessionId session_id_; CdmSessionId session_id_;
@@ -1817,6 +1830,60 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
decryptor_.CloseSession(restore_session_id); decryptor_.CloseSession(restore_session_id);
} }
// This test verifies that repeated generation of the key release message
// for the same key_set_id results in the previous session being
// deallocated (rather than leaked) and a new one allocated.
TEST_F(WvCdmRequestLicenseTest, AutomatedOfflineSessionReleaseTest) {
Unprovision();
Provision(kLevelDefault);
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, client_auth, false);
CdmKeySetId key_set_id = key_set_id_;
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
session_id_.clear();
key_set_id_.clear();
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_);
EXPECT_EQ(wvcdm::KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id));
decryptor_.CloseSession(session_id_);
uint32_t open_sessions =
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS);
session_id_.clear();
key_set_id_.clear();
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;
EXPECT_EQ(
++open_sessions,
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
session_id_.clear();
key_set_id_.clear();
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;
EXPECT_EQ(
open_sessions,
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
VerifyKeyRequestResponse(g_license_server, client_auth, false);
EXPECT_EQ(
--open_sessions,
QueryStatus(kLevelDefault, wvcdm::QUERY_KEY_NUMBER_OF_OPEN_SESSIONS));
}
TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) { TEST_F(WvCdmRequestLicenseTest, StreamingLicenseRenewal) {
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_); decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_);
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming); GenerateKeyRequest(g_key_id, kLicenseTypeStreaming);