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();