Revert "Core CDM: Remove secure stop from LRU algorithm."

This reverts commit 7a34c1748c.

Reason for revert: Feature rejected by Android

Bug: 242289743
Change-Id: I63a730a6e5fa16d0d2425d216fd7f0fff8c09a4c
This commit is contained in:
Alex Dale
2022-12-01 23:50:05 +00:00
committed by Android (Google) Code Review
parent 7a34c1748c
commit 0c353d6851
3 changed files with 427 additions and 202 deletions

View File

@@ -261,9 +261,10 @@ 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 // Sets LRU related metrics based on the provided |staleness| (in
// seconds). // seconds) and |storage_type| of the entry removed.
void RecordLruEventMetrics(metrics::CryptoMetrics* metrics, void RecordLruEventMetrics(metrics::CryptoMetrics* metrics,
uint64_t staleness); uint64_t staleness,
CdmUsageEntryStorageType storage_type);
// Uses an LRU-base algorithm to determine which license should be // Uses an LRU-base algorithm to determine which license 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

View File

@@ -96,6 +96,41 @@ bool RetrieveOfflineLicense(DeviceFiles* device_files,
return true; return true;
} }
bool RetrieveUsageInfoLicense(DeviceFiles* device_files,
const std::string& usage_info_file_name,
const std::string& key_set_id,
CdmKeyResponse* license_message,
uint32_t* usage_entry_number) {
if (device_files == nullptr) {
LOGE("DeviceFiles handle is null");
return false;
}
if (license_message == nullptr) {
LOGE("Output parameter |license_message| is null");
return false;
}
if (usage_entry_number == nullptr) {
LOGE("Output parameter |usage_entry_number| is null");
return false;
}
CdmUsageEntry usage_entry;
std::string provider_session_token;
CdmKeyMessage license_request;
std::string drm_certificate;
CryptoWrappedKey wrapped_private_key;
if (!device_files->RetrieveUsageInfoByKeySetId(
usage_info_file_name, key_set_id, &provider_session_token,
&license_request, license_message, &usage_entry, usage_entry_number,
&drm_certificate, &wrapped_private_key)) {
LOGW(
"Failed to retrieve usage information: "
"key_set_id = %s, usage_info_file_name = %s",
IdToString(key_set_id), IdToString(usage_info_file_name));
return false;
}
return true;
}
bool EntryIsUsageInfo(const CdmUsageEntryInfo& info) { bool EntryIsUsageInfo(const CdmUsageEntryInfo& info) {
// Used for stl filters. // Used for stl filters.
return info.storage_type == kStorageUsageInfo; return info.storage_type == kStorageUsageInfo;
@@ -1128,6 +1163,7 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry(
// Capture metric values now, as the |usage_entry_info| reference will // Capture metric values now, as the |usage_entry_info| reference will
// change after the call to invalidate. // change after the call to invalidate.
const int64_t staleness = current_time - usage_entry_info.last_use_time; const int64_t staleness = current_time - usage_entry_info.last_use_time;
const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type;
const CdmResponseType status = const CdmResponseType status =
InvalidateEntryInternal(entry_number_to_delete, /* defrag_table = */ true, InvalidateEntryInternal(entry_number_to_delete, /* defrag_table = */ true,
@@ -1140,7 +1176,7 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry(
} }
// Record metrics on success. // Record metrics on success.
RecordLruEventMetrics(metrics, staleness); RecordLruEventMetrics(metrics, staleness, storage_type);
return NO_ERROR; return NO_ERROR;
} }
@@ -1173,16 +1209,34 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
usage_entry_number < usage_entry_info_.size(); ++usage_entry_number) { usage_entry_number < usage_entry_info_.size(); ++usage_entry_number) {
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_[usage_entry_number]; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_[usage_entry_number];
if (usage_entry_info.storage_type != kStorageLicense) {
bad_license_file_entries.push_back(usage_entry_number);
continue;
}
uint32_t retrieved_entry_number; uint32_t retrieved_entry_number;
CdmKeyResponse license_message; CdmKeyResponse license_message;
const bool retrieve_response = bool retrieve_response = false;
RetrieveOfflineLicense(device_files_.get(), usage_entry_info.key_set_id, switch (usage_entry_info.storage_type) {
&license_message, &retrieved_entry_number); case kStorageLicense: {
retrieve_response = RetrieveOfflineLicense(
device_files_.get(), usage_entry_info.key_set_id, &license_message,
&retrieved_entry_number);
break;
}
case kStorageUsageInfo: {
retrieve_response = RetrieveUsageInfoLicense(
device_files_.get(), usage_entry_info.usage_info_file_name,
usage_entry_info.key_set_id, &license_message,
&retrieved_entry_number);
break;
}
case kStorageTypeUnknown:
bad_license_file_entries.push_back(usage_entry_number);
continue;
default: {
LOGW("Unknown usage entry storage type: %d, usage_entry_number = %u",
static_cast<int>(usage_entry_info.storage_type),
usage_entry_number);
bad_license_file_entries.push_back(usage_entry_number);
continue;
}
}
if (!retrieve_response) { if (!retrieve_response) {
LOGW("Could not retrieve license message: usage_entry_number = %u", LOGW("Could not retrieve license message: usage_entry_number = %u",
usage_entry_number); usage_entry_number);
@@ -1211,18 +1265,22 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
// for replacement above all others. // for replacement above all others.
usage_entry_info.last_use_time = license.license_start_time(); usage_entry_info.last_use_time = license.license_start_time();
// Only offline licenses need |offline_license_expiry_time| set. if (usage_entry_info.storage_type == kStorageLicense) {
const video_widevine::License::Policy& policy = license.policy(); // Only offline licenses need |offline_license_expiry_time| set.
// TODO(b/139372190): Change how these fields are set once feature is const video_widevine::License::Policy& policy = license.policy();
// implemented. // TODO(b/139372190): Change how these fields are set once feature is
if (policy.license_duration_seconds() == 0) { // implemented.
// Zero implies unlimited license duration. if (policy.license_duration_seconds() == 0) {
usage_entry_info.offline_license_expiry_time = // Zero implies unlimited license duration.
license.license_start_time() + policy.rental_duration_seconds() + usage_entry_info.offline_license_expiry_time =
policy.playback_duration_seconds(); license.license_start_time() + policy.rental_duration_seconds() +
policy.playback_duration_seconds();
} else {
usage_entry_info.offline_license_expiry_time =
license.license_start_time() + policy.license_duration_seconds();
}
} else { } else {
usage_entry_info.offline_license_expiry_time = usage_entry_info.offline_license_expiry_time = 0;
license.license_start_time() + policy.license_duration_seconds();
} }
} // End for loop. } // End for loop.
@@ -1232,14 +1290,31 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() {
return false; return false;
} }
// Maps <usage_info_file_name> -> [<key_set_id>].
std::map<std::string, std::vector<std::string>> usage_info_clean_up;
for (size_t usage_entry_number : bad_license_file_entries) { for (size_t usage_entry_number : bad_license_file_entries) {
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_[usage_entry_number]; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_[usage_entry_number];
if (usage_entry_info.storage_type == kStorageLicense) { if (usage_entry_info.storage_type == kStorageLicense) {
device_files_->DeleteLicense(usage_entry_info.key_set_id); device_files_->DeleteLicense(usage_entry_info.key_set_id);
} } else if (usage_entry_info.storage_type == kStorageUsageInfo) {
// To reduce write cycles, the deletion of usage info will be done
// in bulk.
auto it = usage_info_clean_up.find(usage_entry_info.usage_info_file_name);
if (it == usage_info_clean_up.end()) {
it = usage_info_clean_up
.emplace(usage_entry_info.usage_info_file_name,
std::vector<std::string>())
.first;
}
it->second.push_back(usage_entry_info.key_set_id);
} // else kStorageUnknown { Nothing special }.
usage_entry_info.Clear(); usage_entry_info.Clear();
} }
for (const auto& p : usage_info_clean_up) {
device_files_->DeleteMultipleUsageInfoByKeySetIds(p.first, p.second);
}
return true; return true;
} }
@@ -1252,18 +1327,16 @@ bool UsageTableHeader::GetRemovalCandidate(uint32_t* entry_to_remove) {
lru_unexpired_threshold, entry_to_remove); lru_unexpired_threshold, entry_to_remove);
} }
void UsageTableHeader::RecordLruEventMetrics(metrics::CryptoMetrics* metrics, void UsageTableHeader::RecordLruEventMetrics(
uint64_t staleness) { metrics::CryptoMetrics* metrics, uint64_t staleness,
CdmUsageEntryStorageType storage_type) {
if (metrics == nullptr) return; if (metrics == nullptr) return;
// Usage info are deprecated, always record 0. metrics->usage_table_header_lru_usage_info_count_.Record(UsageInfoCount());
metrics->usage_table_header_lru_usage_info_count_.Record(0);
metrics->usage_table_header_lru_offline_license_count_.Record( metrics->usage_table_header_lru_offline_license_count_.Record(
OfflineEntryCount()); OfflineEntryCount());
metrics->usage_table_header_lru_evicted_entry_staleness_.Record(staleness); metrics->usage_table_header_lru_evicted_entry_staleness_.Record(staleness);
// Can be assumed that only offline licenses are removed.
constexpr int kDefaultEntryType = 0;
metrics->usage_table_header_lru_evicted_entry_type_.Record( metrics->usage_table_header_lru_evicted_entry_type_.Record(
static_cast<int>(kDefaultEntryType)); static_cast<int>(storage_type));
} }
// Static. // Static.
@@ -1286,13 +1359,14 @@ bool UsageTableHeader::DetermineLicenseToRemove(
usage_entry_info_list[j].last_use_time; usage_entry_info_list[j].last_use_time;
}; };
// Find the most stale expired offline license and the // Find the most stale expired offline / streaming license and the
// most stale unexpired offline entry. Count the number of unexpired // most stale unexpired offline entry. Count the number of unexpired
// entries. If any entry is not of storage type license, then it should // entries. If any entry is of storage type unknown, then it should
// be removed. // be removed.
constexpr uint32_t kNoEntry = std::numeric_limits<uint32_t>::max(); constexpr uint32_t kNoEntry = std::numeric_limits<uint32_t>::max();
uint32_t stalest_expired_offline_license = kNoEntry; uint32_t stalest_expired_offline_license = kNoEntry;
uint32_t stalest_unexpired_offline_license = kNoEntry; uint32_t stalest_unexpired_offline_license = kNoEntry;
uint32_t stalest_streaming_license = kNoEntry;
size_t unexpired_license_count = 0; size_t unexpired_license_count = 0;
for (uint32_t entry_number = 0; entry_number < usage_entry_info_list.size(); for (uint32_t entry_number = 0; entry_number < usage_entry_info_list.size();
@@ -1300,28 +1374,37 @@ bool UsageTableHeader::DetermineLicenseToRemove(
const CdmUsageEntryInfo& usage_entry_info = const CdmUsageEntryInfo& usage_entry_info =
usage_entry_info_list[entry_number]; usage_entry_info_list[entry_number];
if (usage_entry_info.storage_type != kStorageLicense) { if (usage_entry_info.storage_type != kStorageLicense &&
// Non-license storage type entries. Remove this entry. usage_entry_info.storage_type != kStorageUsageInfo) {
// Unknown storage type entries. Remove this entry.
*entry_to_remove = entry_number; *entry_to_remove = entry_number;
return true; return true;
} }
if (usage_entry_info.offline_license_expiry_time > current_time) { if (usage_entry_info.storage_type == kStorageLicense &&
usage_entry_info.offline_license_expiry_time > current_time) {
// Unexpired offline. // Unexpired offline.
++unexpired_license_count; ++unexpired_license_count;
if (stalest_unexpired_offline_license == kNoEntry || if (stalest_unexpired_offline_license == kNoEntry ||
is_more_stale(entry_number, stalest_unexpired_offline_license)) { is_more_stale(entry_number, stalest_unexpired_offline_license)) {
stalest_unexpired_offline_license = entry_number; stalest_unexpired_offline_license = entry_number;
} }
} else { } else if (usage_entry_info.storage_type == kStorageLicense) {
// Expired offline. // Expired offline.
if (stalest_expired_offline_license == kNoEntry || if (stalest_expired_offline_license == kNoEntry ||
is_more_stale(entry_number, stalest_expired_offline_license)) { is_more_stale(entry_number, stalest_expired_offline_license)) {
stalest_expired_offline_license = entry_number; 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;
}
} }
} }
if (stalest_expired_offline_license == kNoEntry && if (stalest_expired_offline_license == kNoEntry &&
stalest_streaming_license == kNoEntry &&
unexpired_license_count <= unexpired_threshold) { unexpired_license_count <= unexpired_threshold) {
// Unexpected situation, could be an issue with the threshold. // Unexpected situation, could be an issue with the threshold.
LOGW( LOGW(
@@ -1341,10 +1424,12 @@ bool UsageTableHeader::DetermineLicenseToRemove(
// Only consider an unexpired entry if the threshold is reached. // Only consider an unexpired entry if the threshold is reached.
if (unexpired_license_count > unexpired_threshold) { if (unexpired_license_count > unexpired_threshold) {
*entry_to_remove = select_most_stale(stalest_unexpired_offline_license, const uint32_t temp = select_most_stale(stalest_unexpired_offline_license,
stalest_expired_offline_license); stalest_streaming_license);
*entry_to_remove = select_most_stale(temp, stalest_expired_offline_license);
} else { } else {
*entry_to_remove = stalest_expired_offline_license; *entry_to_remove = select_most_stale(stalest_streaming_license,
stalest_expired_offline_license);
} }
if (*entry_to_remove == kNoEntry) { if (*entry_to_remove == kNoEntry) {

View File

@@ -229,9 +229,9 @@ const CdmKeyResponse kKeyRenewalResponse = "key renewal response";
const std::string kReleaseServerUrl = "some url"; const std::string kReleaseServerUrl = "some url";
const std::string kProviderSessionToken = "provider session token"; const std::string kProviderSessionToken = "provider session token";
const CdmAppParameterMap kEmptyAppParameters; const CdmAppParameterMap kEmptyAppParameters;
constexpr int64_t kPlaybackStartTime = 1030005; int64_t kPlaybackStartTime = 1030005;
constexpr int64_t kPlaybackDuration = 300; int64_t kPlaybackDuration = 300;
constexpr int64_t kGracePeriodEndTime = 60; int64_t kGracePeriodEndTime = 60;
// ==== LRU Upgrade Data ==== // ==== LRU Upgrade Data ====
const CdmUsageTableHeader kUpgradableUsageTableHeader = const CdmUsageTableHeader kUpgradableUsageTableHeader =
@@ -245,9 +245,9 @@ const CdmUsageEntryInfo kUpgradableUsageEntryInfo1 = {
/* last_use_time = */ 0, /* last_use_time = */ 0,
/* offline_license_expiry_time = */ 0}; /* offline_license_expiry_time = */ 0};
const CdmUsageEntryInfo kUpgradableUsageEntryInfo2 = { const CdmUsageEntryInfo kUpgradableUsageEntryInfo2 = {
/* storage_type = */ kStorageLicense, /* storage_type = */ kStorageUsageInfo,
/* key_set_id = */ "offline_key_set_2", /* key_set_id = */ "streaming_key_set_2",
/* usage_info_file_name = */ "", /* usage_info_file_name = */ "streaming_license_file_2",
/* last_use_time = */ 0, /* last_use_time = */ 0,
/* offline_license_expiry_time = */ 0}; /* offline_license_expiry_time = */ 0};
const CdmUsageEntryInfo kUpgradableUsageEntryInfo3 = { const CdmUsageEntryInfo kUpgradableUsageEntryInfo3 = {
@@ -256,11 +256,7 @@ const CdmUsageEntryInfo kUpgradableUsageEntryInfo3 = {
/* usage_info_file_name = */ "", /* usage_info_file_name = */ "",
/* last_use_time = */ 0, /* last_use_time = */ 0,
/* offline_license_expiry_time = */ 0}; /* offline_license_expiry_time = */ 0};
const std::vector<CdmUsageEntryInfo> kUpgradableUsageEntryInfoList{ std::vector<CdmUsageEntryInfo> kUpgradableUsageEntryInfoList;
kUpgradableUsageEntryInfo1, kUpgradableUsageEntryInfo2,
kUpgradableUsageEntryInfo3};
const int64_t kLruBaseTime = 1563399000;
// Offline license 1. // Offline license 1.
// license_start_time = 1563399000 // license_start_time = 1563399000
@@ -270,14 +266,11 @@ const int64_t kLruBaseTime = 1563399000;
const CdmKeyResponse kUpgradableLicenseInfo1 = wvutil::a2bs_hex( const CdmKeyResponse kUpgradableLicenseInfo1 = wvutil::a2bs_hex(
"08021214120C2080F5242880A3053080E90F20D8A6BEE9051A20D5F7ACE8D84A166C69BB" "08021214120C2080F5242880A3053080E90F20D8A6BEE9051A20D5F7ACE8D84A166C69BB"
"27523C84C019464B90AA9BF06B8332004839119BFD14"); "27523C84C019464B90AA9BF06B8332004839119BFD14");
// Offline license 2. // Streaming license 2.
// license_start_time = 1563399000 // license_start_time = 1563399000
// license_duration_seconds = 259200 (3 days)
// rental_duration_seconds = 0 (unlimited)
// playback_duration_seconds = 0 (unlimited)
const CdmKeyResponse kUpgradableLicenseInfo2 = wvutil::a2bs_hex( const CdmKeyResponse kUpgradableLicenseInfo2 = wvutil::a2bs_hex(
"080212101208200028003080E90F20D8A6BEE9051A20D0611C4AFEF3EC9C67143ED39B44" "0802120620D8A6BEE9051A201956F2FD69E5E96DA8C65FDD04A3C294E484F219F2B1A8DD"
"8FAAA67DB6C4DBFDC28A733AF9A2EABDF0B3"); "C2B0737F6EF5BD22");
// Offline license 3. // Offline license 3.
// license_start_time = 1563399000 // license_start_time = 1563399000
// license_duration_seconds = 0 (unlimited) // license_duration_seconds = 0 (unlimited)
@@ -286,56 +279,26 @@ const CdmKeyResponse kUpgradableLicenseInfo2 = wvutil::a2bs_hex(
const CdmKeyResponse kUpgradableLicenseInfo3 = wvutil::a2bs_hex( const CdmKeyResponse kUpgradableLicenseInfo3 = wvutil::a2bs_hex(
"08021212120A2080F5242880A305300020D8A6BEE9051A207B09896F46C4EE443170E215" "08021212120A2080F5242880A305300020D8A6BEE9051A207B09896F46C4EE443170E215"
"B2D8D5F072951027B152F4758AC3A339D7C7B4CE"); "B2D8D5F072951027B152F4758AC3A339D7C7B4CE");
const std::vector<CdmKeyResponse> kUpgradableLicenseInfoList = { std::vector<CdmKeyResponse> kUpgradableLicenseInfoList;
kUpgradableLicenseInfo1, kUpgradableLicenseInfo2, kUpgradableLicenseInfo3}; std::vector<DeviceFiles::CdmLicenseData> kUpgradableLicenseDataList;
// Same as Offline license 1, but without signature. // Same as Offline license 1, but without signature.
const CdmKeyResponse kUnsignedUpgradableLicenseInfo1 = const CdmKeyResponse kUnsignedUpgradableLicenseInfo1 =
wvutil::a2bs_hex("08021214120C2080F5242880A3053080E90F20D8A6BEE905"); wvutil::a2bs_hex("08021214120C2080F5242880A3053080E90F20D8A6BEE905");
// Same as streaming license 2, but message type is certificate, not license.
const CdmKeyResponse kWrongTypedUpgradableLicenseInfo2 = wvutil::a2bs_hex(
"0805120620D8A6BEE9051A201956F2FD69E5E96DA8C65FDD04A3C294E484F219F2B1A8DD"
"C2B0737F6EF5BD22");
const int64_t kLruBaseTime = 1563399000;
const int64_t kUpgradedUsageEntryInfo1LastUsedTime = kLruBaseTime; const int64_t kUpgradedUsageEntryInfo1LastUsedTime = kLruBaseTime;
const int64_t kUpgradedUsageEntryInfo1ExpireTime = kLruBaseTime + 259200; const int64_t kUpgradedUsageEntryInfo1ExpireTime = kLruBaseTime + 259200;
const int64_t kUpgradedUsageEntryInfo2LastUsedTime = kLruBaseTime; const int64_t kUpgradedUsageEntryInfo2LastUsedTime = kLruBaseTime;
const int64_t kUpgradedUsageEntryInfo2ExpireTime = kLruBaseTime + 259200; const int64_t kUpgradedUsageEntryInfo2ExpireTime = 0; // Unset
const int64_t kUpgradedUsageEntryInfo3LastUsedTime = kLruBaseTime; const int64_t kUpgradedUsageEntryInfo3LastUsedTime = kLruBaseTime;
const int64_t kUpgradedUsageEntryInfo3ExpireTime = const int64_t kUpgradedUsageEntryInfo3ExpireTime =
kLruBaseTime + 604800 + 86400; kLruBaseTime + 604800 + 86400;
const CdmUsageTableHeader kUpgradedUsageTableHeader = "Upgraded Table Header"; const CdmUsageTableHeader kUpgradedUsageTableHeader = "Upgraded Table Header";
const CdmUsageEntryInfo kUpgradedUsageEntryInfo1 = { std::vector<CdmUsageEntryInfo> kUpgradedUsageEntryInfoList;
/* storage_type = */ kStorageLicense,
/* key_set_id = */ "offline_key_set_1",
/* usage_info_file_name = */ "",
/* last_use_time = */ kUpgradedUsageEntryInfo1LastUsedTime,
/* offline_license_expiry_time = */ kUpgradedUsageEntryInfo1ExpireTime};
const CdmUsageEntryInfo kUpgradedUsageEntryInfo2 = {
/* storage_type = */ kStorageLicense,
/* key_set_id = */ "offline_key_set_2",
/* usage_info_file_name = */ "",
/* last_use_time = */ kUpgradedUsageEntryInfo2LastUsedTime,
/* offline_license_expiry_time = */ kUpgradedUsageEntryInfo2ExpireTime};
const CdmUsageEntryInfo kUpgradedUsageEntryInfo3 = {
/* storage_type = */ kStorageLicense,
/* key_set_id = */ "offline_key_set_3",
/* usage_info_file_name = */ "",
/* last_use_time = */ kUpgradedUsageEntryInfo3LastUsedTime,
/* offline_license_expiry_time = */ kUpgradedUsageEntryInfo3ExpireTime};
const std::vector<CdmUsageEntryInfo> kUpgradedUsageEntryInfoList = {
kUpgradedUsageEntryInfo1, kUpgradedUsageEntryInfo2,
kUpgradedUsageEntryInfo3};
std::vector<DeviceFiles::CdmLicenseData> CreateUpgradableLicenseInfoList() {
std::vector<DeviceFiles::CdmLicenseData> license_data_list;
for (size_t i = 0; i < kUpgradableLicenseInfoList.size(); ++i) {
DeviceFiles::CdmLicenseData license_data;
license_data.key_set_id = kUpgradableUsageEntryInfoList[i].key_set_id;
license_data.state = kActiveLicenseState;
license_data.license = kUpgradableLicenseInfoList[i];
license_data.usage_entry_number = static_cast<uint32_t>(i);
license_data_list.push_back(license_data);
}
return license_data_list;
}
void InitVectorConstants() { void InitVectorConstants() {
kOverFullUsageEntryInfoVector.clear(); kOverFullUsageEntryInfoVector.clear();
@@ -369,6 +332,40 @@ void InitVectorConstants() {
for (size_t i = 0; i < kLicenseArraySize; i++) { for (size_t i = 0; i < kLicenseArraySize; i++) {
kLicenseList.push_back(kLicenseArray[i]); kLicenseList.push_back(kLicenseArray[i]);
} }
// LRU Data.
kUpgradableUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo1);
kUpgradableUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo2);
kUpgradableUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo3);
kUpgradableLicenseInfoList.push_back(kUpgradableLicenseInfo1);
kUpgradableLicenseInfoList.push_back(kUpgradableLicenseInfo2);
kUpgradableLicenseInfoList.push_back(kUpgradableLicenseInfo3);
kUpgradedUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo1);
kUpgradedUsageEntryInfoList.back().last_use_time =
kUpgradedUsageEntryInfo1LastUsedTime;
kUpgradedUsageEntryInfoList.back().offline_license_expiry_time =
kUpgradedUsageEntryInfo1ExpireTime;
kUpgradedUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo2);
kUpgradedUsageEntryInfoList.back().last_use_time =
kUpgradedUsageEntryInfo2LastUsedTime;
kUpgradedUsageEntryInfoList.back().offline_license_expiry_time =
kUpgradedUsageEntryInfo2ExpireTime;
kUpgradedUsageEntryInfoList.push_back(kUpgradableUsageEntryInfo3);
kUpgradedUsageEntryInfoList.back().last_use_time =
kUpgradedUsageEntryInfo3LastUsedTime;
kUpgradedUsageEntryInfoList.back().offline_license_expiry_time =
kUpgradedUsageEntryInfo3ExpireTime;
for (size_t i = 0; i < kUpgradableLicenseInfoList.size(); ++i) {
DeviceFiles::CdmLicenseData license_data;
license_data.key_set_id = kUpgradableUsageEntryInfoList[i].key_set_id;
license_data.state = kActiveLicenseState;
license_data.license = kUpgradableLicenseInfoList[i];
license_data.usage_entry_number = static_cast<uint32_t>(i);
kUpgradableLicenseDataList.push_back(license_data);
}
} }
void ToVector(std::vector<CdmUsageEntryInfo>& vec, const CdmUsageEntryInfo* arr, void ToVector(std::vector<CdmUsageEntryInfo>& vec, const CdmUsageEntryInfo* arr,
@@ -405,6 +402,9 @@ class MockDeviceFiles : public DeviceFiles {
(override)); (override));
MOCK_METHOD(bool, DeleteUsageInfo, (const std::string&, const std::string&), MOCK_METHOD(bool, DeleteUsageInfo, (const std::string&, const std::string&),
(override)); (override));
MOCK_METHOD(bool, DeleteMultipleUsageInfoByKeySetIds,
(const std::string&, const std::vector<std::string>&),
(override));
MOCK_METHOD(bool, RetrieveUsageInfoByKeySetId, MOCK_METHOD(bool, RetrieveUsageInfoByKeySetId,
(const std::string&, const std::string&, std::string*, (const std::string&, const std::string&, std::string*,
CdmKeyMessage*, CdmKeyResponse*, CdmUsageEntry*, uint32_t*, CdmKeyMessage*, CdmKeyResponse*, CdmUsageEntry*, uint32_t*,
@@ -3728,6 +3728,9 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) {
// These function are called specifically by the LRU upgrading system. // These function are called specifically by the LRU upgrading system.
EXPECT_CALL(*device_files_, RetrieveLicense(_, _, _)).Times(0); EXPECT_CALL(*device_files_, RetrieveLicense(_, _, _)).Times(0);
EXPECT_CALL(*device_files_,
RetrieveUsageInfoByKeySetId(_, _, _, _, _, _, _, _, _))
.Times(0);
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
EXPECT_EQ(usage_table_header_->usage_entry_info(), EXPECT_EQ(usage_table_header_->usage_entry_info(),
@@ -3737,8 +3740,8 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) {
// Initial Test state: // Initial Test state:
// 1. Table info is load from device files; however, device files reports // 1. Table info is load from device files; however, device files reports
// that the table has not been configured for upgrade. // that the table has not been configured for upgrade.
// 2. The usage table header will load offline license to determine // 2. The usage table header will load license or usage information to
// appropriate expiry and last_used times. // determine appropriate expiry and last_used times.
TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) { TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) {
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull())) GetNumberOfOpenSessions(kLevelDefault, NotNull()))
@@ -3753,18 +3756,24 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) {
LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
const std::vector<DeviceFiles::CdmLicenseData> license_info_list =
CreateUpgradableLicenseInfoList();
for (size_t i = 0; i < kUpgradableUsageEntryInfoList.size(); ++i) { for (size_t i = 0; i < kUpgradableUsageEntryInfoList.size(); ++i) {
const CdmUsageEntryInfo& info = kUpgradableUsageEntryInfoList[i]; const CdmUsageEntryInfo& info = kUpgradableUsageEntryInfoList[i];
if (info.storage_type == kStorageLicense) { if (info.storage_type == kStorageLicense) {
// Only offline licenses are supported.
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveLicense(info.key_set_id, NotNull(), NotNull())) RetrieveLicense(info.key_set_id, NotNull(), NotNull()))
.WillOnce( .WillOnce(DoAll(SetArgPointee<1>(kUpgradableLicenseDataList[i]),
DoAll(SetArgPointee<1>(license_info_list[i]), Return(true))); Return(true)));
} else {
EXPECT_CALL(*device_files_,
RetrieveUsageInfoByKeySetId(info.usage_info_file_name,
info.key_set_id, NotNull(),
NotNull(), NotNull(), NotNull(),
NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<4>(kUpgradableLicenseInfoList[i]),
SetArgPointee<6>(static_cast<uint32_t>(i)),
SetArgPointee<7>(kDrmCertificate),
SetArgPointee<8>(kCryptoWrappedKey), Return(true)));
} }
} }
@@ -3774,22 +3783,33 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) {
} }
// Initial Test state: // Initial Test state:
// Table info is load from device files and must be upgraded. // 1. Table info is load from device files and must be upgraded.
// A few entries have the unknown storage type value. // A few entries have the incorrect storage type value.
// 2. During the LRU upgrade process, any license that cannot be
// validated are cleared, and marked to be deleted.
// //
// Entry# Storage type Result info // Entry# Storage type Result info
// ====== ============ =========== // ====== ============ ===========
// 0 Offline Upgraded // 0 Offline Upgraded
// 1 Unknown Cleared // 1 Unknown Cleared
// 2 Unknown Cleared // 2 Invalid (99) Cleared
TEST_F(UsageTableHeaderTest, TEST_F(UsageTableHeaderTest,
LruUsageTableUpgrade_PartialSucceedWithUnknownStorageTypes) { LruUsageTableUpgrade_PartialSucceedWithUnknownStorageTypes) {
const std::vector<CdmUsageEntryInfo> upgradable_usage_entry_info_list = { std::vector<CdmUsageEntryInfo> upgradable_usage_entry_info_list =
kUpgradableUsageEntryInfo1, kUsageEntryInfoStorageTypeUnknown, kUpgradableUsageEntryInfoList;
kUsageEntryInfoStorageTypeUnknown}; std::vector<CdmUsageEntryInfo> upgraded_usage_entry_info_list =
const std::vector<CdmUsageEntryInfo> upgraded_usage_entry_info_list = { kUpgradedUsageEntryInfoList;
kUpgradedUsageEntryInfo1, kUsageEntryInfoStorageTypeUnknown,
kUsageEntryInfoStorageTypeUnknown}; // Set the wrong storage type.
upgradable_usage_entry_info_list[1].storage_type = kStorageTypeUnknown;
upgradable_usage_entry_info_list[2].storage_type =
static_cast<CdmUsageEntryStorageType>(99);
// Set the expected output.
upgraded_usage_entry_info_list[1] = CdmUsageEntryInfo{};
upgraded_usage_entry_info_list[1].storage_type = kStorageTypeUnknown;
upgraded_usage_entry_info_list[2] = CdmUsageEntryInfo{};
upgraded_usage_entry_info_list[2].storage_type = kStorageTypeUnknown;
// Load table expectations. // Load table expectations.
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
@@ -3806,12 +3826,11 @@ TEST_F(UsageTableHeaderTest,
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
// Expectations of the one successful license. // Expectations of the one successful license.
const std::vector<DeviceFiles::CdmLicenseData> license_info_list =
CreateUpgradableLicenseInfoList();
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveLicense(upgradable_usage_entry_info_list[0].key_set_id, RetrieveLicense(upgradable_usage_entry_info_list[0].key_set_id,
NotNull(), NotNull())) NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(license_info_list[0]), Return(true))); .WillOnce(
DoAll(SetArgPointee<1>(kUpgradableLicenseDataList[0]), Return(true)));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
EXPECT_EQ(upgraded_usage_entry_info_list, EXPECT_EQ(upgraded_usage_entry_info_list,
@@ -3827,14 +3846,12 @@ TEST_F(UsageTableHeaderTest,
// Entry# Storage type License Issue Result info // Entry# Storage type License Issue Result info
// ====== ============ ============= =========== // ====== ============ ============= ===========
// 0 Offline No signature Cleared // 0 Offline No signature Cleared
// 1 Unknown Clear entry Cleared // 1 Streaming Wrong type Cleared
// 2 Offline Upgraded // 2 Offline Upgraded
TEST_F(UsageTableHeaderTest, TEST_F(UsageTableHeaderTest,
LruUsageTableUpgrade_PartialSucceedWithLicenseParseIssues) { LruUsageTableUpgrade_PartialSucceedWithLicenseParseIssues) {
std::vector<CdmUsageEntryInfo> upgradable_usage_entry_info_list =
kUpgradableUsageEntryInfoList;
std::vector<DeviceFiles::CdmLicenseData> license_data_list = std::vector<DeviceFiles::CdmLicenseData> license_data_list =
CreateUpgradableLicenseInfoList(); kUpgradableLicenseDataList;
std::vector<CdmKeyResponse> upgradable_license_info_list = std::vector<CdmKeyResponse> upgradable_license_info_list =
kUpgradableLicenseInfoList; kUpgradableLicenseInfoList;
std::vector<CdmUsageEntryInfo> upgraded_usage_entry_info_list = std::vector<CdmUsageEntryInfo> upgraded_usage_entry_info_list =
@@ -3842,12 +3859,21 @@ TEST_F(UsageTableHeaderTest,
// Modify entry 0 to be invalid. // Modify entry 0 to be invalid.
license_data_list[0].license = kUnsignedUpgradableLicenseInfo1; license_data_list[0].license = kUnsignedUpgradableLicenseInfo1;
upgraded_usage_entry_info_list[0].Clear(); CdmUsageEntryInfo& unsigned_usage_entry_info =
upgraded_usage_entry_info_list[0];
unsigned_usage_entry_info.storage_type = kStorageTypeUnknown;
unsigned_usage_entry_info.key_set_id.clear();
unsigned_usage_entry_info.last_use_time = 0;
unsigned_usage_entry_info.offline_license_expiry_time = 0;
// Modify entry 1 to be clear. // Modify entry 1 to be invalid.
upgradable_usage_entry_info_list[1].Clear(); upgradable_license_info_list[1] = kWrongTypedUpgradableLicenseInfo2;
upgradable_license_info_list[1].clear(); CdmUsageEntryInfo& wrond_typed_usage_entry_info =
upgraded_usage_entry_info_list[1].Clear(); upgraded_usage_entry_info_list[1];
wrond_typed_usage_entry_info.storage_type = kStorageTypeUnknown;
wrond_typed_usage_entry_info.key_set_id.clear();
wrond_typed_usage_entry_info.last_use_time = 0;
wrond_typed_usage_entry_info.offline_license_expiry_time = 0;
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull())) GetNumberOfOpenSessions(kLevelDefault, NotNull()))
@@ -3855,27 +3881,46 @@ TEST_F(UsageTableHeaderTest,
EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(),
NotNull(), NotNull())) NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
SetArgPointee<1>(upgradable_usage_entry_info_list), SetArgPointee<1>(kUpgradableUsageEntryInfoList),
SetArgPointee<2>(/* lru_upgrade = */ true), SetArgPointee<2>(/* lru_upgrade = */ true),
SetArgPointee<3>(false), Return(true))); SetArgPointee<3>(false), Return(true)));
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
for (size_t i = 0; i < upgradable_usage_entry_info_list.size(); ++i) { for (size_t i = 0; i < kUpgradableUsageEntryInfoList.size(); ++i) {
const CdmUsageEntryInfo& info = upgradable_usage_entry_info_list[i]; const CdmUsageEntryInfo& info = kUpgradableUsageEntryInfoList[i];
if (info.storage_type == kStorageLicense) { if (info.storage_type == kStorageLicense) {
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveLicense(info.key_set_id, NotNull(), NotNull())) RetrieveLicense(info.key_set_id, NotNull(), NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<1>(license_data_list[i]), Return(true))); DoAll(SetArgPointee<1>(license_data_list[i]), Return(true)));
} else {
EXPECT_CALL(*device_files_,
RetrieveUsageInfoByKeySetId(info.usage_info_file_name,
info.key_set_id, NotNull(),
NotNull(), NotNull(), NotNull(),
NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<4>(upgradable_license_info_list[i]),
SetArgPointee<6>(static_cast<uint32_t>(i)),
SetArgPointee<7>(kDrmCertificate),
SetArgPointee<8>(kCryptoWrappedKey), Return(true)));
} }
} }
// For the entries which failed, there should be an attempt to delete the // For the entries which failed, there should be an attempt to delete the
// files associated with them. // files associated with them.
// Offline license.
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
DeleteLicense(upgradable_usage_entry_info_list[0].key_set_id)) DeleteLicense(kUpgradableUsageEntryInfoList[0].key_set_id))
.WillOnce(Return(true));
// Streaming license.
const std::vector<std::string> corrupted_usage_info_key_set_ids = {
kUpgradableUsageEntryInfoList[1].key_set_id};
EXPECT_CALL(*device_files_,
DeleteMultipleUsageInfoByKeySetIds(
kUpgradableUsageEntryInfoList[1].usage_info_file_name,
ContainerEq(corrupted_usage_info_key_set_ids)))
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
@@ -3908,6 +3953,13 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) {
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveLicense(info.key_set_id, NotNull(), NotNull())) RetrieveLicense(info.key_set_id, NotNull(), NotNull()))
.WillOnce(Return(false)); .WillOnce(Return(false));
} else {
EXPECT_CALL(*device_files_,
RetrieveUsageInfoByKeySetId(info.usage_info_file_name,
info.key_set_id, NotNull(),
NotNull(), NotNull(), NotNull(),
NotNull(), NotNull(), NotNull()))
.WillOnce(Return(false));
} }
} }
@@ -3955,7 +4007,46 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) {
EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry( EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry(
crypto_session_, true /* persistent_license */, crypto_session_, true /* persistent_license */,
expected_new_entry.key_set_id, expected_new_entry.key_set_id,
/* usage_info_file_name */ kEmptyString, kEmptyString, expected_new_entry.usage_info_file_name, kEmptyString,
&usage_entry_number));
EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
EXPECT_EQ(expected_usage_info_list, usage_table_header_->usage_entry_info());
}
TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) {
Init(kSecurityLevelL1, kUpgradedUsageTableHeader,
kUpgradedUsageEntryInfoList);
// Expected values.
const uint32_t expected_usage_entry_number =
static_cast<uint32_t>(kUpgradedUsageEntryInfoList.size());
const CdmUsageEntryInfo expected_new_entry = {
kStorageUsageInfo, "secure_stop_key_set_5", "streaming_license_file_4",
kLruBaseTime, 0 /* No set for streaming license. */
};
std::vector<CdmUsageEntryInfo> expected_usage_info_list =
kUpgradedUsageEntryInfoList;
expected_usage_info_list.push_back(expected_new_entry);
// AddKey expectations
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number),
Return(NO_ERROR)));
MockClock mock_clock;
usage_table_header_->SetClock(&mock_clock);
EXPECT_CALL(mock_clock, GetCurrentTime()).WillOnce(Return(kLruBaseTime));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _));
// The Call.
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry(
crypto_session_, false /* persistent_license */,
expected_new_entry.key_set_id,
expected_new_entry.usage_info_file_name, kEmptyString,
&usage_entry_number)); &usage_entry_number));
EXPECT_EQ(expected_usage_entry_number, usage_entry_number); EXPECT_EQ(expected_usage_entry_number, usage_entry_number);
@@ -4060,7 +4151,6 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) {
// Unexpired offline license. // Unexpired offline license.
CdmUsageEntryInfo unexpired_entry_info; CdmUsageEntryInfo unexpired_entry_info;
unexpired_entry_info.storage_type = kStorageLicense; unexpired_entry_info.storage_type = kStorageLicense;
unexpired_entry_info.key_set_id = "unexpired_key_set_id";
unexpired_entry_info.last_use_time = kLruBaseTime; unexpired_entry_info.last_use_time = kLruBaseTime;
unexpired_entry_info.offline_license_expiry_time = kLruBaseTime + 2 * kOneDay; unexpired_entry_info.offline_license_expiry_time = kLruBaseTime + 2 * kOneDay;
usage_entry_info_list.push_back(unexpired_entry_info); usage_entry_info_list.push_back(unexpired_entry_info);
@@ -4069,43 +4159,59 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) {
// Expired offline license. // Expired offline license.
CdmUsageEntryInfo expired_entry_info; CdmUsageEntryInfo expired_entry_info;
expired_entry_info.storage_type = kStorageLicense; expired_entry_info.storage_type = kStorageLicense;
expired_entry_info.key_set_id = "expired_key_set_id";
expired_entry_info.last_use_time = kLruBaseTime; expired_entry_info.last_use_time = kLruBaseTime;
expired_entry_info.offline_license_expiry_time = kLruBaseTime; expired_entry_info.offline_license_expiry_time = kLruBaseTime;
usage_entry_info_list.push_back(expired_entry_info); usage_entry_info_list.push_back(expired_entry_info);
constexpr uint32_t expired_entry_number = 1; constexpr uint32_t expired_entry_number = 1;
// Streaming license.
CdmUsageEntryInfo streaming_entry_info;
streaming_entry_info.storage_type = kStorageUsageInfo;
streaming_entry_info.last_use_time = kLruBaseTime;
usage_entry_info_list.push_back(streaming_entry_info);
constexpr uint32_t streaming_entry_number = 2;
// Unknown entry. // Unknown entry.
CdmUsageEntryInfo unknown_entry_info; CdmUsageEntryInfo unknown_entry_info;
unknown_entry_info.storage_type = kStorageTypeUnknown; unknown_entry_info.storage_type = kStorageTypeUnknown;
// Should be chosen regardless of |last_use_time|. // Should be chosen regardless of |last_use_time|.
unknown_entry_info.last_use_time = kCurrentTime; unknown_entry_info.last_use_time = kCurrentTime;
usage_entry_info_list.push_back(unknown_entry_info); usage_entry_info_list.push_back(unknown_entry_info);
constexpr uint32_t unknown_entry_number = 2; constexpr uint32_t unknown_entry_number = 3;
// Case 1: If there is a clear entry, it should be selected above // Case 1: If there is an entry with unknown storage type, it should
// any other entry. // be selected above any other entry.
uint32_t entry_to_remove = kInvalidEntry; uint32_t entry_to_remove = kInvalidEntry;
// Expect the clear. // Expect the unknown entry.
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime, usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ 3, &entry_to_remove)); /* unexpired_threshold = */ 3, &entry_to_remove));
EXPECT_EQ(unknown_entry_number, entry_to_remove); EXPECT_EQ(unknown_entry_number, entry_to_remove);
usage_entry_info_list.pop_back(); // Removing clear entry. usage_entry_info_list.pop_back(); // Removing unknown.
// |usage_entry_info_list| only contains 1 expired and 1 unexpired offline
// license.
// Case 2a: Threshold not met, all entries are equally stale. // Case 2a: Threshold not met, all entries are equally stale.
// The expired entry should be selected over the unexpired entry. // The expired entry should be selected over the streaming license.
entry_to_remove = kInvalidEntry; entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime, usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ 3, &entry_to_remove)); /* unexpired_threshold = */ 3, &entry_to_remove));
EXPECT_EQ(expired_entry_number, entry_to_remove); EXPECT_EQ(expired_entry_number, entry_to_remove);
// Case 2b: Threshold met, equally stale entries. Expect the expired // Case 2b: Threshold not met, streaming license is most stale.
usage_entry_info_list[streaming_entry_number].last_use_time--;
entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ 3, &entry_to_remove));
EXPECT_EQ(streaming_entry_number, entry_to_remove);
usage_entry_info_list.pop_back(); // Removing streaming.
// |usage_entry_info_list| only contains 1 expired and 1 unexpired offline
// license.
// Case 2c: Threshold met, equally stale entries. Expect the expired
// entry over the unexpired. // entry over the unexpired.
entry_to_remove = kInvalidEntry; entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
@@ -4167,8 +4273,45 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) {
EXPECT_EQ(unexpired_entry_number, entry_to_remove); EXPECT_EQ(unexpired_entry_number, entry_to_remove);
} }
// Testing algorithm with unexpired offline and streaming license. The
// sum of offline licenses is below the threshold for consideration.
// Only the streaming license should be considered for removal.
TEST_F(UsageTableHeaderTest,
DetermineLicenseToRemove_NoExpiredAndBelowThreshold) {
constexpr int64_t kOneDay = 24 * 60 * 60;
constexpr uint32_t kInvalidEntry = 9999;
std::vector<CdmUsageEntryInfo> usage_entry_info_list =
kUpgradedUsageEntryInfoList;
const size_t offline_threshold = usage_entry_info_list.size() + 1;
size_t usage_info_count = 0;
for (auto& usage_entry_info : usage_entry_info_list) {
if (usage_entry_info.storage_type == kStorageUsageInfo) {
// Make usage info entries less stale than offline.
usage_entry_info.last_use_time += 100;
++usage_info_count;
} else {
// Make offline licenses unexpired.
usage_entry_info.offline_license_expiry_time =
kLruBaseTime + kOneDay * 30;
}
}
// Must exist at least one streaming license for test to work.
ASSERT_LT(0ull, usage_info_count);
uint32_t entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kLruBaseTime, offline_threshold,
&entry_to_remove));
EXPECT_EQ(kStorageUsageInfo,
usage_entry_info_list[entry_to_remove].storage_type);
}
// When the number of unexpired offline licenses are below the // When the number of unexpired offline licenses are below the
// threshold, only expired offline licenses should be considered. // threshold, only streaming licenses and expired offline licenses
// should be considered.
// Providing only offline licenses, only the expired license should be // Providing only offline licenses, only the expired license should be
// considered for removal. // considered for removal.
TEST_F(UsageTableHeaderTest, TEST_F(UsageTableHeaderTest,
@@ -4187,20 +4330,20 @@ TEST_F(UsageTableHeaderTest,
for (uint32_t i = 0; i < kSetSize; ++i) { for (uint32_t i = 0; i < kSetSize; ++i) {
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i];
usage_entry_info.storage_type = kStorageLicense; usage_entry_info.storage_type = kStorageLicense;
usage_entry_info.key_set_id = "unexpired_offline"; usage_entry_info.key_set_id = "nothing_unusual";
usage_entry_info.last_use_time = kLruBaseTime; usage_entry_info.last_use_time = kLruBaseTime;
usage_entry_info.offline_license_expiry_time = usage_entry_info.offline_license_expiry_time =
kCurrentTime + kDefaultExpireDuration; kCurrentTime + kDefaultExpireDuration;
} }
// Mark three licenses as expired. // Mark 3 as expired.
std::vector<uint32_t> expired_license_numbers; std::vector<uint32_t> expired_license_numbers;
while (expired_license_numbers.size() < 3) { while (expired_license_numbers.size() < 3) {
const uint32_t i = const uint32_t i =
static_cast<uint32_t>(wvutil::CdmRandom::RandomInRange(kSetSize - 1)); static_cast<uint32_t>(wvutil::CdmRandom::RandomInRange(kSetSize - 1));
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i];
// Skip already expired ones // Skip already expired ones
if (usage_entry_info.key_set_id != "unexpired_offline") continue; if (usage_entry_info.key_set_id != "nothing_unusual") continue;
// Make these less stale than the unexpired licenses. // Make these less stale than the unexpired licenses.
usage_entry_info.last_use_time = kLruBaseTime + kOneDay; usage_entry_info.last_use_time = kLruBaseTime + kOneDay;
usage_entry_info.offline_license_expiry_time = kCurrentTime - kOneDay; usage_entry_info.offline_license_expiry_time = kCurrentTime - kOneDay;
@@ -4216,31 +4359,16 @@ TEST_F(UsageTableHeaderTest,
EXPECT_THAT(expired_license_numbers, Contains(entry_to_remove)); EXPECT_THAT(expired_license_numbers, Contains(entry_to_remove));
} }
// This primarily tests the robustness of the algorithm for a full // This test primarily tests the robustness of the algorithm for a full
// set of entries (200). Creates 1 offline license which are more // set of entries (200). Creates 1 stale streaming license and 1
// stale than the all other licenses. // offline licenses which are more stale than the streaming.
// //
// All cases: 1 unexpired license is most stale. // First, with the stale offline licenses unexpired, checks that the
// streaming are always selected, so long as |unexpired_threshold|
// is not met.
// //
// Case 1: // Second, with the stale offline license marked as expired, checks that
// - Unexpired threshold met: No // offline licenses are selected over the streaming.
// - Exists expired license: No
// - Expect most stale license to be selected
//
// Case 2:
// - Unexpired threshold met: Yes
// - Exists expired license: No
// - Expect most stale license to be selected
//
// Case 3:
// - Unexpired threshold met: No
// - Exists expired license: Yes
// - Expect expired license to be selected
//
// Case 4:
// - Unexpired threshold met: Yes
// - Exists expired license: Yes
// - Expect most stale license to be selected
TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_LargeMixedSet) { TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_LargeMixedSet) {
constexpr int64_t kOneDay = 24 * 60 * 60; constexpr int64_t kOneDay = 24 * 60 * 60;
constexpr size_t kLargeSetSize = 200; constexpr size_t kLargeSetSize = 200;
@@ -4249,63 +4377,74 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_LargeMixedSet) {
std::vector<CdmUsageEntryInfo> usage_entry_info_list; std::vector<CdmUsageEntryInfo> usage_entry_info_list;
usage_entry_info_list.resize(kLargeSetSize); usage_entry_info_list.resize(kLargeSetSize);
// Create a set of unexpired license entries. // Create a set of usage entries.
for (uint32_t i = 0; i < kLargeSetSize; ++i) { for (uint32_t i = 0; i < kLargeSetSize; ++i) {
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i];
usage_entry_info.storage_type = kStorageLicense; usage_entry_info.key_set_id = "nothing_unusual";
usage_entry_info.key_set_id = "unexpired_offline";
usage_entry_info.last_use_time = kLruBaseTime + kOneDay; usage_entry_info.last_use_time = kLruBaseTime + kOneDay;
usage_entry_info.offline_license_expiry_time = if ((i % 4) == 0) {
kCurrentTime + kDefaultExpireDuration; // Roughly 25% are offline, the rest are streaming.
usage_entry_info.storage_type = kStorageLicense;
usage_entry_info.offline_license_expiry_time =
kCurrentTime + kDefaultExpireDuration;
} else {
usage_entry_info.storage_type = kStorageUsageInfo;
}
}
// Select a streaming license to be more stale than the rest.
uint32_t modified_usage_info_number = kInvalidEntry;
while (modified_usage_info_number == kInvalidEntry) {
const uint32_t i = static_cast<uint32_t>(
wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1));
CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i];
// Skip offline licenses.
if (usage_entry_info.storage_type != kStorageUsageInfo) continue;
usage_entry_info.last_use_time = kLruBaseTime + 10;
usage_entry_info.key_set_id = "stale_streaming";
modified_usage_info_number = i;
} }
// Select a offline license to be even more stale, but unexpired. // Select a offline license to be even more stale, but unexpired.
const uint32_t most_stale_offline_license_number = static_cast<uint32_t>( uint32_t modified_offline_license_number = kInvalidEntry;
wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1)); while (modified_offline_license_number == kInvalidEntry) {
CdmUsageEntryInfo& usage_entry_info = const uint32_t i = static_cast<uint32_t>(
usage_entry_info_list[most_stale_offline_license_number]; wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1));
usage_entry_info.last_use_time = kLruBaseTime; CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i];
usage_entry_info.key_set_id = "stale_offline"; // Skip streaming licenses.
if (usage_entry_info.storage_type != kStorageLicense) continue;
usage_entry_info.last_use_time = kLruBaseTime;
usage_entry_info.key_set_id = "stale_offline";
modified_offline_license_number = i;
}
// Case 1: Most stale license is selected. // Test using only streaming and expired offline licenses
// (which there are none).
uint32_t entry_to_remove = kInvalidEntry; uint32_t entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime, usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ kLargeSetSize, &entry_to_remove)); /* unexpired_threshold = */ kLargeSetSize, &entry_to_remove));
EXPECT_EQ(most_stale_offline_license_number, entry_to_remove); EXPECT_EQ(modified_usage_info_number, entry_to_remove);
// Case 2: Most stale license is selected. // Test where the equality threshold is met, now the stale unexpired
// license should be selected.
entry_to_remove = kInvalidEntry; entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime, usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ 0, &entry_to_remove)); /* unexpired_threshold = */ 0, &entry_to_remove));
EXPECT_EQ(most_stale_offline_license_number, entry_to_remove); EXPECT_EQ(modified_offline_license_number, entry_to_remove);
// Select a offline license to be marked as expired but not the most // Make the stale offline license expired.
// stale. CdmUsageEntryInfo& offline_usage_entry_info =
uint32_t expired_offline_license_number = kInvalidEntry; usage_entry_info_list[modified_offline_license_number];
while (expired_offline_license_number == kInvalidEntry) { offline_usage_entry_info.offline_license_expiry_time = kLruBaseTime;
const uint32_t i = static_cast<uint32_t>(
wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1));
// Don't modify the stale offline license.
if (usage_entry_info_list[i].key_set_id != "unexpired_offline") continue;
usage_entry_info_list[i].offline_license_expiry_time = kLruBaseTime;
expired_offline_license_number = i;
}
// Case 3: Expired license is selected. // Test again, expecting that the expired license should be considered.
entry_to_remove = kInvalidEntry; entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime, usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ kLargeSetSize, &entry_to_remove)); /* unexpired_threshold = */ kLargeSetSize, &entry_to_remove));
EXPECT_EQ(expired_offline_license_number, entry_to_remove); EXPECT_EQ(modified_offline_license_number, entry_to_remove);
// Case 4: Most stale license is selected.
entry_to_remove = kInvalidEntry;
EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting(
usage_entry_info_list, kCurrentTime,
/* unexpired_threshold = */ 0, &entry_to_remove));
EXPECT_EQ(most_stale_offline_license_number, entry_to_remove);
} }
TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Unavailable) { TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Unavailable) {