From 66e3d6930093fd3ffa5741062d8c048193508eaf Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Wed, 18 Mar 2020 17:17:03 -0700 Subject: [PATCH] Update AddEntry() for usage table changes. [ Merge of http://go/wvgerrit/96071 ] Changes to how the usage table method InvalidateEntry() behaves required additional changes to CDM code that uses this method. This involved some refactoring to AddEntry(), moving the LRU related code to its own function. A few unittests had to be changed / removed as the moving multiple entries changes expectations of several existing tests. Several additional helper methods have been created to improve readability. These include getters for information about the usage table, a method for releasing stale entries, and a method of recording LRU metrics. Bug: 150890014 Bug: 150887808 Bug: 154269671 Test: Linux unit tests and Android unit tests Change-Id: I11a98f9a2dea9b2ae57b37d7d4483a37be721763 --- .../cdm/core/include/usage_table_header.h | 19 +- .../cdm/core/src/usage_table_header.cpp | 164 ++++++------ .../core/test/usage_table_header_unittest.cpp | 249 ++---------------- 3 files changed, 130 insertions(+), 302 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 61e28f56..824bd936 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -93,6 +93,13 @@ class UsageTableHeader { size_t potential_table_capacity() const { return potential_table_capacity_; } + // Returns the number of entries currently tracked by the CDM that + // are related to usage info (streaming licenses). + size_t UsageInfoCount() const; + // Returns the number of entries currently tracked by the CDM that + // are related to offline licenses. + size_t OfflineEntryCount() const; + const std::vector& usage_entry_info() const { return usage_entry_info_; } @@ -139,6 +146,10 @@ class UsageTableHeader { CdmResponseType DefragTable(DeviceFiles* device_files, metrics::CryptoMetrics* metrics); + // This will use the LRU algorithm to decide which entry is to be + // evicted. + CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics); + virtual bool is_inited() { return is_inited_; } // Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a @@ -149,6 +160,12 @@ 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. + void RecordLruEventMetrics(metrics::CryptoMetrics* metrics, + uint64_t staleness, + CdmUsageEntryStorageType storage_type); + // Uses an LRU-base algorithm to determine which licenses should be // removed. This is intended to be used if the usage table is full // and a new entry needs to be added. @@ -212,7 +229,7 @@ class UsageTableHeader { // Synchonizes access to the Usage Table Header and bookkeeping // data-structures - std::mutex usage_table_header_lock_; + mutable std::mutex usage_table_header_lock_; metrics::CryptoMetrics alternate_crypto_metrics_; diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index 2784a043..73bafb32 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -16,7 +16,6 @@ namespace wvcdm { namespace { std::string kEmptyString; -size_t kMaxCryptoRetries = 3; wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid"; std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0); std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0); @@ -24,6 +23,7 @@ std::string kOldUsageEntryPoviderSessionToken = "nahZ6achSheiqua3TohQuei0ahwohv"; constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days // Number of elements to be considered for removal using the LRU algorithm. +// TODO(b/155230578): Remove this constant. constexpr size_t kLruRemovalSetSize = 3; // Fraction of table capacity of number of unexpired offline licenses // before they are considered to be removed. This could occur if @@ -132,6 +132,16 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files, } return true; } + +bool EntryIsUsageInfo(const CdmUsageEntryInfo& info) { + // Used for stl filters. + return info.storage_type == kStorageUsageInfo; +} + +bool EntryIsOfflineLicense(const CdmUsageEntryInfo& info) { + // Used for stl filters. + return info.storage_type == kStorageLicense; +} } // namespace UsageTableHeader::UsageTableHeader() @@ -237,6 +247,13 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, result = InvalidateEntry(temporary_usage_entry_number, /* defrag_table = */ true, device_files_.get(), metrics); + if (usage_entry_info_.size() > temporary_usage_entry_number) { + // The entry should have been deleted from the usage table, + // not just marked as type unknown. Failure to call + // Shrink() may be an indicator of other issues. + LOGE("Temporary entry was not deleted"); + result = UNKNOWN_ERROR; + } } if (result != NO_ERROR) { LOGE( @@ -284,66 +301,11 @@ CdmResponseType UsageTableHeader::AddEntry( CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number); if (status == INSUFFICIENT_CRYPTO_RESOURCES_3) { - // If usage entry creation fails due to insufficient resources, release an - // entry based on LRU. - std::vector removal_candidates; - if (!GetRemovalCandidates(&removal_candidates)) { - LOGE("Could not determine which license to remove"); - return status; - } - - // Variables for metrics. - const size_t usage_info_count = - std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(), - [](const CdmUsageEntryInfo& usage_entry) { - return usage_entry.storage_type == kStorageUsageInfo; - }); - const size_t license_count = - std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(), - [](const CdmUsageEntryInfo& usage_entry) { - return usage_entry.storage_type == kStorageLicense; - }); - int64_t staleness_of_removed; - CdmUsageEntryStorageType storage_type_of_removed; - const int64_t current_time = GetCurrentTime(); - - for (size_t i = 0; i < removal_candidates.size() && - status == INSUFFICIENT_CRYPTO_RESOURCES_3; - ++i) { - const uint32_t entry_number_to_invalidate = removal_candidates[i]; - // Calculate metric values. - staleness_of_removed = - current_time - - usage_entry_info_[entry_number_to_invalidate].last_use_time; - storage_type_of_removed = - usage_entry_info_[entry_number_to_invalidate].storage_type; - - if (InvalidateEntry(entry_number_to_invalidate, - /* defrag_table = */ true, device_files_.get(), - metrics) == NO_ERROR) { - // If the entry was deleted, it is still possible for the create new - // entry to fail. If so, we must ensure that the previously last - // entry was not in the |removal_candidates| as it has now been swapped - // with the deleted entry. - for (uint32_t& entry_number : removal_candidates) { - if (entry_number == usage_entry_info_.size()) { - entry_number = entry_number_to_invalidate; - } - } - } + LOGW("Usage table may be full, releasing oldest entry: size = %zu", + usage_entry_info_.size()); + status = ReleaseOldestEntry(metrics); + if (status == NO_ERROR) { status = crypto_session->CreateUsageEntry(usage_entry_number); - - // Record metrics on success. - if (status == NO_ERROR) { - metrics->usage_table_header_lru_usage_info_count_.Record( - usage_info_count); - metrics->usage_table_header_lru_offline_license_count_.Record( - license_count); - metrics->usage_table_header_lru_evicted_entry_staleness_.Record( - staleness_of_removed); - metrics->usage_table_header_lru_evicted_entry_type_.Record( - static_cast(storage_type_of_removed)); - } } } @@ -422,28 +384,9 @@ CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session, metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics(); if (metrics == nullptr) metrics = &alternate_crypto_metrics_; - CdmResponseType status = + const CdmResponseType status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); - // If loading a usage entry fails due to insufficient resources, release a - // random entry different from |usage_entry_number| and try again. If there - // are no more entries to release, we fail. - for (uint32_t retry_count = 0; retry_count < kMaxCryptoRetries && - status == INSUFFICIENT_CRYPTO_RESOURCES_3; - ++retry_count) { - if (usage_entry_info_.size() <= 1) break; - // Get a random entry from the other entries. - uint32_t entry_number_to_invalidate = - CdmRandom::RandomInRange(usage_entry_info_.size() - 2); - if (entry_number_to_invalidate >= usage_entry_number) { - // Exclude |usage_entry_number|. - ++entry_number_to_invalidate; - } - InvalidateEntry(entry_number_to_invalidate, /* defrag_table = */ true, - device_files_.get(), metrics); - status = crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); - } - if (status == NO_ERROR) { usage_entry_info_[usage_entry_number].last_use_time = GetCurrentTime(); } @@ -518,6 +461,20 @@ CdmResponseType UsageTableHeader::InvalidateEntry( return NO_ERROR; } +size_t UsageTableHeader::UsageInfoCount() const { + LOGI("Locking to count usage info (streaming license) entries"); + std::unique_lock auto_lock(usage_table_header_lock_); + return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(), + EntryIsUsageInfo); +} + +size_t UsageTableHeader::OfflineEntryCount() const { + LOGI("Locking to count offline license entries"); + std::unique_lock auto_lock(usage_table_header_lock_); + return std::count_if(usage_entry_info_.cbegin(), usage_entry_info_.cend(), + EntryIsOfflineLicense); +} + CdmResponseType UsageTableHeader::MoveEntry( uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, uint32_t to_usage_entry_number, DeviceFiles* device_files, @@ -986,6 +943,40 @@ CdmResponseType UsageTableHeader::DefragTable(DeviceFiles* device_files, return Shrink(metrics, to_remove); } // End Defrag(). +CdmResponseType UsageTableHeader::ReleaseOldestEntry( + metrics::CryptoMetrics* metrics) { + LOGV("Releasing oldest entry"); + std::vector removal_candidates; + if (!GetRemovalCandidates(&removal_candidates)) { + LOGE("Could not determine which license to remove"); + return UNKNOWN_ERROR; + } + // Only release one entry. + const uint32_t entry_number_to_delete = removal_candidates.front(); + const CdmUsageEntryInfo& usage_entry_info = + usage_entry_info_[entry_number_to_delete]; + + const int64_t current_time = GetCurrentTime(); + // Capture metric values now, as the |usage_entry_info| reference will + // change after the call to invalidate. + const int64_t staleness = current_time - usage_entry_info.last_use_time; + const CdmUsageEntryStorageType storage_type = usage_entry_info.storage_type; + + const CdmResponseType status = + InvalidateEntry(entry_number_to_delete, /* defrag_table = */ true, + device_files_.get(), metrics); + + if (status != NO_ERROR) { + LOGE("Failed to invalidate oldest entry: status = %d", + static_cast(status)); + return status; + } + + // Record metrics on success. + RecordLruEventMetrics(metrics, staleness, storage_type); + return NO_ERROR; +} + // Test only method. void UsageTableHeader::InvalidateEntryForTest(uint32_t usage_entry_number) { LOGV("Deleting entry for test: usage_entry_number = %u", usage_entry_number); @@ -1135,7 +1126,20 @@ bool UsageTableHeader::GetRemovalCandidates( removal_candidates); } +void UsageTableHeader::RecordLruEventMetrics( + metrics::CryptoMetrics* metrics, uint64_t staleness, + CdmUsageEntryStorageType storage_type) { + if (metrics == nullptr) return; + metrics->usage_table_header_lru_usage_info_count_.Record(UsageInfoCount()); + metrics->usage_table_header_lru_offline_license_count_.Record( + OfflineEntryCount()); + metrics->usage_table_header_lru_evicted_entry_staleness_.Record(staleness); + metrics->usage_table_header_lru_evicted_entry_type_.Record( + static_cast(storage_type)); +} + // Static. +// TODO(b/155230578): Change this function to only return 1 entry. bool UsageTableHeader::DetermineLicenseToRemove( const std::vector& usage_entry_info_list, int64_t current_time, size_t unexpired_threshold, size_t removal_count, diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index 44659949..e59a42c1 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -350,28 +350,6 @@ void ToVector(std::vector& vec, } } -// Used to quickly populate a vector of CdmUsageEntryInfo structs with LRU -// information. This is intended to allow tests which are not concerned with -// the LRU replacement policy of the UsageTableHeader, but are affected by its -// presents. -void GenericLruUpgrade(std::vector* usage_entry_info_list, - int64_t last_use_time = kLruBaseTime, - int64_t offline_license_expiry_time = - kLruBaseTime + kDefaultExpireDuration) { - if (usage_entry_info_list == nullptr) { - return; - } - for (auto& usage_entry_info : *usage_entry_info_list) { - usage_entry_info.last_use_time = last_use_time; - if (usage_entry_info.storage_type == kStorageLicense) { - usage_entry_info.offline_license_expiry_time = - offline_license_expiry_time; - } else { - usage_entry_info.offline_license_expiry_time = 0; - } - } -} - }; // namespace class MockDeviceFiles : public DeviceFiles { @@ -484,6 +462,7 @@ using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Ge; using ::testing::Invoke; +using ::testing::InvokeWithoutArgs; using ::testing::Lt; using ::testing::NotNull; using ::testing::Return; @@ -751,7 +730,7 @@ TEST_P(UsageTableHeaderInitializationTest, usage_entries_202)) .WillOnce(Return(true)); - // Expectations for InvalidateEntry + // Expectations for InvalidateEntry, assumes no entry other entry is invalid. EXPECT_CALL(*crypto_session_, Open(security_level)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*crypto_session_, @@ -914,7 +893,8 @@ TEST_F(UsageTableHeaderTest, uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen std::vector final_usage_entries; - uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 1; + const uint32_t expected_usage_entry_number = + k10UsageEntryInfoVector.size() - 1; // Setup expectations EXPECT_CALL(*mock_usage_table_header, @@ -959,123 +939,50 @@ TEST_F(UsageTableHeaderTest, EXPECT_EQ(expected_usage_entries, final_usage_entries); } -TEST_F(UsageTableHeaderTest, - AddEntry_CreateUsageEntryFailsTwice_SucceedsThirdTime) { - // Initialize and setup - MockUsageTableHeader* mock_usage_table_header = SetUpMock(); - std::vector usage_entry_info_vector_at_start = - k10UsageEntryInfoVector; - GenericLruUpgrade(&usage_entry_info_vector_at_start); - Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector_at_start); - - uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen - uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen - std::vector final_usage_entries; - - uint32_t expected_usage_entry_number = k10UsageEntryInfoVector.size() - 2; - - // Setup expectations - EXPECT_CALL(*mock_usage_table_header, - InvalidateEntry(_, true, device_files_, NotNull())) - .WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted), - Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))) - .WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted), - Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))); - - EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) - .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) - .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) - .WillOnce( - DoAll(SetArgPointee<0>(expected_usage_entry_number), - Return(NO_ERROR))); - - EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, _)) - .WillOnce(DoAll(SaveArg<1>(&final_usage_entries), Return(true))); - - // Now invoke the method under test - uint32_t usage_entry_number; - EXPECT_EQ(NO_ERROR, - mock_usage_table_header->AddEntry( - crypto_session_, - kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense, - kUsageEntryInfoOfflineLicense6.key_set_id, - kUsageEntryInfoOfflineLicense6.usage_info_file_name, - kEmptyString /* license */, &usage_entry_number)); - - // Verify added/deleted usage entry number and entries - EXPECT_EQ(expected_usage_entry_number, usage_entry_number); - - EXPECT_LE(0u, usage_entry_number_first_to_be_deleted); - EXPECT_LE(usage_entry_number_first_to_be_deleted, - usage_entry_info_vector_at_start.size() - 1); - EXPECT_LE(0u, usage_entry_number_second_to_be_deleted); - EXPECT_LE(usage_entry_number_second_to_be_deleted, - usage_entry_info_vector_at_start.size() - 1); - - std::vector expected_usage_entries = - usage_entry_info_vector_at_start; - expected_usage_entries[usage_entry_number_first_to_be_deleted] = - expected_usage_entries[expected_usage_entries.size() - 1]; - expected_usage_entries.resize(expected_usage_entries.size() - 1); - expected_usage_entries[usage_entry_number_second_to_be_deleted] = - expected_usage_entries[expected_usage_entries.size() - 1]; - expected_usage_entries.resize(expected_usage_entries.size() - 1); - expected_usage_entries.push_back(kUsageEntryInfoOfflineLicense6); - - EXPECT_EQ(expected_usage_entries, final_usage_entries); -} - -TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsThrice) { +// The usage table should only delete/invalidate a single entry. +// After which, it should fail. +TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) { // Initialize and setup MockUsageTableHeader* mock_usage_table_header = SetUpMock(); Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector); - std::vector usage_entry_info_vector_at_start = - k10UsageEntryInfoVector; - - uint32_t usage_entry_number_first_to_be_deleted; // randomly chosen - uint32_t usage_entry_number_second_to_be_deleted; // randomly chosen - uint32_t usage_entry_number_third_to_be_deleted; // randomly chosen - std::vector final_usage_entries; // Setup expectations EXPECT_CALL(*mock_usage_table_header, InvalidateEntry(_, true, device_files_, NotNull())) - .WillOnce(DoAll(SaveArg<0>(&usage_entry_number_first_to_be_deleted), - Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))) - .WillOnce(DoAll(SaveArg<0>(&usage_entry_number_second_to_be_deleted), - Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))) - .WillOnce(DoAll(SaveArg<0>(&usage_entry_number_third_to_be_deleted), - Invoke(this, &UsageTableHeaderTest::InvalidateEntry), + .WillOnce(DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry), Return(NO_ERROR))); EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) - .Times(4) + .Times(2) .WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)); // Now invoke the method under test uint32_t usage_entry_number; EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3, mock_usage_table_header->AddEntry( - crypto_session_, - kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense, + crypto_session_, true /* persistent */, kUsageEntryInfoOfflineLicense6.key_set_id, kUsageEntryInfoOfflineLicense6.usage_info_file_name, kEmptyString /* license */, &usage_entry_number)); - // Verify deleted usage entry number and entries - EXPECT_LE(0u, usage_entry_number_first_to_be_deleted); - EXPECT_LE(usage_entry_number_first_to_be_deleted, - usage_entry_info_vector_at_start.size() - 1); - EXPECT_LE(0u, usage_entry_number_second_to_be_deleted); - EXPECT_LE(usage_entry_number_second_to_be_deleted, - usage_entry_info_vector_at_start.size() - 1); - EXPECT_LE(0u, usage_entry_number_third_to_be_deleted); - EXPECT_LE(usage_entry_number_third_to_be_deleted, - usage_entry_info_vector_at_start.size() - 1); + // Verify the number of entries deleted. + constexpr uint32_t kExpectedEntriesDeleted = 1; + + const std::vector& final_usage_entries = + mock_usage_table_header->usage_entry_info(); + uint32_t invalid_entries = 0; + for (const CdmUsageEntryInfo& usage_entry_info : final_usage_entries) { + if (usage_entry_info.storage_type == kStorageTypeUnknown) { + ++invalid_entries; + } + } + // Number of entries deleted is equal to the number of entries + // marked as invalid plus the number of fewer entries in the table + // at the end of the call. + const uint32_t entries_deleted = + invalid_entries + + (k10UsageEntryInfoVector.size() - final_usage_entries.size()); + EXPECT_EQ(kExpectedEntriesDeleted, entries_deleted); } TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { @@ -1139,106 +1046,6 @@ TEST_F(UsageTableHeaderTest, UpdateEntry) { usage_table_header_->UpdateEntry(0, crypto_session_, &usage_entry)); } -TEST_F(UsageTableHeaderTest, - LoadEntry_LoadUsageEntryFailsOnce_SucceedsSecondTime) { - // Initialize and setup - MockUsageTableHeader* mock_usage_table_header = SetUpMock(); - Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector); - - // We try to load a usage entry from the first 9 entries, since - // InvalidateEntry can't delete an entry if the last one is in use. - - uint32_t usage_entry_number_to_load = - CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 2); - CdmUsageEntry usage_entry_to_load = kUsageEntry; - - // Setup expectations - EXPECT_CALL(*mock_usage_table_header, - InvalidateEntry(_, true, device_files_, NotNull())) - .Times(1) - .WillRepeatedly( - DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))); - - EXPECT_CALL(*crypto_session_, - LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load)) - .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) - .WillOnce(Return(NO_ERROR)); - - // Now invoke the method under test - EXPECT_EQ(NO_ERROR, - mock_usage_table_header->LoadEntry( - crypto_session_, - usage_entry_to_load, - usage_entry_number_to_load)); -} - -TEST_F(UsageTableHeaderTest, - LoadEntry_LoadUsageEntryFailsTwice_SucceedsThirdTime) { - // Initialize and setup - MockUsageTableHeader* mock_usage_table_header = SetUpMock(); - Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector); - - // We try to load a usage entry from the first 8 entries, since - // InvalidateEntry can't delete an entry if the last one is in use. - uint32_t usage_entry_number_to_load = - CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 3); - CdmUsageEntry usage_entry_to_load = kUsageEntry; - - // Setup expectations - EXPECT_CALL(*mock_usage_table_header, - InvalidateEntry(_, true, device_files_, NotNull())) - .Times(2) - .WillRepeatedly( - DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))); - - EXPECT_CALL(*crypto_session_, - LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load)) - .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) - .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) - .WillOnce(Return(NO_ERROR)); - - // Now invoke the method under test - EXPECT_EQ(NO_ERROR, - mock_usage_table_header->LoadEntry( - crypto_session_, - usage_entry_to_load, - usage_entry_number_to_load)); -} - -TEST_F(UsageTableHeaderTest, LoadEntry_LoadUsageEntryFailsThrice) { - // Initialize and setup - MockUsageTableHeader* mock_usage_table_header = SetUpMock(); - Init(kSecurityLevelL1, kUsageTableHeader, k10UsageEntryInfoVector); - - // We try to load a usage entry from the first 7 entries, since - // InvalidateEntry can't delete an entry if the last one is in use. - uint32_t usage_entry_number_to_load = - CdmRandom::RandomInRange(k10UsageEntryInfoVector.size() - 4); - CdmUsageEntry usage_entry_to_load = kUsageEntry; - - // Setup expectations - EXPECT_CALL(*mock_usage_table_header, - InvalidateEntry(_, true, device_files_, NotNull())) - .Times(3) - .WillRepeatedly( - DoAll(Invoke(this, &UsageTableHeaderTest::InvalidateEntry), - Return(NO_ERROR))); - - EXPECT_CALL(*crypto_session_, - LoadUsageEntry(usage_entry_number_to_load, usage_entry_to_load)) - .Times(4) - .WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)); - - // Now invoke the method under test - EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3, - mock_usage_table_header->LoadEntry( - crypto_session_, - usage_entry_to_load, - usage_entry_number_to_load)); -} - TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); uint32_t usage_entry_number = kUsageEntryInfoVector.size();