diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index 0504d760..2784a043 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -132,7 +132,6 @@ bool RetrieveUsageInfoLicense(DeviceFiles* device_files, } return true; } - } // namespace UsageTableHeader::UsageTableHeader() diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index 82117f94..44659949 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -1787,7 +1787,7 @@ TEST_F(UsageTableHeaderTest, InvalidateEntry_LastEntriesAreStorageTypeUnknown) { .WillOnce(DoAll(SetArgPointee<0>(kYetAnotherUsageEntry), SetArgPointee<1>(kUsageEntry), Return(NO_ERROR))); EXPECT_CALL(*device_files_, StoreUsageTableInfo(kYetAnotherUsageEntry, _)) - .WillOnce(Return(NO_ERROR)); + .WillOnce(Return(true)); EXPECT_CALL(*device_files_, StoreLicense(_, NotNull())) .WillOnce(Return(true)); @@ -2069,7 +2069,7 @@ TEST_F(UsageTableHeaderTest, EXPECT_CALL(*crypto_session_, LoadUsageEntry(3, kUsageEntry)) .WillOnce(Return(LOAD_USAGE_ENTRY_INVALID_SESSION)); - // Excect a call to shrink table to cut off only the unknown entries + // Expect a call to shrink table to cut off only the unknown entries // at the end of the table. EXPECT_CALL(*crypto_session_, ShrinkUsageTableHeader(kLevelDefault, 5, NotNull())) @@ -2171,7 +2171,7 @@ TEST_F( EXPECT_CALL(*crypto_session_, LoadUsageEntry(3, kUsageEntry)) .WillOnce(Return(LOAD_USAGE_ENTRY_INVALID_SESSION)); - // Excect a call to shrink table to cut off only the unknown entries + // Expect a call to shrink table to cut off only the unknown entries // at the end of the table. EXPECT_CALL(*crypto_session_, ShrinkUsageTableHeader(kLevelDefault, 5, NotNull())) @@ -2588,14 +2588,11 @@ TEST_F(UsageTableHeaderTest, // # of usage entries 7 3 TEST_F(UsageTableHeaderTest, InvalidateEntry_LastEntriesAreSecureStopAndUnknknown) { - std::vector usage_entry_info_vector; - const CdmUsageEntryInfo usage_entry_info_array[] = { + const std::vector usage_entry_info_vector = { kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown}; - ToVector(usage_entry_info_vector, usage_entry_info_array, - sizeof(usage_entry_info_array)); Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); const uint32_t usage_entry_number_to_be_deleted = @@ -2684,6 +2681,528 @@ TEST_F(UsageTableHeaderTest, EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); } +// Initial Test state: +// 1. Usage entry to be deleted is not last (Secure stop 1) +// 2. All other entries are invalid. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Defrag is skipped (no calls to Move()). +// c. Usage table will be resized. +// e. Updated table will be saved. +// f. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 Deleted +// Storage Type Unknown 1 Deleted +// Storage Type Unknown 2 Deleted +// Secure stop 1 3 Deleted +// Storage Type Unknown 4 Deleted +// Storage Type Unknown 5 Deleted +// Storage Type Unknown 6 Deleted +// +// # of usage entries 7 0 +TEST_F(UsageTableHeaderTest, InvalidateEntry_NoValidSessionsAfter) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 3; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // No calls related to defragging, just shrinking the table and save. + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(kLevelDefault, 0, NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + EXPECT_TRUE(usage_table_header_->usage_entry_info().empty()); +} + +// 1. Usage entry to be deleted is last valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. OEMCrypto is at max sessions, and any attempt to open a new session +// will fail with INSUFFICIENT_CRYPTO_RESOURCES. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Opening session for move will fail. +// d. Defrag is aborted; but shrink is still made up to last valid. +// e. Usage table will be resized. +// f. Updated table will be saved. +// g. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 0 +// Offline License 1 1 1 +// Secure Stop 1 2 Deleted +// Storage Type Unknown 3 Deleted +// +// # of usage entries 4 2 +TEST_F(UsageTableHeaderTest, InvalidateEntry_MaxSessionReached) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 2; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + // But will fail when opening a crypto session. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES)); + + // Despite being unable to open session, the table should be resized to + // exclude the trailing invalid entries. + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(kLevelDefault, 2, NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + ElementsAre(kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 2; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + +// 1. Usage entry to be deleted is first valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. OEMCrypto is at max sessions, and any attempt to open a new session +// will fail with INSUFFICIENT_CRYPTO_RESOURCES. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Opening session for move will fail. +// d. Defrag is aborted; but shrink is still made up to last valid. +// e. Usage table will be resized. +// f. Updated table will be saved. +// g. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 (Storage type unknown) +// Offline License 1 1 1 (Failed to move) +// Storage Type Unknown 2 Deleted +// Storage Type Unknown 3 Deleted +// +// # of usage entries 4 2 +TEST_F(UsageTableHeaderTest, InvalidateEntry_FirstEntry_MaxSessionReached) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 0; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + // But will fail when opening a crypto session. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .WillRepeatedly(Return(INSUFFICIENT_CRYPTO_RESOURCES)); + + // Despite being unable to open session, the table should be resized to + // exclude the trailing invalid entries. + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(kLevelDefault, 2, NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kAnotherUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + ElementsAre(kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 2; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + +// 1. Usage entry to be deleted is last valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. Moving entry will result in a system invalidation error. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Moving entry will result in system invalidation. +// d. Defrag is aborted; no call to Shrink() +// f. Updated table will be saved. +// g. InvalidateEntry() will return SYSTEM_INVALIDATED_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 0 +// Offline License 1 1 1 +// Secure Stop 1 2 2 (Storage Type Unknown) +// Storage Type Unknown 3 3 +// +// # of usage entries 4 4 +TEST_F(UsageTableHeaderTest, InvalidateEntry_SystemInvalidation_OnMove) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 2; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + // But will fail when moving. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, LoadUsageEntry(1, kUsageEntry)); + EXPECT_CALL(*crypto_session_, MoveUsageEntry(0)) + .WillOnce(Return(SYSTEM_INVALIDATED_ERROR)); + + // Defrag is aborted, and table is saved, but no call to shrink(). + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kUsageTableHeader, + ElementsAre(kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(SYSTEM_INVALIDATED_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, + true, device_files_, &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 4; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + +// 1. Usage entry to be deleted is last valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. Moving entry will result in a session invalidation error. +// +// Note: This is very similar to InvalidateEntry_SystemInvalidation_OnMove +// except that the error returned is not of importance to the calling +// session and is ignored (NO_ERROR returned). +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Moving entry will result in session invalidation. +// d. Defrag is aborted; no call to Shrink() +// f. Updated table will be saved. +// g. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 0 +// Offline License 1 1 1 +// Secure Stop 1 2 2 (Storage Type Unknown) +// Storage Type Unknown 3 3 +// +// # of usage entries 4 4 +TEST_F(UsageTableHeaderTest, InvalidateEntry_SessionInvalidation_OnMove) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 2; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + // But will fail when moving. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, LoadUsageEntry(1, kUsageEntry)); + EXPECT_CALL(*crypto_session_, MoveUsageEntry(0)) + .WillOnce(Return(SESSION_LOST_STATE_ERROR)); + + // Defrag is aborted, and table is saved, but no call to shrink(). + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kUsageTableHeader, + ElementsAre(kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + // The underlying error should not be returned to the caller. + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 4; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + +// 1. Usage entry to be deleted is last valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. Shrinking table will fail due to an unspecified entry being in use. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Entry will be moved successfully. +// d. Shrinking table fill fail with SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE. +// f. Updated table will be saved, with trailing invalid entries. +// g. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 1 (swapped Offline License 1) +// Offline License 1 1 0 (moved) +// Secure Stop 1 2 2 (Storage Type Unknown) +// Storage Type Unknown 3 3 +// +// # of usage entries 4 4 +TEST_F(UsageTableHeaderTest, InvalidateEntry_ShrinkFails) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 2; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .Times(2) // First to get entry, then again to update. + .WillRepeatedly( + DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, LoadUsageEntry(1, kUsageEntry)); + EXPECT_CALL(*crypto_session_, MoveUsageEntry(0)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kUsageEntry), Return(NO_ERROR))); + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + ElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + EXPECT_CALL(*device_files_, StoreLicense(_, NotNull())) + .WillOnce(Return(true)); + + // Expect a call to shrink table, but will fail due to an unspecified + // entry being in use. + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(kLevelDefault, 1, NotNull())) + .WillOnce(Return(SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 4; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + +// 1. Usage entry to be deleted is last valid entry (Secure stop 1) +// 2. There exists an entry to be moved (Offline License 1) +// 3. Moving entry cannot be done as the destination is in use. +// (this is unexpected but possible in normal operation) +// +// Attempting to delete the entry in (1) will result in: +// a. The entry will be marked as kStorageTypeUnknown. +// b. Only remaining entry is selected for move (Offline license 1) +// c. Call to move will fail with MOVE_USAGE_ENTRY_DESTINATION_IN_USE. +// d. Usage table will be resized. +// e. Updated table will be saved. +// f. InvalidateEntry() will return NO_ERROR. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Storage Type Unknown 0 0 +// Offline License 1 1 1 (unable to move) +// Secure Stop 1 2 Deleted +// Storage Type Unknown 3 Deleted +// +// # of usage entries 4 2 +TEST_F(UsageTableHeaderTest, InvalidateEntry_DestinationInUse_OnMove) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + const uint32_t usage_entry_number_to_be_deleted = + 2; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + // Expected calls for moving offline license 1 (position 1) to position 0. + // But will fail due to the destination being in use. + const DeviceFiles::CdmLicenseData offline_license_1_data{ + kUsageEntryInfoOfflineLicense1.key_set_id, + kActiveLicenseState, + kPsshData, + kKeyRequest, + kKeyResponse, + kKeyRenewalRequest, + kKeyRenewalResponse, + kReleaseServerUrl, + kPlaybackStartTime, + kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, + kEmptyAppParameters, + kUsageEntry, + static_cast(1)}; + EXPECT_CALL(*device_files_, + RetrieveLicense(kUsageEntryInfoOfflineLicense1.key_set_id, + NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(offline_license_1_data), Return(true))); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, LoadUsageEntry(1, kUsageEntry)); + EXPECT_CALL(*crypto_session_, MoveUsageEntry(0)) + .WillOnce(Return(MOVE_USAGE_ENTRY_DESTINATION_IN_USE)); + // No expectations for updating the entry or the header from the move + // operation. + + // Shrink table down to the last valid entry. + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(kLevelDefault, 2, NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, + StoreUsageTableInfo(kAnotherUsageEntry, + ElementsAre(kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->InvalidateEntry( + usage_entry_number_to_be_deleted, true, device_files_, + &metrics)); + // Check the end state of the usage table. + constexpr size_t expected_size = 2; + EXPECT_EQ(expected_size, usage_table_header_->usage_entry_info().size()); +} + // If the crypto session says the usage table header is stale, init should fail. TEST_F(UsageTableHeaderTest, StaleHeader) { std::vector usage_entry_info_vector;