Merge "New usage entries are moved lower after creation." into sc-dev am: 9c47be6aa8 am: 7291bb5fc5 am: a48adbebee
Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/14354899 Change-Id: I6a65a4a0b913e4e53abe103a5daf12d29bea2239
This commit is contained in:
@@ -154,6 +154,46 @@ class UsageTableHeader {
|
|||||||
bool DetermineTableCapacity(CryptoSession* crypto_session);
|
bool DetermineTableCapacity(CryptoSession* crypto_session);
|
||||||
|
|
||||||
// == Table operation methods ==
|
// == Table operation methods ==
|
||||||
|
// NOTE: The following "Table operation methods" require
|
||||||
|
// |usage_table_header_lock_| to be taken before calling.
|
||||||
|
|
||||||
|
// Creates a new entry for the provided crypto session. If the
|
||||||
|
// entry is created successfully in OEMCrypto, then a new entry
|
||||||
|
// info is added to the table's vector of entry info.
|
||||||
|
CdmResponseType CreateEntry(CryptoSession* const crypto_session,
|
||||||
|
uint32_t* usage_entry_number);
|
||||||
|
|
||||||
|
// Attempts to relocate a newly created usage entry associated with
|
||||||
|
// the provided |crypto_session| to the lowest unoccupied position in
|
||||||
|
// the table.
|
||||||
|
// |usage_entry_number| is treated as both an input and output.
|
||||||
|
// Returns NO_ERROR so long as no internal operation fails,
|
||||||
|
// regardless of whether the entry was moved or not.
|
||||||
|
CdmResponseType RelocateNewEntry(CryptoSession* const crypto_session,
|
||||||
|
uint32_t* usage_entry_number);
|
||||||
|
|
||||||
|
// Checks if the specified |usage_entry_number| is known to be
|
||||||
|
// unoccupied (released).
|
||||||
|
bool IsEntryUnoccupied(const uint32_t usage_entry_number) const;
|
||||||
|
|
||||||
|
// SetOfflineEntryInfo() and SetUsageInfoEntryInfo() populate the
|
||||||
|
// entry meta-data with the required information based on the type
|
||||||
|
// of entry.
|
||||||
|
void SetOfflineEntryInfo(const uint32_t usage_entry_number,
|
||||||
|
const std::string& key_set_id,
|
||||||
|
const CdmKeyResponse& license_message);
|
||||||
|
void SetUsageInfoEntryInfo(const uint32_t usage_entry_number,
|
||||||
|
const std::string& key_set_id,
|
||||||
|
const std::string& usage_info_file_name);
|
||||||
|
|
||||||
|
// Shrinks the table, removing all trailing unoccupied entries.
|
||||||
|
// |usage_entry_info_| will be resized appropriately.
|
||||||
|
// Caller must store the table after a successful call.
|
||||||
|
CdmResponseType RefitTable(CryptoSession* const crypto_session);
|
||||||
|
|
||||||
|
virtual CdmResponseType InvalidateEntryInternal(
|
||||||
|
uint32_t usage_entry_number, bool defrag_table, DeviceFiles* device_files,
|
||||||
|
metrics::CryptoMetrics* metrics);
|
||||||
|
|
||||||
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,
|
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,
|
||||||
const CdmUsageEntry& from_usage_entry,
|
const CdmUsageEntry& from_usage_entry,
|
||||||
@@ -279,6 +319,7 @@ class UsageTableHeader {
|
|||||||
#if defined(UNIT_TEST)
|
#if defined(UNIT_TEST)
|
||||||
// Test related declarations
|
// Test related declarations
|
||||||
friend class UsageTableHeaderTest;
|
friend class UsageTableHeaderTest;
|
||||||
|
|
||||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_NoneOfTable);
|
FRIEND_TEST(UsageTableHeaderTest, Shrink_NoneOfTable);
|
||||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_PartOfTable);
|
FRIEND_TEST(UsageTableHeaderTest, Shrink_PartOfTable);
|
||||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_AllOfTable);
|
FRIEND_TEST(UsageTableHeaderTest, Shrink_AllOfTable);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
namespace wvcdm {
|
namespace wvcdm {
|
||||||
namespace {
|
namespace {
|
||||||
|
using TableLock = std::unique_lock<std::mutex>;
|
||||||
|
|
||||||
const std::string kEmptyString;
|
const std::string kEmptyString;
|
||||||
const wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
const wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||||
|
|
||||||
@@ -252,75 +254,39 @@ CdmResponseType UsageTableHeader::AddEntry(
|
|||||||
CryptoSession* crypto_session, bool persistent_license,
|
CryptoSession* crypto_session, bool persistent_license,
|
||||||
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
|
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
|
||||||
const CdmKeyResponse& license_message, uint32_t* usage_entry_number) {
|
const CdmKeyResponse& license_message, uint32_t* usage_entry_number) {
|
||||||
LOGD("oec_session_id = %u, type = %s, current_size = %zu",
|
LOGD("key_set_id = %s, type = %s, current_size = %zu", IdToString(key_set_id),
|
||||||
crypto_session->oec_session_id(),
|
|
||||||
persistent_license ? "OfflineLicense" : "Streaming",
|
persistent_license ? "OfflineLicense" : "Streaming",
|
||||||
usage_entry_info_.size());
|
usage_entry_info_.size());
|
||||||
|
|
||||||
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
||||||
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
|
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
|
||||||
|
TableLock auto_lock(usage_table_header_lock_);
|
||||||
|
|
||||||
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
CdmResponseType status = CreateEntry(crypto_session, usage_entry_number);
|
||||||
|
|
||||||
if (status == INSUFFICIENT_CRYPTO_RESOURCES) {
|
if (status == INSUFFICIENT_CRYPTO_RESOURCES) {
|
||||||
LOGW("Usage table may be full, releasing oldest entry: size = %zu",
|
LOGW("Usage table may be full, releasing oldest entry: size = %zu",
|
||||||
usage_entry_info_.size());
|
usage_entry_info_.size());
|
||||||
status = ReleaseOldestEntry(metrics);
|
status = ReleaseOldestEntry(metrics);
|
||||||
if (status == NO_ERROR) {
|
if (status == NO_ERROR) {
|
||||||
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
status = CreateEntry(crypto_session, usage_entry_number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != NO_ERROR) return status;
|
if (status != NO_ERROR) return status;
|
||||||
|
|
||||||
LOGV("Locking to add entry");
|
status = RelocateNewEntry(crypto_session, usage_entry_number);
|
||||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
if (status != NO_ERROR) return status;
|
||||||
if (*usage_entry_number < usage_entry_info_.size()) {
|
|
||||||
LOGE(
|
|
||||||
"New entry number is smaller than table size: "
|
|
||||||
"entry_info_number = %u, table_size = %zu",
|
|
||||||
*usage_entry_number, usage_entry_info_.size());
|
|
||||||
return USAGE_INVALID_NEW_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*usage_entry_number > usage_entry_info_.size()) {
|
if (persistent_license) {
|
||||||
LOGW(
|
SetOfflineEntryInfo(*usage_entry_number, key_set_id, license_message);
|
||||||
"New entry number is larger than table size, resizing: "
|
|
||||||
"entry_info_number = %u, table_size = %zu",
|
|
||||||
*usage_entry_number, usage_entry_info_.size());
|
|
||||||
const size_t number_of_entries = usage_entry_info_.size();
|
|
||||||
usage_entry_info_.resize(*usage_entry_number + 1);
|
|
||||||
for (size_t i = number_of_entries; i < usage_entry_info_.size() - 1; ++i) {
|
|
||||||
usage_entry_info_[i].Clear();
|
|
||||||
}
|
|
||||||
} else /* *usage_entry_number == usage_entry_info_.size() */ {
|
|
||||||
usage_entry_info_.resize(*usage_entry_number + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
usage_entry_info_[*usage_entry_number].storage_type =
|
|
||||||
persistent_license ? kStorageLicense : kStorageUsageInfo;
|
|
||||||
usage_entry_info_[*usage_entry_number].key_set_id = key_set_id;
|
|
||||||
usage_entry_info_[*usage_entry_number].last_use_time = GetCurrentTime();
|
|
||||||
if (!persistent_license) {
|
|
||||||
usage_entry_info_[*usage_entry_number].usage_info_file_name =
|
|
||||||
usage_info_file_name;
|
|
||||||
usage_entry_info_[*usage_entry_number].offline_license_expiry_time = 0;
|
|
||||||
} else {
|
} else {
|
||||||
// Need to determine the expire time for offline licenses.
|
SetUsageInfoEntryInfo(*usage_entry_number, key_set_id,
|
||||||
video_widevine::License license;
|
usage_info_file_name);
|
||||||
if (license_message.size() > 0 &&
|
}
|
||||||
ParseLicenseFromLicenseMessage(license_message, &license)) {
|
|
||||||
const video_widevine::License::Policy& policy = license.policy();
|
status = RefitTable(crypto_session);
|
||||||
usage_entry_info_[*usage_entry_number].offline_license_expiry_time =
|
if (status != NO_ERROR) {
|
||||||
license.license_start_time() + policy.rental_duration_seconds() +
|
usage_entry_info_[*usage_entry_number].Clear();
|
||||||
policy.playback_duration_seconds();
|
return status;
|
||||||
} else {
|
|
||||||
// If the license duration cannot be determined for any reason, it
|
|
||||||
// is assumed to last at most 33 days.
|
|
||||||
usage_entry_info_[*usage_entry_number].offline_license_expiry_time =
|
|
||||||
usage_entry_info_[*usage_entry_number].last_use_time +
|
|
||||||
kDefaultExpireDuration;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call to update the usage table header, but don't store the usage
|
// Call to update the usage table header, but don't store the usage
|
||||||
@@ -334,8 +300,6 @@ CdmResponseType UsageTableHeader::AddEntry(
|
|||||||
usage_entry_info_[*usage_entry_number].Clear();
|
usage_entry_info_[*usage_entry_number].Clear();
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("usage_entry_number = %u", *usage_entry_number);
|
|
||||||
StoreTable(device_files_.get());
|
StoreTable(device_files_.get());
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
@@ -393,7 +357,14 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
|||||||
uint32_t usage_entry_number, bool defrag_table, DeviceFiles* device_files,
|
uint32_t usage_entry_number, bool defrag_table, DeviceFiles* device_files,
|
||||||
metrics::CryptoMetrics* metrics) {
|
metrics::CryptoMetrics* metrics) {
|
||||||
LOGD("usage_entry_number = %u", usage_entry_number);
|
LOGD("usage_entry_number = %u", usage_entry_number);
|
||||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
TableLock auto_lock(usage_table_header_lock_);
|
||||||
|
return InvalidateEntryInternal(usage_entry_number, defrag_table, device_files,
|
||||||
|
metrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType UsageTableHeader::InvalidateEntryInternal(
|
||||||
|
uint32_t usage_entry_number, bool defrag_table, DeviceFiles* device_files,
|
||||||
|
metrics::CryptoMetrics* metrics) {
|
||||||
// OEMCrypto does not have any concept of "deleting" an entry.
|
// OEMCrypto does not have any concept of "deleting" an entry.
|
||||||
// Instead, the CDM marks the entry's meta data as invalid (storage
|
// Instead, the CDM marks the entry's meta data as invalid (storage
|
||||||
// type unknown) and then performs a "defrag" of the OEMCrypto table.
|
// type unknown) and then performs a "defrag" of the OEMCrypto table.
|
||||||
@@ -436,14 +407,12 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
|||||||
|
|
||||||
size_t UsageTableHeader::UsageInfoCount() const {
|
size_t UsageTableHeader::UsageInfoCount() const {
|
||||||
LOGV("Locking to count usage info (streaming license) entries");
|
LOGV("Locking to count usage info (streaming license) entries");
|
||||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
|
||||||
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
||||||
EntryIsUsageInfo);
|
EntryIsUsageInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t UsageTableHeader::OfflineEntryCount() const {
|
size_t UsageTableHeader::OfflineEntryCount() const {
|
||||||
LOGV("Locking to count offline license entries");
|
LOGV("Locking to count offline license entries");
|
||||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
|
||||||
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
||||||
EntryIsOfflineLicense);
|
EntryIsOfflineLicense);
|
||||||
}
|
}
|
||||||
@@ -547,6 +516,151 @@ bool UsageTableHeader::DetermineTableCapacity(CryptoSession* crypto_session) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType UsageTableHeader::CreateEntry(
|
||||||
|
CryptoSession* const crypto_session, uint32_t* usage_entry_number) {
|
||||||
|
const CdmResponseType status =
|
||||||
|
crypto_session->CreateUsageEntry(usage_entry_number);
|
||||||
|
if (status != NO_ERROR) return status;
|
||||||
|
// If the new entry number is smaller than expected, then the usage
|
||||||
|
// table may be out of sync or OEMCrypto has been rolled back.
|
||||||
|
// Not safe to continue.
|
||||||
|
if (*usage_entry_number < usage_entry_info_.size()) {
|
||||||
|
LOGE(
|
||||||
|
"New entry number is smaller than table size: "
|
||||||
|
"entry_info_number = %u, table_size = %zu",
|
||||||
|
*usage_entry_number, usage_entry_info_.size());
|
||||||
|
return USAGE_INVALID_NEW_ENTRY;
|
||||||
|
}
|
||||||
|
LOGI("usage_entry_number = %u", *usage_entry_number);
|
||||||
|
const size_t previous_size = usage_entry_info_.size();
|
||||||
|
usage_entry_info_.resize(*usage_entry_number + 1);
|
||||||
|
if (*usage_entry_number > previous_size) {
|
||||||
|
LOGW(
|
||||||
|
"New entry number is larger than table size, resizing: "
|
||||||
|
"entry_info_number = %u, table_size = %zu",
|
||||||
|
*usage_entry_number, previous_size);
|
||||||
|
for (size_t i = previous_size; i < usage_entry_info_.size() - 1; ++i) {
|
||||||
|
usage_entry_info_[i].Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usage_entry_info_[*usage_entry_number].Clear();
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType UsageTableHeader::RelocateNewEntry(
|
||||||
|
CryptoSession* const crypto_session, uint32_t* usage_entry_number) {
|
||||||
|
static constexpr uint32_t kMinimumEntryNumber = 0;
|
||||||
|
const uint32_t initial_entry_number = *usage_entry_number;
|
||||||
|
if (initial_entry_number == kMinimumEntryNumber) {
|
||||||
|
// First entry in the table.
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
uint32_t unoccupied_entry_number = initial_entry_number;
|
||||||
|
for (uint32_t i = kMinimumEntryNumber; i < initial_entry_number; i++) {
|
||||||
|
if (IsEntryUnoccupied(i)) {
|
||||||
|
unoccupied_entry_number = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unoccupied_entry_number == initial_entry_number) {
|
||||||
|
// No open position.
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
const CdmResponseType status =
|
||||||
|
crypto_session->MoveUsageEntry(unoccupied_entry_number);
|
||||||
|
if (status == MOVE_USAGE_ENTRY_DESTINATION_IN_USE) {
|
||||||
|
// Not unexpected, there is a window of time between releasing the
|
||||||
|
// entry and closing the OEMCrypto session.
|
||||||
|
LOGD("Released entry still in use: index = %u", unoccupied_entry_number);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
if (status != NO_ERROR) return status;
|
||||||
|
LOGI("Entry moved: from_index = %u, to_index = %u", initial_entry_number,
|
||||||
|
unoccupied_entry_number);
|
||||||
|
*usage_entry_number = unoccupied_entry_number;
|
||||||
|
usage_entry_info_[unoccupied_entry_number] =
|
||||||
|
std::move(usage_entry_info_[initial_entry_number]);
|
||||||
|
usage_entry_info_[initial_entry_number].Clear();
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UsageTableHeader::IsEntryUnoccupied(
|
||||||
|
const uint32_t usage_entry_number) const {
|
||||||
|
if (usage_entry_info_[usage_entry_number].storage_type !=
|
||||||
|
kStorageTypeUnknown) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO(sigquit): Check that entry is not in use by another session.
|
||||||
|
// NOTE: The |storage_type| check will protect the integrity of the
|
||||||
|
// entry. Attempting to use an entry index that is used by another
|
||||||
|
// session is recoverable and will not affect any opened sessions.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsageTableHeader::SetOfflineEntryInfo(
|
||||||
|
const uint32_t usage_entry_number, const std::string& key_set_id,
|
||||||
|
const CdmKeyResponse& license_message) {
|
||||||
|
CdmUsageEntryInfo& entry_info = usage_entry_info_[usage_entry_number];
|
||||||
|
entry_info.Clear();
|
||||||
|
entry_info.storage_type = kStorageLicense;
|
||||||
|
entry_info.key_set_id = key_set_id;
|
||||||
|
entry_info.last_use_time = GetCurrentTime();
|
||||||
|
// Need to determine the expire time for offline licenses.
|
||||||
|
video_widevine::License license;
|
||||||
|
if (!license_message.empty() &&
|
||||||
|
ParseLicenseFromLicenseMessage(license_message, &license)) {
|
||||||
|
const video_widevine::License::Policy& policy = license.policy();
|
||||||
|
entry_info.offline_license_expiry_time = license.license_start_time() +
|
||||||
|
policy.rental_duration_seconds() +
|
||||||
|
policy.playback_duration_seconds();
|
||||||
|
} else {
|
||||||
|
// If the license duration cannot be determined for any reason, it
|
||||||
|
// is assumed to last at most 33 days.
|
||||||
|
entry_info.offline_license_expiry_time =
|
||||||
|
entry_info.last_use_time + kDefaultExpireDuration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsageTableHeader::SetUsageInfoEntryInfo(
|
||||||
|
const uint32_t usage_entry_number, const std::string& key_set_id,
|
||||||
|
const std::string& usage_info_file_name) {
|
||||||
|
CdmUsageEntryInfo& entry_info = usage_entry_info_[usage_entry_number];
|
||||||
|
entry_info.Clear();
|
||||||
|
entry_info.storage_type = kStorageUsageInfo;
|
||||||
|
entry_info.key_set_id = key_set_id;
|
||||||
|
entry_info.last_use_time = GetCurrentTime();
|
||||||
|
entry_info.usage_info_file_name = usage_info_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmResponseType UsageTableHeader::RefitTable(
|
||||||
|
CryptoSession* const crypto_session) {
|
||||||
|
// Remove all unoccupied entries at end of the table.
|
||||||
|
uint32_t entries_to_remove = 0;
|
||||||
|
for (uint32_t i = 0; i < usage_entry_info_.size(); i++) {
|
||||||
|
const uint32_t usage_entry_number = usage_entry_info_.size() - i - 1;
|
||||||
|
if (!IsEntryUnoccupied(usage_entry_number)) break;
|
||||||
|
++entries_to_remove;
|
||||||
|
}
|
||||||
|
if (entries_to_remove == 0) return NO_ERROR;
|
||||||
|
const uint32_t new_size = usage_entry_info_.size() - entries_to_remove;
|
||||||
|
const CdmResponseType status = crypto_session->ShrinkUsageTableHeader(
|
||||||
|
requested_security_level_, new_size, &usage_table_header_);
|
||||||
|
if (status == SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE) {
|
||||||
|
// This error likely indicates that another session has released
|
||||||
|
// its entry via a call to InvalidateEntry(), but has yet to close
|
||||||
|
// its OEMCrypto session.
|
||||||
|
// Safe to assume table state is not invalidated.
|
||||||
|
LOGW("Unexpected entry in use: range = [%u, %zu]", new_size,
|
||||||
|
usage_entry_info_.size() - 1);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
if (status != NO_ERROR) return status;
|
||||||
|
LOGD("Table shrunk: old_size = %zu, new_size = %u", usage_entry_info_.size(),
|
||||||
|
new_size);
|
||||||
|
usage_entry_info_.resize(new_size);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
CdmResponseType UsageTableHeader::MoveEntry(
|
CdmResponseType UsageTableHeader::MoveEntry(
|
||||||
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
|
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
|
||||||
uint32_t to_usage_entry_number, DeviceFiles* device_files,
|
uint32_t to_usage_entry_number, DeviceFiles* device_files,
|
||||||
@@ -1035,8 +1149,8 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry(
|
|||||||
const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type;
|
const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type;
|
||||||
|
|
||||||
const CdmResponseType status =
|
const CdmResponseType status =
|
||||||
InvalidateEntry(entry_number_to_delete, /* defrag_table = */ true,
|
InvalidateEntryInternal(entry_number_to_delete, /* defrag_table = */ true,
|
||||||
device_files_.get(), metrics);
|
device_files_.get(), metrics);
|
||||||
|
|
||||||
if (status != NO_ERROR) {
|
if (status != NO_ERROR) {
|
||||||
LOGE("Failed to invalidate oldest entry: status = %d",
|
LOGE("Failed to invalidate oldest entry: status = %d",
|
||||||
@@ -1051,7 +1165,7 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry(
|
|||||||
|
|
||||||
// Test only method.
|
// Test only method.
|
||||||
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
|
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
|
||||||
LOGV("usage_entry_number = %u", usage_entry_number);
|
LOGD("usage_entry_number = %u", usage_entry_number);
|
||||||
if (usage_entry_number >= usage_entry_info_.size()) {
|
if (usage_entry_number >= usage_entry_info_.size()) {
|
||||||
LOGE(
|
LOGE(
|
||||||
"Requested usage entry number is larger than table size: "
|
"Requested usage entry number is larger than table size: "
|
||||||
@@ -1188,8 +1302,6 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool UsageTableHeader::GetRemovalCandidate(uint32_t* entry_to_remove) {
|
bool UsageTableHeader::GetRemovalCandidate(uint32_t* entry_to_remove) {
|
||||||
LOGV("Locking to determine removal candidates");
|
|
||||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
|
||||||
const size_t lru_unexpired_threshold =
|
const size_t lru_unexpired_threshold =
|
||||||
HasUnlimitedTableCapacity()
|
HasUnlimitedTableCapacity()
|
||||||
? kLruUnexpiredThresholdFraction * size()
|
? kLruUnexpiredThresholdFraction * size()
|
||||||
|
|||||||
@@ -202,8 +202,16 @@ const DeviceFiles::CdmUsageData kCdmUsageData3 = {
|
|||||||
const std::vector<DeviceFiles::CdmUsageData> kEmptyUsageInfoUsageDataList;
|
const std::vector<DeviceFiles::CdmUsageData> kEmptyUsageInfoUsageDataList;
|
||||||
|
|
||||||
const std::vector<CdmUsageEntryInfo> kEmptyUsageEntryInfoVector;
|
const std::vector<CdmUsageEntryInfo> kEmptyUsageEntryInfoVector;
|
||||||
std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector;
|
const std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector = {
|
||||||
std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector;
|
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
||||||
|
kUsageEntryInfoStorageTypeUnknown};
|
||||||
|
const std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector = {
|
||||||
|
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
||||||
|
kUsageEntryInfoOfflineLicense2, kUsageEntryInfoSecureStop2,
|
||||||
|
kUsageEntryInfoOfflineLicense3, kUsageEntryInfoSecureStop3,
|
||||||
|
kUsageEntryInfoOfflineLicense4, kUsageEntryInfoSecureStop4,
|
||||||
|
kUsageEntryInfoOfflineLicense5, kUsageEntryInfoSecureStop5,
|
||||||
|
};
|
||||||
std::vector<CdmUsageEntryInfo> kOverFullUsageEntryInfoVector;
|
std::vector<CdmUsageEntryInfo> kOverFullUsageEntryInfoVector;
|
||||||
|
|
||||||
const CdmOfflineLicenseState kActiveLicenseState = kLicenseStateActive;
|
const CdmOfflineLicenseState kActiveLicenseState = kLicenseStateActive;
|
||||||
@@ -289,23 +297,6 @@ std::vector<CdmUsageEntryInfo> kUpgradedUsageEntryInfoList;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void InitVectorConstants() {
|
void InitVectorConstants() {
|
||||||
kUsageEntryInfoVector.clear();
|
|
||||||
kUsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1);
|
|
||||||
kUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1);
|
|
||||||
kUsageEntryInfoVector.push_back(kUsageEntryInfoStorageTypeUnknown);
|
|
||||||
|
|
||||||
k10UsageEntryInfoVector.clear();
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense2);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop2);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense3);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop3);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense4);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop4);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense5);
|
|
||||||
k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop5);
|
|
||||||
|
|
||||||
kOverFullUsageEntryInfoVector.clear();
|
kOverFullUsageEntryInfoVector.clear();
|
||||||
for (size_t i = 0; i < (kDefaultTableCapacity + 1); ++i) {
|
for (size_t i = 0; i < (kDefaultTableCapacity + 1); ++i) {
|
||||||
switch (i % 4) {
|
switch (i % 4) {
|
||||||
@@ -479,8 +470,9 @@ class MockCryptoSession : public TestCryptoSession {
|
|||||||
class MockUsageTableHeader : public UsageTableHeader {
|
class MockUsageTableHeader : public UsageTableHeader {
|
||||||
public:
|
public:
|
||||||
MockUsageTableHeader() : UsageTableHeader() {}
|
MockUsageTableHeader() : UsageTableHeader() {}
|
||||||
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
|
MOCK_METHOD4(InvalidateEntryInternal,
|
||||||
metrics::CryptoMetrics*));
|
CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||||
|
metrics::CryptoMetrics*));
|
||||||
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
|
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
|
||||||
const CdmKeySetId&, const std::string&,
|
const CdmKeySetId&, const std::string&,
|
||||||
const CdmKeyResponse&, uint32_t*));
|
const CdmKeyResponse&, uint32_t*));
|
||||||
@@ -1041,25 +1033,53 @@ TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) {
|
|||||||
kEmptyString /* license */, &usage_entry_number));
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initial Test state:
|
||||||
|
// 1. Table has a few entries, one of which is unoccupied.
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. A successful call to OEMCrypto to create an entry
|
||||||
|
// at the end of the current table
|
||||||
|
// b. Moving the new entry to the unoccupied entry index
|
||||||
|
// c. Shrink table to remove the now empty entry slot created in (a)
|
||||||
|
// d. Storing the new updated usage table
|
||||||
|
//
|
||||||
|
// Storage type Usage entries
|
||||||
|
// at start at end
|
||||||
|
// ============= ======== ======
|
||||||
|
// Offline License 1 0 0
|
||||||
|
// Secure Stop 1 1 1
|
||||||
|
// Storage Type Unknown 2 Replaced
|
||||||
|
// Offline License 2 DNE 2
|
||||||
|
//
|
||||||
|
// DNE = Does Not Exist
|
||||||
|
//
|
||||||
|
// # of usage entries 3 3
|
||||||
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
|
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
|
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
|
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
|
||||||
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
||||||
kUsageEntryInfoVector;
|
kUsageEntryInfoVector;
|
||||||
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
|
expect_usage_entry_info_vector[final_usage_entry_number] =
|
||||||
expect_usage_entry_info_vector[expect_usage_entry_number] =
|
|
||||||
kUsageEntryInfoOfflineLicense2;
|
kUsageEntryInfoOfflineLicense2;
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
|
||||||
|
.WillOnce(Return(NO_ERROR));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*crypto_session_,
|
||||||
|
ShrinkUsageTableHeader(kLevelDefault,
|
||||||
|
expect_usage_entry_info_vector.size(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(
|
||||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
|
||||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
Return(NO_ERROR)));
|
||||||
EXPECT_CALL(*device_files_,
|
EXPECT_CALL(*device_files_,
|
||||||
StoreUsageTableInfo(
|
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
|
||||||
kAnotherUsageTableHeader,
|
expect_usage_entry_info_vector))
|
||||||
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
uint32_t usage_entry_number = 0;
|
uint32_t usage_entry_number = 0;
|
||||||
@@ -1070,29 +1090,182 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
|
|||||||
kUsageEntryInfoOfflineLicense2.key_set_id,
|
kUsageEntryInfoOfflineLicense2.key_set_id,
|
||||||
kUsageEntryInfoOfflineLicense2.usage_info_file_name,
|
kUsageEntryInfoOfflineLicense2.usage_info_file_name,
|
||||||
kEmptyString /* license */, &usage_entry_number));
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
|
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initial Test state:
|
||||||
|
// 1. Table has a few entries, one of which is unoccupied.
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. A successful call to OEMCrypto to create an entry
|
||||||
|
// at the end of the current table
|
||||||
|
// b. Moving the new entry to the unoccupied entry index
|
||||||
|
// c. Shrink table to remove the now empty entry slot created in (a)
|
||||||
|
// d. Storing the new updated usage table
|
||||||
|
//
|
||||||
|
// Storage type Usage entries
|
||||||
|
// at start at end
|
||||||
|
// ============= ======== ======
|
||||||
|
// Offline License 1 0 0
|
||||||
|
// Secure Stop 1 1 1
|
||||||
|
// Storage Type Unknown 2 Replaced
|
||||||
|
// Secure Stop 2 DNE 2
|
||||||
|
//
|
||||||
|
// DNE = Does Not Exist
|
||||||
|
//
|
||||||
|
// # of usage entries 3 3
|
||||||
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
|
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
|
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
|
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
|
||||||
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
||||||
kUsageEntryInfoVector;
|
kUsageEntryInfoVector;
|
||||||
|
expect_usage_entry_info_vector[final_usage_entry_number] =
|
||||||
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
|
|
||||||
expect_usage_entry_info_vector[expect_usage_entry_number] =
|
|
||||||
kUsageEntryInfoSecureStop2;
|
kUsageEntryInfoSecureStop2;
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
|
||||||
|
.WillOnce(Return(NO_ERROR));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*crypto_session_,
|
||||||
|
ShrinkUsageTableHeader(kLevelDefault,
|
||||||
|
expect_usage_entry_info_vector.size(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(
|
||||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*device_files_,
|
||||||
|
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
|
||||||
|
expect_usage_entry_info_vector))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
uint32_t usage_entry_number = 0;
|
||||||
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
usage_table_header_->AddEntry(
|
||||||
|
crypto_session_,
|
||||||
|
kUsageEntryInfoSecureStop2.storage_type == kStorageLicense,
|
||||||
|
kUsageEntryInfoSecureStop2.key_set_id,
|
||||||
|
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||||
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
|
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial Test state:
|
||||||
|
// 1. Table has a few entries, one of which is unoccupied.
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. An odd but successful call to OEMCrypto to create an entry
|
||||||
|
// beyond the end of the current table
|
||||||
|
// b. Empty entries will fill the gap between the original table
|
||||||
|
// and the new entry
|
||||||
|
// c. Move the new entry to the *lowest* unoccupied entry index
|
||||||
|
// d. Shrink table to remove the now empty entry slot created in (a)
|
||||||
|
// and the filler gap entries created in (b)
|
||||||
|
// e. Storing the new updated usage table
|
||||||
|
//
|
||||||
|
// Storage type Usage entries
|
||||||
|
// at start at end
|
||||||
|
// ============= ======== ======
|
||||||
|
// Offline License 1 0 0
|
||||||
|
// Secure Stop 1 1 1
|
||||||
|
// Storage Type Unknown 2 Replaced
|
||||||
|
// Storage Type Unknown DNE Removed (never stored)
|
||||||
|
// Storage Type Unknown DNE Removed (never stored)
|
||||||
|
// Storage Type Unknown DNE Removed (never stored)
|
||||||
|
// Secure Stop 2 DNE 2
|
||||||
|
//
|
||||||
|
// DNE = Does Not Exist
|
||||||
|
//
|
||||||
|
// # of usage entries 3 3
|
||||||
|
TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
|
||||||
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
|
const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
|
const size_t skip_usage_entries = 3;
|
||||||
|
const uint32_t initial_usage_entry_number =
|
||||||
|
next_usage_entry_number + skip_usage_entries;
|
||||||
|
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
|
||||||
|
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
||||||
|
kUsageEntryInfoVector;
|
||||||
|
expect_usage_entry_info_vector[final_usage_entry_number] =
|
||||||
|
kUsageEntryInfoSecureStop2;
|
||||||
|
|
||||||
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
|
||||||
|
.WillOnce(Return(NO_ERROR));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*crypto_session_,
|
||||||
|
ShrinkUsageTableHeader(kLevelDefault,
|
||||||
|
expect_usage_entry_info_vector.size(), NotNull()))
|
||||||
|
.WillOnce(
|
||||||
|
DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*device_files_,
|
||||||
|
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
|
||||||
|
expect_usage_entry_info_vector))
|
||||||
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
|
uint32_t usage_entry_number = 0;
|
||||||
|
EXPECT_EQ(NO_ERROR,
|
||||||
|
usage_table_header_->AddEntry(
|
||||||
|
crypto_session_,
|
||||||
|
kUsageEntryInfoSecureStop2.storage_type == kStorageLicense,
|
||||||
|
kUsageEntryInfoSecureStop2.key_set_id,
|
||||||
|
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||||
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
|
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial Test state:
|
||||||
|
// 1. Table has a few entries, one of which is unoccupied.
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. A successful call to OEMCrypto to create an entry
|
||||||
|
// at the end of the current table
|
||||||
|
// b. Cannot move the new entry to the unoccupied entry index
|
||||||
|
// due to entry being in use (according to OEMCrypto)
|
||||||
|
// c. Storing the new updated usage table
|
||||||
|
//
|
||||||
|
// Storage type Usage entries
|
||||||
|
// at start at end
|
||||||
|
// ============= ======== ======
|
||||||
|
// Offline License 1 0 0
|
||||||
|
// Secure Stop 1 1 1
|
||||||
|
// Storage Type Unknown 2 2
|
||||||
|
// Secure Stop 2 DNE 3
|
||||||
|
//
|
||||||
|
// DNE = Does Not Exist
|
||||||
|
//
|
||||||
|
// # of usage entries 3 4
|
||||||
|
TEST_F(UsageTableHeaderTest, AddEntry_CannotMoveNewEntry) {
|
||||||
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
|
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
|
const uint32_t attempted_usage_entry_number =
|
||||||
|
kUsageEntryInfoVector.size() - 1;
|
||||||
|
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
||||||
|
kUsageEntryInfoVector;
|
||||||
|
expect_usage_entry_info_vector.push_back(kUsageEntryInfoSecureStop2);
|
||||||
|
|
||||||
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
|
.WillOnce(
|
||||||
|
DoAll(SetArgPointee<0>(final_usage_entry_number), Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, MoveUsageEntry(attempted_usage_entry_number))
|
||||||
|
.WillOnce(Return(MOVE_USAGE_ENTRY_DESTINATION_IN_USE));
|
||||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(
|
||||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
EXPECT_CALL(*device_files_,
|
EXPECT_CALL(*device_files_,
|
||||||
StoreUsageTableInfo(
|
StoreUsageTableInfo(kAnotherUsageTableHeader,
|
||||||
kAnotherUsageTableHeader,
|
expect_usage_entry_info_vector))
|
||||||
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
uint32_t usage_entry_number = 0;
|
uint32_t usage_entry_number = 0;
|
||||||
@@ -1103,32 +1276,58 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
|
|||||||
kUsageEntryInfoSecureStop2.key_set_id,
|
kUsageEntryInfoSecureStop2.key_set_id,
|
||||||
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||||
kEmptyString /* license */, &usage_entry_number));
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
|
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
|
// Initial Test state:
|
||||||
|
// 1. Table has a few entries, one of which is unoccupied.
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. A successful call to OEMCrypto to create an entry
|
||||||
|
// at the end of the current table
|
||||||
|
// b. Moving the new entry to the unoccupied entry index
|
||||||
|
// c. Fail to shrink table due to occupied entry
|
||||||
|
// d. Storing the new updated usage table
|
||||||
|
//
|
||||||
|
// Storage type Usage entries
|
||||||
|
// at start at end
|
||||||
|
// ============= ======== ======
|
||||||
|
// Offline License 1 0 0
|
||||||
|
// Secure Stop 1 1 1
|
||||||
|
// Storage Type Unknown 2 Replaced
|
||||||
|
// Secure Stop 2 DNE 2
|
||||||
|
// Storage Type Unknown DNE 3 (created when new entry moved)
|
||||||
|
//
|
||||||
|
// DNE = Does Not Exist
|
||||||
|
//
|
||||||
|
// # of usage entries 3 4
|
||||||
|
TEST_F(UsageTableHeaderTest, AddEntry_CannotShinkAfterMove) {
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
|
const uint32_t initial_usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
size_t skip_usage_entries = 3;
|
const uint32_t final_usage_entry_number = kUsageEntryInfoVector.size() - 1;
|
||||||
uint32_t expect_usage_entry_number =
|
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
|
||||||
next_usage_entry_number + skip_usage_entries;
|
kUsageEntryInfoVector;
|
||||||
|
expect_usage_entry_info_vector[final_usage_entry_number] =
|
||||||
|
kUsageEntryInfoSecureStop2;
|
||||||
|
expect_usage_entry_info_vector.push_back(kUsageEntryInfoStorageTypeUnknown);
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(DoAll(SetArgPointee<0>(initial_usage_entry_number),
|
||||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
Return(NO_ERROR)));
|
||||||
|
EXPECT_CALL(*crypto_session_, MoveUsageEntry(final_usage_entry_number))
|
||||||
|
.WillOnce(Return(NO_ERROR));
|
||||||
|
EXPECT_CALL(
|
||||||
|
*crypto_session_,
|
||||||
|
ShrinkUsageTableHeader(
|
||||||
|
kLevelDefault, expect_usage_entry_info_vector.size() - 1, NotNull()))
|
||||||
|
.WillOnce(Return(SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE));
|
||||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(
|
||||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(*device_files_,
|
||||||
*device_files_,
|
StoreUsageTableInfo(kAnotherUsageTableHeader,
|
||||||
StoreUsageTableInfo(
|
expect_usage_entry_info_vector))
|
||||||
kAnotherUsageTableHeader,
|
|
||||||
UnorderedElementsAre(
|
|
||||||
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
|
||||||
kUsageEntryInfoStorageTypeUnknown,
|
|
||||||
kUsageEntryInfoStorageTypeUnknown,
|
|
||||||
kUsageEntryInfoStorageTypeUnknown,
|
|
||||||
kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2)))
|
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
uint32_t usage_entry_number = 0;
|
uint32_t usage_entry_number = 0;
|
||||||
@@ -1139,9 +1338,21 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
|
|||||||
kUsageEntryInfoSecureStop2.key_set_id,
|
kUsageEntryInfoSecureStop2.key_set_id,
|
||||||
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||||
kEmptyString /* license */, &usage_entry_number));
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
EXPECT_EQ(expect_usage_entry_number, usage_entry_number);
|
EXPECT_EQ(final_usage_entry_number, usage_entry_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initial Test state:
|
||||||
|
// 1. Table is full with entries
|
||||||
|
// 2. An entry-less session requires a new entry.
|
||||||
|
//
|
||||||
|
// Attempting to add an entry will result in:
|
||||||
|
// a. First call to OEMCrypto to create an entry fails due to
|
||||||
|
// table being full
|
||||||
|
// b. One of the existing entries will be removed, shrinking table
|
||||||
|
// by one
|
||||||
|
// c. Table will be stored
|
||||||
|
// d. Second call to OEMCrypto to create an entry will succeed
|
||||||
|
// e. Storing the new updated usage table
|
||||||
TEST_F(UsageTableHeaderTest,
|
TEST_F(UsageTableHeaderTest,
|
||||||
AddEntry_CreateUsageEntryFailsOnce_SucceedsSecondTime) {
|
AddEntry_CreateUsageEntryFailsOnce_SucceedsSecondTime) {
|
||||||
// Initialize and setup
|
// Initialize and setup
|
||||||
@@ -1150,32 +1361,33 @@ TEST_F(UsageTableHeaderTest,
|
|||||||
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
|
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
|
||||||
k10UsageEntryInfoVector;
|
k10UsageEntryInfoVector;
|
||||||
|
|
||||||
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
uint32_t invalidated_entry = 0; // Randomly chosen by UsageTableHeader
|
||||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
|
||||||
|
|
||||||
const uint32_t expected_usage_entry_number =
|
const uint32_t expected_usage_entry_number =
|
||||||
k10UsageEntryInfoVector.size() - 1;
|
k10UsageEntryInfoVector.size() - 1;
|
||||||
|
|
||||||
// Setup expectations
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
|
// First call fails
|
||||||
|
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES))
|
||||||
|
// Second call succeeds
|
||||||
|
.WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number),
|
||||||
|
Return(NO_ERROR)));
|
||||||
|
// Covers all other expectations.
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
EXPECT_CALL(*mock_usage_table_header,
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
InvalidateEntryInternal(_, true, device_files_, NotNull()))
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
|
.WillOnce(DoAll(SaveArg<0>(&invalidated_entry),
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
||||||
Return(NO_ERROR)));
|
Return(NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES))
|
|
||||||
.WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||||
.WillOnce(
|
.WillOnce(
|
||||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||||
|
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
||||||
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _))
|
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _))
|
||||||
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
|
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
|
||||||
|
|
||||||
// Now invoke the method under test
|
// Now invoke the method under test
|
||||||
uint32_t usage_entry_number;
|
uint32_t usage_entry_number = 0;
|
||||||
EXPECT_EQ(NO_ERROR,
|
EXPECT_EQ(NO_ERROR,
|
||||||
mock_usage_table_header->SuperAddEntry(
|
mock_usage_table_header->SuperAddEntry(
|
||||||
crypto_session_,
|
crypto_session_,
|
||||||
@@ -1187,17 +1399,15 @@ TEST_F(UsageTableHeaderTest,
|
|||||||
// Verify added/deleted usage entry number and entries
|
// Verify added/deleted usage entry number and entries
|
||||||
EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
|
EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
|
||||||
|
|
||||||
EXPECT_LE(0u, usage_entry_number_first_to_be_deleted);
|
EXPECT_LE(0u, invalidated_entry);
|
||||||
EXPECT_LE(usage_entry_number_first_to_be_deleted,
|
EXPECT_LE(invalidated_entry, k10UsageEntryInfoVector.size() - 1);
|
||||||
usage_entry_info_vector_at_start.size() - 1);
|
|
||||||
|
|
||||||
std::vector<CdmUsageEntryInfo> expected_usage_entries =
|
std::vector<CdmUsageEntryInfo> expected_usage_entries =
|
||||||
usage_entry_info_vector_at_start;
|
k10UsageEntryInfoVector;
|
||||||
expected_usage_entries[usage_entry_number_first_to_be_deleted] =
|
|
||||||
expected_usage_entries[expected_usage_entries.size() - 1];
|
|
||||||
expected_usage_entries.resize(expected_usage_entries.size() - 1);
|
|
||||||
expected_usage_entries.push_back(kUsageEntryInfoOfflineLicense6);
|
|
||||||
|
|
||||||
|
expected_usage_entries[invalidated_entry] = expected_usage_entries.back();
|
||||||
|
expected_usage_entries.pop_back();
|
||||||
|
expected_usage_entries.push_back(kUsageEntryInfoOfflineLicense6);
|
||||||
EXPECT_EQ(expected_usage_entries, final_usage_entries);
|
EXPECT_EQ(expected_usage_entries, final_usage_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,7 +1420,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
|
|||||||
|
|
||||||
// Setup expectations
|
// Setup expectations
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
EXPECT_CALL(*mock_usage_table_header,
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
InvalidateEntryInternal(_, true, device_files_, NotNull()))
|
||||||
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
||||||
Return(NO_ERROR)));
|
Return(NO_ERROR)));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user