Merge "Enforce OEMCrypto insufficient resources error reporting"
This commit is contained in:
@@ -71,6 +71,14 @@ class UsageTableHeader {
|
||||
DeviceFiles* handle,
|
||||
metrics::CryptoMetrics* metrics);
|
||||
|
||||
// Test only method. This method emulates the behavior of DeleteEntry
|
||||
// without actually invoking OEMCrypto (through CryptoSession)
|
||||
// or storage (through DeviceFiles). It modifies internal data structures
|
||||
// when DeleteEntry is mocked. This allows one to test methods that are
|
||||
// dependent on DeleteEntry without having to set expectations
|
||||
// for the objects that DeleteEntry depends on.
|
||||
void DeleteEntryForTest(uint32_t usage_entry_number);
|
||||
|
||||
private:
|
||||
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,
|
||||
const CdmUsageEntry& from_usage_entry,
|
||||
@@ -119,6 +127,9 @@ class UsageTableHeader {
|
||||
|
||||
metrics::CryptoMetrics alternate_crypto_metrics_;
|
||||
|
||||
// TODO(rfrias): Move to utility class
|
||||
uint32_t GetRandomInRange(size_t upper_bound_inclusive);
|
||||
|
||||
// Test related declarations
|
||||
friend class UsageTableHeaderTest;
|
||||
|
||||
|
||||
@@ -56,7 +56,8 @@ CdmSession::CdmSession(FileSystem* file_system,
|
||||
CdmSession::~CdmSession() {
|
||||
if (usage_support_type_ == kUsageEntrySupport &&
|
||||
has_provider_session_token() &&
|
||||
usage_table_header_ != NULL) {
|
||||
usage_table_header_ != NULL &&
|
||||
!is_release_) {
|
||||
UpdateUsageEntryInformation();
|
||||
}
|
||||
|
||||
|
||||
@@ -74,9 +74,28 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
if (status == NO_ERROR) {
|
||||
if (usage_entry_info_.size() > kMinUsageEntriesSupported) {
|
||||
uint32_t temporary_usage_entry_number;
|
||||
CdmResponseType result = AddEntry(crypto_session, true,
|
||||
kDummyKeySetId, kEmptyString,
|
||||
&temporary_usage_entry_number);
|
||||
|
||||
// Create a new temporary usage entry, close the session and then
|
||||
// try to delete it.
|
||||
CdmResponseType result = NO_ERROR;
|
||||
{
|
||||
// |local_crypto_session| points to an object whose scope is this
|
||||
// method or a test object whose scope is the lifetime of this class
|
||||
std::unique_ptr<CryptoSession> scoped_crypto_session;
|
||||
CryptoSession* local_crypto_session = test_crypto_session_.get();
|
||||
if (local_crypto_session == NULL) {
|
||||
scoped_crypto_session.reset(
|
||||
(CryptoSession::MakeCryptoSession(metrics)));
|
||||
local_crypto_session = scoped_crypto_session.get();
|
||||
}
|
||||
|
||||
result = local_crypto_session->Open(requested_security_level_);
|
||||
if (result == NO_ERROR) {
|
||||
result = AddEntry(local_crypto_session, true,
|
||||
kDummyKeySetId, kEmptyString,
|
||||
&temporary_usage_entry_number);
|
||||
}
|
||||
}
|
||||
if (result == NO_ERROR) {
|
||||
result = DeleteEntry(temporary_usage_entry_number,
|
||||
file_handle_.get(), metrics);
|
||||
@@ -125,17 +144,20 @@ CdmResponseType UsageTableHeader::AddEntry(
|
||||
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
||||
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
|
||||
|
||||
uint32_t retry_count = 0;
|
||||
CdmResponseType status = NO_ERROR;
|
||||
do {
|
||||
{
|
||||
AutoLock auto_lock(usage_table_header_lock_);
|
||||
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||
}
|
||||
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3)
|
||||
DeleteEntry(retry_count, file_handle_.get(), metrics);
|
||||
} while (status == INSUFFICIENT_CRYPTO_RESOURCES_3 &&
|
||||
++retry_count < kMaxCryptoRetries);
|
||||
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||
|
||||
// If usage entry creation fails due to insufficient resources, release a
|
||||
// random entry and try again.
|
||||
for (uint32_t retry_count = 0;
|
||||
retry_count < kMaxCryptoRetries &&
|
||||
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
|
||||
++retry_count) {
|
||||
uint32_t entry_number_to_delete =
|
||||
GetRandomInRange(usage_entry_info_.size());
|
||||
DeleteEntry(entry_number_to_delete, file_handle_.get(), metrics);
|
||||
|
||||
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||
}
|
||||
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
@@ -189,17 +211,22 @@ CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
|
||||
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
||||
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
|
||||
|
||||
uint32_t retry_count = 0;
|
||||
CdmResponseType status = NO_ERROR;
|
||||
do {
|
||||
{
|
||||
AutoLock auto_lock(usage_table_header_lock_);
|
||||
CdmResponseType status =
|
||||
crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
||||
|
||||
// If loading a usage entry fails due to insufficient resources, release a
|
||||
// random entry and try again.
|
||||
for (uint32_t retry_count = 0;
|
||||
retry_count < kMaxCryptoRetries &&
|
||||
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
|
||||
++retry_count) {
|
||||
uint32_t entry_number_to_delete =
|
||||
GetRandomInRange(usage_entry_info_.size());
|
||||
if (usage_entry_number != entry_number_to_delete) {
|
||||
DeleteEntry(entry_number_to_delete, file_handle_.get(), metrics);
|
||||
status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
||||
}
|
||||
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3)
|
||||
DeleteEntry(retry_count, file_handle_.get(), metrics);
|
||||
} while (status == INSUFFICIENT_CRYPTO_RESOURCES_3 &&
|
||||
++retry_count < kMaxCryptoRetries);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -680,6 +707,12 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
uint32_t UsageTableHeader::GetRandomInRange(size_t upper_bound_inclusive) {
|
||||
if (upper_bound_inclusive == 0) return 0;
|
||||
return rand() / (RAND_MAX / upper_bound_inclusive + 1);
|
||||
}
|
||||
|
||||
// TODO(fredgc): remove when b/65730828 is addressed
|
||||
bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
|
||||
return crypto_session->CreateOldUsageEntry(
|
||||
kOldUsageEntryTimeSinceLicenseReceived,
|
||||
@@ -691,4 +724,20 @@ bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) {
|
||||
kOldUsageEntryPoviderSessionToken);
|
||||
}
|
||||
|
||||
// Test only method.
|
||||
void UsageTableHeader::DeleteEntryForTest(uint32_t usage_entry_number) {
|
||||
LOGV("UsageTableHeader::DeleteEntryForTest: usage_entry_number: %d",
|
||||
usage_entry_number);
|
||||
if (usage_entry_number >= usage_entry_info_.size()) {
|
||||
LOGE("UsageTableHeader::DeleteEntryForTest: usage entry number %d larger "
|
||||
"than usage entry size %d", usage_entry_number,
|
||||
usage_entry_info_.size());
|
||||
return;
|
||||
}
|
||||
// Move last entry into deleted location and shrink usage entries
|
||||
usage_entry_info_[usage_entry_number] =
|
||||
usage_entry_info_[usage_entry_info_.size() - 1];
|
||||
usage_entry_info_.resize(usage_entry_info_.size() - 1);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1604,41 +1604,61 @@ TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
|
||||
std::string key_id;
|
||||
std::string client_auth;
|
||||
GetOfflineConfiguration(&key_id, &client_auth);
|
||||
std::vector<CdmKeySetId> key_set_ids;
|
||||
|
||||
// Download large number of offline licenses. If OEMCrypto returns
|
||||
// OEMCrypto_ERROR_INSUFFICIENT_RESOURCES when usage table is at capacity,
|
||||
// licenses will be deleted internally to make space and we might
|
||||
// licenses will be deleted internally to make space and we will
|
||||
// not encounter an error.
|
||||
CdmResponseType response = NO_ERROR;
|
||||
for (size_t i = 0; i < 2000; ++i) {
|
||||
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline, &response);
|
||||
if (response != KEY_MESSAGE) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
break;
|
||||
}
|
||||
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false, &response);
|
||||
if (response != KEY_ADDED) {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
break;
|
||||
}
|
||||
EXPECT_EQ(KEY_ADDED, response);
|
||||
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
|
||||
|
||||
key_set_ids.push_back(key_set_id_);
|
||||
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
// If we encountered an error, verify that on UsageTableHeader creation
|
||||
// the usage entries will be deleted and that we can add new ones.
|
||||
if (response != KEY_ADDED && response != KEY_MESSAGE) {
|
||||
Provision();
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(key_id, kLicenseTypeOffline);
|
||||
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
|
||||
uint32_t number_of_valid_offline_sessions = 0;
|
||||
for (size_t i = 0; i < key_set_ids.size(); ++i) {
|
||||
session_id_.clear();
|
||||
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
CdmResponseType result = decryptor_.RestoreKey(session_id_, key_set_ids[i]);
|
||||
|
||||
if (result == KEY_ADDED) {
|
||||
++number_of_valid_offline_sessions;
|
||||
|
||||
// Decrypt data
|
||||
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
|
||||
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;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_EQ(data->decrypt_data, decrypt_buffer);
|
||||
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
// Release the license
|
||||
GenerateKeyRelease(key_set_ids[i]);
|
||||
key_set_id_ = key_set_ids[i];
|
||||
VerifyKeyRequestResponse(config_.license_server(), client_auth, false);
|
||||
} else {
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_GE(number_of_valid_offline_sessions, 200u);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user