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 "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);
};

View File

@@ -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 = "";

View File

@@ -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> {};

View File

@@ -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);