LRU algorithm adapts to usage table capacity.
[ Merge of http://go/wvgerrit/93564 ] OEMCrypto v16 introduced the ability to report the maximum possible size of the usage table to the CDM. The LRU algorithm will take the table capacity into account when deciding which entry is removed. Bug: 148795097 Bug: 135298906 Test: CDM unit tests Change-Id: Ibba88813618c13a9bf1121e560b8cc02b1c7e7a6
This commit is contained in:
@@ -88,6 +88,8 @@ class UsageTableHeader {
|
||||
|
||||
size_t size() { return usage_entry_info_.size(); }
|
||||
|
||||
size_t potential_table_capacity() const { return potential_table_capacity_; }
|
||||
|
||||
const std::vector<CdmUsageEntryInfo>& usage_entry_info() const {
|
||||
return usage_entry_info_;
|
||||
}
|
||||
@@ -209,6 +211,12 @@ class UsageTableHeader {
|
||||
// |clock_| variable, however, it can be overrided for testing purpose.
|
||||
Clock* clock_ref_;
|
||||
|
||||
// The maximum number of entries that the underlying OEMCrypto
|
||||
// implementation can support. Some implementations might not
|
||||
// support reporting the table capacity, if so, then this value is
|
||||
// assumed to be |kMinimumUsageTableEntriesSupported|.
|
||||
size_t potential_table_capacity_ = 0u;
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
// Test related declarations
|
||||
friend class UsageTableHeaderTest;
|
||||
|
||||
@@ -25,14 +25,14 @@ std::string kOldUsageEntryPoviderSessionToken =
|
||||
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
|
||||
// Number of elements to consider for removal using the LRU algorithm.
|
||||
constexpr size_t kLruRemovalSetSize = 3;
|
||||
// Threshold for maximum number of unexpired offline licenses before
|
||||
// they are considered to be removed. This could occur if there are
|
||||
// not enough expired offline or streaming licenses to remove.
|
||||
// This threshold is set to prevent thrashing in the case that there
|
||||
// are a very large number of unexpired offline licenses and few
|
||||
// expired / streaming licenses (ie, number of unexpired licenses nears
|
||||
// the capacity of the usage table).
|
||||
constexpr size_t kLruUnexpiredThreshold = 150;
|
||||
// Fraction of table capacity of number of unexpired offline licenses
|
||||
// before they are considered to be removed. This could occur if
|
||||
// there are not enough expired offline or streaming licenses to
|
||||
// remove. This threshold is set to prevent thrashing in the case that
|
||||
// there are a very large number of unexpired offline licenses and few
|
||||
// expired / streaming licenses (ie, number of unexpired licenses
|
||||
// nears the capacity of the usage table).
|
||||
constexpr double kLruUnexpiredThresholdFraction = 0.75;
|
||||
|
||||
// Convert |license_message| -> SignedMessage -> License.
|
||||
bool ParseLicenseFromLicenseMessage(const CdmKeyResponse& license_message,
|
||||
@@ -162,6 +162,17 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
requested_security_level_ =
|
||||
security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
|
||||
|
||||
if (!crypto_session->GetMaximumUsageTableEntries(
|
||||
requested_security_level_, &potential_table_capacity_)) {
|
||||
potential_table_capacity_ = kMinimumUsageTableEntriesSupported;
|
||||
} 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;
|
||||
}
|
||||
|
||||
if (!file_handle_->Init(security_level)) {
|
||||
LOGE("Failed to initialize device files");
|
||||
return false;
|
||||
@@ -193,7 +204,7 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
|
||||
// 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 (status == NO_ERROR && lru_success) {
|
||||
if (usage_entry_info_.size() > kMinimumUsageTableEntriesSupported) {
|
||||
if (usage_entry_info_.size() > potential_table_capacity()) {
|
||||
uint32_t temporary_usage_entry_number;
|
||||
|
||||
// Create a new temporary usage entry, close the session and then
|
||||
@@ -886,8 +897,10 @@ bool UsageTableHeader::GetRemovalCandidates(
|
||||
std::vector<uint32_t>* removal_candidates) {
|
||||
LOGI("Locking to determine removal candidates");
|
||||
std::unique_lock<std::mutex> auto_lock(usage_table_header_lock_);
|
||||
const size_t lru_unexpired_threshold =
|
||||
kLruUnexpiredThresholdFraction * potential_table_capacity();
|
||||
return DetermineLicenseToRemove(usage_entry_info_, GetCurrentTime(),
|
||||
kLruUnexpiredThreshold, kLruRemovalSetSize,
|
||||
lru_unexpired_threshold, kLruRemovalSetSize,
|
||||
removal_candidates);
|
||||
}
|
||||
|
||||
|
||||
@@ -434,6 +434,27 @@ class MockCryptoSession : public TestCryptoSession {
|
||||
MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t));
|
||||
MOCK_METHOD2(ShrinkUsageTableHeader,
|
||||
CdmResponseType(uint32_t, CdmUsageTableHeader*));
|
||||
|
||||
// Fake method for testing. Having an EXPECT_CALL causes complexities
|
||||
// for getting table capacity during initialization.
|
||||
virtual bool GetMaximumUsageTableEntries(SecurityLevel security_level,
|
||||
size_t* number_of_entries) {
|
||||
if (number_of_entries == nullptr || !maximum_usage_table_entries_set_)
|
||||
return false;
|
||||
*number_of_entries = maximum_usage_table_entries_;
|
||||
return true;
|
||||
}
|
||||
void SetMaximumUsageTableEntries(size_t number_of_entries) {
|
||||
maximum_usage_table_entries_ = number_of_entries;
|
||||
maximum_usage_table_entries_set_ = true;
|
||||
}
|
||||
void UnsetMaximumUsageTableEntries() {
|
||||
maximum_usage_table_entries_set_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t maximum_usage_table_entries_ = 0;
|
||||
bool maximum_usage_table_entries_set_ = false;
|
||||
};
|
||||
|
||||
// Partial mock of the UsageTableHeader. This is to test when dependency
|
||||
@@ -618,6 +639,7 @@ TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveUsageInfo) {
|
||||
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
|
||||
kEmptyUsageEntryInfoVector))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
|
||||
}
|
||||
|
||||
@@ -655,7 +677,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
SetArgPointee<1>(k201UsageEntryInfoVector),
|
||||
SetArgPointee<2>(false), Return(true)));
|
||||
|
||||
SecurityLevel security_level =
|
||||
const SecurityLevel security_level =
|
||||
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
Open(security_level)).WillOnce(Return(NO_ERROR));
|
||||
@@ -672,7 +694,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
.WillOnce(Return(true));
|
||||
|
||||
// Expectations for AddEntry
|
||||
uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
const uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
|
||||
Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)));
|
||||
@@ -703,7 +725,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
.WillOnce(Return(true));
|
||||
|
||||
// Expectations for AddEntry
|
||||
uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
const uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
|
||||
Return(NO_ERROR)));
|
||||
@@ -712,7 +734,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
.WillOnce(Return(true));
|
||||
|
||||
// Expectations for DeleteEntry
|
||||
SecurityLevel security_level =
|
||||
const SecurityLevel security_level =
|
||||
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
Open(security_level))
|
||||
@@ -740,7 +762,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
.WillOnce(Return(NO_ERROR));
|
||||
|
||||
// Expectations for AddEntry
|
||||
uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
const uint32_t expect_usage_entry_number = k201UsageEntryInfoVector.size();
|
||||
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
|
||||
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
|
||||
Return(NO_ERROR)));
|
||||
@@ -749,7 +771,7 @@ TEST_P(UsageTableHeaderInitializationTest,
|
||||
.WillOnce(Return(true));
|
||||
|
||||
// Expectations for DeleteEntry
|
||||
SecurityLevel security_level =
|
||||
const SecurityLevel security_level =
|
||||
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
|
||||
|
||||
EXPECT_CALL(*crypto_session_,
|
||||
@@ -3467,4 +3489,37 @@ TEST_F(UsageTableHeaderTest, DetermineLicenseToRemove_LargeMixedSet) {
|
||||
UnorderedElementsAreArray(modified_offline_license_numbers));
|
||||
}
|
||||
|
||||
TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Unavailable) {
|
||||
crypto_session_->UnsetMaximumUsageTableEntries();
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(UsageTableHeaderTest, PotentialTableCapacity_TooSmall) {
|
||||
// This will issue a warning about the reported capacity is unexpected,
|
||||
// and will default to the version's required minimum.
|
||||
crypto_session_->SetMaximumUsageTableEntries(
|
||||
kMinimumUsageTableEntriesSupported / 2);
|
||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||
EXPECT_EQ(usage_table_header_->potential_table_capacity(),
|
||||
kMinimumUsageTableEntriesSupported);
|
||||
}
|
||||
|
||||
TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Available) {
|
||||
constexpr size_t kTableCapacity = 2000u;
|
||||
crypto_session_->SetMaximumUsageTableEntries(kTableCapacity);
|
||||
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
|
||||
EXPECT_EQ(usage_table_header_->potential_table_capacity(), kTableCapacity);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user