am dffa3ca9: Merge "MediaDrm throws an exception when Secure Stops are requested" into lmp-mr1-dev

* commit 'dffa3ca991c64035e1852543a215f99935b58c7f':
  MediaDrm throws an exception when Secure Stops are requested
This commit is contained in:
Rahul Frias
2014-11-14 20:45:36 +00:00
committed by Android Git Automerger
9 changed files with 203 additions and 39 deletions

View File

@@ -100,6 +100,8 @@ class CdmSession {
is_usage_update_needed_ = false;
}
bool DeleteLicense();
private:
// Internal constructor
void Create(CdmLicense* license_parser, CryptoSession* crypto_session,
@@ -112,7 +114,6 @@ class CdmSession {
CdmResponseType StoreLicense();
bool StoreLicense(DeviceFiles::LicenseState state);
bool DeleteLicense();
// instance variables
bool initialized_;

View File

@@ -108,7 +108,9 @@ class DeviceFiles {
FRIEND_TEST(DeviceFilesUsageInfoTest, Store);
FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest);
FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test);
FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest);
FRIEND_TEST(WvCdmUsageInfoTest, UsageInfo);
FRIEND_TEST(WvCdmExtendedDurationTest, UsageOverflowTest);
#endif
scoped_ptr<File> file_;

View File

@@ -36,8 +36,8 @@ class CdmLicense {
const CdmSessionId& session_id,
CdmKeyMessage* signed_request,
std::string* server_url);
virtual bool PrepareKeyUpdateRequest(bool is_renewal, CdmKeyMessage* signed_request,
std::string* server_url);
virtual CdmResponseType PrepareKeyUpdateRequest(
bool is_renewal, CdmKeyMessage* signed_request, std::string* server_url);
virtual CdmResponseType HandleKeyResponse(const CdmKeyResponse& license_response);
virtual CdmResponseType HandleKeyUpdateResponse(
bool is_renewal, const CdmKeyResponse& license_response);

View File

@@ -654,16 +654,22 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
CdmUsageInfo* usage_info) {
// Return a random usage report from a random security level
SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3;
CdmResponseType status = GetUsageInfo(app_id, security_level, usage_info);
CdmResponseType status = UNKNOWN_ERROR;
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (KEY_MESSAGE == status && !usage_info->empty())
return status;
} while (KEY_CANCELED == status);
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
do {
status = GetUsageInfo(app_id, security_level, usage_info);
if (NEED_PROVISIONING == status)
return NO_ERROR; // Valid scenario that one of the security
// levels has not been provisioned
} while (KEY_CANCELED == status);
return status;
}
@@ -716,13 +722,20 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
status = usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url);
if (KEY_MESSAGE != status) {
switch (status) {
case KEY_MESSAGE:
break;
case KEY_CANCELED: // usage information not present in
usage_session_->DeleteLicense(); // OEMCrypto, delete and try again
usage_info->clear();
break;
default:
LOGE("CdmEngine::GetUsageInfo: generate release request error: %d",
status);
usage_info->clear();
return status;
break;
}
return KEY_MESSAGE;
return status;
}
CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) {

View File

@@ -385,11 +385,11 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
// session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
std::string* server_url) {
if (!license_parser_->PrepareKeyUpdateRequest(true, key_request,
server_url)) {
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
return KEY_ERROR;
}
CdmResponseType status =
license_parser_->PrepareKeyUpdateRequest(true, key_request, server_url);
if (KEY_MESSAGE != status)
return status;
if (is_offline_) {
offline_key_renewal_request_ = *key_request;
@@ -413,8 +413,11 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
std::string* server_url) {
is_release_ = true;
if (!license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url))
return UNKNOWN_ERROR;
CdmResponseType status =
license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url);
if (KEY_MESSAGE != status)
return status;
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))

View File

@@ -716,13 +716,19 @@ CdmResponseType CryptoSession::DeactivateUsageInformation(
OEMCryptoResult status =
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
if (OEMCrypto_SUCCESS != status) {
switch(status) {
case OEMCrypto_SUCCESS:
return NO_ERROR;
case OEMCrypto_ERROR_INVALID_CONTEXT:
LOGE("CryptoSession::DeactivateUsageInformation: Deactivate Usage Entry "
" invalid context error");
return KEY_CANCELED;
default:
LOGE("CryptoSession::DeactivateUsageInformation: Deactivate Usage Entry "
" error=%ld",
status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
CdmResponseType CryptoSession::GenerateUsageReport(

View File

@@ -476,20 +476,21 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
return true;
}
bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
bool is_renewal,
CdmKeyMessage* signed_request,
std::string* server_url) {
if (!initialized_) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized");
return false;
return UNKNOWN_ERROR;
}
if (!signed_request) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: No signed request provided");
return false;
return UNKNOWN_ERROR;
}
if (!server_url) {
LOGE("CdmLicense::PrepareKeyUpdateRequest: No server url provided");
return false;
return UNKNOWN_ERROR;
}
LicenseRequest license_request;
@@ -513,7 +514,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
CdmResponseType status =
session_->DeactivateUsageInformation(provider_session_token_);
if (NO_ERROR != status)
return false;
return status;
}
std::string usage_report;
@@ -526,7 +527,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
if (NO_ERROR == status)
current_license->set_session_usage_table_entry(usage_report);
else
return false;
return KEY_ERROR;
}
}
@@ -545,7 +546,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
// of the license response.
uint32_t nonce;
if (!session_->GenerateNonce(&nonce)) {
return false;
return KEY_ERROR;
}
license_request.set_key_control_nonce(nonce);
LOGD("PrepareKeyUpdateRequest: nonce=%u", nonce);
@@ -559,13 +560,13 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
std::string license_request_signature;
if (!session_->PrepareRenewalRequest(serialized_license_req,
&license_request_signature))
return false;
return KEY_ERROR;
if (license_request_signature.empty()) {
LOGE(
"CdmLicense::PrepareKeyUpdateRequest: empty license request"
" signature");
return false;
return KEY_ERROR;
}
// Put serialize license request and signature together
@@ -576,7 +577,7 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
signed_message.SerializeToString(signed_request);
*server_url = server_url_;
return true;
return KEY_MESSAGE;
}
CdmResponseType CdmLicense::HandleKeyResponse(

View File

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

View File

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