Release offline release sessions -- DO NOT MERGE
am: 9a55ca3249
Change-Id: If92d87a6d1cf98a786c72070122d7db68444be4f
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "certificate_provisioning.h"
|
||||
#include "clock.h"
|
||||
#include "crypto_session.h"
|
||||
#include "initialization_data.h"
|
||||
#include "lock.h"
|
||||
@@ -22,7 +23,10 @@ class UsagePropertySet;
|
||||
class WvCdmEventListener;
|
||||
|
||||
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 {
|
||||
public:
|
||||
@@ -185,11 +189,14 @@ class CdmEngine {
|
||||
|
||||
std::string MapHdcpVersion(CryptoSession::HdcpCapability version);
|
||||
|
||||
void CloseExpiredReleaseSessions();
|
||||
|
||||
// instance variables
|
||||
CdmSessionMap sessions_;
|
||||
CdmReleaseKeySetMap release_key_sets_;
|
||||
scoped_ptr<CertificateProvisioning> cert_provisioning_;
|
||||
SecurityLevel cert_provisioning_requested_security_level_;
|
||||
Clock clock_;
|
||||
|
||||
static bool seeded_;
|
||||
|
||||
@@ -206,6 +213,8 @@ class CdmEngine {
|
||||
// occur simultaneously with OpenSession or CloseSession.
|
||||
Lock session_list_lock_;
|
||||
|
||||
Lock release_key_sets_lock_;
|
||||
|
||||
CORE_DISALLOW_COPY_AND_ASSIGN(CdmEngine);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
namespace {
|
||||
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
const size_t kUsageReportsPerRequest = 1;
|
||||
} // namespace
|
||||
@@ -62,8 +63,7 @@ CdmEngine::CdmEngine()
|
||||
last_usage_information_update_time_(0) {
|
||||
Properties::Init();
|
||||
if (!seeded_) {
|
||||
Clock clock;
|
||||
srand(clock.GetCurrentTime());
|
||||
srand(clock_.GetCurrentTime());
|
||||
seeded_ = true;
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
}
|
||||
}
|
||||
|
||||
CloseExpiredReleaseSessions();
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(origin));
|
||||
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
|
||||
event_listener);
|
||||
@@ -149,6 +151,14 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
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;
|
||||
CdmResponseType sts =
|
||||
OpenSession(KEY_SYSTEM, property_set, origin, event_listener,
|
||||
@@ -156,7 +166,10 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -177,19 +190,30 @@ CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
|
||||
CdmResponseType CdmEngine::CloseKeySetSession(const CdmKeySetId& key_set_id) {
|
||||
LOGI("CdmEngine::CloseKeySetSession");
|
||||
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
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;
|
||||
CdmSessionId session_id;
|
||||
{
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
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);
|
||||
release_key_sets_.erase(iter);
|
||||
CdmResponseType sts = CloseSession(session_id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool CdmEngine::IsOpenSession(const CdmSessionId& session_id) {
|
||||
AutoLock lock(session_list_lock_);
|
||||
CdmSessionMap::iterator iter = sessions_.find(session_id);
|
||||
return iter != sessions_.end();
|
||||
}
|
||||
@@ -218,6 +242,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return INVALID_SESSION_ID;
|
||||
}
|
||||
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::GenerateKeyRequest: key set ID not found = %s",
|
||||
@@ -225,7 +250,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest(
|
||||
return KEYSET_ID_NOT_FOUND_2;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
@@ -291,13 +316,14 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
return EMPTY_KEYSET_ID_ENG_3;
|
||||
}
|
||||
|
||||
AutoLock lock(release_key_sets_lock_);
|
||||
CdmReleaseKeySetMap::iterator iter = release_key_sets_.find(*key_set_id);
|
||||
if (iter == release_key_sets_.end()) {
|
||||
LOGE("CdmEngine::AddKey: key set id not found = %s", key_set_id->c_str());
|
||||
return KEYSET_ID_NOT_FOUND_3;
|
||||
}
|
||||
|
||||
id = iter->second;
|
||||
id = iter->second.first;
|
||||
}
|
||||
|
||||
CdmSessionMap::iterator iter = sessions_.find(id);
|
||||
@@ -1159,6 +1185,8 @@ void CdmEngine::OnTimerEvent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseExpiredReleaseSessions();
|
||||
}
|
||||
|
||||
void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
|
||||
@@ -1188,6 +1216,29 @@ std::string CdmEngine::MapHdcpVersion(
|
||||
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() {
|
||||
std::string device_base_path_level1 = "";
|
||||
std::string device_base_path_level3 = "";
|
||||
|
||||
@@ -166,6 +166,14 @@ std::string kOfflineClip2PstInitData = wvcdm::a2bs_hex(
|
||||
"08011a0d7769646576696e655f74657374220d6f" // pssh data
|
||||
"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) {
|
||||
std::istringstream ss(input);
|
||||
ss >> *output;
|
||||
@@ -286,19 +294,26 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
||||
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;
|
||||
CdmInitData init_data;
|
||||
CdmAppParameterMap app_parameters;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest(
|
||||
EXPECT_EQ(expected_response, decryptor_.GenerateKeyRequest(
|
||||
session_id, key_set_id, "video/mp4", init_data,
|
||||
kLicenseTypeRelease, app_parameters, NULL,
|
||||
EMPTY_ORIGIN, &key_request));
|
||||
|
||||
key_msg_ = key_request.message;
|
||||
EXPECT_EQ(kKeyRequestTypeRelease, key_request.type);
|
||||
if (expected_response == KEY_MESSAGE) {
|
||||
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) {
|
||||
@@ -612,6 +627,19 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
|
||||
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) {
|
||||
decryptor_.OpenSession(g_key_system, property_set, EMPTY_ORIGIN, NULL,
|
||||
&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,
|
||||
public ::testing::WithParamInterface<size_t> {};
|
||||
|
||||
|
||||
@@ -1211,6 +1211,19 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
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_;
|
||||
CdmKeyMessage key_msg_;
|
||||
CdmSessionId session_id_;
|
||||
@@ -1817,6 +1830,60 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
|
||||
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) {
|
||||
decryptor_.OpenSession(g_key_system, NULL, EMPTY_ORIGIN, NULL, &session_id_);
|
||||
GenerateKeyRequest(g_key_id, kLicenseTypeStreaming);
|
||||
|
||||
Reference in New Issue
Block a user