Update AddEntry() for usage table changes.

[ Merge of http://go/wvgerrit/96071 ]

Changes to how the usage table method InvalidateEntry() behaves
required additional changes to CDM code that uses this method.

This involved some refactoring to AddEntry(), moving the LRU
related code to its own function.

A few unittests had to be changed / removed as the moving
multiple entries changes expectations of several existing tests.

Several additional helper methods have been created to improve
readability.  These include getters for information about the
usage table, a method for releasing stale entries, and a method of
recording LRU metrics.

Bug: 150890014
Bug: 150887808
Bug: 154269671
Test: Linux unit tests and Android unit tests
Change-Id: I11a98f9a2dea9b2ae57b37d7d4483a37be721763
This commit is contained in:
Alex Dale
2020-03-18 17:17:03 -07:00
parent 1e5e221909
commit 66e3d69300
3 changed files with 130 additions and 302 deletions

View File

@@ -350,28 +350,6 @@ void ToVector(std::vector<CdmUsageEntryInfo>& 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<CdmUsageEntryInfo>* 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo> 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<CdmUsageEntryInfo>& 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();