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