MediaDrm throws an exception when Secure Stops are requested
Our recommendation to OEMs is that they support a table of at least 50 usage entries in OEMCrypto. If more usage entries are stored, the PSTs get added to the CDM but are LRU'ed out of the OEMCrypto usage table. When the CDM queries those usage entries, OEMCrypto will return a OEMCrypto_ERROR_INVALID_CONTEXT. Rather than return an error and have MediaDrm throw an exception, CDM should delete this PST and return the next usage entry, when queried. [ Merge of https://widevine-internal-review.googlesource.com/#/c/11457/ from Widevine cdm repo ] b/17994711 Change-Id: I00e3f93000096fb434d94333e22958de795a4bb5
This commit is contained in:
@@ -32,6 +32,8 @@ const uint32_t kMinute = 60;
|
||||
const uint32_t kClockTolerance = 5;
|
||||
const uint32_t kTwoMinutes = 120;
|
||||
|
||||
const uint32_t kMaxUsageTableSize = 50;
|
||||
|
||||
// Default license server, can be configured using --server command line option
|
||||
// Default key id (pssh), can be configured using --keyid command line option
|
||||
std::string g_client_auth;
|
||||
@@ -116,6 +118,29 @@ SubSampleInfo kEncryptedStreamingClip1SubSample = {
|
||||
"37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"),
|
||||
wvcdm::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3};
|
||||
|
||||
SubSampleInfo kEncryptedStreamingClip5SubSample = {
|
||||
true, 1, true, true, false,
|
||||
wvcdm::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"),
|
||||
wvcdm::a2b_hex(
|
||||
"934997779aa1aeb45d6ba8845f13786575d0adf85a5e93674d9597f8d4286ed7"
|
||||
"dcce02f306e502bbd9f1cadf502f354038ca921276d158d911bdf3171d335b18"
|
||||
"0ae0f9abece16ff31ee263228354f724da2f3723b19caa38ea02bd6563b01208"
|
||||
"fb5bf57854ac0fe38d5883197ef90324b2721ff20fdcf9a53819515e6daa096e"
|
||||
"70f6f5c1d29a4a13dafd127e2e1f761ea0e28fd451607552ecbaef5da3c780bc"
|
||||
"aaf2667b4cc4f858f01d480cac9e32c3fbb5705e5d2adcceebefc2535c117208"
|
||||
"e65f604799fc3d7223e16908550f287a4bea687008cb0064cf14d3aeedb8c705"
|
||||
"09ebc5c2b8b5315f43c04d78d2f55f4b32c7d33e157114362106395cc0bb6d93"),
|
||||
wvcdm::a2b_hex(
|
||||
"2dd54eee1307753508e1f250d637044d6e8f5abf057dab73e9e95f83910e4efc"
|
||||
"191c9bac63950f13fd51833dd94a4d03f2b64fb5c721970c418fe53fa6f74ad5"
|
||||
"a6e16477a35c7aa6e28909b069cd25770ef80da20918fc30fe95fd5c87fd3522"
|
||||
"1649de17ca2c7b3dc31f936f0cbdf97c7b1c15de3a86b279dc4b4de64943914a"
|
||||
"99734556c4b7a1a0b022c1933cb0786068fc18d49fed2f2b49f3ac6d01c32d07"
|
||||
"92175ce2844eaf9064e6a3fcffade038d690cbed81659351163a22432f0d0545"
|
||||
"037e1c805d8e92a1272b4196ad0ce22f26bb80063137a8e454d3b97e2414283d"
|
||||
"ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"),
|
||||
wvcdm::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0, 3};
|
||||
|
||||
SubSampleInfo kEncryptedOfflineClip2SubSample = {
|
||||
true, 1, true, true, false,
|
||||
wvcdm::a2bs_hex("3260F39E12CCF653529990168A3583FF"),
|
||||
@@ -150,6 +175,7 @@ std::string kOfflineClip2PstInitData = wvcdm::a2bs_hex(
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
|
||||
"08011a0d7769646576696e655f74657374220d6f" // pssh data
|
||||
"66666c696e655f636c697032");
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -167,6 +193,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
session_sharing_id_(0) {}
|
||||
virtual ~TestWvCdmClientPropertySet() {}
|
||||
|
||||
virtual const std::string& app_id() const { return app_id_; }
|
||||
virtual const std::string& security_level() const { return security_level_; }
|
||||
virtual const std::string& service_certificate() const {
|
||||
return service_certificate_;
|
||||
@@ -177,6 +204,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
}
|
||||
virtual uint32_t session_sharing_id() const { return session_sharing_id_; }
|
||||
|
||||
void set_app_id(const std::string& app_id) { app_id_ = app_id; }
|
||||
void set_security_level(const std::string& security_level) {
|
||||
if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) ||
|
||||
!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) {
|
||||
@@ -195,6 +223,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet {
|
||||
void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; }
|
||||
|
||||
private:
|
||||
std::string app_id_;
|
||||
std::string security_level_;
|
||||
std::string service_certificate_;
|
||||
bool use_privacy_mode_;
|
||||
@@ -794,6 +823,53 @@ TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) {
|
||||
Provision();
|
||||
SubSampleInfo* data = &kEncryptedStreamingClip5SubSample;
|
||||
TestWvCdmClientPropertySet client_property_set;
|
||||
TestWvCdmClientPropertySet* property_set = NULL;
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
DeviceFiles handle;
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(""));
|
||||
|
||||
for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) {
|
||||
decryptor_.OpenSession(g_key_system, property_set, &session_id_);
|
||||
std::string key_id = a2bs_hex(
|
||||
"000000427073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
||||
"747265616d696e675f636c697035");
|
||||
|
||||
GenerateKeyRequest(key_id, kLicenseTypeStreaming);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
uint32_t num_usage_info = 0;
|
||||
CdmUsageInfo usage_info;
|
||||
CdmUsageInfoReleaseMessage release_msg;
|
||||
CdmResponseType status = decryptor_.GetUsageInfo("", &usage_info);
|
||||
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
||||
while (usage_info.size() > 0) {
|
||||
for (size_t i = 0; i < usage_info.size(); ++i) {
|
||||
release_msg =
|
||||
GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]);
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg));
|
||||
}
|
||||
status = decryptor_.GetUsageInfo("", &usage_info);
|
||||
switch (status) {
|
||||
case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break;
|
||||
case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break;
|
||||
default: FAIL() << "GetUsageInfo failed with error "
|
||||
<< static_cast<int>(status) ; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WvCdmStreamingNoPstTest : public WvCdmExtendedDurationTest,
|
||||
public ::testing::WithParamInterface<size_t> {};
|
||||
|
||||
|
||||
@@ -1200,6 +1200,68 @@ TEST_F(WvCdmRequestLicenseTest, RemoveKeys) {
|
||||
ASSERT_EQ(NO_ERROR, decryptor_.CloseSession(session_id_));
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsageInfoRetryTest) {
|
||||
Unprovision();
|
||||
Provision(kLevelDefault);
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
std::string app_id = "";
|
||||
DeviceFiles handle;
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
File file;
|
||||
handle.SetTestFile(&file);
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(app_id));
|
||||
|
||||
SubSampleInfo* data = &usage_info_sub_samples_icp[0];
|
||||
decryptor_.OpenSession(g_key_system, NULL, &session_id_);
|
||||
std::string key_id = a2bs_hex(
|
||||
"000000427073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
||||
"747265616d696e675f636c697033");
|
||||
|
||||
GenerateKeyRequest(key_id, kLicenseTypeStreaming, NULL);
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
|
||||
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
CdmDecryptionParameters decryption_parameters(
|
||||
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
||||
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
uint32_t num_usage_info = 0;
|
||||
CdmUsageInfo usage_info;
|
||||
CdmUsageInfoReleaseMessage release_msg;
|
||||
CdmResponseType status = decryptor_.GetUsageInfo(app_id, &usage_info);
|
||||
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
||||
|
||||
// Discard and retry to verify usage reports can be generated multiple times
|
||||
// before release.
|
||||
status = decryptor_.GetUsageInfo(app_id, &usage_info);
|
||||
EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status);
|
||||
while (usage_info.size() > 0) {
|
||||
for (size_t i = 0; i < usage_info.size(); ++i) {
|
||||
release_msg =
|
||||
GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]);
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg));
|
||||
}
|
||||
status = decryptor_.GetUsageInfo(app_id, &usage_info);
|
||||
switch (status) {
|
||||
case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break;
|
||||
case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break;
|
||||
default: FAIL() << "GetUsageInfo failed with error "
|
||||
<< static_cast<int>(status) ; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WvCdmUsageInfoTest
|
||||
: public WvCdmRequestLicenseTest,
|
||||
public ::testing::WithParamInterface<UsageInfoSubSampleInfo*> {};
|
||||
|
||||
Reference in New Issue
Block a user