Merge "Delete usage information on insufficient resources" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-08-30 03:31:52 +00:00
committed by Android (Google) Code Review
7 changed files with 1500 additions and 32 deletions

View File

@@ -129,6 +129,8 @@ class DeviceFiles {
const std::string& usage_info_file_name, const std::string& usage_info_file_name,
std::vector<std::string>* provider_session_tokens); std::vector<std::string>* provider_session_tokens);
virtual bool DeleteAllUsageInfo();
// Retrieve one usage info from the file. Subsequent calls will retrieve // Retrieve one usage info from the file. Subsequent calls will retrieve
// subsequent entries in the table for this app_id. // subsequent entries in the table for this app_id.
virtual bool RetrieveUsageInfo( virtual bool RetrieveUsageInfo(
@@ -186,6 +188,8 @@ class DeviceFiles {
CdmUsageTableHeader* usage_table_header, CdmUsageTableHeader* usage_table_header,
std::vector<CdmUsageEntryInfo>* usage_entry_info); std::vector<CdmUsageEntryInfo>* usage_entry_info);
virtual bool DeleteUsageTableInfo();
private: private:
// Extract serial number and system ID from DRM Device certificate // Extract serial number and system ID from DRM Device certificate
bool ExtractDeviceInfo(const std::string& device_certificate, bool ExtractDeviceInfo(const std::string& device_certificate,

View File

@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "crypto_session.h"
#include "device_files.h" #include "device_files.h"
#include "file_store.h" #include "file_store.h"
#include "lock.h" #include "lock.h"
@@ -17,8 +18,6 @@
namespace wvcdm { namespace wvcdm {
class CryptoSession;
// Offline licenses/secure stops may be securely tracked using usage // Offline licenses/secure stops may be securely tracked using usage
// tables (OEMCrypto v9-12) or usage table headers+usage entries // tables (OEMCrypto v9-12) or usage table headers+usage entries
// (OEMCrypto v13+). This class assists with the latter, synchronizing // (OEMCrypto v13+). This class assists with the latter, synchronizing
@@ -117,6 +116,8 @@ class UsageTableHeader {
// data-structures // data-structures
Lock usage_table_header_lock_; Lock usage_table_header_lock_;
metrics::CryptoMetrics alternate_crypto_metrics_;
// Test related declarations // Test related declarations
friend class UsageTableHeaderTest; friend class UsageTableHeaderTest;

View File

@@ -2085,6 +2085,8 @@ CdmResponseType CryptoSession::LoadUsageEntry(
return LOAD_USAGE_ENTRY_GENERATION_SKEW; return LOAD_USAGE_ENTRY_GENERATION_SKEW;
case OEMCrypto_ERROR_SIGNATURE_FAILURE: case OEMCrypto_ERROR_SIGNATURE_FAILURE:
return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE; return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE;
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
return INSUFFICIENT_CRYPTO_RESOURCES_3;
default: default:
return LOAD_USAGE_ENTRY_UNKNOWN_ERROR; return LOAD_USAGE_ENTRY_UNKNOWN_ERROR;
} }

View File

@@ -611,6 +611,15 @@ bool DeviceFiles::DeleteAllUsageInfoForApp(
return RemoveFile(usage_info_file_name); return RemoveFile(usage_info_file_name);
} }
bool DeviceFiles::DeleteAllUsageInfo() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteAllUsageInfo: not initialized");
return false;
}
return RemoveFile(kUsageInfoFileNamePrefix + std::string(kWildcard) +
kUsageInfoFileNameExt);
}
bool DeviceFiles::RetrieveUsageInfo( bool DeviceFiles::RetrieveUsageInfo(
const std::string& usage_info_file_name, const std::string& usage_info_file_name,
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) { std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> >* usage_info) {
@@ -1105,6 +1114,14 @@ bool DeviceFiles::RetrieveUsageTableInfo(
return true; return true;
} }
bool DeviceFiles::DeleteUsageTableInfo() {
if (!initialized_) {
LOGW("DeviceFiles::DeleteUsageTableInfo: not initialized");
return false;
}
return RemoveFile(GetUsageTableFileName());
}
bool DeviceFiles::StoreFileWithHash(const std::string& name, bool DeviceFiles::StoreFileWithHash(const std::string& name,
const std::string& serialized_file) { const std::string& serialized_file) {
// calculate SHA hash // calculate SHA hash

View File

@@ -11,6 +11,9 @@
namespace { namespace {
std::string kEmptyString; std::string kEmptyString;
size_t kMaxCryptoRetries = 3;
size_t kMinUsageEntriesSupported = 200;
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0; uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0;
uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0; uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0;
uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0; uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0;
@@ -58,14 +61,42 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
} }
CdmResponseType status = USAGE_INFO_NOT_FOUND; CdmResponseType status = USAGE_INFO_NOT_FOUND;
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == NULL) metrics = &alternate_crypto_metrics_;
if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_, if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_,
&usage_entry_info_)) { &usage_entry_info_)) {
status = crypto_session->LoadUsageTableHeader(usage_table_header_); status = crypto_session->LoadUsageTableHeader(usage_table_header_);
// If the usage table header has been successfully loaded, and is at
// minimum capacity (>200), we need to make sure we can still add and
// remove entries. If not, clear files/data and recreate usage header table.
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);
if (result == NO_ERROR) {
result = DeleteEntry(temporary_usage_entry_number,
file_handle_.get(), metrics);
}
if (result != NO_ERROR) {
LOGE("UsageTableHeader::Init: Unable to create/delete new entry. "
"Clear usage entries, security level: %d, usage entries: %d",
security_level, usage_entry_info_.size());
status = result;
}
}
}
if (status != NO_ERROR) { if (status != NO_ERROR) {
LOGE( LOGE(
"UsageTableHeader::Init: load usage table failed, security level: %d", "UsageTableHeader::Init: load usage table failed, security level: %d",
security_level); security_level);
file_handle_->DeleteAllLicenses(); file_handle_->DeleteAllLicenses();
file_handle_->DeleteAllUsageInfo();
file_handle_->DeleteUsageTableInfo();
usage_entry_info_.clear(); usage_entry_info_.clear();
usage_table_header_.clear(); usage_table_header_.clear();
status = crypto_session->CreateUsageTableHeader(&usage_table_header_); status = crypto_session->CreateUsageTableHeader(&usage_table_header_);
@@ -77,11 +108,6 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
if (status != NO_ERROR) return false; if (status != NO_ERROR) return false;
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
metrics::CryptoMetrics alternate_metrics;
metrics::CryptoMetrics* metrics =
crypto_session->GetCryptoMetrics() != NULL ?
crypto_session->GetCryptoMetrics() : &alternate_metrics;
UpgradeFromUsageTable(file_handle_.get(), metrics); UpgradeFromUsageTable(file_handle_.get(), metrics);
file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_);
} }
@@ -95,11 +121,25 @@ CdmResponseType UsageTableHeader::AddEntry(
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name, const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
uint32_t* usage_entry_number) { uint32_t* usage_entry_number) {
LOGV("UsageTableHeader::AddEntry: Lock"); LOGV("UsageTableHeader::AddEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number); 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);
if (status != NO_ERROR) return status; if (status != NO_ERROR) return status;
AutoLock auto_lock(usage_table_header_lock_);
if (*usage_entry_number < usage_entry_info_.size()) { if (*usage_entry_number < usage_entry_info_.size()) {
LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d", LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d",
*usage_entry_number, usage_entry_info_.size()); *usage_entry_number, usage_entry_info_.size());
@@ -134,17 +174,34 @@ CdmResponseType UsageTableHeader::AddEntry(
CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session, CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
const CdmUsageEntry& usage_entry, const CdmUsageEntry& usage_entry,
uint32_t usage_entry_number) { uint32_t usage_entry_number) {
LOGV("UsageTableHeader::LoadEntry: Lock"); {
AutoLock auto_lock(usage_table_header_lock_); LOGV("UsageTableHeader::LoadEntry: Lock");
AutoLock auto_lock(usage_table_header_lock_);
if (usage_entry_number >= usage_entry_info_.size()) { if (usage_entry_number >= usage_entry_info_.size()) {
LOGE( LOGE(
"UsageTableHeader::LoadEntry: usage entry number %d larger than table " "UsageTableHeader::LoadEntry: usage entry number %d larger than "
"size: %d", "table size: %d",
usage_entry_number, usage_entry_info_.size()); usage_entry_number, usage_entry_info_.size());
return USAGE_INVALID_LOAD_ENTRY; return USAGE_INVALID_LOAD_ENTRY;
}
} }
return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); 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->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;
} }
CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session, CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session,

