Update AddEntry() for usage table changes.
[ Merge of http://go/wvgerrit/96071 ] Changes to how the usage table method InvalidateEntry() behaves required additional changes to CDM code that uses this method. This involved some refactoring to AddEntry(), moving the LRU related code to its own function. A few unittests had to be changed / removed as the moving multiple entries changes expectations of several existing tests. Several additional helper methods have been created to improve readability. These include getters for information about the usage table, a method for releasing stale entries, and a method of recording LRU metrics. Bug: 150890014 Bug: 150887808 Bug: 154269671 Test: Linux unit tests and Android unit tests Change-Id: I11a98f9a2dea9b2ae57b37d7d4483a37be721763
This commit is contained in:
@@ -16,7 +16,6 @@ namespace wvcdm {
|
||||
|
||||
namespace {
|
||||
std::string kEmptyString;
|
||||
size_t kMaxCryptoRetries = 3;
|
||||
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
|
||||
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||
@@ -24,6 +23,7 @@ std::string kOldUsageEntryPoviderSessionToken =
|
||||
"nahZ6achSheiqua3TohQuei0ahwohv";
|
||||
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
|
||||
// Number of elements to be considered for removal using the LRU algorithm.
|
||||
// TODO(b/155230578): Remove this constant.
|
||||
constexpr size_t kLruRemovalSetSize = 3;
|
||||
// Fraction of table capacity of number of unexpired offline licenses
|
||||
// before they are considered to be removed. This could occur if
|
||||
@@ -132,6 +132,16 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files,
|
||||
}
|
||||
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
|
||||
|
||||
UsageTableHeader::UsageTableHeader()
|
||||
@@ -237,6 +247,13 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
result = InvalidateEntry(temporary_usage_entry_number,
|
||||
/* defrag_table = */ true,
|
||||
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) {
|
||||
LOGE(
|
||||
@@ -284,66 +301,11 @@ CdmResponseType UsageTableHeader::AddEntry(
|
||||
CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number);
|
||||
|
||||
if (status == INSUFFICIENT_CRYPTO_RESOURCES_3) {
|
||||
// If usage entry creation fails due to insufficient resources, release an
|
||||
// entry based on LRU.
|
||||
std::vector<uint32_t> removal_candidates;
|
||||
if (!GetRemovalCandidates(&removal_candidates)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
// 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();
|
||||
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
|
||||
|
||||
CdmResponseType status =
|
||||
const CdmResponseType status =
|
||||
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) {
|
||||
usage_entry_info_[usage_entry_number].last_use_time = GetCurrentTime();
|
||||
}
|
||||
@@ -518,6 +461,20 @@ CdmResponseType UsageTableHeader::InvalidateEntry(
|
||||
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(
|
||||
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
|
||||
uint32_t to_usage_entry_number, DeviceFiles* device_files,
|
||||
@@ -986,6 +943,40 @@ CdmResponseType UsageTableHeader::DefragTable(DeviceFiles* device_files,
|
||||
return Shrink(metrics, to_remove);
|
||||
} // 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.
|
||||
void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) {
|
||||
LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number);
|
||||
@@ -1135,7 +1126,20 @@ bool UsageTableHeader::GetRemovalCandidates(
|
||||
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.
|
||||
// TODO(b/155230578): Change this function to only return 1 entry.
|
||||
bool UsageTableHeader::DetermineLicenseToRemove(
|
||||
const std::vector<CdmUsageEntryInfo>& usage_entry_info_list,
|
||||
int64_t current_time, size_t unexpired_threshold, size_t removal_count,
|
||||
|
||||
Reference in New Issue
Block a user