Update AddEntry() for usage table changes. am: 66e3d69300 am: 4c97abd57a am: c15d453a9d
Change-Id: Ia8e996d1193cfabf99d854fee6e3f0eefcced1e5
This commit is contained in:
@@ -93,6 +93,13 @@ class UsageTableHeader {
|
|||||||
|
|
||||||
size_t potential_table_capacity() const { return potential_table_capacity_; }
|
size_t potential_table_capacity() const { return potential_table_capacity_; }
|
||||||
|
|
||||||
|
// Returns the number of entries currently tracked by the CDM that
|
||||||
|
// are related to usage info (streaming licenses).
|
||||||
|
size_t UsageInfoCount() const;
|
||||||
|
// Returns the number of entries currently tracked by the CDM that
|
||||||
|
// are related to offline licenses.
|
||||||
|
size_t OfflineEntryCount() const;
|
||||||
|
|
||||||
const std::vector<CdmUsageEntryInfo>& usage_entry_info() const {
|
const std::vector<CdmUsageEntryInfo>& usage_entry_info() const {
|
||||||
return usage_entry_info_;
|
return usage_entry_info_;
|
||||||
}
|
}
|
||||||
@@ -139,6 +146,10 @@ class UsageTableHeader {
|
|||||||
CdmResponseType DefragTable(DeviceFiles* device_files,
|
CdmResponseType DefragTable(DeviceFiles* device_files,
|
||||||
metrics::CryptoMetrics* metrics);
|
metrics::CryptoMetrics* metrics);
|
||||||
|
|
||||||
|
// This will use the LRU algorithm to decide which entry is to be
|
||||||
|
// evicted.
|
||||||
|
CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics);
|
||||||
|
|
||||||
virtual bool is_inited() { return is_inited_; }
|
virtual bool is_inited() { return is_inited_; }
|
||||||
|
|
||||||
// Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a
|
// Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a
|
||||||
@@ -149,6 +160,12 @@ class UsageTableHeader {
|
|||||||
|
|
||||||
int64_t GetCurrentTime() { return clock_ref_->GetCurrentTime(); }
|
int64_t GetCurrentTime() { return clock_ref_->GetCurrentTime(); }
|
||||||
|
|
||||||
|
// Sets LRU related metrics based on the provided |staleness| (in
|
||||||
|
// seconds) and |storage_type| of the entry removed.
|
||||||
|
void RecordLruEventMetrics(metrics::CryptoMetrics* metrics,
|
||||||
|
uint64_t staleness,
|
||||||
|
CdmUsageEntryStorageType storage_type);
|
||||||
|
|
||||||
// Uses an LRU-base algorithm to determine which licenses should be
|
// Uses an LRU-base algorithm to determine which licenses should be
|
||||||
// removed. This is intended to be used if the usage table is full
|
// removed. This is intended to be used if the usage table is full
|
||||||
// and a new entry needs to be added.
|
// and a new entry needs to be added.
|
||||||
@@ -212,7 +229,7 @@ class UsageTableHeader {
|
|||||||
|
|
||||||
// Synchonizes access to the Usage Table Header and bookkeeping
|
// Synchonizes access to the Usage Table Header and bookkeeping
|
||||||
// data-structures
|
// data-structures
|
||||||
std::mutex usage_table_header_lock_;
|
mutable std::mutex usage_table_header_lock_;
|
||||||
|
|
||||||
metrics::CryptoMetrics alternate_crypto_metrics_;
|
metrics::CryptoMetrics alternate_crypto_metrics_;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ namespace wvcdm {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string kEmptyString;
|
std::string kEmptyString;
|
||||||
size_t kMaxCryptoRetries = 3;
|
|
||||||
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||||
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||||
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||||
@@ -24,6 +23,7 @@ std::string kOldUsageEntryPoviderSessionToken =
|
|||||||
"nahZ6achSheiqua3TohQuei0ahwohv";
|
"nahZ6achSheiqua3TohQuei0ahwohv";
|
||||||
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
|
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
|
||||||
// Number of elements to be considered for removal using the LRU algorithm.
|
// Number of elements to be considered for removal using the LRU algorithm.
|
||||||
|
// TODO(b/155230578): Remove this constant.
|
||||||
constexpr size_t kLruRemovalSetSize = 3;
|
constexpr size_t kLruRemovalSetSize = 3;
|
||||||
// Fraction of table capacity of number of unexpired offline licenses
|
// Fraction of table capacity of number of unexpired offline licenses
|
||||||
// before they are considered to be removed. This could occur if
|
// before they are considered to be removed. This could occur if
|
||||||
@@ -132,6 +132,16 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EntryIsUsageInfo(const CdmUsageEntryInfo& info) {
|
||||||
|
// Used for stl filters.
|
||||||
|
return info.storage_type == kStorageUsageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryIsOfflineLicense(const CdmUsageEntryInfo& info) {
|
||||||
|
// Used for stl filters.
|
||||||
|
return info.storage_type == kStorageLicense;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
UsageTableHeader::UsageTableHeader()
|
UsageTableHeader::UsageTableHeader()
|
||||||
@@ -237,6 +247,13 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
|||||||
result = InvalidateEntry(temporary_usage_entry_number,
|
result = InvalidateEntry(temporary_usage_entry_number,
|
||||||
/* defrag_table = */ true,
|
/* defrag_table = */ true,
|
||||||
device_files_.get(), metrics);
|
device_files_.get(), metrics);
|
||||||
|
if (usage_entry_info_.size() > temporary_usage_entry_number) {
|
||||||
|
// The entry should have been deleted from the usage table,
|
||||||
|
// not just marked as type unknown. Failure to call
|
||||||
|
// Shrink() may be an indicator of other issues.
|
||||||
|
LOGE("Temporary entry was not deleted");
|
||||||
|
result = UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result != NO_ERROR) {
|
if (result != NO_ERROR) {
|
||||||
LOGE(
|
LOGE(
|
||||||
@@ -284,66 +301,11 @@ CdmResponseType UsageTableHeader::AddEntry(
|
|||||||
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||||
|
|
||||||
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3) {
|
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3) {
|
||||||
// If usage entry creation fails due to insufficient resources, release an
|
LOGW("Usage table may be full, releasing oldest entry: size = %zu",
|
||||||
// entry based on LRU.
|
usage_entry_info_.size());
|
||||||
std::vector<uint32_t> removal_candidates;
|
status = ReleaseOldestEntry(metrics);
|
||||||
if (!GetRemovalCandidates(&removal_candidates)) {
|
if (status == NO_ERROR) {
|
||||||
LOGE("Could not determine which license to remove");
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables for metrics.
|
|
||||||
const size_t usage_info_count =
|
|
||||||
std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
|
||||||
[](const CdmUsageEntryInfo& usage_entry) {
|
|
||||||
return usage_entry.storage_type == kStorageUsageInfo;
|
|
||||||
});
|
|
||||||
const size_t license_count =
|
|
||||||
std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(),
|
|
||||||
[](const CdmUsageEntryInfo& usage_entry) {
|
|
||||||
return usage_entry.storage_type == kStorageLicense;
|
|
||||||
});
|
|
||||||
int64_t staleness_of_removed;
|
|
||||||
CdmUsageEntryStorageType storage_type_of_removed;
|
|
||||||
const int64_t current_time = GetCurrentTime();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < removal_candidates.size() &&
|
|
||||||
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
|
|
||||||
++i) {
|
|
||||||
const uint32_t entry_number_to_invalidate = removal_candidates[i];
|
|
||||||
// Calculate metric values.
|
|
||||||
staleness_of_removed =
|
|
||||||
current_time -
|
|
||||||
usage_entry_info_[entry_number_to_invalidate].last_use_time;
|
|
||||||
storage_type_of_removed =
|
|
||||||
usage_entry_info_[entry_number_to_invalidate].storage_type;
|
|
||||||
|
|
||||||
if (InvalidateEntry(entry_number_to_invalidate,
|
|
||||||
/* defrag_table = */ true, device_files_.get(),
|
|
||||||
metrics) == NO_ERROR) {
|
|
||||||
// If the entry was deleted, it is still possible for the create new
|
|
||||||
// entry to fail. If so, we must ensure that the previously last
|
|
||||||
// entry was not in the |removal_candidates| as it has now been swapped
|
|
||||||
// with the deleted entry.
|
|
||||||
for (uint32_t& entry_number : removal_candidates) {
|
|
||||||
if (entry_number == usage_entry_info_.size()) {
|
|
||||||
entry_number = entry_number_to_invalidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||||
|
|
||||||
// Record metrics on success.
|
|
||||||
if (status == NO_ERROR) {
|
|
||||||
metrics->usage_table_header_lru_usage_info_count_.Record(
|
|
||||||
usage_info_count);
|
|
||||||
metrics->usage_table_header_lru_offline_license_count_.Record(
|
|
||||||
license_count);
|
|
||||||
metrics->usage_table_header_lru_evicted_entry_staleness_.Record(
|
|
||||||
staleness_of_removed);
|
|
||||||
metrics->usage_table_header_lru_evicted_entry_type_.Record(
|
|
||||||
static_cast<int>(storage_type_of_removed));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,28 +384,9 @@ CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session,
|
|||||||
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_;
|
||||||
|
|
||||||
CdmResponseType status =
|
const CdmResponseType status =
|
||||||
crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
||||||
|
|
||||||
// If loading a usage entry fails due to insufficient resources, release a
|
|
||||||
// random entry different from |usage_entry_number| and try again. If there
|
|
||||||
// are no more entries to release, we fail.
|
|
||||||
for (uint32_t retry_count = 0; retry_count < kMaxCryptoRetries &&
|
|
||||||
status == INSUFFICIENT_CRYPTO_RESOURCES_3;
|
|
||||||
++retry_count) {
|
|
||||||
if (usage_entry_info_.size() <= 1) break;
|
|
||||||
// Get a random entry from the other entries.
|
|
||||||
uint32_t entry_number_to_invalidate =
|
|
||||||
CdmRandom::RandomInRange(usage_entry_info_.size() - 2);
|
|
||||||
if (entry_number_to_invalidate >= usage_entry_number) {
|
|
||||||
// Exclude |usage_entry_number|.
|
|
||||||
++entry_number_to_invalidate;
|
|
||||||
}
|
|
||||||
InvalidateEntry(entry_number_to_invalidate, /* defrag_table = */ true,
|
|
||||||
device_files_.get(), metrics);
|
|
||||||
status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == NO_ERROR) {
|
if (status == NO_ERROR) {
|
||||||
usage_entry_info_[usage_entry_number].last_use_time = GetCurrentTime();
|
usage_entry_info_[usage_entry_number].last_use_time = GetCurrentTime();
|
||||||
}
|
}
|
||||||
@@ -518,6 +461,20 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t UsageTableHeader::UsageInfoCount() const {
|
||||||
|
LOGI("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 {
|
||||||
|
LOGI("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);
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
@@ -986,6 +943,40 @@ CdmResponseType UsageTableHeader::DefragTable(DeviceFiles* device_files,
|
|||||||
return Shrink(metrics, to_remove);
|
return Shrink(metrics, to_remove);
|
||||||
} // End Defrag().
|
} // End Defrag().
|
||||||
|
|
||||||
|
CdmResponseType UsageTableHeader::ReleaseOldestEntry(
|
||||||
|
metrics::CryptoMetrics* metrics) {
|
||||||
|
LOGV("Releasing oldest entry");
|
||||||
|
std::vector<uint32_t> removal_candidates;
|
||||||
|
if (!GetRemovalCandidates(&removal_candidates)) {
|
||||||
|
LOGE("Could not determine which license to remove");
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
// Only release one entry.
|
||||||
|
const uint32_t entry_number_to_delete = removal_candidates.front();
|
||||||
|
const CdmUsageEntryInfo& usage_entry_info =
|
||||||
|
usage_entry_info_[entry_number_to_delete];
|
||||||
|
|
||||||
|
const int64_t current_time = GetCurrentTime();
|
||||||
|
// Capture metric values now, as the |usage_entry_info| reference will
|
||||||
|
// change after the call to invalidate.
|
||||||
|
const int64_t staleness = current_time - usage_entry_info.last_use_time;
|
||||||
|
const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type;
|
||||||
|
|
||||||
|
const CdmResponseType status =
|
||||||
|
InvalidateEntry(entry_number_to_delete, /* defrag_table = */ true,
|
||||||
|
device_files_.get(), metrics);
|
||||||
|
|
||||||
|
if (status != NO_ERROR) {
|
||||||
|
LOGE("Failed to invalidate oldest entry: status = %d",
|
||||||
|
static_cast<int>(status));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record metrics on success.
|
||||||
|
RecordLruEventMetrics(metrics, staleness, storage_type);
|
||||||
|
return NO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
// Test only method.
|
// Test only method.
|
||||||
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
|
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
|
||||||
LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number);
|
LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number);
|
||||||
@@ -1135,7 +1126,20 @@ bool UsageTableHeader::GetRemovalCandidates(
|
|||||||
removal_candidates);
|
removal_candidates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UsageTableHeader::RecordLruEventMetrics(
|
||||||
|
metrics::CryptoMetrics* metrics, uint64_t staleness,
|
||||||
|
CdmUsageEntryStorageType storage_type) {
|
||||||
|
if (metrics == nullptr) return;
|
||||||
|
metrics->usage_table_header_lru_usage_info_count_.Record(UsageInfoCount());
|
||||||
|
metrics->usage_table_header_lru_offline_license_count_.Record(
|
||||||
|
OfflineEntryCount());
|
||||||
|
metrics->usage_table_header_lru_evicted_entry_staleness_.Record(staleness);
|
||||||
|
metrics->usage_table_header_lru_evicted_entry_type_.Record(
|
||||||
|
static_cast<int>(storage_type));
|
||||||
|
}
|
||||||
|
|
||||||
// Static.
|
// Static.
|
||||||
|
// TODO(b/155230578): Change this function to only return 1 entry.
|
||||||
bool UsageTableHeader::DetermineLicenseToRemove(
|
bool UsageTableHeader::DetermineLicenseToRemove(
|
||||||
const std::vector<CdmUsageEntryInfo>& usage_entry_info_list,
|
const std::vector<CdmUsageEntryInfo>& usage_entry_info_list,
|
||||||
int64_t current_time, size_t unexpired_threshold, size_t removal_count,
|
int64_t current_time, size_t unexpired_threshold, size_t removal_count,
|
||||||
|
|||||||
@@ -350,28 +350,6 @@ void ToVector(std::vector<CdmUsageEntryInfo>& vec,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to quickly populate a vector of CdmUsageEntryInfo structs with LRU
|
|
||||||
// information. This is intended to allow tests which are not concerned with
|
|
||||||
// the LRU replacement policy of the UsageTableHeader, but are affected by its
|
|
||||||
// presents.
|
|
||||||
void GenericLruUpgrade(std::vector<CdmUsageEntryInfo>* usage_entry_info_list,
|
|
||||||
int64_t last_use_time = kLruBaseTime,
|
|
||||||
int64_t offline_license_expiry_time =
|
|
||||||
kLruBaseTime + kDefaultExpireDuration) {
|
|
||||||
if (usage_entry_info_list == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto& usage_entry_info : *usage_entry_info_list) {
|
|
||||||
usage_entry_info.last_use_time = last_use_time;
|
|
||||||
if (usage_entry_info.storage_type == kStorageLicense) {
|
|
||||||
usage_entry_info.offline_license_expiry_time =
|
|
||||||
offline_license_expiry_time;
|
|
||||||
} else {
|
|
||||||
usage_entry_info.offline_license_expiry_time = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace
|
}; // namespace
|
||||||
|
|
||||||
class MockDeviceFiles : public DeviceFiles {
|
class MockDeviceFiles : public DeviceFiles {
|
||||||
@@ -484,6 +462,7 @@ using ::testing::ElementsAre;
|
|||||||
using ::testing::ElementsAreArray;
|
using ::testing::ElementsAreArray;
|
||||||
using ::testing::Ge;
|
using ::testing::Ge;
|
||||||
using ::testing::Invoke;
|
using ::testing::Invoke;
|
||||||
|
using ::testing::InvokeWithoutArgs;
|
||||||
using ::testing::Lt;
|
using ::testing::Lt;
|
||||||
using ::testing::NotNull;
|
using ::testing::NotNull;
|
||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
@@ -751,7 +730,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
|||||||
usage_entries_202))
|
usage_entries_202))
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
|
|
||||||
// Expectations for InvalidateEntry
|
// Expectations for InvalidateEntry, assumes no entry other entry is invalid.
|
||||||
EXPECT_CALL(*crypto_session_, Open(security_level))
|
EXPECT_CALL(*crypto_session_, Open(security_level))
|
||||||
.WillOnce(Return(NO_ERROR));
|
.WillOnce(Return(NO_ERROR));
|
||||||
EXPECT_CALL(*crypto_session_,
|
EXPECT_CALL(*crypto_session_,
|
||||||
@@ -914,7 +893,8 @@ TEST_F(UsageTableHeaderTest,
|
|||||||
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
||||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
||||||
|
|
||||||
uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 1;
|
const uint32_t expected_usage_entry_number =
|
||||||
|
k10UsageEntryInfoVector.size() - 1;
|
||||||
|
|
||||||
// Setup expectations
|
// Setup expectations
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
EXPECT_CALL(*mock_usage_table_header,
|
||||||
@@ -959,123 +939,50 @@ TEST_F(UsageTableHeaderTest,
|
|||||||
EXPECT_EQ(expected_usage_entries, final_usage_entries);
|
EXPECT_EQ(expected_usage_entries, final_usage_entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest,
|
// The usage table should only delete/invalidate a single entry.
|
||||||
AddEntry_CreateUsageEntryFailsTwice_SucceedsThirdTime) {
|
// After which, it should fail.
|
||||||
// Initialize and setup
|
TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
|
||||||
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
|
||||||
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
|
|
||||||
k10UsageEntryInfoVector;
|
|
||||||
GenericLruUpgrade(&usage_entry_info_vector_at_start);
|
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector_at_start);
|
|
||||||
|
|
||||||
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
|
||||||
uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen
|
|
||||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
|
||||||
|
|
||||||
uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 2;
|
|
||||||
|
|
||||||
// Setup expectations
|
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
|
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)))
|
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted),
|
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
|
|
||||||
.WillOnce(
|
|
||||||
DoAll(SetArgPointee<0>(expected_usage_entry_number),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, _))
|
|
||||||
.WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true)));
|
|
||||||
|
|
||||||
// Now invoke the method under test
|
|
||||||
uint32_t usage_entry_number;
|
|
||||||
EXPECT_EQ(NO_ERROR,
|
|
||||||
mock_usage_table_header->AddEntry(
|
|
||||||
crypto_session_,
|
|
||||||
kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense,
|
|
||||||
kUsageEntryInfoOfflineLicense6.key_set_id,
|
|
||||||
kUsageEntryInfoOfflineLicense6.usage_info_file_name,
|
|
||||||
kEmptyString /* license */, &usage_entry_number));
|
|
||||||
|
|
||||||
// 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, usage_entry_number_second_to_be_deleted);
|
|
||||||
EXPECT_LE(usage_entry_number_second_to_be_deleted,
|
|
||||||
usage_entry_info_vector_at_start.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[usage_entry_number_second_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);
|
|
||||||
|
|
||||||
EXPECT_EQ(expected_usage_entries, final_usage_entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsThrice) {
|
|
||||||
// Initialize and setup
|
// Initialize and setup
|
||||||
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
|
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
|
||||||
std::vector<CdmUsageEntryInfo> usage_entry_info_vector_at_start =
|
|
||||||
k10UsageEntryInfoVector;
|
|
||||||
|
|
||||||
uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen
|
|
||||||
uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen
|
|
||||||
uint32_t usage_entry_number_third_to_be_deleted; // randomly chosen
|
|
||||||
std::vector<CdmUsageEntryInfo> final_usage_entries;
|
|
||||||
|
|
||||||
// Setup expectations
|
// Setup expectations
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
EXPECT_CALL(*mock_usage_table_header,
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
InvalidateEntry(_, true, device_files_, NotNull()))
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted),
|
.WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)))
|
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted),
|
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)))
|
|
||||||
.WillOnce(DoAll(SaveArg<0>(&usage_entry_number_third_to_be_deleted),
|
|
||||||
Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)));
|
Return(NO_ERROR)));
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||||
.Times(4)
|
.Times(2)
|
||||||
.WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3));
|
.WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3));
|
||||||
|
|
||||||
// Now invoke the method under test
|
// Now invoke the method under test
|
||||||
uint32_t usage_entry_number;
|
uint32_t usage_entry_number;
|
||||||
EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3,
|
EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3,
|
||||||
mock_usage_table_header->AddEntry(
|
mock_usage_table_header->AddEntry(
|
||||||
crypto_session_,
|
crypto_session_, true /* persistent */,
|
||||||
kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense,
|
|
||||||
kUsageEntryInfoOfflineLicense6.key_set_id,
|
kUsageEntryInfoOfflineLicense6.key_set_id,
|
||||||
kUsageEntryInfoOfflineLicense6.usage_info_file_name,
|
kUsageEntryInfoOfflineLicense6.usage_info_file_name,
|
||||||
kEmptyString /* license */, &usage_entry_number));
|
kEmptyString /* license */, &usage_entry_number));
|
||||||
|
|
||||||
// Verify deleted usage entry number and entries
|
// Verify the number of entries deleted.
|
||||||
EXPECT_LE(0u, usage_entry_number_first_to_be_deleted);
|
constexpr uint32_t kExpectedEntriesDeleted = 1;
|
||||||
EXPECT_LE(usage_entry_number_first_to_be_deleted,
|
|
||||||
usage_entry_info_vector_at_start.size() - 1);
|
const std::vector<CdmUsageEntryInfo>& final_usage_entries =
|
||||||
EXPECT_LE(0u, usage_entry_number_second_to_be_deleted);
|
mock_usage_table_header->usage_entry_info();
|
||||||
EXPECT_LE(usage_entry_number_second_to_be_deleted,
|
uint32_t invalid_entries = 0;
|
||||||
usage_entry_info_vector_at_start.size() - 1);
|
for (const CdmUsageEntryInfo& usage_entry_info : final_usage_entries) {
|
||||||
EXPECT_LE(0u, usage_entry_number_third_to_be_deleted);
|
if (usage_entry_info.storage_type == kStorageTypeUnknown) {
|
||||||
EXPECT_LE(usage_entry_number_third_to_be_deleted,
|
++invalid_entries;
|
||||||
usage_entry_info_vector_at_start.size() - 1);
|
}
|
||||||
|
}
|
||||||
|
// Number of entries deleted is equal to the number of entries
|
||||||
|
// marked as invalid plus the number of fewer entries in the table
|
||||||
|
// at the end of the call.
|
||||||
|
const uint32_t entries_deleted =
|
||||||
|
invalid_entries +
|
||||||
|
(k10UsageEntryInfoVector.size() - final_usage_entries.size());
|
||||||
|
EXPECT_EQ(kExpectedEntriesDeleted, entries_deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) {
|
TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) {
|
||||||
@@ -1139,106 +1046,6 @@ TEST_F(UsageTableHeaderTest, UpdateEntry) {
|
|||||||
usage_table_header_->UpdateEntry(0, crypto_session_, &usage_entry));
|
usage_table_header_->UpdateEntry(0, crypto_session_, &usage_entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest,
|
|
||||||
LoadEntry_LoadUsageEntryFailsOnce_SucceedsSecondTime) {
|
|
||||||
// Initialize and setup
|
|
||||||
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
|
|
||||||
|
|
||||||
// We try to load a usage entry from the first 9 entries, since
|
|
||||||
// InvalidateEntry can't delete an entry if the last one is in use.
|
|
||||||
|
|
||||||
uint32_t usage_entry_number_to_load =
|
|
||||||
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 2);
|
|
||||||
CdmUsageEntry usage_entry_to_load = kUsageEntry;
|
|
||||||
|
|
||||||
// Setup expectations
|
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
|
||||||
.Times(1)
|
|
||||||
.WillRepeatedly(
|
|
||||||
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_,
|
|
||||||
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
|
|
||||||
.WillOnce(Return(NO_ERROR));
|
|
||||||
|
|
||||||
// Now invoke the method under test
|
|
||||||
EXPECT_EQ(NO_ERROR,
|
|
||||||
mock_usage_table_header->LoadEntry(
|
|
||||||
crypto_session_,
|
|
||||||
usage_entry_to_load,
|
|
||||||
usage_entry_number_to_load));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest,
|
|
||||||
LoadEntry_LoadUsageEntryFailsTwice_SucceedsThirdTime) {
|
|
||||||
// Initialize and setup
|
|
||||||
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
|
|
||||||
|
|
||||||
// We try to load a usage entry from the first 8 entries, since
|
|
||||||
// InvalidateEntry can't delete an entry if the last one is in use.
|
|
||||||
uint32_t usage_entry_number_to_load =
|
|
||||||
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 3);
|
|
||||||
CdmUsageEntry usage_entry_to_load = kUsageEntry;
|
|
||||||
|
|
||||||
// Setup expectations
|
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
|
||||||
.Times(2)
|
|
||||||
.WillRepeatedly(
|
|
||||||
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_,
|
|
||||||
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
|
|
||||||
.WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3))
|
|
||||||
.WillOnce(Return(NO_ERROR));
|
|
||||||
|
|
||||||
// Now invoke the method under test
|
|
||||||
EXPECT_EQ(NO_ERROR,
|
|
||||||
mock_usage_table_header->LoadEntry(
|
|
||||||
crypto_session_,
|
|
||||||
usage_entry_to_load,
|
|
||||||
usage_entry_number_to_load));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest, LoadEntry_LoadUsageEntryFailsThrice) {
|
|
||||||
// Initialize and setup
|
|
||||||
MockUsageTableHeader* mock_usage_table_header = SetUpMock();
|
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector);
|
|
||||||
|
|
||||||
// We try to load a usage entry from the first 7 entries, since
|
|
||||||
// InvalidateEntry can't delete an entry if the last one is in use.
|
|
||||||
uint32_t usage_entry_number_to_load =
|
|
||||||
CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 4);
|
|
||||||
CdmUsageEntry usage_entry_to_load = kUsageEntry;
|
|
||||||
|
|
||||||
// Setup expectations
|
|
||||||
EXPECT_CALL(*mock_usage_table_header,
|
|
||||||
InvalidateEntry(_, true, device_files_, NotNull()))
|
|
||||||
.Times(3)
|
|
||||||
.WillRepeatedly(
|
|
||||||
DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry),
|
|
||||||
Return(NO_ERROR)));
|
|
||||||
|
|
||||||
EXPECT_CALL(*crypto_session_,
|
|
||||||
LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load))
|
|
||||||
.Times(4)
|
|
||||||
.WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3));
|
|
||||||
|
|
||||||
// Now invoke the method under test
|
|
||||||
EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3,
|
|
||||||
mock_usage_table_header->LoadEntry(
|
|
||||||
crypto_session_,
|
|
||||||
usage_entry_to_load,
|
|
||||||
usage_entry_number_to_load));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) {
|
TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) {
|
||||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||||
uint32_t usage_entry_number = kUsageEntryInfoVector.size();
|
uint32_t usage_entry_number = kUsageEntryInfoVector.size();
|
||||||
|
|||||||
Reference in New Issue
Block a user