File diff suppressed because it is too large Load Diff

View File

@@ -268,16 +268,26 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
void GenerateKeyRequest(const std::string& init_data, void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type) { CdmLicenseType license_type) {
CdmResponseType response;
GenerateKeyRequest(init_data, license_type, &response);
EXPECT_EQ(KEY_MESSAGE, response);
}
void GenerateKeyRequest(const std::string& init_data,
CdmLicenseType license_type,
CdmResponseType *response) {
CdmAppParameterMap app_parameters; CdmAppParameterMap app_parameters;
CdmKeyRequest key_request; CdmKeyRequest key_request;
EXPECT_EQ(KEY_MESSAGE, decryptor_.GenerateKeyRequest( *response = decryptor_.GenerateKeyRequest(
session_id_, key_set_id_, "video/mp4", init_data, session_id_, key_set_id_, "video/mp4", init_data,
license_type, app_parameters, NULL, license_type, app_parameters, NULL,
kDefaultCdmIdentifier, &key_request)); kDefaultCdmIdentifier, &key_request);
EXPECT_EQ(kKeyRequestTypeInitial, key_request.type); if (*response == KEY_MESSAGE) {
key_msg_ = key_request.message; EXPECT_EQ(kKeyRequestTypeInitial, key_request.type);
EXPECT_EQ(0u, key_request.url.size()); key_msg_ = key_request.message;
EXPECT_EQ(0u, key_request.url.size());
}
} }
void GenerateRenewalRequest(CdmLicenseType license_type, void GenerateRenewalRequest(CdmLicenseType license_type,
@@ -406,14 +416,21 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
void VerifyKeyRequestResponse(const std::string& server_url, void VerifyKeyRequestResponse(const std::string& server_url,
const std::string& client_auth, const std::string& client_auth,
bool is_renewal) { bool is_renewal) {
std::string resp = GetKeyRequestResponse(server_url, client_auth); VerifyKeyRequestResponse(server_url, client_auth, is_renewal, NULL);
}
if (is_renewal) { void VerifyKeyRequestResponse(const std::string& server_url,
// TODO application makes a license request, CDM will renew the license const std::string& client_auth,
// when appropriate bool /* is_renewal */,
EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_)); CdmResponseType* status) {
std::string resp = GetKeyRequestResponse(server_url, client_auth);
CdmResponseType sts =
decryptor_.AddKey(session_id_, resp, &key_set_id_);
if (status == NULL) {
EXPECT_EQ(KEY_ADDED, sts);
} else { } else {
EXPECT_EQ(KEY_ADDED, decryptor_.AddKey(session_id_, resp, &key_set_id_)); *status = sts;
} }
} }
@@ -1574,6 +1591,55 @@ TEST_P(WvCdmOfflineUsageReportTest, UsageTest) {
INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest, INSTANTIATE_TEST_CASE_P(Cdm, WvCdmOfflineUsageReportTest,
::testing::Values(0, 1, 2)); ::testing::Values(0, 1, 2));
// This test verifies that a device can still work if the maximum capacity
// of the usage entry table is reached. New usage entries, for offline
// licenses, can still be added and existing licenses can still be played back.
TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) {
Unprovision();
Provision();
// override default settings unless configured through the command line
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// 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
// not encounter an error.
CdmResponseType response = NO_ERROR;
for (size_t i = 0; i < 2000; ++i) {
decryptor_.OpenSession(g_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);
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(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
&session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(kUatLicenseServer, client_auth, false);
decryptor_.CloseSession(session_id_);
}
}
}
} // namespace wvcdm } // namespace wvcdm
void show_menu(char* prog_name) { void show_menu(char* prog_name) {