New usage entries are moved lower after creation.
[ Merge of http://go/wvgerrit/124004 ] When the CDM creates a new usage entry for an offline or streaming license, the new entry is immediately moved to the lowest available entry index that has been marked as vacant (kStorageTypeUnknown). When a license is released, its meta data that is managed by the CDM is cleared; however, the usage entry's index is marked vacant, but it is not released. This creates wasted entry space in the usage table. Unfortunately, defragging the table is computationally expensive and may not be able to actually free up much space depending on when it is performed. For a typical user, this will likely not be an issue as the table can get quite large compared to the number of licenses an app uses and the table is partially cleaned on each boot. GTS tests, however, have reached a point where they fill the usage table before all tests are complete. This is causing many unexpected failures for devices. Most of these tests release their license, but the CDM never reaches a state where it can clean up the table. By moving newly created entries to the lowest available index directly after creating the entries, the table never needs to grow unless all entries are in use. Clean up is now almost never required. Bug: 180639135 Bug: 180638990 Bug: 180638530 Test: MediaDrmTest#testWidevineApi28 Change-Id: I1a68d90d51384094298b27037778747ce7435374
This commit is contained in:
@@ -154,6 +154,46 @@ class UsageTableHeader {
|
||||
bool DetermineTableCapacity(CryptoSession* crypto_session);
|
||||
|
||||
// == 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 */,
|
||||
const CdmUsageEntry& from_usage_entry,
|
||||
@@ -279,6 +319,7 @@ class UsageTableHeader {
|
||||
#if defined(UNIT_TEST)
|
||||
// Test related declarations
|
||||
friend class UsageTableHeaderTest;
|
||||
|
||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_NoneOfTable);
|
||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_PartOfTable);
|
||||
FRIEND_TEST(UsageTableHeaderTest, Shrink_AllOfTable);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
namespace wvcdm {
|
||||
namespace {
|
||||
using TableLock = std::unique_lock<std::mutex>;
|
||||
|
||||
const std::string kEmptyString;
|
||||
const wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||
|
||||
@@ -252,75 +254,39 @@ CdmResponseType UsageTableHeader::AddEntry(
|
||||
CryptoSession* crypto_session, bool persistent_license,
|
||||
const CdmKeySetId& key_set_id, const std::string& usage_info_file_name,
|
||||
const CdmKeyResponse& license_message, uint32_t* usage_entry_number) {
|
||||
LOGD("oec_session_id = %u, type = %s, current_size = %zu",
|
||||
crypto_session->oec_session_id(),
|
||||
LOGD("key_set_id = %s, type = %s, current_size = %zu", IdToString(key_set_id),
|
||||
persistent_license ? "OfflineLicense" : "Streaming",
|
||||
usage_entry_info_.size());
|
||||
|
||||
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
|
||||
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) {
|
||||
LOGW("Usage table may be full, releasing oldest entry: size = %zu",
|
||||
usage_entry_info_.size());
|
||||
status = ReleaseOldestEntry(metrics);
|
||||
if (status == NO_ERROR) {
|
||||
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||
status = CreateEntry(crypto_session, usage_entry_number);
|
||||
}
|
||||
}
|
||||
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
LOGV("Locking to add entry");
|
||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
||||
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;
|
||||
}
|
||||
status = RelocateNewEntry(crypto_session, usage_entry_number);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (*usage_entry_number > usage_entry_info_.size()) {
|
||||
LOGW(
|
||||
"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;
|
||||
if (persistent_license) {
|
||||
SetOfflineEntryInfo(*usage_entry_number, key_set_id, license_message);
|
||||
} else {
|
||||
// Need to determine the expire time for offline licenses.
|
||||
video_widevine::License license;
|
||||
if (license_message.size() > 0 &&
|
||||
ParseLicenseFromLicenseMessage(license_message, &license)) {
|
||||
const video_widevine::License::Policy& policy = license.policy();
|
||||
usage_entry_info_[*usage_entry_number].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.
|
||||
usage_entry_info_[*usage_entry_number].offline_license_expiry_time =
|
||||
usage_entry_info_[*usage_entry_number].last_use_time +
|
||||
kDefaultExpireDuration;
|
||||
}
|
||||
SetUsageInfoEntryInfo(*usage_entry_number, key_set_id,
|
||||
usage_info_file_name);
|
||||
}
|
||||
|
||||
status = RefitTable(crypto_session);
|
||||
if (status != NO_ERROR) {
|
||||
usage_entry_info_[*usage_entry_number].Clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
// 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();
|
||||
return status;
|
||||
}
|
||||
|
||||
LOGI("usage_entry_number = %u", *usage_entry_number);
|
||||
StoreTable(device_files_.get());
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -393,7 +357,14 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
||||
uint32_t usage_entry_number, bool defrag_table, DeviceFiles* device_files,
|
||||
metrics::CryptoMetrics* metrics) {
|
||||
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.
|
||||
// Instead, the CDM marks the entry's meta data as invalid (storage
|
||||
// type unknown) and then performs a "defrag" of the OEMCrypto table.
|
||||
@@ -436,14 +407,12 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
||||
|
||||
size_t UsageTableHeader::UsageInfoCount() const {
|
||||
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(),
|
||||
EntryIsUsageInfo);
|
||||
}
|
||||
|
||||
size_t UsageTableHeader::OfflineEntryCount() const {
|
||||
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(),
|
||||
EntryIsOfflineLicense);
|
||||
}
|
||||
@@ -547,6 +516,151 @@ bool UsageTableHeader::DetermineTableCapacity(CryptoSession* crypto_session) {
|
||||
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(
|
||||
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
|
||||
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 CdmResponseType status =
|
||||
InvalidateEntry(entry_number_to_delete, /* defrag_table = */ true,
|
||||
device_files_.get(), metrics);
|
||||
InvalidateEntryInternal(entry_number_to_delete, /* defrag_table = */ true,
|
||||
device_files_.get(), metrics);
|
||||
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Failed to invalidate oldest entry: status = %d",
|
||||
@@ -1051,7 +1165,7 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry(
|
||||
|
||||
// Test only method.
|
||||
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()) {
|
||||
LOGE(
|
||||
"Requested usage entry number is larger than table size: "
|
||||
@@ -1188,8 +1302,6 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
}
|
||||
|
||||
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 =
|
||||
HasUnlimitedTableCapacity()
|
||||
? kLruUnexpiredThresholdFraction * size()
|
||||
|
||||
@@ -202,8 +202,16 @@ const DeviceFiles::CdmUsageData kCdmUsageData3 = {
|
||||
const std::vector<DeviceFiles::CdmUsageData> kEmptyUsageInfoUsageDataList;
|
||||
|
||||
const std::vector<CdmUsageEntryInfo> kEmptyUsageEntryInfoVector;
|
||||
std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector;
|
||||
std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector;
|
||||
const std::vector<CdmUsageEntryInfo> kUsageEntryInfoVector = {
|
||||
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
||||
kUsageEntryInfoStorageTypeUnknown};
|
||||
const std::vector<CdmUsageEntryInfo> k10UsageEntryInfoVector = {
|
||||
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
||||
kUsageEntryInfoOfflineLicense2, kUsageEntryInfoSecureStop2,
|
||||
kUsageEntryInfoOfflineLicense3, kUsageEntryInfoSecureStop3,
|
||||
kUsageEntryInfoOfflineLicense4, kUsageEntryInfoSecureStop4,
|
||||
kUsageEntryInfoOfflineLicense5, kUsageEntryInfoSecureStop5,
|
||||
};
|
||||
std::vector<CdmUsageEntryInfo> kOverFullUsageEntryInfoVector;
|
||||
|
||||
const CdmOfflineLicenseState kActiveLicenseState = kLicenseStateActive;
|
||||
@@ -289,23 +297,6 @@ std::vector<CdmUsageEntryInfo> kUpgradedUsageEntryInfoList;
|
||||
namespace {
|
||||
|
||||
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();
|
||||
for (size_t i = 0; i < (kDefaultTableCapacity + 1); ++i) {
|
||||
switch (i % 4) {
|
||||
@@ -479,8 +470,9 @@ class MockCryptoSession : public TestCryptoSession {
|
||||
class MockUsageTableHeader : public UsageTableHeader {
|
||||
public:
|
||||
MockUsageTableHeader() : UsageTableHeader() {}
|
||||
MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||
metrics::CryptoMetrics*));
|
||||
MOCK_METHOD4(InvalidateEntryInternal,
|
||||
CdmResponseType(uint32_t, bool, DeviceFiles*,
|
||||
metrics::CryptoMetrics*));
|
||||
MOCK_METHOD6(AddEntry, CdmResponseType(CryptoSession*, bool,
|
||||
const CdmKeySetId&, const std::string&,
|
||||
const CdmKeyResponse&, uint32_t*));
|
||||
@@ -1041,25 +1033,53 @@ TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) {
|
||||
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) {
|
||||
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 =
|
||||
kUsageEntryInfoVector;
|
||||
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
|
||||
expect_usage_entry_info_vector[expect_usage_entry_number] =
|
||||
expect_usage_entry_info_vector[final_usage_entry_number] =
|
||||
kUsageEntryInfoOfflineLicense2;
|
||||
|
||||
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<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>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
.WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageTableHeader),
|
||||
Return(NO_ERROR)));
|
||||
EXPECT_CALL(*device_files_,
|
||||
StoreUsageTableInfo(
|
||||
kAnotherUsageTableHeader,
|
||||
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
|
||||
StoreUsageTableInfo(kYetAnotherUsageTableHeader,
|
||||
expect_usage_entry_info_vector))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
uint32_t usage_entry_number = 0;
|
||||
@@ -1070,29 +1090,182 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
|
||||
kUsageEntryInfoOfflineLicense2.key_set_id,
|
||||
kUsageEntryInfoOfflineLicense2.usage_info_file_name,
|
||||
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) {
|
||||
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 =
|
||||
kUsageEntryInfoVector;
|
||||
|
||||
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
|
||||
expect_usage_entry_info_vector[expect_usage_entry_number] =
|
||||
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<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()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
EXPECT_CALL(*device_files_,
|
||||
StoreUsageTableInfo(
|
||||
kAnotherUsageTableHeader,
|
||||
UnorderedElementsAreArray(expect_usage_entry_info_vector)))
|
||||
StoreUsageTableInfo(kAnotherUsageTableHeader,
|
||||
expect_usage_entry_info_vector))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
uint32_t usage_entry_number = 0;
|
||||
@@ -1103,32 +1276,58 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
|
||||
kUsageEntryInfoSecureStop2.key_set_id,
|
||||
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||
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);
|
||||
const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
|
||||
size_t skip_usage_entries = 3;
|
||||
uint32_t expect_usage_entry_number =
|
||||
next_usage_entry_number + skip_usage_entries;
|
||||
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 =
|
||||
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()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
|
||||
.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() - 1, NotNull()))
|
||||
.WillOnce(Return(SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE));
|
||||
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
EXPECT_CALL(
|
||||
*device_files_,
|
||||
StoreUsageTableInfo(
|
||||
kAnotherUsageTableHeader,
|
||||
UnorderedElementsAre(
|
||||
kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1,
|
||||
kUsageEntryInfoStorageTypeUnknown,
|
||||
kUsageEntryInfoStorageTypeUnknown,
|
||||
kUsageEntryInfoStorageTypeUnknown,
|
||||
kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2)))
|
||||
EXPECT_CALL(*device_files_,
|
||||
StoreUsageTableInfo(kAnotherUsageTableHeader,
|
||||
expect_usage_entry_info_vector))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
uint32_t usage_entry_number = 0;
|
||||
@@ -1139,9 +1338,21 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
|
||||
kUsageEntryInfoSecureStop2.key_set_id,
|
||||
kUsageEntryInfoSecureStop2.usage_info_file_name,
|
||||
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,
|
||||
AddEntry_CreateUsageEntryFailsOnce_SucceedsSecondTime) {
|
||||
// Initialize and setup
|
||||
@@ -1150,32 +1361,33 @@ TEST_F(UsageTableHeaderTest,
|
||||
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
|
||||
k10UsageEntryInfoVector;
|
||||
|
||||
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
||||
uint32_t invalidated_entry = 0; // Randomly chosen by UsageTableHeader
|
||||
|
||||
const uint32_t expected_usage_entry_number =
|
||||
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,
|
||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
|
||||
InvalidateEntryInternal(_, true, device_files_, NotNull()))
|
||||
.WillOnce(DoAll(SaveArg<0>(&invalidated_entry),
|
||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
||||
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()))
|
||||
.WillOnce(
|
||||
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
|
||||
|
||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
||||
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _))
|
||||
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
|
||||
|
||||
// Now invoke the method under test
|
||||
uint32_t usage_entry_number;
|
||||
uint32_t usage_entry_number = 0;
|
||||
EXPECT_EQ(NO_ERROR,
|
||||
mock_usage_table_header->SuperAddEntry(
|
||||
crypto_session_,
|
||||
@@ -1187,17 +1399,15 @@ TEST_F(UsageTableHeaderTest,
|
||||
// Verify added/deleted usage entry number and entries
|
||||
EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
|
||||
|
||||
EXPECT_LE(0u, usage_entry_number_first_to_be_deleted);
|
||||
EXPECT_LE(usage_entry_number_first_to_be_deleted,
|
||||
usage_entry_info_vector_at_start.size() - 1);
|
||||
EXPECT_LE(0u, invalidated_entry);
|
||||
EXPECT_LE(invalidated_entry, k10UsageEntryInfoVector.size() - 1);
|
||||
|
||||
std::vector<CdmUsageEntryInfo> expected_usage_entries =
|
||||
usage_entry_info_vector_at_start;
|
||||
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);
|
||||
k10UsageEntryInfoVector;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1210,7 +1420,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
|
||||
|
||||
// Setup expectations
|
||||
EXPECT_CALL(*mock_usage_table_header,
|
||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
||||
InvalidateEntryInternal(_, true, device_files_, NotNull()))
|
||||
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
||||
Return(NO_ERROR)));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user