diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 8b3926ef..016378a8 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -277,9 +277,14 @@ class DeviceFiles { // When retrieving usage table information from the file system; any // table that has yet to be updated for the LRU attributes will be // indicated by |lru_upgrade|. + // Tables from earlier CDM releases might contain USAGE_INFO type + // entries. This entries are no long required, by their presence + // requires the usage table to be cleaned up. |has_usage_info_entries| + // is set to true if any are detected. virtual bool RetrieveUsageTableInfo( CdmUsageTableHeader* usage_table_header, - std::vector* usage_entry_info, bool* lru_upgrade); + std::vector* usage_entry_info, bool* lru_upgrade, + bool* has_usage_info_entries); virtual bool DeleteUsageTableInfo(); diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 66aef9b3..269a9ab5 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -261,10 +261,9 @@ class UsageTableHeader { 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. + // seconds). void RecordLruEventMetrics(metrics::CryptoMetrics* metrics, - uint64_t staleness, - CdmUsageEntryStorageType storage_type); + uint64_t staleness); // Uses an LRU-base algorithm to determine which license should be // removed. This is intended to be used if the usage table is full diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index b637b6f2..78aedb62 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -1678,11 +1678,13 @@ bool DeviceFiles::StoreUsageTableInfo( bool DeviceFiles::RetrieveUsageTableInfo( CdmUsageTableHeader* usage_table_header, - std::vector* usage_entry_info, bool* lru_upgrade) { + std::vector* usage_entry_info, bool* lru_upgrade, + bool* has_usage_info_entries) { RETURN_FALSE_IF_UNINITIALIZED(); RETURN_FALSE_IF_NULL(usage_table_header); RETURN_FALSE_IF_NULL(usage_entry_info); RETURN_FALSE_IF_NULL(lru_upgrade); + RETURN_FALSE_IF_NULL(has_usage_info_entries); video_widevine_client::sdk::File file; if (RetrieveHashedFile(GetUsageTableFileName(), &file) != kNoError) { @@ -1712,6 +1714,7 @@ bool DeviceFiles::RetrieveUsageTableInfo( const UsageTableInfo& usage_table_info = file.usage_table_info(); *lru_upgrade = !usage_table_info.use_lru(); + *has_usage_info_entries = false; *usage_table_header = usage_table_info.usage_table_header(); usage_entry_info->resize(usage_table_info.usage_entry_info_size()); @@ -1727,10 +1730,8 @@ bool DeviceFiles::RetrieveUsageTableInfo( info.offline_license_expiry_time(); break; case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO: - (*usage_entry_info)[i].storage_type = kStorageUsageInfo; - (*usage_entry_info)[i].usage_info_file_name = - info.usage_info_file_name(); - (*usage_entry_info)[i].last_use_time = info.last_use_time(); + (*usage_entry_info)[i].storage_type = kStorageTypeUnknown; + *has_usage_info_entries = true; break; case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN: default: diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index f0cc7969..39ff2954 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -96,41 +96,6 @@ bool RetrieveOfflineLicense(DeviceFiles* device_files, 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) { // Used for stl filters. return info.storage_type == kStorageUsageInfo; @@ -194,8 +159,10 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, bool UsageTableHeader::RestoreTable(CryptoSession* const crypto_session) { bool run_lru_upgrade = false; + bool has_usage_info_entries = false; if (!device_files_->RetrieveUsageTableInfo( - &usage_table_header_, &usage_entry_info_, &run_lru_upgrade)) { + &usage_table_header_, &usage_entry_info_, &run_lru_upgrade, + &has_usage_info_entries)) { LOGW("Could not retrieve usage table"); return false; } @@ -210,6 +177,15 @@ bool UsageTableHeader::RestoreTable(CryptoSession* const crypto_session) { return false; } + // Remove all usage info entries from storage and clear their + // table meta data. + if (has_usage_info_entries) { + LOGI("Removing all usage info entries"); + device_files_->DeleteAllUsageInfo(); + // Store table to remove usage info entries from storage. + StoreTable(); + } + // If the saved usage entries/meta data is missing LRU information, // then the entries and their meta data must be updated. if (run_lru_upgrade && !LruUpgradeAllUsageEntries()) { @@ -1152,7 +1128,6 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry( // 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 = InvalidateEntryInternal(entry_number_to_delete, /* defrag_table = */ true, @@ -1165,7 +1140,7 @@ CdmResponseType UsageTableHeader::ReleaseOldestEntry( } // Record metrics on success. - RecordLruEventMetrics(metrics, staleness, storage_type); + RecordLruEventMetrics(metrics, staleness); return NO_ERROR; } @@ -1198,34 +1173,16 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() { usage_entry_number < usage_entry_info_.size(); ++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; CdmKeyResponse license_message; - bool retrieve_response = false; - switch (usage_entry_info.storage_type) { - 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(usage_entry_info.storage_type), - usage_entry_number); - bad_license_file_entries.push_back(usage_entry_number); - continue; - } - } + const bool retrieve_response = + RetrieveOfflineLicense(device_files_.get(), usage_entry_info.key_set_id, + &license_message, &retrieved_entry_number); if (!retrieve_response) { LOGW("Could not retrieve license message: usage_entry_number = %u", usage_entry_number); @@ -1254,22 +1211,18 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() { // for replacement above all others. usage_entry_info.last_use_time = license.license_start_time(); - if (usage_entry_info.storage_type == kStorageLicense) { - // Only offline licenses need |offline_license_expiry_time| set. - const video_widevine::License::Policy& policy = license.policy(); - // TODO(b/139372190): Change how these fields are set once feature is - // implemented. - if (policy.license_duration_seconds() == 0) { - // Zero implies unlimited license duration. - usage_entry_info.offline_license_expiry_time = - 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(); - } + // Only offline licenses need |offline_license_expiry_time| set. + const video_widevine::License::Policy& policy = license.policy(); + // TODO(b/139372190): Change how these fields are set once feature is + // implemented. + if (policy.license_duration_seconds() == 0) { + // Zero implies unlimited license duration. + usage_entry_info.offline_license_expiry_time = + license.license_start_time() + policy.rental_duration_seconds() + + policy.playback_duration_seconds(); } else { - usage_entry_info.offline_license_expiry_time = 0; + usage_entry_info.offline_license_expiry_time = + license.license_start_time() + policy.license_duration_seconds(); } } // End for loop. @@ -1279,31 +1232,14 @@ bool UsageTableHeader::LruUpgradeAllUsageEntries() { return false; } - // Maps -> []. - std::map> usage_info_clean_up; for (size_t usage_entry_number : bad_license_file_entries) { CdmUsageEntryInfo& usage_entry_info = usage_entry_info_[usage_entry_number]; if (usage_entry_info.storage_type == kStorageLicense) { 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()) - .first; - } - it->second.push_back(usage_entry_info.key_set_id); - } // else kStorageUnknown { Nothing special }. + } usage_entry_info.Clear(); } - for (const auto& p : usage_info_clean_up) { - device_files_->DeleteMultipleUsageInfoByKeySetIds(p.first, p.second); - } - return true; } @@ -1316,16 +1252,18 @@ bool UsageTableHeader::GetRemovalCandidate(uint32_t* entry_to_remove) { lru_unexpired_threshold, entry_to_remove); } -void UsageTableHeader::RecordLruEventMetrics( - metrics::CryptoMetrics* metrics, uint64_t staleness, - CdmUsageEntryStorageType storage_type) { +void UsageTableHeader::RecordLruEventMetrics(metrics::CryptoMetrics* metrics, + uint64_t staleness) { if (metrics == nullptr) return; - metrics->usage_table_header_lru_usage_info_count_.Record(UsageInfoCount()); + // Usage info are deprecated, always record 0. + metrics->usage_table_header_lru_usage_info_count_.Record(0); metrics->usage_table_header_lru_offline_license_count_.Record( OfflineEntryCount()); 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( - static_cast(storage_type)); + static_cast(kDefaultEntryType)); } // Static. @@ -1348,14 +1286,13 @@ bool UsageTableHeader::DetermineLicenseToRemove( usage_entry_info_list[j].last_use_time; }; - // Find the most stale expired offline / streaming license and the + // Find the most stale expired offline 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 + // entries. If any entry is not of storage type license, then it should // be removed. constexpr uint32_t kNoEntry = std::numeric_limits::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(); @@ -1363,37 +1300,28 @@ bool UsageTableHeader::DetermineLicenseToRemove( 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. + if (usage_entry_info.storage_type != kStorageLicense) { + // Non-license 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) { + if (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) { + } else { // 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; - } } } 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( @@ -1413,12 +1341,10 @@ bool UsageTableHeader::DetermineLicenseToRemove( // 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, + *entry_to_remove = select_most_stale(stalest_unexpired_offline_license, stalest_expired_offline_license); + } else { + *entry_to_remove = stalest_expired_offline_license; } if (*entry_to_remove == kNoEntry) { diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 8c6acb66..19f88511 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -3713,8 +3713,7 @@ const UsageTableTestInfo kUsageTableInfoTestData[] = { }; const CdmUsageEntryInfo kUsageEntriesWithoutLruData[] = { - {kStorageLicense, "ksid0", "", 0, 0}, - {kStorageUsageInfo, "", "app_id_1", 0, 0}}; + {kStorageLicense, "ksid0", "", 0, 0}, {kStorageTypeUnknown, "", "", 0, 0}}; const std::string kUsageTableWithoutLruData = a2bs_hex( "0A1F080510013A191209080112056B73696430120C08021A086170705F69645F" @@ -5815,13 +5814,21 @@ TEST_P(DeviceFilesUsageTableTest, Read) { std::vector usage_entry_info; CdmUsageTableHeader usage_table_header; bool lru_upgrade; + bool has_usage_info_entries; ASSERT_TRUE(device_files.RetrieveUsageTableInfo( - &usage_table_header, &usage_entry_info, &lru_upgrade)); + &usage_table_header, &usage_entry_info, &lru_upgrade, + &has_usage_info_entries)); EXPECT_EQ(kUsageTableInfoTestData[index].usage_table_header, usage_table_header); EXPECT_EQ(index + 1u, usage_entry_info.size()); for (size_t i = 0; i <= index; ++i) { + // TODO(b/242289743): Update test data to exclude usage info files. + if (kUsageEntriesTestData[i].storage_type == kStorageUsageInfo) { + // Usage info entry types are no longer loaded. + EXPECT_EQ(usage_entry_info[i].storage_type, kStorageTypeUnknown); + continue; + } EXPECT_EQ(kUsageEntriesTestData[i].storage_type, usage_entry_info[i].storage_type); EXPECT_EQ(kUsageEntriesTestData[i].key_set_id, @@ -5863,8 +5870,10 @@ TEST_F(DeviceFilesUsageTableTest, ReadWithoutLruData) { std::vector usage_entry_info; CdmUsageTableHeader usage_table_header; bool lru_upgrade; + bool has_usage_info_entries; ASSERT_TRUE(device_files.RetrieveUsageTableInfo( - &usage_table_header, &usage_entry_info, &lru_upgrade)); + &usage_table_header, &usage_entry_info, &lru_upgrade, + &has_usage_info_entries)); EXPECT_EQ(ArraySize(kUsageEntriesWithoutLruData), usage_entry_info.size()); diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index 98464e29..fccce60e 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -212,6 +212,12 @@ const std::vector k10UsageEntryInfoVector = { kUsageEntryInfoOfflineLicense4, kUsageEntryInfoSecureStop4, kUsageEntryInfoOfflineLicense5, kUsageEntryInfoSecureStop5, }; +const std::vector k5UsageEntryInfoVector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense4, + kUsageEntryInfoOfflineLicense5, +}; + std::vector kOverFullUsageEntryInfoVector; const CdmOfflineLicenseState kActiveLicenseState = kLicenseStateActive; @@ -223,9 +229,9 @@ const CdmKeyResponse kKeyRenewalResponse = "key renewal response"; const std::string kReleaseServerUrl = "some url"; const std::string kProviderSessionToken = "provider session token"; const CdmAppParameterMap kEmptyAppParameters; -int64_t kPlaybackStartTime = 1030005; -int64_t kPlaybackDuration = 300; -int64_t kGracePeriodEndTime = 60; +constexpr int64_t kPlaybackStartTime = 1030005; +constexpr int64_t kPlaybackDuration = 300; +constexpr int64_t kGracePeriodEndTime = 60; // ==== LRU Upgrade Data ==== const CdmUsageTableHeader kUpgradableUsageTableHeader = @@ -239,9 +245,9 @@ const CdmUsageEntryInfo kUpgradableUsageEntryInfo1 = { /* last_use_time = */ 0, /* offline_license_expiry_time = */ 0}; const CdmUsageEntryInfo kUpgradableUsageEntryInfo2 = { - /* storage_type = */ kStorageUsageInfo, - /* key_set_id = */ "streaming_key_set_2", - /* usage_info_file_name = */ "streaming_license_file_2", + /* storage_type = */ kStorageLicense, + /* key_set_id = */ "offline_key_set_2", + /* usage_info_file_name = */ "", /* last_use_time = */ 0, /* offline_license_expiry_time = */ 0}; const CdmUsageEntryInfo kUpgradableUsageEntryInfo3 = { @@ -250,7 +256,11 @@ const CdmUsageEntryInfo kUpgradableUsageEntryInfo3 = { /* usage_info_file_name = */ "", /* last_use_time = */ 0, /* offline_license_expiry_time = */ 0}; -std::vector kUpgradableUsageEntryInfoList; +const std::vector kUpgradableUsageEntryInfoList{ + kUpgradableUsageEntryInfo1, kUpgradableUsageEntryInfo2, + kUpgradableUsageEntryInfo3}; + +const int64_t kLruBaseTime = 1563399000; // Offline license 1. // license_start_time = 1563399000 @@ -260,11 +270,14 @@ std::vector kUpgradableUsageEntryInfoList; const CdmKeyResponse kUpgradableLicenseInfo1 = wvutil::a2bs_hex( "08021214120C2080F5242880A3053080E90F20D8A6BEE9051A20D5F7ACE8D84A166C69BB" "27523C84C019464B90AA9BF06B8332004839119BFD14"); -// Streaming license 2. +// Offline license 2. // 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( - "0802120620D8A6BEE9051A201956F2FD69E5E96DA8C65FDD04A3C294E484F219F2B1A8DD" - "C2B0737F6EF5BD22"); + "080212101208200028003080E90F20D8A6BEE9051A20D0611C4AFEF3EC9C67143ED39B44" + "8FAAA67DB6C4DBFDC28A733AF9A2EABDF0B3"); // Offline license 3. // license_start_time = 1563399000 // license_duration_seconds = 0 (unlimited) @@ -273,28 +286,56 @@ const CdmKeyResponse kUpgradableLicenseInfo2 = wvutil::a2bs_hex( const CdmKeyResponse kUpgradableLicenseInfo3 = wvutil::a2bs_hex( "08021212120A2080F5242880A305300020D8A6BEE9051A207B09896F46C4EE443170E215" "B2D8D5F072951027B152F4758AC3A339D7C7B4CE"); -std::vector kUpgradableLicenseInfoList; -std::vector kUpgradableLicenseDataList; +const std::vector kUpgradableLicenseInfoList = { + kUpgradableLicenseInfo1, kUpgradableLicenseInfo2, kUpgradableLicenseInfo3}; + // Same as Offline license 1, but without signature. const CdmKeyResponse kUnsignedUpgradableLicenseInfo1 = 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 kUpgradedUsageEntryInfo1ExpireTime = kLruBaseTime + 259200; const int64_t kUpgradedUsageEntryInfo2LastUsedTime = kLruBaseTime; -const int64_t kUpgradedUsageEntryInfo2ExpireTime = 0; // Unset +const int64_t kUpgradedUsageEntryInfo2ExpireTime = kLruBaseTime + 259200; const int64_t kUpgradedUsageEntryInfo3LastUsedTime = kLruBaseTime; const int64_t kUpgradedUsageEntryInfo3ExpireTime = kLruBaseTime + 604800 + 86400; const CdmUsageTableHeader kUpgradedUsageTableHeader = "Upgraded Table Header"; -std::vector kUpgradedUsageEntryInfoList; +const CdmUsageEntryInfo kUpgradedUsageEntryInfo1 = { + /* 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}; -namespace { +const std::vector kUpgradedUsageEntryInfoList = { + kUpgradedUsageEntryInfo1, kUpgradedUsageEntryInfo2, + kUpgradedUsageEntryInfo3}; + +std::vector CreateUpgradableLicenseInfoList() { + std::vector 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(i); + license_data_list.push_back(license_data); + } + return license_data_list; +} void InitVectorConstants() { kOverFullUsageEntryInfoVector.clear(); @@ -328,40 +369,6 @@ void InitVectorConstants() { for (size_t i = 0; i < kLicenseArraySize; 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(i); - kUpgradableLicenseDataList.push_back(license_data); - } } void ToVector(std::vector& vec, const CdmUsageEntryInfo* arr, @@ -373,8 +380,6 @@ void ToVector(std::vector& vec, const CdmUsageEntryInfo* arr, } } -}; // namespace - class MockDeviceFiles : public DeviceFiles { public: MockDeviceFiles() : DeviceFiles(&file_system_) { Init(kSecurityLevelL1); } @@ -392,7 +397,7 @@ class MockDeviceFiles : public DeviceFiles { MOCK_METHOD(bool, RetrieveUsageTableInfo, (CdmUsageTableHeader*, std::vector*, - bool* lru_upgrade), + bool* lru_upgrade, bool* has_usage_info_entries), (override)); MOCK_METHOD(bool, StoreUsageTableInfo, (const CdmUsageTableHeader&, @@ -400,9 +405,6 @@ class MockDeviceFiles : public DeviceFiles { (override)); MOCK_METHOD(bool, DeleteUsageInfo, (const std::string&, const std::string&), (override)); - MOCK_METHOD(bool, DeleteMultipleUsageInfoByKeySetIds, - (const std::string&, const std::vector&), - (override)); MOCK_METHOD(bool, RetrieveUsageInfoByKeySetId, (const std::string&, const std::string&, std::string*, CdmKeyMessage*, CdmKeyResponse*, CdmUsageEntry*, uint32_t*, @@ -574,11 +576,12 @@ class UsageTableHeaderTest : public WvCdmTestBase { const std::vector& usage_entry_info_vector) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(_, NotNull())) .WillOnce(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(usage_table_header), SetArgPointee<1>(usage_entry_info_vector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(_, usage_table_header)) .WillOnce(Return(NO_ERROR)); EXPECT_TRUE(usage_table_header_->Init(security_level, crypto_session_)); @@ -623,11 +626,12 @@ TEST_P(UsageTableHeaderInitializationTest, RestoreUsageTable_Success) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -644,11 +648,12 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -678,8 +683,8 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(Return(false)); // Expectations for create: @@ -710,11 +715,12 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW)); @@ -745,8 +751,8 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(Return(false)); // Expectations for create: // 1) Create new header within OEMCrypto fails @@ -769,8 +775,8 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(Return(false)); // Expectations for create: // 1) Create new header within OEMCrypto succeeds @@ -797,11 +803,11 @@ TEST_P(UsageTableHeaderInitializationTest, RestoreUsageTable_AtCapacity) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -824,11 +830,11 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -854,11 +860,12 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kOverFullUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -911,11 +918,12 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kOverFullUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -963,11 +971,12 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(security_level, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kOverFullUsageEntryInfoVector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -1014,6 +1023,31 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); } +// If the stored usage table info contains entries of type USAGE_INFO, +// these entries will not be included in the store, however, the +// UsageTableHeader must delete the existing usage info files and +// store the table. +TEST_P(UsageTableHeaderInitializationTest, RestoreTableWithUsageInfo) { + EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(_, NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) + .WillOnce(DoAll( + SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(k5UsageEntryInfoVector), SetArgPointee<2>(false), + SetArgPointee<3>(/* has_usage_info_entries = */ true), Return(true))); + + EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true)); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kUsageTableHeader, k5UsageEntryInfoVector)) + .WillOnce(Return(true)); + + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(_, kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + INSTANTIATE_TEST_SUITE_P(Cdm, UsageTableHeaderInitializationTest, ::testing::Values(kSecurityLevelL1, kSecurityLevelL3)); @@ -3578,11 +3612,12 @@ TEST_F(UsageTableHeaderTest, StaleHeader) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(usage_entry_info_vector), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<2>(false), SetArgPointee<3>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUsageTableHeader)) .WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW)); @@ -3681,21 +3716,18 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), SetArgPointee<2>(/* lru_upgrade = */ false), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) .WillOnce(Return(NO_ERROR)); // These function are called specifically by the LRU upgrading system. EXPECT_CALL(*device_files_, RetrieveLicense(_, _, _)).Times(0); - EXPECT_CALL(*device_files_, - RetrieveUsageInfoByKeySetId(_, _, _, _, _, _, _, _, _)) - .Times(0); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_EQ(usage_table_header_->usage_entry_info(), @@ -3705,40 +3737,34 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) { // Initial Test state: // 1. Table info is load from device files; however, device files reports // that the table has not been configured for upgrade. -// 2. The usage table header will load license or usage information to -// determine appropriate expiry and last_used times. +// 2. The usage table header will load offline license to determine +// appropriate expiry and last_used times. TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), SetArgPointee<2>(/* lru_upgrade = */ true), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) .WillOnce(Return(NO_ERROR)); + const std::vector license_info_list = + CreateUpgradableLicenseInfoList(); + for (size_t i = 0; i < kUpgradableUsageEntryInfoList.size(); ++i) { const CdmUsageEntryInfo& info = kUpgradableUsageEntryInfoList[i]; if (info.storage_type == kStorageLicense) { + // Only offline licenses are supported. EXPECT_CALL(*device_files_, RetrieveLicense(info.key_set_id, NotNull(), NotNull())) - .WillOnce(DoAll(SetArgPointee<1>(kUpgradableLicenseDataList[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>(kUpgradableLicenseInfoList[i]), - SetArgPointee<6>(static_cast(i)), - SetArgPointee<7>(kDrmCertificate), - SetArgPointee<8>(kCryptoWrappedKey), Return(true))); + .WillOnce( + DoAll(SetArgPointee<1>(license_info_list[i]), Return(true))); } } @@ -3748,54 +3774,44 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) { } // Initial Test state: -// 1. Table info is load from device files and must be upgraded. -// 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. +// Table info is load from device files and must be upgraded. +// A few entries have the unknown storage type value. // // Entry# Storage type Result info // ====== ============ =========== // 0 Offline Upgraded // 1 Unknown Cleared -// 2 Invalid (99) Cleared +// 2 Unknown Cleared TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_PartialSucceedWithUnknownStorageTypes) { - std::vector upgradable_usage_entry_info_list = - kUpgradableUsageEntryInfoList; - std::vector upgraded_usage_entry_info_list = - kUpgradedUsageEntryInfoList; - - // Set the wrong storage type. - upgradable_usage_entry_info_list[1].storage_type = kStorageTypeUnknown; - upgradable_usage_entry_info_list[2].storage_type = - static_cast(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; + const std::vector upgradable_usage_entry_info_list = { + kUpgradableUsageEntryInfo1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + const std::vector upgraded_usage_entry_info_list = { + kUpgradedUsageEntryInfo1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; // Load table expectations. EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(upgradable_usage_entry_info_list), SetArgPointee<2>(/* lru_upgrade = */ true), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) .WillOnce(Return(NO_ERROR)); // Expectations of the one successful license. + const std::vector license_info_list = + CreateUpgradableLicenseInfoList(); EXPECT_CALL(*device_files_, RetrieveLicense(upgradable_usage_entry_info_list[0].key_set_id, NotNull(), NotNull())) - .WillOnce( - DoAll(SetArgPointee<1>(kUpgradableLicenseDataList[0]), Return(true))); + .WillOnce(DoAll(SetArgPointee<1>(license_info_list[0]), Return(true))); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_EQ(upgraded_usage_entry_info_list, @@ -3811,12 +3827,14 @@ TEST_F(UsageTableHeaderTest, // Entry# Storage type License Issue Result info // ====== ============ ============= =========== // 0 Offline No signature Cleared -// 1 Streaming Wrong type Cleared +// 1 Unknown Clear entry Cleared // 2 Offline Upgraded TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_PartialSucceedWithLicenseParseIssues) { + std::vector upgradable_usage_entry_info_list = + kUpgradableUsageEntryInfoList; std::vector license_data_list = - kUpgradableLicenseDataList; + CreateUpgradableLicenseInfoList(); std::vector upgradable_license_info_list = kUpgradableLicenseInfoList; std::vector upgraded_usage_entry_info_list = @@ -3824,68 +3842,40 @@ TEST_F(UsageTableHeaderTest, // Modify entry 0 to be invalid. license_data_list[0].license = kUnsignedUpgradableLicenseInfo1; - 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; + upgraded_usage_entry_info_list[0].Clear(); - // Modify entry 1 to be invalid. - upgradable_license_info_list[1] = kWrongTypedUpgradableLicenseInfo2; - CdmUsageEntryInfo& wrond_typed_usage_entry_info = - 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; + // Modify entry 1 to be clear. + upgradable_usage_entry_info_list[1].Clear(); + upgradable_license_info_list[1].clear(); + upgraded_usage_entry_info_list[1].Clear(); EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), - SetArgPointee<1>(kUpgradableUsageEntryInfoList), + SetArgPointee<1>(upgradable_usage_entry_info_list), SetArgPointee<2>(/* lru_upgrade = */ true), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) .WillOnce(Return(NO_ERROR)); - for (size_t i = 0; i < kUpgradableUsageEntryInfoList.size(); ++i) { - const CdmUsageEntryInfo& info = kUpgradableUsageEntryInfoList[i]; + for (size_t i = 0; i < upgradable_usage_entry_info_list.size(); ++i) { + const CdmUsageEntryInfo& info = upgradable_usage_entry_info_list[i]; if (info.storage_type == kStorageLicense) { EXPECT_CALL(*device_files_, RetrieveLicense(info.key_set_id, NotNull(), NotNull())) .WillOnce( 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(i)), - SetArgPointee<7>(kDrmCertificate), - SetArgPointee<8>(kCryptoWrappedKey), Return(true))); } } // For the entries which failed, there should be an attempt to delete the // files associated with them. - // Offline license. EXPECT_CALL(*device_files_, - DeleteLicense(kUpgradableUsageEntryInfoList[0].key_set_id)) - .WillOnce(Return(true)); - // Streaming license. - const std::vector 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))) + DeleteLicense(upgradable_usage_entry_info_list[0].key_set_id)) .WillOnce(Return(true)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); @@ -3901,12 +3891,12 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) { EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(kLevelDefault, NotNull())) .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), + NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), SetArgPointee<1>(kUpgradableUsageEntryInfoList), SetArgPointee<2>(/* lru_upgrade = */ true), - Return(true))); + SetArgPointee<3>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUpgradableUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -3918,13 +3908,6 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) { EXPECT_CALL(*device_files_, RetrieveLicense(info.key_set_id, NotNull(), NotNull())) .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)); } } @@ -3972,46 +3955,7 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) { EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, true /* persistent_license */, expected_new_entry.key_set_id, - 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(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 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_info_file_name */ kEmptyString, kEmptyString, &usage_entry_number)); EXPECT_EQ(expected_usage_entry_number, usage_entry_number); @@ -4116,6 +4060,7 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) { // Unexpired offline license. CdmUsageEntryInfo unexpired_entry_info; 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.offline_license_expiry_time = kLruBaseTime + 2 * kOneDay; usage_entry_info_list.push_back(unexpired_entry_info); @@ -4124,59 +4069,43 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) { // Expired offline license. CdmUsageEntryInfo expired_entry_info; 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.offline_license_expiry_time = kLruBaseTime; usage_entry_info_list.push_back(expired_entry_info); 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. CdmUsageEntryInfo unknown_entry_info; unknown_entry_info.storage_type = kStorageTypeUnknown; // Should be chosen regardless of |last_use_time|. unknown_entry_info.last_use_time = kCurrentTime; usage_entry_info_list.push_back(unknown_entry_info); - constexpr uint32_t unknown_entry_number = 3; + constexpr uint32_t unknown_entry_number = 2; - // Case 1: If there is an entry with unknown storage type, it should - // be selected above any other entry. + // Case 1: If there is a clear entry, it should be selected above + // any other entry. uint32_t entry_to_remove = kInvalidEntry; - // Expect the unknown entry. + // Expect the clear. EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( usage_entry_info_list, kCurrentTime, /* unexpired_threshold = */ 3, &entry_to_remove)); EXPECT_EQ(unknown_entry_number, entry_to_remove); - usage_entry_info_list.pop_back(); // Removing unknown. + usage_entry_info_list.pop_back(); // Removing clear entry. + // |usage_entry_info_list| only contains 1 expired and 1 unexpired offline + // license. // Case 2a: Threshold not met, all entries are equally stale. - // The expired entry should be selected over the streaming license. + // The expired entry should be selected over the unexpired entry. entry_to_remove = kInvalidEntry; EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( usage_entry_info_list, kCurrentTime, /* unexpired_threshold = */ 3, &entry_to_remove)); EXPECT_EQ(expired_entry_number, entry_to_remove); - // 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 + // Case 2b: Threshold met, equally stale entries. Expect the expired // entry over the unexpired. entry_to_remove = kInvalidEntry; EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( @@ -4238,45 +4167,8 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_BasicPriorities) { 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 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 -// threshold, only streaming licenses and expired offline licenses -// should be considered. +// threshold, only expired offline licenses should be considered. // Providing only offline licenses, only the expired license should be // considered for removal. TEST_F(UsageTableHeaderTest, @@ -4295,20 +4187,20 @@ TEST_F(UsageTableHeaderTest, for (uint32_t i = 0; i < kSetSize; ++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; usage_entry_info.offline_license_expiry_time = kCurrentTime + kDefaultExpireDuration; } - // Mark 3 as expired. + // Mark three licenses as expired. std::vector expired_license_numbers; while (expired_license_numbers.size() < 3) { const uint32_t i = static_cast(wvutil::CdmRandom::RandomInRange(kSetSize - 1)); CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; // Skip already expired ones - if (usage_entry_info.key_set_id != "nothing_unusual") continue; + if (usage_entry_info.key_set_id != "unexpired_offline") continue; // Make these less stale than the unexpired licenses. usage_entry_info.last_use_time = kLruBaseTime + kOneDay; usage_entry_info.offline_license_expiry_time = kCurrentTime - kOneDay; @@ -4324,16 +4216,31 @@ TEST_F(UsageTableHeaderTest, EXPECT_THAT(expired_license_numbers, Contains(entry_to_remove)); } -// This test primarily tests the robustness of the algorithm for a full -// set of entries (200). Creates 1 stale streaming license and 1 -// offline licenses which are more stale than the streaming. +// This primarily tests the robustness of the algorithm for a full +// set of entries (200). Creates 1 offline license which are more +// stale than the all other licenses. // -// First, with the stale offline licenses unexpired, checks that the -// streaming are always selected, so long as |unexpired_threshold| -// is not met. +// All cases: 1 unexpired license is most stale. // -// Second, with the stale offline license marked as expired, checks that -// offline licenses are selected over the streaming. +// Case 1: +// - Unexpired threshold met: No +// - 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) { constexpr int64_t kOneDay = 24 * 60 * 60; constexpr size_t kLargeSetSize = 200; @@ -4342,74 +4249,63 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_LargeMixedSet) { std::vector usage_entry_info_list; usage_entry_info_list.resize(kLargeSetSize); - // Create a set of usage entries. + // Create a set of unexpired license entries. for (uint32_t i = 0; i < kLargeSetSize; ++i) { CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; - usage_entry_info.key_set_id = "nothing_unusual"; + usage_entry_info.storage_type = kStorageLicense; + usage_entry_info.key_set_id = "unexpired_offline"; usage_entry_info.last_use_time = kLruBaseTime + kOneDay; - if ((i % 4) == 0) { - // 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( - 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; + usage_entry_info.offline_license_expiry_time = + kCurrentTime + kDefaultExpireDuration; } // Select a offline license to be even more stale, but unexpired. - uint32_t modified_offline_license_number = kInvalidEntry; - while (modified_offline_license_number == kInvalidEntry) { - const uint32_t i = static_cast( - wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1)); - CdmUsageEntryInfo& usage_entry_info = usage_entry_info_list[i]; - // 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; - } + const uint32_t most_stale_offline_license_number = static_cast( + wvutil::CdmRandom::RandomInRange(kLargeSetSize - 1)); + CdmUsageEntryInfo& usage_entry_info = + usage_entry_info_list[most_stale_offline_license_number]; + usage_entry_info.last_use_time = kLruBaseTime; + usage_entry_info.key_set_id = "stale_offline"; - // Test using only streaming and expired offline licenses - // (which there are none). + // Case 1: Most stale license is selected. uint32_t entry_to_remove = kInvalidEntry; EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( usage_entry_info_list, kCurrentTime, /* unexpired_threshold = */ kLargeSetSize, &entry_to_remove)); - EXPECT_EQ(modified_usage_info_number, entry_to_remove); + EXPECT_EQ(most_stale_offline_license_number, entry_to_remove); - // Test where the equality threshold is met, now the stale unexpired - // license should be selected. + // Case 2: 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(modified_offline_license_number, entry_to_remove); + EXPECT_EQ(most_stale_offline_license_number, entry_to_remove); - // Make the stale offline license expired. - CdmUsageEntryInfo& offline_usage_entry_info = - usage_entry_info_list[modified_offline_license_number]; - offline_usage_entry_info.offline_license_expiry_time = kLruBaseTime; + // Select a offline license to be marked as expired but not the most + // stale. + uint32_t expired_offline_license_number = kInvalidEntry; + while (expired_offline_license_number == kInvalidEntry) { + const uint32_t i = static_cast( + 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; + } - // Test again, expecting that the expired license should be considered. + // Case 3: Expired license is selected. entry_to_remove = kInvalidEntry; EXPECT_TRUE(UsageTableHeader::DetermineLicenseToRemoveForTesting( usage_entry_info_list, kCurrentTime, /* unexpired_threshold = */ kLargeSetSize, &entry_to_remove)); - EXPECT_EQ(modified_offline_license_number, entry_to_remove); + EXPECT_EQ(expired_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) {