Check for open session when initializing usage table. am: 023b06eded

Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/14296026

Change-Id: I4b0b46d585f99c492e30e608a0a18708fc40b042
This commit is contained in:
Alex Dale
2021-05-24 21:30:44 +00:00
committed by Automerger Merge Worker
5 changed files with 672 additions and 481 deletions

View File

@@ -293,7 +293,7 @@ class CryptoSession {
// exist as long as the new CryptoSession exists. // exist as long as the new CryptoSession exists.
explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics);
int session_count() { return session_count_; } int session_count() const { return session_count_; }
private: private:
friend class CryptoSessionForTest; friend class CryptoSessionForTest;
@@ -313,6 +313,13 @@ class CryptoSession {
} }
void Init(); void Init();
// Will set up the UsageTableHeader for this session. This may require
// creating a new UsageTableHeader if the global instance has not
// been initialized.
// Note: This function will lock the global static field lock in write mode.
bool SetUpUsageTableHeader(SecurityLevel requested_security_level);
CdmResponseType GetTokenFromKeybox(std::string* token); CdmResponseType GetTokenFromKeybox(std::string* token);
CdmResponseType GetTokenFromOemCert(std::string* token); CdmResponseType GetTokenFromOemCert(std::string* token);
static bool ExtractSystemIdFromOemCert(const std::string& oem_cert, static bool ExtractSystemIdFromOemCert(const std::string& oem_cert,
@@ -355,9 +362,15 @@ class CryptoSession {
// These methods should be used to take the various CryptoSession mutexes in // These methods should be used to take the various CryptoSession mutexes in
// preference to taking the mutexes directly. // preference to taking the mutexes directly.
// //
// A lock should be taken on the Static Field Mutex before accessing any of // A lock should be taken on the Static Field Mutex before accessing
// CryptoSession's non-atomic static fields. It can be taken as a reader or as // any of CryptoSession's non-atomic static fields with the exception
// a writer, depending on how you will be accessing the static fields. // of the Usage Table Mutex. The Static Field Mutex can be taken as
// a reader or as a writer, depending on how you will be accessing
// the static fields. The Usage Table Mutex should be taken when
// reading and writing to the static usage table fields (creating,
// destroying or taking a pointer of the handles). The purpose of
// having a separate mutex for usage table is due to the recursive
// nature of initializing the global usage table.
// //
// Before calling into OEMCrypto, code must take locks on the OEMCrypto Mutex // Before calling into OEMCrypto, code must take locks on the OEMCrypto Mutex
// and/or the OEMCrypto Session Mutex. Which of them should be taken and how // and/or the OEMCrypto Session Mutex. Which of them should be taken and how
@@ -414,6 +427,9 @@ class CryptoSession {
static shared_mutex static_field_mutex_; static shared_mutex static_field_mutex_;
static shared_mutex oem_crypto_mutex_; static shared_mutex oem_crypto_mutex_;
std::mutex oem_crypto_session_mutex_; std::mutex oem_crypto_session_mutex_;
// Usage table mutex used only when performing write operations on
// the static usage table pointers.
static std::recursive_mutex usage_table_mutex_;
static bool initialized_; static bool initialized_;
static int session_count_; static int session_count_;
@@ -446,8 +462,10 @@ class CryptoSession {
// Open session-cached result of OEMCrypto_SupportsUsageTable(). // Open session-cached result of OEMCrypto_SupportsUsageTable().
CachedBooleanProperty has_usage_info_support_ = kBooleanUnset; CachedBooleanProperty has_usage_info_support_ = kBooleanUnset;
UsageTableHeader* usage_table_header_ = nullptr; UsageTableHeader* usage_table_header_ = nullptr;
static UsageTableHeader* usage_table_header_l1_; // These fields are protected by |usage_table_mutex_| and not
static UsageTableHeader* usage_table_header_l3_; // |static_field_mutex_|.
static std::unique_ptr<UsageTableHeader> usage_table_header_l1_;
static std::unique_ptr<UsageTableHeader> usage_table_header_l3_;
std::string request_id_; std::string request_id_;
static std::atomic<uint64_t> request_id_index_source_; static std::atomic<uint64_t> request_id_index_source_;

View File

@@ -53,8 +53,12 @@ class UsageTableHeader {
UsageTableHeader(); UsageTableHeader();
virtual ~UsageTableHeader() {} virtual ~UsageTableHeader() {}
// |crypto_session| is used to create or load a usage master table and // |crypto_session| is used to create or load a usage master table
// not cached beyound this call. // and not cached beyound this call.
// First attempts to restore the usage table from the device files.
// If restoring fails, then a new usage table is created.
// Note: No OEMCrypto session for the same security level should be
// opened before calling.
bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session); bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session);
// |persistent_license| false indicates usage info record // |persistent_license| false indicates usage info record
@@ -125,6 +129,32 @@ class UsageTableHeader {
} }
private: private:
// == Initialization methods ==
// These should only be called during table initialization.
// All parameters are assumed valid.
// Creates a new, empty usage table. Any existing usage table files
// will be deleted.
bool CreateNewTable(CryptoSession* const crypto_session);
// Attempts to restore the usage table from persistent storage, and
// loads the usage table header into OEMCrypto.
// Note: No other OEMCrypto session should be opened before calling.
bool RestoreTable(CryptoSession* const crypto_session);
// Performs a check that there are no open OEMCrypto sessions for
// the current security level of the usage table.
bool OpenSessionCheck(CryptoSession* const crypto_session);
// Performs a check that the OEMCrypto table can support at least
// one more entry if the table is at or near the reported capacity.
// If this check fails, a new usage table SHOULD be created.
bool CapacityCheck(CryptoSession* const crypto_session);
// Attempts to determine the capacity of the OEMCrypto usage table.
// Sets the result to |potential_table_capacity_|.
bool DetermineTableCapacity(CryptoSession* crypto_session);
// == Table operation methods ==
CdmResponseType MoveEntry(uint32_t from /* usage entry number */, CdmResponseType MoveEntry(uint32_t from /* usage entry number */,
const CdmUsageEntry& from_usage_entry, const CdmUsageEntry& from_usage_entry,
uint32_t to /* usage entry number */, uint32_t to /* usage entry number */,
@@ -153,8 +183,6 @@ class UsageTableHeader {
// evicted. // evicted.
CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics); CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics);
virtual bool is_inited() { return is_inited_; }
// Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a // Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a
// device file that had not yet been upgraded to use the LRU data. // device file that had not yet been upgraded to use the LRU data.
virtual bool LruUpgradeAllUsageEntries(); virtual bool LruUpgradeAllUsageEntries();
@@ -213,16 +241,15 @@ class UsageTableHeader {
// by CdmSession. // by CdmSession.
std::unique_ptr<DeviceFiles> device_files_; std::unique_ptr<DeviceFiles> device_files_;
std::unique_ptr<FileSystem> file_system_; std::unique_ptr<FileSystem> file_system_;
CdmSecurityLevel security_level_; CdmSecurityLevel security_level_ = kSecurityLevelUninitialized;
SecurityLevel requested_security_level_; SecurityLevel requested_security_level_ = kLevelDefault;
CdmUsageTableHeader usage_table_header_; CdmUsageTableHeader usage_table_header_;
std::vector<CdmUsageEntryInfo> usage_entry_info_; std::vector<CdmUsageEntryInfo> usage_entry_info_;
// Lock to ensure that a single object is created for each security level // Table is sync with persistent storage and can be used by the CDM
// and data member to represent whether an object has been correctly // to interact with OEMCrypto.
// initialized. bool is_initialized_ = false;
bool is_inited_;
// Synchonizes access to the Usage Table Header and bookkeeping // Synchonizes access to the Usage Table Header and bookkeeping
// data-structures // data-structures

View File

@@ -51,8 +51,9 @@
} }
namespace wvcdm { namespace wvcdm {
namespace { namespace {
using UsageTableLock = std::unique_lock<std::recursive_mutex>;
constexpr size_t KiB = 1024; constexpr size_t KiB = 1024;
constexpr size_t MiB = 1024 * 1024; constexpr size_t MiB = 1024 * 1024;
@@ -166,20 +167,22 @@ size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm) {
} }
} // namespace } // namespace
// CryptoSession variables allocation.
shared_mutex CryptoSession::static_field_mutex_; shared_mutex CryptoSession::static_field_mutex_;
shared_mutex CryptoSession::oem_crypto_mutex_; shared_mutex CryptoSession::oem_crypto_mutex_;
bool CryptoSession::initialized_ = false; bool CryptoSession::initialized_ = false;
int CryptoSession::session_count_ = 0; int CryptoSession::session_count_ = 0;
int CryptoSession::termination_counter_ = 0; int CryptoSession::termination_counter_ = 0;
UsageTableHeader* CryptoSession::usage_table_header_l1_ = nullptr; std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l1_;
UsageTableHeader* CryptoSession::usage_table_header_l3_ = nullptr; std::unique_ptr<UsageTableHeader> CryptoSession::usage_table_header_l3_;
std::recursive_mutex CryptoSession::usage_table_mutex_;
std::atomic<uint64_t> CryptoSession::request_id_index_source_(0); std::atomic<uint64_t> CryptoSession::request_id_index_source_(0);
size_t GetOffset(std::string message, std::string field) { size_t GetOffset(std::string message, std::string field) {
size_t pos = message.find(field); size_t pos = message.find(field);
if (pos == std::string::npos) { if (pos == std::string::npos) {
LOGE("Cannot find the |field| offset in message: field = %s", LOGE("Cannot find the |field| offset in message: field = %s",
field.c_str()); IdToString(field));
pos = 0; pos = 0;
} }
return pos; return pos;
@@ -195,7 +198,7 @@ OEMCrypto_Substring GetSubstring(const std::string& message,
size_t pos = message.find(field); size_t pos = message.find(field);
if (pos == std::string::npos) { if (pos == std::string::npos) {
LOGW("Cannot find the |field| substring in message: field = %s", LOGW("Cannot find the |field| substring in message: field = %s",
field.c_str()); IdToString(field));
substring.offset = 0; substring.offset = 0;
substring.length = 0; substring.length = 0;
} else { } else {
@@ -359,7 +362,7 @@ void CryptoSession::Init() {
bool CryptoSession::TryTerminate() { bool CryptoSession::TryTerminate() {
LOGV("Terminating crypto session"); LOGV("Terminating crypto session");
WithStaticFieldWriteLock("TryTerminate", [&] { const bool terminated = WithStaticFieldWriteLock("TryTerminate", [&] {
LOGV( LOGV(
"Terminating crypto session: initialized_ = %s, session_count_ = %d, " "Terminating crypto session: initialized_ = %s, session_count_ = %d, "
"termination_counter_ = %d", "termination_counter_ = %d",
@@ -371,25 +374,20 @@ bool CryptoSession::TryTerminate() {
if (session_count_ > 0 || termination_counter_ > 0 || !initialized_) if (session_count_ > 0 || termination_counter_ > 0 || !initialized_)
return false; return false;
OEMCryptoResult sts; const OEMCryptoResult sts =
WithOecWriteLock("Terminate", [&] { sts = OEMCrypto_Terminate(); }); WithOecWriteLock("Terminate", [&] { return OEMCrypto_Terminate(); });
if (OEMCrypto_SUCCESS != sts) { if (OEMCrypto_SUCCESS != sts) {
LOGE("OEMCrypto_Terminate failed: status = %d", static_cast<int>(sts)); LOGE("OEMCrypto_Terminate failed: status = %d", static_cast<int>(sts));
} }
if (usage_table_header_l1_ != nullptr) {
delete usage_table_header_l1_;
usage_table_header_l1_ = nullptr;
}
if (usage_table_header_l3_ != nullptr) {
delete usage_table_header_l3_;
usage_table_header_l3_ = nullptr;
}
initialized_ = false; initialized_ = false;
return true; return true;
}); });
return true; if (terminated) {
UsageTableLock lock(usage_table_mutex_);
usage_table_header_l1_.reset();
usage_table_header_l3_.reset();
}
return terminated;
} }
void CryptoSession::DisableDelayedTermination() { void CryptoSession::DisableDelayedTermination() {
@@ -398,7 +396,63 @@ void CryptoSession::DisableDelayedTermination() {
[&] { termination_counter_ = 0; }); [&] { termination_counter_ = 0; });
} }
bool CryptoSession::SetUpUsageTableHeader(
SecurityLevel requested_security_level) {
if (usage_table_header_ != nullptr) {
LOGE("Usage table is already set up for the current crypto session");
return false;
}
const CdmSecurityLevel security_level =
GetSecurityLevel(requested_security_level);
if (security_level != kSecurityLevelL1 &&
security_level != kSecurityLevelL3) {
LOGD("Unsupported security level for usage support: security_level = %d",
static_cast<int>(security_level));
return false;
}
// Check if usage support is available.
bool supports_usage_table = false;
if (!HasUsageInfoSupport(requested_security_level, &supports_usage_table)) {
metrics_->oemcrypto_usage_table_support_.SetError(
USAGE_INFORMATION_SUPPORT_FAILED);
return false;
}
metrics_->oemcrypto_usage_table_support_.Record(
supports_usage_table ? kUsageEntrySupport : kNonSecureUsageSupport);
if (!supports_usage_table) {
return false;
}
LOGV("Usage table lock: SetUpUsageTableHeader()");
UsageTableLock auto_lock(usage_table_mutex_);
// TODO(b/141350978): Prevent any recursive logic.
// Manipulate only the usage table for the requested security level.
std::unique_ptr<UsageTableHeader>& header = security_level == kSecurityLevelL1
? usage_table_header_l1_
: usage_table_header_l3_;
if (!header) {
// This may be called twice within the same thread when the table
// is initialized. On the second call |header| will not be null,
// causing this block to be skipped.
header.reset(new UsageTableHeader());
if (!header->Init(security_level, this)) {
LOGE("Failed to initialize and sync usage usage table");
// Must be cleared globally to prevent the next session to be
// opened from using the invalid UsageTableHeader.
header.reset();
return false;
}
}
usage_table_header_ = header.get();
metrics_->usage_table_header_initial_size_.Record(
usage_table_header_->size());
return true;
}
CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) { CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) {
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
RETURN_IF_NULL(token, PARAMETER_NULL); RETURN_IF_NULL(token, PARAMETER_NULL);
std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0'); std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0');
@@ -422,6 +476,7 @@ CdmResponseType CryptoSession::GetTokenFromKeybox(std::string* token) {
} }
CdmResponseType CryptoSession::GetTokenFromOemCert(std::string* token) { CdmResponseType CryptoSession::GetTokenFromOemCert(std::string* token) {
RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
RETURN_IF_NULL(token, PARAMETER_NULL); RETURN_IF_NULL(token, PARAMETER_NULL);
OEMCryptoResult status; OEMCryptoResult status;
@@ -752,9 +807,16 @@ uint8_t CryptoSession::GetSecurityPatchLevel() {
CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
LOGD("Opening crypto session: requested_security_level = %s", LOGD("Opening crypto session: requested_security_level = %s",
SecurityLevelToString(requested_security_level)); SecurityLevelToString(requested_security_level));
RETURN_IF_UNINITIALIZED(UNKNOWN_ERROR); RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED);
if (open_) return NO_ERROR; if (open_) return NO_ERROR;
if (!SetUpUsageTableHeader(requested_security_level)) {
// Ignore errors since we do not know when a session is opened,
// if it is intended to be used for offline/usage session related
// or otherwise.
LOGW("Session opened without a usage table");
}
CdmResponseType result = GetProvisioningMethod(requested_security_level, CdmResponseType result = GetProvisioningMethod(requested_security_level,
&pre_provision_token_type_); &pre_provision_token_type_);
if (result != NO_ERROR) return result; if (result != NO_ERROR) return result;
@@ -817,70 +879,10 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
key_session_.reset(new ContentKeySession(oec_session_id_, metrics_)); key_session_.reset(new ContentKeySession(oec_session_id_, metrics_));
}); });
// Set up Usage Table support
//
// This MUST be the last thing in the function because it contains the
// successful return paths of the function. It will attempt to initialize
// usage tables. However, whether they are employed is determined by
// information in the license, which will not be received until later. In case
// usage tables are not even needed, initialization errors here are ignored by
// returning successfully.
//
// TODO(b/141350978): Refactor this code so that it does not have this
// problem.
if (!GetApiVersion(&api_version_)) { if (!GetApiVersion(&api_version_)) {
LOGE("Failed to get API version"); LOGE("Failed to get API version");
return USAGE_SUPPORT_GET_API_FAILED; return USAGE_SUPPORT_GET_API_FAILED;
} }
bool supports_usage_table = false;
if (HasUsageInfoSupport(&supports_usage_table)) {
metrics_->oemcrypto_usage_table_support_.Record(
supports_usage_table ? kUsageEntrySupport : kNonSecureUsageSupport);
if (supports_usage_table) {
CdmSecurityLevel security_level = GetSecurityLevel();
if (security_level == kSecurityLevelL1 ||
security_level == kSecurityLevelL3) {
// This block cannot use |WithStaticFieldWriteLock| because it needs
// to unlock the lock partway through.
LOGV("Static field write lock: Open() initializing usage table");
std::unique_lock<shared_mutex> auto_lock(static_field_mutex_);
UsageTableHeader** header = security_level == kSecurityLevelL1
? &usage_table_header_l1_
: &usage_table_header_l3_;
if (*header == nullptr) {
*header = new UsageTableHeader();
// Ignore errors since we do not know when a session is opened,
// if it is intended to be used for offline/usage session related
// or otherwise.
auto_lock.unlock();
bool is_usage_table_header_inited =
(*header)->Init(security_level, this);
auto_lock.lock();
if (!is_usage_table_header_inited) {
delete *header;
*header = nullptr;
usage_table_header_ = nullptr;
return NO_ERROR;
}
}
usage_table_header_ = *header;
metrics_->usage_table_header_initial_size_.Record((*header)->size());
} // End |static_field_mutex_| block.
}
} else {
metrics_->oemcrypto_usage_table_support_.SetError(
USAGE_INFORMATION_SUPPORT_FAILED);
}
// Do not add logic after this point as it may never get exercised. In the
// event of errors initializing the usage tables, the code will return early,
// never reaching this point. See the comment above the "Set up Usage Table
// support" section for details.
//
// TODO(b/139973602): Refactor this code.
return NO_ERROR; return NO_ERROR;
} }
@@ -889,13 +891,13 @@ void CryptoSession::Close() {
open_ ? "true" : "false"); open_ ? "true" : "false");
if (!open_) return; if (!open_) return;
OEMCryptoResult close_sts; const OEMCryptoResult close_sts = WithOecWriteLock(
WithOecWriteLock( "Close", [&] { return OEMCrypto_CloseSession(oec_session_id_); });
"Close", [&] { close_sts = OEMCrypto_CloseSession(oec_session_id_); });
metrics_->oemcrypto_close_session_.Increment(close_sts); metrics_->oemcrypto_close_session_.Increment(close_sts);
// Clear cached values. // Clear cached values.
has_usage_info_support_ = kBooleanUnset; has_usage_info_support_ = kBooleanUnset;
oem_token_.clear();
if (close_sts != OEMCrypto_SUCCESS) { if (close_sts != OEMCrypto_SUCCESS) {
LOGW("OEMCrypto_CloseSession failed: status = %d", LOGW("OEMCrypto_CloseSession failed: status = %d",
@@ -905,6 +907,7 @@ void CryptoSession::Close() {
case OEMCrypto_SUCCESS: case OEMCrypto_SUCCESS:
case OEMCrypto_ERROR_INVALID_SESSION: case OEMCrypto_ERROR_INVALID_SESSION:
case OEMCrypto_ERROR_SYSTEM_INVALIDATED: case OEMCrypto_ERROR_SYSTEM_INVALIDATED:
usage_table_header_ = nullptr;
open_ = false; open_ = false;
break; break;
case OEMCrypto_ERROR_CLOSE_SESSION_FAILED: case OEMCrypto_ERROR_CLOSE_SESSION_FAILED:
@@ -2488,14 +2491,13 @@ CdmResponseType CryptoSession::LoadUsageTableHeader(
LOGV("Loading usage table header: requested_security_level = %s", LOGV("Loading usage table header: requested_security_level = %s",
SecurityLevelToString(requested_security_level)); SecurityLevelToString(requested_security_level));
OEMCryptoResult result; const OEMCryptoResult result = WithOecWriteLock("LoadUsageTableHeader", [&] {
WithOecWriteLock("LoadUsageTableHeader", [&] { return OEMCrypto_LoadUsageTableHeader(
result = OEMCrypto_LoadUsageTableHeader(
requested_security_level, requested_security_level,
reinterpret_cast<const uint8_t*>(usage_table_header.data()), reinterpret_cast<const uint8_t*>(usage_table_header.data()),
usage_table_header.size()); usage_table_header.size());
metrics_->oemcrypto_load_usage_table_header_.Increment(result);
}); });
metrics_->oemcrypto_load_usage_table_header_.Increment(result);
if (result != OEMCrypto_SUCCESS) { if (result != OEMCrypto_SUCCESS) {
if (result == OEMCrypto_WARNING_GENERATION_SKEW) { if (result == OEMCrypto_WARNING_GENERATION_SKEW) {

View File

@@ -15,12 +15,9 @@
namespace wvcdm { namespace wvcdm {
namespace { namespace {
std::string kEmptyString; const std::string kEmptyString;
wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid"; const wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid";
std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0);
std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0);
std::string kOldUsageEntryPoviderSessionToken =
"nahZ6achSheiqua3TohQuei0ahwohv";
constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days
// Fraction of table capacity of number of unexpired offline licenses // Fraction of table capacity of number of unexpired offline licenses
// before they are considered to be removed. This could occur if // before they are considered to be removed. This could occur if
@@ -141,13 +138,19 @@ bool EntryIsOfflineLicense(const CdmUsageEntryInfo& info) {
// Used for stl filters. // Used for stl filters.
return info.storage_type == kStorageLicense; return info.storage_type == kStorageLicense;
} }
bool IsValidCdmSecurityLevelForUsageInfo(CdmSecurityLevel security_level) {
return security_level == kSecurityLevelL1 ||
security_level == kSecurityLevelL3;
}
SecurityLevel CdmSecurityLevelToRequestedLevel(
CdmSecurityLevel security_level) {
return security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
}
} // namespace } // namespace
UsageTableHeader::UsageTableHeader() UsageTableHeader::UsageTableHeader() : clock_ref_(&clock_) {
: security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_inited_(false),
clock_ref_(&clock_) {
file_system_.reset(new FileSystem()); file_system_.reset(new FileSystem());
device_files_.reset(new DeviceFiles(file_system_.get())); device_files_.reset(new DeviceFiles(file_system_.get()));
} }
@@ -159,152 +162,89 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level,
LOGE("No crypto session provided"); LOGE("No crypto session provided");
return false; return false;
} }
if (is_initialized_) {
switch (security_level) { LOGE("Cannot reinitialize usage table: security_level = %s",
case kSecurityLevelL1: CdmSecurityLevelToString(security_level));
case kSecurityLevelL3: return false;
break; }
default: if (!IsValidCdmSecurityLevelForUsageInfo(security_level)) {
LOGE("Invalid security level provided: security_level = %d", LOGE("Invalid security level provided: security_level = %d",
static_cast<int>(security_level)); static_cast<int>(security_level));
return false; return false;
} }
security_level_ = security_level; security_level_ = security_level;
requested_security_level_ = requested_security_level_ = CdmSecurityLevelToRequestedLevel(security_level);
security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
if (!crypto_session->GetMaximumUsageTableEntries( if (!OpenSessionCheck(crypto_session)) {
requested_security_level_, &potential_table_capacity_)) { return false;
LOGW( }
"Could not determine usage table capacity, assuming default: " if (!DetermineTableCapacity(crypto_session)) {
"default = %zu", return false;
kMinimumUsageTableEntriesSupported);
potential_table_capacity_ = kMinimumUsageTableEntriesSupported;
} else if (potential_table_capacity_ == 0) {
LOGD("Usage table capacity is unlimited: security_level = %s",
CdmSecurityLevelToString(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 = %s",
potential_table_capacity_, CdmSecurityLevelToString(security_level));
} }
if (!device_files_->Init(security_level)) { if (!device_files_->Init(security_level)) {
LOGE("Failed to initialize device files"); LOGE("Failed to initialize device files");
return false; return false;
} }
// Attempt restoring first, if unable to restore, then create a new
// table.
return RestoreTable(crypto_session) || CreateNewTable(crypto_session);
}
CdmResponseType status = USAGE_INFO_NOT_FOUND; bool UsageTableHeader::RestoreTable(CryptoSession* const crypto_session) {
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
bool run_lru_upgrade = false; bool run_lru_upgrade = false;
if (device_files_->RetrieveUsageTableInfo( if (!device_files_->RetrieveUsageTableInfo(
&usage_table_header_, &usage_entry_info_, &run_lru_upgrade)) { &usage_table_header_, &usage_entry_info_, &run_lru_upgrade)) {
LOGI("Number of usage entries: %zu", usage_entry_info_.size()); LOGW("Could not retrieve usage table");
status = crypto_session->LoadUsageTableHeader(requested_security_level_, return false;
usage_table_header_); }
LOGI("Found usage table to restore: entry_count = %zu",
usage_entry_info_.size());
bool lru_success = true; const CdmResponseType status = crypto_session->LoadUsageTableHeader(
if (status == NO_ERROR && run_lru_upgrade) { requested_security_level_, usage_table_header_);
// If the loaded table info does not contain LRU information, then if (status != NO_ERROR) {
// the information must be added immediately before being used. LOGE("Failed to load usage table header: sts = %d",
if (!LruUpgradeAllUsageEntries()) { static_cast<int>(status));
LOGE( return false;
"Unable to init usage table header: "
"Failed to perform LRU upgrade to usage entry table");
lru_success = false;
}
}
// 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 ((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
// try to delete it.
CdmResponseType result = NO_ERROR;
{
// |local_crypto_session| points to an object whose scope is this
// method or a test object whose scope is the lifetime of this class
std::unique_ptr<CryptoSession> scoped_crypto_session;
CryptoSession* local_crypto_session = test_crypto_session_.get();
if (local_crypto_session == nullptr) {
scoped_crypto_session.reset(
CryptoSession::MakeCryptoSession(metrics));
local_crypto_session = scoped_crypto_session.get();
}
result = local_crypto_session->Open(requested_security_level_);
if (result == NO_ERROR) {
result = AddEntry(local_crypto_session, true, kDummyKeySetId,
kEmptyString, kEmptyString,
&temporary_usage_entry_number);
}
}
if (result == NO_ERROR) {
result = InvalidateEntry(temporary_usage_entry_number,
/* defrag_table = */ true,
device_files_.get(), metrics);
if (usage_entry_info_.size() > temporary_usage_entry_number) {
// The entry should have been deleted from the usage table,
// not just marked as type unknown. Failure to call
// Shrink() may be an indicator of other issues.
LOGE("Temporary entry was not deleted");
result = UNKNOWN_ERROR;
}
}
if (result != NO_ERROR) {
LOGE(
"Unable to create/delete new entry, clearing usage entries: "
"security_level = %s, usage_entry_count = %zu",
CdmSecurityLevelToString(security_level),
usage_entry_info_.size());
status = result;
}
}
}
if (status != NO_ERROR || !lru_success) {
LOGE("Failed to load usage table: security_level = %s, status = %d",
CdmSecurityLevelToString(security_level), static_cast<int>(status));
device_files_->DeleteAllLicenses();
device_files_->DeleteAllUsageInfo();
device_files_->DeleteUsageTableInfo();
usage_entry_info_.clear();
usage_table_header_.clear();
status = crypto_session->CreateUsageTableHeader(requested_security_level_,
&usage_table_header_);
if (status != NO_ERROR) return false;
StoreTable(device_files_.get());
}
} else {
status = crypto_session->CreateUsageTableHeader(requested_security_level_,
&usage_table_header_);
if (status != NO_ERROR) return false;
StoreTable(device_files_.get());
} }
is_inited_ = true; // If the saved usage entries/meta data is missing LRU information,
// then the entries and their meta data must be updated.
if (run_lru_upgrade && !LruUpgradeAllUsageEntries()) {
LOGE("Failed to perform LRU upgrade to usage entry table");
return false;
}
if (!CapacityCheck(crypto_session)) {
LOGE("Cannot restore table due to failing capacity check");
return false;
}
is_initialized_ = true;
return true;
}
bool UsageTableHeader::CreateNewTable(CryptoSession* const crypto_session) {
LOGD("Removing all usage table files");
// Existing files need to be deleted to avoid attempts to restore
// licenses which no longer have a usage entry.
device_files_->DeleteAllLicenses();
device_files_->DeleteAllUsageInfo();
device_files_->DeleteUsageTableInfo();
usage_entry_info_.clear();
usage_table_header_.clear();
const CdmResponseType status = crypto_session->CreateUsageTableHeader(
requested_security_level_, &usage_table_header_);
if (status != NO_ERROR) {
LOGE("Failed to create new usage table header");
return false;
}
if (!StoreTable(device_files_.get())) {
LOGE("Failed to store new usage table header");
return false;
}
is_initialized_ = true;
return true; return true;
} }
@@ -395,7 +335,7 @@ CdmResponseType UsageTableHeader::AddEntry(
return status; return status;
} }
LOGI("New usage entry: usage_entry_number = %u", *usage_entry_number); LOGI("usage_entry_number = %u", *usage_entry_number);
StoreTable(device_files_.get()); StoreTable(device_files_.get());
return NO_ERROR; return NO_ERROR;
} }
@@ -508,6 +448,105 @@ size_t UsageTableHeader::OfflineEntryCount() const {
EntryIsOfflineLicense); EntryIsOfflineLicense);
} }
bool UsageTableHeader::OpenSessionCheck(CryptoSession* const crypto_session) {
// The UsageTableHeader for the specified |requested_security_level_|
// MUST be initialized before any sessions are opened.
size_t session_count = 0;
const CdmResponseType status = crypto_session->GetNumberOfOpenSessions(
requested_security_level_, &session_count);
if (status != NO_ERROR || session_count > 0) {
LOGE(
"Cannot initialize usage table header with open crypto session: "
"status = %d, count = %zu",
static_cast<int>(status), session_count);
return false;
}
return true;
}
bool UsageTableHeader::CapacityCheck(CryptoSession* const crypto_session) {
// If the table is around capacity or if unlimited and the table is
// larger than the minimally required capacity, then a test must be
// performed to ensure that the usage table is not in a state which
// will prevent create operations.
const size_t capacity_threshold = HasUnlimitedTableCapacity()
? kMinimumUsageTableEntriesSupported
: potential_table_capacity();
if (usage_entry_info_.size() <= capacity_threshold) {
// No need to perform test if below capacity.
return true;
}
metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics();
if (metrics == nullptr) metrics = &alternate_crypto_metrics_;
// |local_crypto_session| points to an object whose scope is this
// method or a test object whose scope is the lifetime of this class
CryptoSession* local_crypto_session = test_crypto_session_.get();
std::unique_ptr<CryptoSession> scoped_crypto_session;
if (local_crypto_session == nullptr) {
scoped_crypto_session.reset(CryptoSession::MakeCryptoSession(metrics));
local_crypto_session = scoped_crypto_session.get();
}
CdmResponseType status =
local_crypto_session->Open(requested_security_level_);
if (status != NO_ERROR) {
LOGE("Failed to open crypto session for capacity test: sts = %d",
static_cast<int>(status));
return false;
}
uint32_t temporary_usage_entry_number;
status = AddEntry(local_crypto_session, true, kDummyKeySetId, kEmptyString,
kEmptyString, &temporary_usage_entry_number);
if (status != NO_ERROR) {
LOGE("Failed to add entry for capacity test: sts = %d",
static_cast<int>(status));
return false;
}
status =
InvalidateEntry(temporary_usage_entry_number,
/* defrag_table = */ true, device_files_.get(), metrics);
if (status != NO_ERROR) {
LOGE("Failed to invalidate entry for capacity test: sts = %d",
static_cast<int>(status));
return false;
}
if (usage_entry_info_.size() > temporary_usage_entry_number) {
// The entry should have been deleted from the usage table,
// not just marked as type unknown. Failure to call
// Shrink() may be an indicator of other issues.
LOGE("Failed to shrink table for capacity test");
return false;
}
return true;
}
bool UsageTableHeader::DetermineTableCapacity(CryptoSession* crypto_session) {
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("capacity = unlimited, security_level = %s",
CdmSecurityLevelToString(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("capacity = %zu, security_level = %s", potential_table_capacity_,
CdmSecurityLevelToString(security_level_));
}
return true;
}
CdmResponseType UsageTableHeader::MoveEntry( CdmResponseType UsageTableHeader::MoveEntry(
uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry,
uint32_t to_usage_entry_number, DeviceFiles* device_files, uint32_t to_usage_entry_number, DeviceFiles* device_files,

View File

@@ -44,9 +44,7 @@ using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray; using ::testing::UnorderedElementsAreArray;
namespace wvcdm { namespace wvcdm {
namespace { namespace {
const std::string kEmptyString; const std::string kEmptyString;
constexpr size_t kDefaultTableCapacity = 300; constexpr size_t kDefaultTableCapacity = 300;
@@ -451,6 +449,8 @@ class MockCryptoSession : public TestCryptoSession {
MOCK_METHOD2(UpdateUsageEntry, MOCK_METHOD2(UpdateUsageEntry,
CdmResponseType(CdmUsageTableHeader*, CdmUsageEntry*)); CdmResponseType(CdmUsageTableHeader*, CdmUsageEntry*));
MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t)); MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t));
MOCK_METHOD2(GetNumberOfOpenSessions,
CdmResponseType(SecurityLevel, size_t*));
// Fake method for testing. Having an EXPECT_CALL causes complexities // Fake method for testing. Having an EXPECT_CALL causes complexities
// for getting table capacity during initialization. // for getting table capacity during initialization.
@@ -566,6 +566,8 @@ class UsageTableHeaderTest : public WvCdmTestBase {
void Init(CdmSecurityLevel security_level, void Init(CdmSecurityLevel security_level,
const CdmUsageTableHeader& usage_table_header, const CdmUsageTableHeader& usage_table_header,
const std::vector<CdmUsageEntryInfo>& usage_entry_info_vector) { const std::vector<CdmUsageEntryInfo>& usage_entry_info_vector) {
EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(_, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(usage_table_header), .WillOnce(DoAll(SetArgPointee<0>(usage_table_header),
@@ -576,6 +578,12 @@ class UsageTableHeaderTest : public WvCdmTestBase {
EXPECT_TRUE(usage_table_header_->Init(security_level, crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(security_level, crypto_session_));
} }
void ExpectToDeleteUsageTableFiles() {
EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, DeleteUsageTableInfo()).WillOnce(Return(true));
}
MockDeviceFiles* device_files_; MockDeviceFiles* device_files_;
metrics::CryptoMetrics crypto_metrics_; metrics::CryptoMetrics crypto_metrics_;
MockCryptoSession* crypto_session_; MockCryptoSession* crypto_session_;
@@ -598,70 +606,17 @@ class UsageTableHeaderInitializationTest
public ::testing::WithParamInterface<CdmSecurityLevel> { public ::testing::WithParamInterface<CdmSecurityLevel> {
public: public:
static void SetUpTestCase() { InitVectorConstants(); } static void SetUpTestCase() { InitVectorConstants(); }
SecurityLevel GetSecurityLevel() const {
return (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
}
}; };
TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) { TEST_P(UsageTableHeaderInitializationTest, RestoreUsageTable_Success) {
const SecurityLevel security_level = const SecurityLevel security_level = GetSecurityLevel();
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader),
SetArgPointee<1>(kEmptyUsageEntryInfoVector),
SetArgPointee<2>(false), Return(false)));
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull())) GetNumberOfOpenSessions(security_level, NotNull()))
.WillOnce( .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
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, Upgrade_UnableToRetrieveLicenses) {
const SecurityLevel security_level =
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader),
SetArgPointee<1>(kEmptyUsageEntryInfoVector),
SetArgPointee<2>(false), Return(false)));
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(
DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR)));
// TODO: Why not being called?
// EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
}
TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveUsageInfo) {
const SecurityLevel security_level =
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader),
SetArgPointee<1>(kEmptyUsageEntryInfoVector),
SetArgPointee<2>(false), Return(false)));
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, UsageTableHeaderExists) {
const SecurityLevel security_level =
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
@@ -670,15 +625,172 @@ TEST_P(UsageTableHeaderInitializationTest, UsageTableHeaderExists) {
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// First attempt at initializing the table succeeds, however, attempting
// to reinitialize the usage table should result in a failure.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_FailureDueToReInit) {
const SecurityLevel security_level = GetSecurityLevel();
// First run, success.
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(kUsageEntryInfoVector),
SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
// Second run, failure.
EXPECT_FALSE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// Table MUST not be initialized before another session has been opened.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_FailureDueToExistingSessions) {
const SecurityLevel security_level = GetSecurityLevel();
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(1), Return(NO_ERROR)));
EXPECT_FALSE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// No existing usage table in storage, creating a new table succeeds.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_CreateNew_AfterRetrieveFails) {
const SecurityLevel security_level = GetSecurityLevel();
// Expectations for restore:
// 1) Fail to retrieve existing table file
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(Return(false));
// Expectations for create:
// 1) Existing table is destroyed (files etc.)
// 2) Create new header within OEMCrypto succeeds
// 3) Storing the table header succeeds
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(
DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
} }
TEST_P(UsageTableHeaderInitializationTest, UsageEntriesAtCapacity) { // A table exists in storage, but it cannot be loaded due to generation
// skew. Creating a new table succeeds.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_CreateNew_AfterLoadFails) {
const SecurityLevel security_level = GetSecurityLevel();
// Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto fails
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(kUsageEntryInfoVector),
SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW));
// Expectations for create:
// 1) Existing table is destroyed (files etc.)
// 2) Create new header within OEMCrypto succeeds
// 3) Storing the table header succeeds
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(
DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// No existing table in storage, and attempting to create a new usage
// table fails for unknown reason. Initialization MUST fail.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_CreateNew_CreateFails) {
const SecurityLevel security_level = GetSecurityLevel();
// Expectations for restore:
// 1) No table info file exists
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(Return(false));
// Expectations for create:
// 1) Create new header within OEMCrypto fails
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(Return(CREATE_USAGE_TABLE_ERROR));
EXPECT_FALSE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// No existing table in storage, attempting to create a new usage
// table succeeds; however, storing the new table header fails.
// Initialization MUST fail.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_CreateNew_StoreFails) {
const SecurityLevel security_level = GetSecurityLevel();
// Expectations for restore:
// 1) No table info file exists
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(Return(false));
// Expectations for create:
// 1) Create new header within OEMCrypto succeeds
// 2) Storing the table header fails
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(DoAll(SetArgPointee<1>(kUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(false));
EXPECT_FALSE(usage_table_header_->Init(GetParam(), crypto_session_));
}
// Restoring table succeeds, and the current table is at capacity.
// No special action needs to be taken.
TEST_P(UsageTableHeaderInitializationTest, RestoreUsageTable_AtCapacity) {
std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector; std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector;
usage_entries.resize(kDefaultTableCapacity); usage_entries.resize(kDefaultTableCapacity);
const SecurityLevel security_level = const SecurityLevel security_level = GetSecurityLevel();
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; // Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto succeeds
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
@@ -687,18 +799,25 @@ TEST_P(UsageTableHeaderInitializationTest, UsageEntriesAtCapacity) {
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
} }
// Restoring table succeeds, OEMCrypto does not specify an absolute
// capacity, existing table is under the minimum required capacity.
// No special action needs to be taken.
TEST_P(UsageTableHeaderInitializationTest, TEST_P(UsageTableHeaderInitializationTest,
UsageEntries_NoCapacity_UnderMinimum) { RestoreUsageTable_NoCapacity_UnderMinimum) {
crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited.
std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector; std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector;
constexpr size_t kHalfMinCapacity = kDefaultTableCapacity / 2; constexpr size_t kHalfMinCapacity = kDefaultTableCapacity / 2;
usage_entries.resize(kHalfMinCapacity); usage_entries.resize(kHalfMinCapacity);
const SecurityLevel security_level = const SecurityLevel security_level = GetSecurityLevel();
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; // Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto succeeds
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
@@ -707,136 +826,134 @@ TEST_P(UsageTableHeaderInitializationTest,
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .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_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
} }
TEST_P(UsageTableHeaderInitializationTest, UsageEntries_NoCapacity) { // Restoring table succeeds, OEMCrypto does not specify an absolute
// capacity, existing table is above the minimum required capacity.
// After restoring, the header class must check that new entries can
// be added. This capacity check succeeds.
TEST_P(UsageTableHeaderInitializationTest,
RestoreUsageTable_NoCapacity_AboveMinimum) {
crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited.
std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector; ASSERT_LT(kDefaultTableCapacity, kOverFullUsageEntryInfoVector.size());
usage_entries.resize(kDefaultTableCapacity); const size_t kTableStartSize = kOverFullUsageEntryInfoVector.size();
const SecurityLevel security_level = const SecurityLevel security_level = GetSecurityLevel();
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
// Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto succeeds
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), SetArgPointee<1>(kOverFullUsageEntryInfoVector),
Return(true))); SetArgPointee<2>(false), Return(true)));
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
// Expect an attempt to create a new entry. // Expectations for capacity check:
// 1) Open a new crypto session.
// 2) Creating a new usage entry within OEMCrypto succeeds
// 3) Header and usage entry is updated
// 4) Table state is stored
// 5) New session is closed
// 6) Table is shrunk by 1
// 7) Table state is stored
EXPECT_CALL(*crypto_session_, Open(security_level)) EXPECT_CALL(*crypto_session_, Open(security_level))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
const uint32_t expect_usage_entry_number = kDefaultTableCapacity; const uint32_t expect_usage_entry_number = kTableStartSize;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
StoreUsageTableInfo(kAnotherUsageTableHeader, SizeIs(kTableStartSize + 1)))
SizeIs(kDefaultTableCapacity + 1)))
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_CALL(*crypto_session_, ShrinkUsageTableHeader(
// Delete the entry after. security_level, kTableStartSize, NotNull()))
EXPECT_CALL(
*crypto_session_,
ShrinkUsageTableHeader(security_level, kDefaultTableCapacity, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader), .WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader),
Return(NO_ERROR))); Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_, StoreUsageTableInfo(kYetAnotherUsageTableHeader,
StoreUsageTableInfo(kYetAnotherUsageTableHeader, SizeIs(kTableStartSize)))
SizeIs(kDefaultTableCapacity)))
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
} }
// Restoring table succeeds, OEMCrypto does not specify an absolute
// capacity, existing table is above the minimum required capacity.
// After restoring, the header class must check that new entries can
// be added. This capacity check fails due to adding an entry.
// The result is a new usage table.
TEST_P(UsageTableHeaderInitializationTest, TEST_P(UsageTableHeaderInitializationTest,
UsageEntriesOverCapacity_AddEntryFails_UsageTableHeaderRecreated) { RestoreUsageTable_AboveCapacity_AddEntryFails) {
ASSERT_LT(kDefaultTableCapacity, kOverFullUsageEntryInfoVector.size());
const SecurityLevel security_level = GetSecurityLevel();
// Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto succeeds
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
SetArgPointee<1>(kOverFullUsageEntryInfoVector), SetArgPointee<1>(kOverFullUsageEntryInfoVector),
SetArgPointee<2>(false), Return(true))); SetArgPointee<2>(false), Return(true)));
const SecurityLevel security_level =
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
EXPECT_CALL(*crypto_session_, Open(security_level))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull()))
.WillOnce(
DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR)));
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_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(true));
// Expectations for AddEntry // Expectations for capacity check:
const uint32_t expect_usage_entry_number = // 1) Open a new crypto session.
kOverFullUsageEntryInfoVector.size(); // 2) Creating a new usage entry within OEMCrypto fails
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
}
TEST_P(UsageTableHeaderInitializationTest,
UsageEntries_NoCapacity_AddEntryFails_UsageTableHeaderRecreated) {
crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited.
std::vector<CdmUsageEntryInfo> 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)) EXPECT_CALL(*crypto_session_, Open(security_level))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)); .WillOnce(Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR));
// Expect clean up.
EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); // Expectations for create:
EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true)); // 1) Existing table is destroyed (files etc.)
EXPECT_CALL(*device_files_, DeleteUsageTableInfo()).WillOnce(Return(true)); // 2) Create new header within OEMCrypto succeeds
// Expect recreation of usage table. // 3) Storing the table header fails
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(security_level, NotNull())) CreateUsageTableHeader(security_level, NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR))); DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
kEmptyUsageEntryInfoVector)) kEmptyUsageEntryInfoVector))
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
} }
// Restoring table succeeds, OEMCrypto does not specify an absolute
// capacity, existing table is above the minimum required capacity.
// After restoring, the header class must check that new entries can
// be added. This capacity check fails due to invalidating an entry.
// The result is a new usage table.
TEST_P(UsageTableHeaderInitializationTest, TEST_P(UsageTableHeaderInitializationTest,
UsageEntriesOverCapacity_AddInvalidateEntrySucceeds) { RestoreUsageTable_NoCapacity_AboveMinimum_InvalidateEntryFails) {
// Capacity +2. crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited.
std::vector<CdmUsageEntryInfo> usage_entries = kOverFullUsageEntryInfoVector; ASSERT_LT(kDefaultTableCapacity, kOverFullUsageEntryInfoVector.size());
usage_entries.push_back(kDummyUsageEntryInfo); const size_t kTableStartSize = kOverFullUsageEntryInfoVector.size();
const SecurityLevel security_level = GetSecurityLevel();
const SecurityLevel security_level =
(GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault;
// Expectations for restore:
// 1) Existing table file is retrieved
// 2) Loading existing header within OEMCrypto succeeds
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(security_level, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
@@ -846,30 +963,42 @@ TEST_P(UsageTableHeaderInitializationTest,
LoadUsageTableHeader(security_level, kUsageTableHeader)) LoadUsageTableHeader(security_level, kUsageTableHeader))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
// Expectations for AddEntry // Expectations for capacity check:
const uint32_t expect_usage_entry_number = // 1) Open a new crypto session.
kOverFullUsageEntryInfoVector.size(); // 2) Creating a new usage entry within OEMCrypto succeeds.
// 3) Header and usage entry is updated
// 4) Table state is stored
// 5) New session is closed
// 6) Shrinking table fails
EXPECT_CALL(*crypto_session_, Open(security_level))
.WillOnce(Return(NO_ERROR));
const uint32_t expect_usage_entry_number = kTableStartSize;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, // Called twice due to defrag.
StoreUsageTableInfo(kAnotherUsageTableHeader, usage_entries)) EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
.WillOnce(Return(true)); SizeIs(kTableStartSize + 1)))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*crypto_session_, ShrinkUsageTableHeader(
security_level, kTableStartSize, NotNull()))
.WillOnce(Return(SHRINK_USAGE_TABLE_HEADER_UNKNOWN_ERROR));
// Expectations for InvalidateEntry, assumes no entry other entry is invalid. // Expectations for create:
EXPECT_CALL(*crypto_session_, Open(security_level)) // 1) Existing table is destroyed (files etc.)
.WillOnce(Return(NO_ERROR)); // 2) Create new header within OEMCrypto succeeds
// 3) Storing the table header succeeds
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
ShrinkUsageTableHeader(security_level, usage_entries.size() - 1, CreateUsageTableHeader(security_level, NotNull()))
NotNull())) .WillOnce(DoAll(SetArgPointee<1>(kYetAnotherUsageTableHeader),
.WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader),
Return(NO_ERROR))); Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_, StoreUsageTableInfo(kYetAnotherUsageTableHeader,
StoreUsageTableInfo(kYetAnotherUsageTableHeader, kEmptyUsageEntryInfoVector))
SizeIs(kOverFullUsageEntryInfoVector.size())))
.WillOnce(Return(true)); .WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_));
@@ -880,13 +1009,12 @@ INSTANTIATE_TEST_CASE_P(Cdm, UsageTableHeaderInitializationTest,
TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed_UnknownError) { TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed_UnknownError) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number; const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number),
Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR))); Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)));
uint32_t usage_entry_number = 0;
EXPECT_NE(NO_ERROR, EXPECT_NE(NO_ERROR,
usage_table_header_->AddEntry( usage_table_header_->AddEntry(
crypto_session_, crypto_session_,
@@ -898,13 +1026,12 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed_UnknownError) {
TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) { TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number; const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size() - 1;
uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size() - 1;
EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR)));
uint32_t usage_entry_number = 0;
EXPECT_NE(NO_ERROR, EXPECT_NE(NO_ERROR,
usage_table_header_->AddEntry( usage_table_header_->AddEntry(
crypto_session_, crypto_session_,
@@ -916,11 +1043,9 @@ TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) {
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number; const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector = std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector; kUsageEntryInfoVector;
expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1); expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1);
expect_usage_entry_info_vector[expect_usage_entry_number] = expect_usage_entry_info_vector[expect_usage_entry_number] =
kUsageEntryInfoOfflineLicense2; kUsageEntryInfoOfflineLicense2;
@@ -937,6 +1062,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
UnorderedElementsAreArray(expect_usage_entry_info_vector))) UnorderedElementsAreArray(expect_usage_entry_info_vector)))
.WillOnce(Return(true)); .WillOnce(Return(true));
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
usage_table_header_->AddEntry( usage_table_header_->AddEntry(
crypto_session_, crypto_session_,
@@ -949,8 +1075,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) {
TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number; const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size();
std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector = std::vector<CdmUsageEntryInfo> expect_usage_entry_info_vector =
kUsageEntryInfoVector; kUsageEntryInfoVector;
@@ -970,6 +1095,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
UnorderedElementsAreArray(expect_usage_entry_info_vector))) UnorderedElementsAreArray(expect_usage_entry_info_vector)))
.WillOnce(Return(true)); .WillOnce(Return(true));
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
usage_table_header_->AddEntry( usage_table_header_->AddEntry(
crypto_session_, crypto_session_,
@@ -982,8 +1108,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) {
TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number; const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
uint32_t next_usage_entry_number = kUsageEntryInfoVector.size();
size_t skip_usage_entries = 3; size_t skip_usage_entries = 3;
uint32_t expect_usage_entry_number = uint32_t expect_usage_entry_number =
next_usage_entry_number + skip_usage_entries; next_usage_entry_number + skip_usage_entries;
@@ -1006,6 +1131,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) {
kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2))) kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2)))
.WillOnce(Return(true)); .WillOnce(Return(true));
uint32_t usage_entry_number = 0;
EXPECT_EQ(NO_ERROR, EXPECT_EQ(NO_ERROR,
usage_table_header_->AddEntry( usage_table_header_->AddEntry(
crypto_session_, crypto_session_,
@@ -1123,7 +1249,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) {
TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number = kUsageEntryInfoVector.size() + 3; const uint32_t usage_entry_number = kUsageEntryInfoVector.size() + 3;
EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry( EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry(
crypto_session_, kUsageEntry, usage_entry_number)); crypto_session_, kUsageEntry, usage_entry_number));
@@ -1131,7 +1257,7 @@ TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) {
TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) { TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number = 1; const uint32_t usage_entry_number = 1;
EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry))
.WillOnce(Return(LOAD_USAGE_ENTRY_GENERATION_SKEW)); .WillOnce(Return(LOAD_USAGE_ENTRY_GENERATION_SKEW));
@@ -1142,7 +1268,7 @@ TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) {
TEST_F(UsageTableHeaderTest, LoadEntry) { TEST_F(UsageTableHeaderTest, LoadEntry) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number = 1; const uint32_t usage_entry_number = 1;
EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry))
.WillOnce(Return(NO_ERROR)); .WillOnce(Return(NO_ERROR));
@@ -1184,7 +1310,7 @@ TEST_F(UsageTableHeaderTest, UpdateEntry) {
TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) { TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) {
Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector);
uint32_t usage_entry_number = kUsageEntryInfoVector.size(); const uint32_t usage_entry_number = kUsageEntryInfoVector.size();
metrics::CryptoMetrics metrics; metrics::CryptoMetrics metrics;
EXPECT_NE(NO_ERROR, usage_table_header_->InvalidateEntry( EXPECT_NE(NO_ERROR, usage_table_header_->InvalidateEntry(
@@ -3203,6 +3329,9 @@ TEST_F(UsageTableHeaderTest, StaleHeader) {
ToVector(usage_entry_info_vector, usage_entry_info_array, ToVector(usage_entry_info_vector, usage_entry_info_array,
sizeof(usage_entry_info_array)); sizeof(usage_entry_info_array));
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader),
@@ -3211,14 +3340,12 @@ TEST_F(UsageTableHeaderTest, StaleHeader) {
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUsageTableHeader)) LoadUsageTableHeader(kLevelDefault, kUsageTableHeader))
.WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW)); .WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW));
ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(kLevelDefault, NotNull())) CreateUsageTableHeader(kLevelDefault, NotNull()))
.WillOnce( .WillOnce(
DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR))); DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader,
EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, DeleteUsageTableInfo()).WillOnce(Return(true));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader,
kEmptyUsageEntryInfoVector)) kEmptyUsageEntryInfoVector))
.WillOnce(Return(true)); .WillOnce(Return(true));
@@ -3302,6 +3429,9 @@ TEST_F(UsageTableHeaderTest, Shrink_MoreThanTable) {
// the table header and entries are configured for LRU. // the table header and entries are configured for LRU.
// 2. No upgrading action is taken. // 2. No upgrading action is taken.
TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) { TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) {
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
@@ -3329,6 +3459,9 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) {
// 2. The usage table header will load license or usage information to // 2. The usage table header will load license or usage information to
// determine appropriate expiry and last_used times. // determine appropriate expiry and last_used times.
TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) { TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) {
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
@@ -3395,6 +3528,9 @@ TEST_F(UsageTableHeaderTest,
upgraded_usage_entry_info_list[2].storage_type = kStorageTypeUnknown; upgraded_usage_entry_info_list[2].storage_type = kStorageTypeUnknown;
// Load table expectations. // Load table expectations.
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
@@ -3455,6 +3591,9 @@ TEST_F(UsageTableHeaderTest,
wrond_typed_usage_entry_info.last_use_time = 0; wrond_typed_usage_entry_info.last_use_time = 0;
wrond_typed_usage_entry_info.offline_license_expiry_time = 0; wrond_typed_usage_entry_info.offline_license_expiry_time = 0;
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
@@ -3510,6 +3649,9 @@ TEST_F(UsageTableHeaderTest,
// that the table has not been configured for upgrade. // that the table has not been configured for upgrade.
// 2. None of the entries can have their license info loaded. // 2. None of the entries can have their license info loaded.
TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) { TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) {
EXPECT_CALL(*crypto_session_,
GetNumberOfOpenSessions(kLevelDefault, NotNull()))
.WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, EXPECT_CALL(*device_files_,
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader),
@@ -3539,29 +3681,20 @@ TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) {
// After failure, these will be called to clear files and create a new // After failure, these will be called to clear files and create a new
// usage table header. // usage table header.
EXPECT_CALL(*device_files_, DeleteAllLicenses()); ExpectToDeleteUsageTableFiles();
EXPECT_CALL(*device_files_, DeleteAllUsageInfo());
EXPECT_CALL(*device_files_, DeleteUsageTableInfo());
EXPECT_CALL(*crypto_session_, EXPECT_CALL(*crypto_session_,
CreateUsageTableHeader(kLevelDefault, NotNull())) CreateUsageTableHeader(kLevelDefault, NotNull()))
.WillOnce(Return(NO_ERROR)); .WillOnce(DoAll(SetArgPointee<1>(kUsageTableHeader), Return(NO_ERROR)));
EXPECT_CALL(*device_files_, StoreUsageTableInfo(_, _)); EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader,
kEmptyUsageEntryInfoVector))
.WillOnce(Return(true));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
} }
TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) { TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) {
// General setup. Init(kSecurityLevelL1, kUpgradedUsageTableHeader,
EXPECT_CALL(*device_files_, kUpgradedUsageEntryInfoList);
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradedUsageTableHeader),
SetArgPointee<1>(kUpgradedUsageEntryInfoList),
SetArgPointee<2>(/* lru_upgrade = */ false),
Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUpgradedUsageTableHeader))
.WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
// Expected values. // Expected values.
const uint32_t expected_usage_entry_number = const uint32_t expected_usage_entry_number =
@@ -3598,17 +3731,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) {
} }
TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) { TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) {
// General setup. Init(kSecurityLevelL1, kUpgradedUsageTableHeader,
EXPECT_CALL(*device_files_, kUpgradedUsageEntryInfoList);
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradedUsageTableHeader),
SetArgPointee<1>(kUpgradedUsageEntryInfoList),
SetArgPointee<2>(/* lru_upgrade = */ false),
Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUpgradedUsageTableHeader))
.WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
// Expected values. // Expected values.
const uint32_t expected_usage_entry_number = const uint32_t expected_usage_entry_number =
@@ -3646,17 +3770,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) {
} }
TEST_F(UsageTableHeaderTest, LruLastUsedTime_UpdateEntry) { TEST_F(UsageTableHeaderTest, LruLastUsedTime_UpdateEntry) {
// General setup. Init(kSecurityLevelL1, kUpgradedUsageTableHeader,
EXPECT_CALL(*device_files_, kUpgradedUsageEntryInfoList);
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradedUsageTableHeader),
SetArgPointee<1>(kUpgradedUsageEntryInfoList),
SetArgPointee<2>(/* lru_upgrade = */ false),
Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUpgradedUsageTableHeader))
.WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
std::vector<CdmUsageEntryInfo> expected_usage_info_list = std::vector<CdmUsageEntryInfo> expected_usage_info_list =
kUpgradedUsageEntryInfoList; kUpgradedUsageEntryInfoList;
@@ -3689,17 +3804,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_UpdateEntry) {
} }
TEST_F(UsageTableHeaderTest, LruLastUsedTime_LoadEntry) { TEST_F(UsageTableHeaderTest, LruLastUsedTime_LoadEntry) {
// General setup. Init(kSecurityLevelL1, kUpgradedUsageTableHeader,
EXPECT_CALL(*device_files_, kUpgradedUsageEntryInfoList);
RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull()))
.WillOnce(DoAll(SetArgPointee<0>(kUpgradedUsageTableHeader),
SetArgPointee<1>(kUpgradedUsageEntryInfoList),
SetArgPointee<2>(/* lru_upgrade = */ false),
Return(true)));
EXPECT_CALL(*crypto_session_,
LoadUsageTableHeader(kLevelDefault, kUpgradedUsageTableHeader))
.WillOnce(Return(NO_ERROR));
EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_));
std::vector<CdmUsageEntryInfo> expected_usage_info_list = std::vector<CdmUsageEntryInfo> expected_usage_info_list =
kUpgradedUsageEntryInfoList; kUpgradedUsageEntryInfoList;
@@ -4091,5 +4197,4 @@ TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Available) {
EXPECT_EQ(usage_table_header_->potential_table_capacity(), kTableCapacity); EXPECT_EQ(usage_table_header_->potential_table_capacity(), kTableCapacity);
EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity()); EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity());
} }
} // namespace wvcdm } // namespace wvcdm