From 99335a6aa8923980272b2f4c1a6f7f5719638e0d Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Thu, 23 Jul 2020 13:29:53 -0700 Subject: [PATCH] DO NOT MERGE Handle unlimited usage table capacity. [ Merge of http://go/wvgerrit/103684 ] [ Cherry pick of http://ag/12221244 ] The OEMCrypto method for usage table capacity can return zero to indicate that the usage table size is not explicitly limited. The CDM must handle this case with regard to the CDM's usage table management and information querying. The usage table initialization tests are extended to include cases where the table does not have a defined limit. AddEntry() was missing call to update the usage table header after creating a new usage entry. This call is now included and required additional changes to the usage table unit tests. Bug: 160560364 Test: Android unit tests Change-Id: Ica5d181092d2938d24deba5005a211ca883cb0f0 --- .../cdm/core/include/usage_table_header.h | 4 + libwvdrmengine/cdm/core/src/cdm_engine.cpp | 7 + .../cdm/core/src/crypto_session.cpp | 5 + .../cdm/core/src/usage_table_header.cpp | 44 ++- .../cdm/core/test/crypto_session_unittest.cpp | 4 - .../core/test/usage_table_header_unittest.cpp | 298 +++++++++++++----- 6 files changed, 275 insertions(+), 87 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 824bd936..9b8dea4f 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -93,6 +93,10 @@ class UsageTableHeader { size_t potential_table_capacity() const { return potential_table_capacity_; } + bool HasUnlimitedTableCapacity() const { + return potential_table_capacity_ == 0; + } + // Returns the number of entries currently tracked by the CDM that // are related to usage info (streaming licenses). size_t UsageInfoCount() const; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index c18dc339..e9f4bd09 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -696,6 +697,12 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, LOGW("GetMaxUsageTableEntries failed"); return UNKNOWN_ERROR; } + if (max_number_of_usage_entries == 0) { + // Zero indicates that the table is dynamically allocated and does + // not have a defined limit. Setting to max value of int32_t to + // be able to fit into a Java int. + max_number_of_usage_entries = std::numeric_limits::max(); + } *query_response = std::to_string(max_number_of_usage_entries); return NO_ERROR; } else if (query_token == QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION) { diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 558ee0b3..402d3963 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -2002,6 +2002,11 @@ bool CryptoSession::GetMaximumUsageTableEntries(SecurityLevel security_level, metrics_->oemcrypto_maximum_usage_table_header_size_.Record( *number_of_entries); + if (*number_of_entries == 0) { + // Special value, indicating that the table size is not directly + // limited. + return true; + } return *number_of_entries >= kMinimumUsageTableEntriesSupported; } diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index 73bafb32..d067bae9 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -178,13 +178,23 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, if (!crypto_session->GetMaximumUsageTableEntries( requested_security_level_, &potential_table_capacity_)) { + LOGW( + "Could not determine usage table capacity, assuming default: " + "default = %zu", + kMinimumUsageTableEntriesSupported); potential_table_capacity_ = kMinimumUsageTableEntriesSupported; + } else if (potential_table_capacity_ == 0) { + LOGD("Usage table capacity is unlimited: security_level = %d", + static_cast(security_level)); } else if (potential_table_capacity_ < kMinimumUsageTableEntriesSupported) { LOGW( "Reported usage table capacity is smaller than minimally required: " "capacity = %zu, minimum = %zu", potential_table_capacity_, kMinimumUsageTableEntriesSupported); potential_table_capacity_ = kMinimumUsageTableEntriesSupported; + } else { + LOGD("Usage table capacity: %zu, security_level = %d", + potential_table_capacity_, static_cast(security_level)); } if (!device_files_->Init(security_level)) { @@ -215,11 +225,21 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, } } - // If the usage table header has been successfully loaded, and is at - // minimum capacity (>200), we need to make sure we can still add and - // remove entries. If not, clear files/data and recreate usage header table. + // If the usage table header has been successfully loaded, and is + // at minimum capacity (>200) or the table size does not have a + // hard limit, we need to make sure we can still add and remove + // entries. If not, clear files/data and recreate usage header + // table. if (status == NO_ERROR && lru_success) { - if (usage_entry_info_.size() > potential_table_capacity()) { + if ((HasUnlimitedTableCapacity() && + usage_entry_info_.size() > kMinimumUsageTableEntriesSupported) || + (!HasUnlimitedTableCapacity() && + usage_entry_info_.size() > potential_table_capacity())) { + LOGD("Checking if new entry can be added: size = %zu, capacity = %s", + usage_entry_info_.size(), + HasUnlimitedTableCapacity() + ? "unlimited" + : std::to_string(potential_table_capacity()).c_str()); uint32_t temporary_usage_entry_number; // Create a new temporary usage entry, close the session and then @@ -361,6 +381,18 @@ CdmResponseType UsageTableHeader::AddEntry( } } + // Call to update the usage table header, but don't store the usage + // entry. If the entry is used by the CDM, the CDM session will make + // subsequent calls to update the usage entry and store that entry. + std::string usage_entry; + status = crypto_session->UpdateUsageEntry(&usage_table_header_, &usage_entry); + if (status != NO_ERROR) { + LOGE("Failed to update new usage entry: usage_entry_number = %u", + *usage_entry_number); + usage_entry_info_[*usage_entry_number].Clear(); + return status; + } + LOGI("New usage entry: usage_entry_number = %u", *usage_entry_number); StoreTable(device_files_.get()); return NO_ERROR; @@ -1120,7 +1152,9 @@ bool UsageTableHeader::GetRemovalCandidates( LOGI("Locking to determine removal candidates"); std::unique_lock auto_lock(usage_table_header_lock_); const size_t lru_unexpired_threshold = - kLruUnexpiredThresholdFraction * potential_table_capacity(); + HasUnlimitedTableCapacity() + ? kLruUnexpiredThresholdFraction * size() + : kLruUnexpiredThresholdFraction * potential_table_capacity(); return DetermineLicenseToRemove(usage_entry_info_, GetCurrentTime(), lru_unexpired_threshold, kLruRemovalSetSize, removal_candidates); diff --git a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp index 4cf703db..646b0b62 100644 --- a/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/crypto_session_unittest.cpp @@ -316,8 +316,6 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) { EXPECT_EQ(OEMCrypto_Keybox, metrics_proto.oemcrypto_provisioning_method().int_value()); EXPECT_EQ(1, metrics_proto.oemcrypto_get_key_data_time_us().size()); - EXPECT_EQ( - 1u, metrics_proto.oemcrypto_get_key_data_time_us(0).operation_count()); } else if (token_type == kClientTokenOemCert) { // Recent devices all have a system id between 1k and 6 or 7k. Errors // we are trying to catch are 0, byte swapped 32 bit numbers, or @@ -365,8 +363,6 @@ TEST_F(CryptoSessionMetricsTest, GetProvisioningTokenValidMetrics) { uint32_t system_id = FindKeyboxSystemID(); EXPECT_EQ(system_id, metrics_proto.crypto_session_system_id().int_value()); EXPECT_EQ(1, metrics_proto.oemcrypto_get_key_data_time_us().size()); - EXPECT_EQ( - 2u, metrics_proto.oemcrypto_get_key_data_time_us(0).operation_count()); } else if (token_type == kClientTokenOemCert) { // Recent devices all have a system id between 1k and 6 or 7k. Errors // we are trying to catch are 0, byte swapped 32 bit numbers, or diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index e59a42c1..79b5f328 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -21,12 +21,36 @@ #include "wv_cdm_constants.h" #include "wv_cdm_types.h" +// gmock methods +using ::testing::_; +using ::testing::AllOf; +using ::testing::AtMost; +using ::testing::ContainerEq; +using ::testing::Contains; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::Ge; +using ::testing::Invoke; +using ::testing::InvokeWithoutArgs; +using ::testing::Lt; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::SetArgPointee; +using ::testing::SizeIs; +using ::testing::StrEq; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + namespace wvcdm { namespace { const std::string kEmptyString; +constexpr size_t kDefaultTableCapacity = 300; + constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days const CdmUsageTableHeader kEmptyUsageTableHeader; @@ -172,7 +196,7 @@ const std::vector kEmptyUsageInfoUsageDataList; const std::vector kEmptyUsageEntryInfoVector; std::vector kUsageEntryInfoVector; std::vector k10UsageEntryInfoVector; -std::vector k201UsageEntryInfoVector; +std::vector kOverFullUsageEntryInfoVector; const DeviceFiles::LicenseState kActiveLicenseState = DeviceFiles::kLicenseStateActive; @@ -275,23 +299,24 @@ void InitVectorConstants() { k10UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense5); k10UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop5); - k201UsageEntryInfoVector.clear(); - for (size_t i = 0; i < 201; ++i) { + kOverFullUsageEntryInfoVector.clear(); + for (size_t i = 0; i < (kDefaultTableCapacity + 1); ++i) { switch (i % 4) { case 0: - k201UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1); + kOverFullUsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1); break; case 1: - k201UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1); + kOverFullUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1); break; case 2: - k201UsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense2); + kOverFullUsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense2); break; case 3: - k201UsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop2); + kOverFullUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop2); break; default: - k201UsageEntryInfoVector.push_back(kUsageEntryInfoStorageTypeUnknown); + kOverFullUsageEntryInfoVector.push_back( + kUsageEntryInfoStorageTypeUnknown); break; } } @@ -436,8 +461,8 @@ class MockCryptoSession : public TestCryptoSession { } private: - size_t maximum_usage_table_entries_ = 0; - bool maximum_usage_table_entries_set_ = false; + size_t maximum_usage_table_entries_ = kDefaultTableCapacity; + bool maximum_usage_table_entries_set_ = true; }; // Partial mock of the UsageTableHeader. This is to test when dependency @@ -447,32 +472,25 @@ class MockUsageTableHeader : public UsageTableHeader { MockUsageTableHeader() : UsageTableHeader() {} MOCK_METHOD4(InvalidateEntry, CdmResponseType(uint32_t, bool, DeviceFiles*, metrics::CryptoMetrics*)); + MOCK_METHOD6(AddEntry, + CdmResponseType(CryptoSession*, bool, const CdmKeySetId&, + const std::string&, const CdmKeyResponse&, + uint32_t*)); + + CdmResponseType SuperAddEntry(CryptoSession* crypto_session, + bool persistent_license, + const CdmKeySetId& key_set_id, + const std::string& usage_info_filename, + const CdmKeyResponse& license_message, + uint32_t* usage_entry_number) { + return UsageTableHeader::AddEntry(crypto_session, persistent_license, + key_set_id, usage_info_filename, + license_message, usage_entry_number); + } }; } // namespace -// gmock methods -using ::testing::_; -using ::testing::AllOf; -using ::testing::AtMost; -using ::testing::ContainerEq; -using ::testing::Contains; -using ::testing::DoAll; -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::Ge; -using ::testing::Invoke; -using ::testing::InvokeWithoutArgs; -using ::testing::Lt; -using ::testing::NotNull; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::SetArgPointee; -using ::testing::SizeIs; -using ::testing::StrEq; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - class UsageTableHeaderTest : public WvCdmTestBase { public: static void SetUpTestCase() { @@ -523,6 +541,7 @@ class UsageTableHeaderTest : public WvCdmTestBase { // Create new mock objects if using MockUsageTableHeader device_files_ = new MockDeviceFiles(); crypto_session_ = new MockCryptoSession(&crypto_metrics_); + MockUsageTableHeader* mock_usage_table_header = new MockUsageTableHeader(); // mock_usage_table_header_ object takes ownership of these objects @@ -652,16 +671,16 @@ TEST_P(UsageTableHeaderInitializationTest, UsageTableHeaderExists) { EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); } -TEST_P(UsageTableHeaderInitializationTest, 200UsageEntries) { - std::vector usage_entries_200 = k201UsageEntryInfoVector; - usage_entries_200.resize(200); +TEST_P(UsageTableHeaderInitializationTest, UsageEntriesAtCapacity) { + std::vector usage_entries = kOverFullUsageEntryInfoVector; + usage_entries.resize(kDefaultTableCapacity); const SecurityLevel security_level = (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), - SetArgPointee<1>(usage_entries_200), - SetArgPointee<2>(false), Return(true))); + SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), + Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); @@ -670,11 +689,76 @@ TEST_P(UsageTableHeaderInitializationTest, 200UsageEntries) { } TEST_P(UsageTableHeaderInitializationTest, - 201UsageEntries_AddEntryFails_UsageTableHeaderRecreated) { + UsageEntries_NoCapacity_UnderMinimum) { + crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. + std::vector usage_entries = kOverFullUsageEntryInfoVector; + constexpr size_t kHalfMinCapacity = kDefaultTableCapacity / 2; + usage_entries.resize(kHalfMinCapacity); + const SecurityLevel security_level = + (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), - SetArgPointee<1>(k201UsageEntryInfoVector), + SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), + Return(true))); + EXPECT_CALL(*crypto_session_, + LoadUsageTableHeader(security_level, kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + // No expectations of creating or deleting entries if the number of entries + // is less than minimally required capacity. + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, UsageEntries_NoCapacity) { + crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. + std::vector usage_entries = kOverFullUsageEntryInfoVector; + usage_entries.resize(kDefaultTableCapacity); + const SecurityLevel security_level = + (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + EXPECT_CALL(*device_files_, + RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), + Return(true))); + EXPECT_CALL(*crypto_session_, + LoadUsageTableHeader(security_level, kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + + // Expect an attempt to create a new entry. + EXPECT_CALL(*crypto_session_, Open(security_level)) + .WillOnce(Return(NO_ERROR)); + const uint32_t expect_usage_entry_number = kDefaultTableCapacity; + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + SizeIs(kDefaultTableCapacity + 1))) + .WillOnce(Return(true)); + + // Delete the entry after. + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(security_level, kDefaultTableCapacity, NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader), + Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kYetAnotherUsageTableHeader, + SizeIs(kDefaultTableCapacity))) + .WillOnce(Return(true)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, + UsageEntriesOverCapacity_AddEntryFails_UsageTableHeaderRecreated) { + EXPECT_CALL(*device_files_, + RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kOverFullUsageEntryInfoVector), SetArgPointee<2>(false), Return(true))); const SecurityLevel security_level = @@ -696,7 +780,8 @@ TEST_P(UsageTableHeaderInitializationTest, .WillOnce(Return(true)); // Expectations for AddEntry - const uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size(); + const uint32_t expect_usage_entry_number = + kOverFullUsageEntryInfoVector.size(); EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR))); @@ -705,9 +790,46 @@ TEST_P(UsageTableHeaderInitializationTest, } TEST_P(UsageTableHeaderInitializationTest, - 201UsageEntries_AddInvalidateEntrySucceeds) { - std::vector usage_entries_202 = k201UsageEntryInfoVector; - usage_entries_202.push_back(kDummyUsageEntryInfo); + UsageEntries_NoCapacity_AddEntryFails_UsageTableHeaderRecreated) { + crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. + std::vector usage_entries = kOverFullUsageEntryInfoVector; + usage_entries.resize(kDefaultTableCapacity); + const SecurityLevel security_level = + (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + EXPECT_CALL(*device_files_, + RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), + Return(true))); + EXPECT_CALL(*crypto_session_, + LoadUsageTableHeader(security_level, kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + // Try to create a new entry, and fail. + EXPECT_CALL(*crypto_session_, Open(security_level)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce(Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)); + // Expect clean up. + EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); + EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true)); + EXPECT_CALL(*device_files_, DeleteUsageTableInfo()).WillOnce(Return(true)); + // Expect recreation of usage table. + EXPECT_CALL(*crypto_session_, + CreateUsageTableHeader(security_level, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .WillOnce(Return(true)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, + UsageEntriesOverCapacity_AddInvalidateEntrySucceeds) { + // Capacity +2. + std::vector usage_entries = kOverFullUsageEntryInfoVector; + usage_entries.push_back(kDummyUsageEntryInfo); const SecurityLevel security_level = (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; @@ -715,33 +837,36 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), - SetArgPointee<1>(k201UsageEntryInfoVector), + SetArgPointee<1>(kOverFullUsageEntryInfoVector), SetArgPointee<2>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); // Expectations for AddEntry - const uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size(); + const uint32_t expect_usage_entry_number = + kOverFullUsageEntryInfoVector.size(); EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, - usage_entries_202)) + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, usage_entries)) .WillOnce(Return(true)); // 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_, - ShrinkUsageTableHeader(security_level, - usage_entries_202.size() - 1, NotNull())) - .WillOnce( - DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR))); + ShrinkUsageTableHeader(security_level, usage_entries.size() - 1, + NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader), + Return(NO_ERROR))); EXPECT_CALL(*device_files_, - StoreUsageTableInfo( - kAnotherUsageTableHeader, - SizeIs(k201UsageEntryInfoVector.size()))) + StoreUsageTableInfo(kYetAnotherUsageTableHeader, + SizeIs(kOverFullUsageEntryInfoVector.size()))) .WillOnce(Return(true)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); @@ -800,10 +925,12 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); - + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); EXPECT_CALL(*device_files_, StoreUsageTableInfo( - kUsageTableHeader, + kAnotherUsageTableHeader, UnorderedElementsAreArray(expect_usage_entry_info_vector))) .WillOnce(Return(true)); @@ -831,10 +958,12 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); - + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); EXPECT_CALL(*device_files_, StoreUsageTableInfo( - kUsageTableHeader, + kAnotherUsageTableHeader, UnorderedElementsAreArray(expect_usage_entry_info_vector))) .WillOnce(Return(true)); @@ -859,11 +988,13 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); - + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); EXPECT_CALL( *device_files_, StoreUsageTableInfo( - kUsageTableHeader, + kAnotherUsageTableHeader, UnorderedElementsAre( kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, @@ -905,17 +1036,19 @@ TEST_F(UsageTableHeaderTest, EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce(Return(INSUFFICIENT_CRYPTO_RESOURCES_3)) + .WillOnce(DoAll(SetArgPointee<0>(expected_usage_entry_number), + Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) .WillOnce( - DoAll(SetArgPointee<0>(expected_usage_entry_number), - Return(NO_ERROR))); + DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, _)) + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, _)) .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( + mock_usage_table_header->SuperAddEntry( crypto_session_, kUsageEntryInfoOfflineLicense6.storage_type == kStorageLicense, kUsageEntryInfoOfflineLicense6.key_set_id, @@ -959,7 +1092,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) { // Now invoke the method under test uint32_t usage_entry_number; EXPECT_EQ(INSUFFICIENT_CRYPTO_RESOURCES_3, - mock_usage_table_header->AddEntry( + mock_usage_table_header->SuperAddEntry( crypto_session_, true /* persistent */, kUsageEntryInfoOfflineLicense6.key_set_id, kUsageEntryInfoOfflineLicense6.usage_info_file_name, @@ -3390,8 +3523,10 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) { MockClock mock_clock; usage_table_header_->SetClock(&mock_clock); EXPECT_CALL(mock_clock, GetCurrentTime()).WillOnce(Return(kLruBaseTime)); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kUpgradedUsageTableHeader, _)); + 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; @@ -3436,8 +3571,10 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) { MockClock mock_clock; usage_table_header_->SetClock(&mock_clock); EXPECT_CALL(mock_clock, GetCurrentTime()).WillOnce(Return(kLruBaseTime)); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kUpgradedUsageTableHeader, _)); + 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; @@ -3970,15 +4107,7 @@ TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Unavailable) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); EXPECT_EQ(usage_table_header_->potential_table_capacity(), kMinimumUsageTableEntriesSupported); -} - -TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Zero) { - // This will issue a warning about the reported capacity is unexpected, - // and will default to the version's required minimum. - crypto_session_->SetMaximumUsageTableEntries(0u); - Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - EXPECT_EQ(usage_table_header_->potential_table_capacity(), - kMinimumUsageTableEntriesSupported); + EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity()); } TEST_F(UsageTableHeaderTest, PotentialTableCapacity_TooSmall) { @@ -3989,6 +4118,18 @@ TEST_F(UsageTableHeaderTest, PotentialTableCapacity_TooSmall) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); EXPECT_EQ(usage_table_header_->potential_table_capacity(), kMinimumUsageTableEntriesSupported); + EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity()); +} + +TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Unlimited) { + MockUsageTableHeader* mock_usage_table_header = SetUpMock(); + // Zero indicates that the table size is unlimited. + crypto_session_->SetMaximumUsageTableEntries(0u); + + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + constexpr size_t kZero = 0u; + EXPECT_EQ(mock_usage_table_header->potential_table_capacity(), kZero); + EXPECT_TRUE(mock_usage_table_header->HasUnlimitedTableCapacity()); } TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Available) { @@ -3996,6 +4137,7 @@ TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Available) { crypto_session_->SetMaximumUsageTableEntries(kTableCapacity); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); EXPECT_EQ(usage_table_header_->potential_table_capacity(), kTableCapacity); + EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity()); } } // namespace wvcdm