Usage table LRU only retrieves a single entry.
[ Merge of http://go/wvgerrit/102167 ] After changes made to how the usage table is defragged by the CDM, it was determined that there is no use in selecting more than a single entry from the usage table to evict. The only failure that can occur when evicting an entry is if the last entry is in use, in that case, evicting other entries will still result in a failure. This change cleans up the LRU algorithm and test cases to reflect the new functionality. Bug: 155230578 Test: Linux unit tests Change-Id: I817c039670d9f72c0e4f6c3fdac45c98ed5b6b21
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "usage_table_header.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "cdm_random.h"
|
||||
#include "crypto_session.h"
|
||||
@@ -22,9 +23,6 @@ std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
|
||||
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
|
||||
// there are not enough expired offline or streaming licenses to
|
||||
@@ -946,13 +944,11 @@ CdmResponseType UsageTableHeader::DefragTable(DeviceFiles* device_files,
|
||||
CdmResponseType UsageTableHeader::ReleaseOldestEntry(
|
||||
metrics::CryptoMetrics* metrics) {
|
||||
LOGV("Releasing oldest entry");
|
||||
std::vector<uint32_t> removal_candidates;
|
||||
if (!GetRemovalCandidates(&removal_candidates)) {
|
||||
uint32_t entry_number_to_delete;
|
||||
if (!GetRemovalCandidate(&entry_number_to_delete)) {
|
||||
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];
|
||||
|
||||
@@ -1115,15 +1111,13 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UsageTableHeader::GetRemovalCandidates(
|
||||
std::vector<uint32_t>* removal_candidates) {
|
||||
bool UsageTableHeader::GetRemovalCandidate(uint32_t* entry_to_remove) {
|
||||
LOGI("Locking to determine removal candidates");
|
||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
||||
const size_t lru_unexpired_threshold =
|
||||
kLruUnexpiredThresholdFraction * potential_table_capacity();
|
||||
return DetermineLicenseToRemove(usage_entry_info_, GetCurrentTime(),
|
||||
lru_unexpired_threshold, kLruRemovalSetSize,
|
||||
removal_candidates);
|
||||
lru_unexpired_threshold, entry_to_remove);
|
||||
}
|
||||
|
||||
void UsageTableHeader::RecordLruEventMetrics(
|
||||
@@ -1139,125 +1133,105 @@ void UsageTableHeader::RecordLruEventMetrics(
|
||||
}
|
||||
|
||||
// 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,
|
||||
std::vector<uint32_t>* removal_candidates) {
|
||||
if (removal_candidates == nullptr) {
|
||||
LOGE("Output parameter |removal_candidates| is null");
|
||||
return false;
|
||||
}
|
||||
if (removal_count == 0) {
|
||||
LOGE("|removal_count| cannot be zero");
|
||||
int64_t current_time, size_t unexpired_threshold,
|
||||
uint32_t* entry_to_remove) {
|
||||
if (entry_to_remove == nullptr) {
|
||||
LOGE("Output parameter |entry_to_remove| is null");
|
||||
return false;
|
||||
}
|
||||
if (usage_entry_info_list.empty()) {
|
||||
return false;
|
||||
}
|
||||
removal_candidates->clear();
|
||||
|
||||
std::vector<uint32_t> unknown_storage_entry_numbers;
|
||||
// |entry_numbers| contains expired offline and streaming license.
|
||||
std::vector<uint32_t> entry_numbers;
|
||||
std::vector<uint32_t> unexpired_offline_license_entry_numbers;
|
||||
|
||||
// Separate the entries based on their priority properties.
|
||||
for (uint32_t entry_number = 0; entry_number < usage_entry_info_list.size();
|
||||
++entry_number) {
|
||||
const CdmUsageEntryInfo& usage_entry_info =
|
||||
usage_entry_info_list[entry_number];
|
||||
if (usage_entry_info.storage_type == kStorageLicense) {
|
||||
if (usage_entry_info.offline_license_expiry_time > current_time) {
|
||||
// Unexpired offline.
|
||||
unexpired_offline_license_entry_numbers.push_back(entry_number);
|
||||
} else {
|
||||
// Expired offline.
|
||||
entry_numbers.push_back(entry_number);
|
||||
}
|
||||
} else if (usage_entry_info.storage_type == kStorageUsageInfo) {
|
||||
// Streaming.
|
||||
entry_numbers.push_back(entry_number);
|
||||
} else {
|
||||
// Unknown entries.
|
||||
unknown_storage_entry_numbers.push_back(entry_number);
|
||||
}
|
||||
}
|
||||
|
||||
// Select any entries of unknown storage type.
|
||||
if (!unknown_storage_entry_numbers.empty()) {
|
||||
if (unknown_storage_entry_numbers.size() >= removal_count) {
|
||||
// Case: There are enough entries with unknown storage types to
|
||||
// fill the removal set.
|
||||
removal_candidates->insert(
|
||||
removal_candidates->begin(), unknown_storage_entry_numbers.begin(),
|
||||
unknown_storage_entry_numbers.begin() + removal_count);
|
||||
return true;
|
||||
}
|
||||
// Fill whatever are available, and check for more.
|
||||
*removal_candidates = std::move(unknown_storage_entry_numbers);
|
||||
}
|
||||
|
||||
// Sort licenses based on last used time.
|
||||
const auto compare_last_used = [&](uint32_t i, uint32_t j) {
|
||||
// Returns true if entry of first index is more stale than the
|
||||
// entry of the second index.
|
||||
const auto is_more_stale = [&](uint32_t i, uint32_t j) -> bool {
|
||||
return usage_entry_info_list[i].last_use_time <
|
||||
usage_entry_info_list[j].last_use_time;
|
||||
};
|
||||
|
||||
// Check if unexpired licenses should be considered too.
|
||||
if (unexpired_offline_license_entry_numbers.size() > unexpired_threshold) {
|
||||
std::sort(unexpired_offline_license_entry_numbers.begin(),
|
||||
unexpired_offline_license_entry_numbers.end(), compare_last_used);
|
||||
if (unexpired_offline_license_entry_numbers.size() > removal_count) {
|
||||
unexpired_offline_license_entry_numbers.resize(removal_count);
|
||||
// Find the most stale expired offline / streaming license and the
|
||||
// most stale unexpired offline entry. Count the number of unexpired
|
||||
// entries. If any entry is of storage type unknown, then it should
|
||||
// be removed.
|
||||
constexpr uint32_t kNoEntry = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t stalest_expired_offline_license = kNoEntry;
|
||||
uint32_t stalest_unexpired_offline_license = kNoEntry;
|
||||
uint32_t stalest_streaming_license = kNoEntry;
|
||||
size_t unexpired_license_count = 0;
|
||||
|
||||
for (uint32_t entry_number = 0; entry_number < usage_entry_info_list.size();
|
||||
++entry_number) {
|
||||
const CdmUsageEntryInfo& usage_entry_info =
|
||||
usage_entry_info_list[entry_number];
|
||||
|
||||
if (usage_entry_info.storage_type != kStorageLicense &&
|
||||
usage_entry_info.storage_type != kStorageUsageInfo) {
|
||||
// Unknown storage type entries. Remove this entry.
|
||||
*entry_to_remove = entry_number;
|
||||
return true;
|
||||
}
|
||||
if (usage_entry_info.storage_type == kStorageLicense &&
|
||||
usage_entry_info.offline_license_expiry_time > current_time) {
|
||||
// Unexpired offline.
|
||||
++unexpired_license_count;
|
||||
if (stalest_unexpired_offline_license == kNoEntry ||
|
||||
is_more_stale(entry_number, stalest_unexpired_offline_license)) {
|
||||
stalest_unexpired_offline_license = entry_number;
|
||||
}
|
||||
} else if (usage_entry_info.storage_type == kStorageLicense) {
|
||||
// Expired offline.
|
||||
if (stalest_expired_offline_license == kNoEntry ||
|
||||
is_more_stale(entry_number, stalest_expired_offline_license)) {
|
||||
stalest_expired_offline_license = entry_number;
|
||||
}
|
||||
} else {
|
||||
// Streaming.
|
||||
if (stalest_streaming_license == kNoEntry ||
|
||||
is_more_stale(entry_number, stalest_streaming_license)) {
|
||||
stalest_streaming_license = entry_number;
|
||||
}
|
||||
}
|
||||
// Merge the sets.
|
||||
entry_numbers.insert(entry_numbers.end(),
|
||||
unexpired_offline_license_entry_numbers.begin(),
|
||||
unexpired_offline_license_entry_numbers.end());
|
||||
}
|
||||
|
||||
// Sort expired offline and streaming license based on last used time.
|
||||
std::sort(entry_numbers.begin(), entry_numbers.end(), compare_last_used);
|
||||
|
||||
if ((entry_numbers.size() + removal_candidates->size()) <= removal_count) {
|
||||
// Under testing conditions, it is possible for there to be fewer usage
|
||||
// entries than there are being requested.
|
||||
|
||||
// Move whatever values are available to the removal candidates.
|
||||
removal_candidates->insert(removal_candidates->end(), entry_numbers.begin(),
|
||||
entry_numbers.end());
|
||||
return removal_candidates->size() > 0;
|
||||
if (stalest_expired_offline_license == kNoEntry &&
|
||||
stalest_streaming_license == kNoEntry &&
|
||||
unexpired_license_count <= unexpired_threshold) {
|
||||
// Unexpected situation, could be an issue with the threshold.
|
||||
LOGW(
|
||||
"Table only contains unexpired offline licenses, "
|
||||
"but threshold not met: size = %zu, count = %zu, threshold = %zu",
|
||||
usage_entry_info_list.size(), unexpired_license_count,
|
||||
unexpired_threshold);
|
||||
*entry_to_remove = stalest_unexpired_offline_license;
|
||||
return true;
|
||||
}
|
||||
|
||||
const size_t remaining_removal_count =
|
||||
removal_count - removal_candidates->size();
|
||||
|
||||
// Based on the last use time the |remaining_removal_count|-th
|
||||
// least recently used entry, filter out all elements which have
|
||||
// been used more recently than it. This might result in a set
|
||||
// which is larger than the desired size.
|
||||
const int64_t cutoff_last_use_time =
|
||||
usage_entry_info_list[entry_numbers[remaining_removal_count - 1]]
|
||||
.last_use_time;
|
||||
const auto equal_to_cutoff = [&](uint32_t entry_number) {
|
||||
return usage_entry_info_list[entry_number].last_use_time ==
|
||||
cutoff_last_use_time;
|
||||
const auto select_most_stale = [&](uint32_t a, uint32_t b) -> uint32_t {
|
||||
if (a == kNoEntry) return b;
|
||||
if (b == kNoEntry) return a;
|
||||
return is_more_stale(a, b) ? a : b;
|
||||
};
|
||||
|
||||
const auto first_cutoff_it =
|
||||
std::find_if(entry_numbers.begin(), entry_numbers.end(), equal_to_cutoff);
|
||||
// Only consider an unexpired entry if the threshold is reached.
|
||||
if (unexpired_license_count > unexpired_threshold) {
|
||||
const uint32_t temp = select_most_stale(stalest_unexpired_offline_license,
|
||||
stalest_streaming_license);
|
||||
*entry_to_remove = select_most_stale(temp, stalest_expired_offline_license);
|
||||
} else {
|
||||
*entry_to_remove = select_most_stale(stalest_streaming_license,
|
||||
stalest_expired_offline_license);
|
||||
}
|
||||
|
||||
const auto after_last_cutoff_it =
|
||||
std::find_if_not(first_cutoff_it, entry_numbers.end(), equal_to_cutoff);
|
||||
|
||||
// To avoid always selecting the greatest entry number (due to the
|
||||
// sort & reverse), we randomize the set.
|
||||
std::shuffle(first_cutoff_it, after_last_cutoff_it,
|
||||
std::default_random_engine(CdmRandom::Rand()));
|
||||
|
||||
removal_candidates->insert(removal_candidates->end(), entry_numbers.cbegin(),
|
||||
entry_numbers.cbegin() + remaining_removal_count);
|
||||
if (*entry_to_remove == kNoEntry) {
|
||||
// Illegal state check. The loop above should have found at least
|
||||
// one entry given that |usage_entry_info_list| is not empty.
|
||||
LOGE("No entry could be used for removal: size = %zu",
|
||||
usage_entry_info_list.size());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user