diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 6ed570be..4d2e9371 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -293,7 +293,7 @@ class CryptoSession { // exist as long as the new CryptoSession exists. explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); - int session_count() { return session_count_; } + int session_count() const { return session_count_; } private: friend class CryptoSessionForTest; @@ -313,6 +313,13 @@ class CryptoSession { } 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 GetTokenFromOemCert(std::string* token); 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 // preference to taking the mutexes directly. // - // A lock should be taken on the Static Field Mutex before accessing any of - // CryptoSession's non-atomic static fields. It can be taken as a reader or as - // a writer, depending on how you will be accessing the static fields. + // A lock should be taken on the Static Field Mutex before accessing + // any of CryptoSession's non-atomic static fields with the exception + // 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 // 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 oem_crypto_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 int session_count_; @@ -446,8 +462,10 @@ class CryptoSession { // Open session-cached result of OEMCrypto_SupportsUsageTable(). CachedBooleanProperty has_usage_info_support_ = kBooleanUnset; UsageTableHeader* usage_table_header_ = nullptr; - static UsageTableHeader* usage_table_header_l1_; - static UsageTableHeader* usage_table_header_l3_; + // These fields are protected by |usage_table_mutex_| and not + // |static_field_mutex_|. + static std::unique_ptr usage_table_header_l1_; + static std::unique_ptr usage_table_header_l3_; std::string request_id_; static std::atomic request_id_index_source_; diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index ab5ac80e..9f7dc34f 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -53,8 +53,12 @@ class UsageTableHeader { UsageTableHeader(); virtual ~UsageTableHeader() {} - // |crypto_session| is used to create or load a usage master table and - // not cached beyound this call. + // |crypto_session| is used to create or load a usage master table + // 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); // |persistent_license| false indicates usage info record @@ -125,6 +129,32 @@ class UsageTableHeader { } 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 */, const CdmUsageEntry& from_usage_entry, uint32_t to /* usage entry number */, @@ -153,8 +183,6 @@ class UsageTableHeader { // evicted. CdmResponseType ReleaseOldestEntry(metrics::CryptoMetrics* metrics); - virtual bool is_inited() { return is_inited_; } - // Performs and LRU upgrade on all loaded CdmUsageEntryInfo from a // device file that had not yet been upgraded to use the LRU data. virtual bool LruUpgradeAllUsageEntries(); @@ -213,16 +241,15 @@ class UsageTableHeader { // by CdmSession. std::unique_ptr device_files_; std::unique_ptr file_system_; - CdmSecurityLevel security_level_; - SecurityLevel requested_security_level_; + CdmSecurityLevel security_level_ = kSecurityLevelUninitialized; + SecurityLevel requested_security_level_ = kLevelDefault; CdmUsageTableHeader usage_table_header_; std::vector usage_entry_info_; - // Lock to ensure that a single object is created for each security level - // and data member to represent whether an object has been correctly - // initialized. - bool is_inited_; + // Table is sync with persistent storage and can be used by the CDM + // to interact with OEMCrypto. + bool is_initialized_ = false; // Synchonizes access to the Usage Table Header and bookkeeping // data-structures diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index ac39dc6f..051554f1 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -51,8 +51,9 @@ } namespace wvcdm { - namespace { +using UsageTableLock = std::unique_lock; + constexpr size_t KiB = 1024; constexpr size_t MiB = 1024 * 1024; @@ -166,20 +167,22 @@ size_t GenericEncryptionBlockSize(CdmEncryptionAlgorithm algorithm) { } } // namespace +// CryptoSession variables allocation. shared_mutex CryptoSession::static_field_mutex_; shared_mutex CryptoSession::oem_crypto_mutex_; bool CryptoSession::initialized_ = false; int CryptoSession::session_count_ = 0; int CryptoSession::termination_counter_ = 0; -UsageTableHeader* CryptoSession::usage_table_header_l1_ = nullptr; -UsageTableHeader* CryptoSession::usage_table_header_l3_ = nullptr; +std::unique_ptr CryptoSession::usage_table_header_l1_; +std::unique_ptr CryptoSession::usage_table_header_l3_; +std::recursive_mutex CryptoSession::usage_table_mutex_; std::atomic CryptoSession::request_id_index_source_(0); size_t GetOffset(std::string message, std::string field) { size_t pos = message.find(field); if (pos == std::string::npos) { LOGE("Cannot find the |field| offset in message: field = %s", - field.c_str()); + IdToString(field)); pos = 0; } return pos; @@ -195,7 +198,7 @@ OEMCrypto_Substring GetSubstring(const std::string& message, size_t pos = message.find(field); if (pos == std::string::npos) { LOGW("Cannot find the |field| substring in message: field = %s", - field.c_str()); + IdToString(field)); substring.offset = 0; substring.length = 0; } else { @@ -359,7 +362,7 @@ void CryptoSession::Init() { bool CryptoSession::TryTerminate() { LOGV("Terminating crypto session"); - WithStaticFieldWriteLock("TryTerminate", [&] { + const bool terminated = WithStaticFieldWriteLock("TryTerminate", [&] { LOGV( "Terminating crypto session: initialized_ = %s, session_count_ = %d, " "termination_counter_ = %d", @@ -371,25 +374,20 @@ bool CryptoSession::TryTerminate() { if (session_count_ > 0 || termination_counter_ > 0 || !initialized_) return false; - OEMCryptoResult sts; - WithOecWriteLock("Terminate", [&] { sts = OEMCrypto_Terminate(); }); + const OEMCryptoResult sts = + WithOecWriteLock("Terminate", [&] { return OEMCrypto_Terminate(); }); if (OEMCrypto_SUCCESS != sts) { LOGE("OEMCrypto_Terminate failed: status = %d", static_cast(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; 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() { @@ -398,7 +396,63 @@ void CryptoSession::DisableDelayedTermination() { [&] { 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(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& 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) { + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); RETURN_IF_NULL(token, PARAMETER_NULL); 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) { + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); RETURN_IF_NULL(token, PARAMETER_NULL); OEMCryptoResult status; @@ -752,9 +807,16 @@ uint8_t CryptoSession::GetSecurityPatchLevel() { CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { LOGD("Opening crypto session: requested_security_level = %s", SecurityLevelToString(requested_security_level)); - RETURN_IF_UNINITIALIZED(UNKNOWN_ERROR); + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); 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, &pre_provision_token_type_); 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_)); }); - // 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_)) { LOGE("Failed to get API version"); 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 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; } @@ -889,13 +891,13 @@ void CryptoSession::Close() { open_ ? "true" : "false"); if (!open_) return; - OEMCryptoResult close_sts; - WithOecWriteLock( - "Close", [&] { close_sts = OEMCrypto_CloseSession(oec_session_id_); }); + const OEMCryptoResult close_sts = WithOecWriteLock( + "Close", [&] { return OEMCrypto_CloseSession(oec_session_id_); }); metrics_->oemcrypto_close_session_.Increment(close_sts); // Clear cached values. has_usage_info_support_ = kBooleanUnset; + oem_token_.clear(); if (close_sts != OEMCrypto_SUCCESS) { LOGW("OEMCrypto_CloseSession failed: status = %d", @@ -905,6 +907,7 @@ void CryptoSession::Close() { case OEMCrypto_SUCCESS: case OEMCrypto_ERROR_INVALID_SESSION: case OEMCrypto_ERROR_SYSTEM_INVALIDATED: + usage_table_header_ = nullptr; open_ = false; break; case OEMCrypto_ERROR_CLOSE_SESSION_FAILED: @@ -2488,14 +2491,13 @@ CdmResponseType CryptoSession::LoadUsageTableHeader( LOGV("Loading usage table header: requested_security_level = %s", SecurityLevelToString(requested_security_level)); - OEMCryptoResult result; - WithOecWriteLock("LoadUsageTableHeader", [&] { - result = OEMCrypto_LoadUsageTableHeader( + const OEMCryptoResult result = WithOecWriteLock("LoadUsageTableHeader", [&] { + return OEMCrypto_LoadUsageTableHeader( requested_security_level, reinterpret_cast(usage_table_header.data()), 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_WARNING_GENERATION_SKEW) { diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index 919c0ea5..7cd7fb95 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -15,12 +15,9 @@ namespace wvcdm { namespace { -std::string kEmptyString; -wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid"; -std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0); -std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0); -std::string kOldUsageEntryPoviderSessionToken = - "nahZ6achSheiqua3TohQuei0ahwohv"; +const std::string kEmptyString; +const wvcdm::CdmKeySetId kDummyKeySetId = "DummyKsid"; + constexpr int64_t kDefaultExpireDuration = 33 * 24 * 60 * 60; // 33 Days // Fraction of table capacity of number of unexpired offline licenses // before they are considered to be removed. This could occur if @@ -141,13 +138,19 @@ bool EntryIsOfflineLicense(const CdmUsageEntryInfo& info) { // Used for stl filters. 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 -UsageTableHeader::UsageTableHeader() - : security_level_(kSecurityLevelUninitialized), - requested_security_level_(kLevelDefault), - is_inited_(false), - clock_ref_(&clock_) { +UsageTableHeader::UsageTableHeader() : clock_ref_(&clock_) { file_system_.reset(new FileSystem()); device_files_.reset(new DeviceFiles(file_system_.get())); } @@ -159,152 +162,89 @@ bool UsageTableHeader::Init(CdmSecurityLevel security_level, LOGE("No crypto session provided"); return false; } - - switch (security_level) { - case kSecurityLevelL1: - case kSecurityLevelL3: - break; - default: - LOGE("Invalid security level provided: security_level = %d", - static_cast(security_level)); - return false; + if (is_initialized_) { + LOGE("Cannot reinitialize usage table: security_level = %s", + CdmSecurityLevelToString(security_level)); + return false; + } + if (!IsValidCdmSecurityLevelForUsageInfo(security_level)) { + LOGE("Invalid security level provided: security_level = %d", + static_cast(security_level)); + return false; } security_level_ = security_level; - requested_security_level_ = - security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; + requested_security_level_ = CdmSecurityLevelToRequestedLevel(security_level); - if (!crypto_session->GetMaximumUsageTableEntries( - requested_security_level_, &potential_table_capacity_)) { - LOGW( - "Could not determine usage table capacity, assuming default: " - "default = %zu", - kMinimumUsageTableEntriesSupported); - potential_table_capacity_ = kMinimumUsageTableEntriesSupported; - } else if (potential_table_capacity_ == 0) { - LOGD("Usage table capacity is unlimited: security_level = %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 (!OpenSessionCheck(crypto_session)) { + return false; + } + if (!DetermineTableCapacity(crypto_session)) { + return false; } - if (!device_files_->Init(security_level)) { LOGE("Failed to initialize device files"); 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; - metrics::CryptoMetrics* metrics = crypto_session->GetCryptoMetrics(); - if (metrics == nullptr) metrics = &alternate_crypto_metrics_; - +bool UsageTableHeader::RestoreTable(CryptoSession* const crypto_session) { bool run_lru_upgrade = false; - if (device_files_->RetrieveUsageTableInfo( + if (!device_files_->RetrieveUsageTableInfo( &usage_table_header_, &usage_entry_info_, &run_lru_upgrade)) { - LOGI("Number of usage entries: %zu", usage_entry_info_.size()); - status = crypto_session->LoadUsageTableHeader(requested_security_level_, - usage_table_header_); + LOGW("Could not retrieve usage table"); + return false; + } + LOGI("Found usage table to restore: entry_count = %zu", + usage_entry_info_.size()); - bool lru_success = true; - if (status == NO_ERROR && run_lru_upgrade) { - // If the loaded table info does not contain LRU information, then - // the information must be added immediately before being used. - if (!LruUpgradeAllUsageEntries()) { - LOGE( - "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 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(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()); + const CdmResponseType status = crypto_session->LoadUsageTableHeader( + requested_security_level_, usage_table_header_); + if (status != NO_ERROR) { + LOGE("Failed to load usage table header: sts = %d", + static_cast(status)); + return false; } - 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; } @@ -395,7 +335,7 @@ CdmResponseType UsageTableHeader::AddEntry( 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()); return NO_ERROR; } @@ -508,6 +448,105 @@ size_t UsageTableHeader::OfflineEntryCount() const { 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(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 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(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(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(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( uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, uint32_t to_usage_entry_number, DeviceFiles* device_files, diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp index 7f86e8c8..fb0b145b 100644 --- a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -44,9 +44,7 @@ using ::testing::UnorderedElementsAre; using ::testing::UnorderedElementsAreArray; namespace wvcdm { - namespace { - const std::string kEmptyString; constexpr size_t kDefaultTableCapacity = 300; @@ -451,6 +449,8 @@ class MockCryptoSession : public TestCryptoSession { MOCK_METHOD2(UpdateUsageEntry, CdmResponseType(CdmUsageTableHeader*, CdmUsageEntry*)); MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t)); + MOCK_METHOD2(GetNumberOfOpenSessions, + CdmResponseType(SecurityLevel, size_t*)); // Fake method for testing. Having an EXPECT_CALL causes complexities // for getting table capacity during initialization. @@ -566,6 +566,8 @@ class UsageTableHeaderTest : public WvCdmTestBase { void Init(CdmSecurityLevel security_level, const CdmUsageTableHeader& usage_table_header, const std::vector& usage_entry_info_vector) { + EXPECT_CALL(*crypto_session_, GetNumberOfOpenSessions(_, NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .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_)); } + 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_; metrics::CryptoMetrics crypto_metrics_; MockCryptoSession* crypto_session_; @@ -598,70 +606,17 @@ class UsageTableHeaderInitializationTest public ::testing::WithParamInterface { public: static void SetUpTestCase() { InitVectorConstants(); } + + SecurityLevel GetSecurityLevel() const { + return (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + } }; -TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) { - 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))); +TEST_P(UsageTableHeaderInitializationTest, RestoreUsageTable_Success) { + const SecurityLevel security_level = GetSecurityLevel(); 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, 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; + 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), @@ -670,15 +625,172 @@ TEST_P(UsageTableHeaderInitializationTest, UsageTableHeaderExists) { EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .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_)); } -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 usage_entries = kOverFullUsageEntryInfoVector; usage_entries.resize(kDefaultTableCapacity); - const SecurityLevel security_level = - (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + 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_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), @@ -687,18 +799,25 @@ TEST_P(UsageTableHeaderInitializationTest, UsageEntriesAtCapacity) { EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); - 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, - UsageEntries_NoCapacity_UnderMinimum) { + RestoreUsageTable_NoCapacity_UnderMinimum) { crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. std::vector usage_entries = kOverFullUsageEntryInfoVector; constexpr size_t kHalfMinCapacity = kDefaultTableCapacity / 2; usage_entries.resize(kHalfMinCapacity); - const SecurityLevel security_level = - (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + 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_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), @@ -707,136 +826,134 @@ TEST_P(UsageTableHeaderInitializationTest, EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); - // No expectations of creating or deleting entries if the number of entries - // is less than minimally required capacity. + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); } -TEST_P(UsageTableHeaderInitializationTest, UsageEntries_NoCapacity) { +// 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. - std::vector usage_entries = kOverFullUsageEntryInfoVector; - usage_entries.resize(kDefaultTableCapacity); - const SecurityLevel security_level = - (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + ASSERT_LT(kDefaultTableCapacity, kOverFullUsageEntryInfoVector.size()); + const size_t kTableStartSize = 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_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), - SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), - Return(true))); + SetArgPointee<1>(kOverFullUsageEntryInfoVector), + SetArgPointee<2>(false), Return(true))); EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); - // Expect an attempt to create a new entry. + // 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)) .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())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) .WillOnce( DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kAnotherUsageTableHeader, - SizeIs(kDefaultTableCapacity + 1))) + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, + SizeIs(kTableStartSize + 1))) .WillOnce(Return(true)); - - // Delete the entry after. - EXPECT_CALL( - *crypto_session_, - ShrinkUsageTableHeader(security_level, kDefaultTableCapacity, NotNull())) + EXPECT_CALL(*crypto_session_, ShrinkUsageTableHeader( + security_level, kTableStartSize, NotNull())) .WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kYetAnotherUsageTableHeader, - SizeIs(kDefaultTableCapacity))) + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kYetAnotherUsageTableHeader, + SizeIs(kTableStartSize))) .WillOnce(Return(true)); 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, - 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_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), SetArgPointee<1>(kOverFullUsageEntryInfoVector), 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_, LoadUsageTableHeader(security_level, kUsageTableHeader)) .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 - const uint32_t expect_usage_entry_number = - kOverFullUsageEntryInfoVector.size(); - EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), - Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR))); - - EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); -} - -TEST_P(UsageTableHeaderInitializationTest, - UsageEntries_NoCapacity_AddEntryFails_UsageTableHeaderRecreated) { - crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. - std::vector usage_entries = kOverFullUsageEntryInfoVector; - usage_entries.resize(kDefaultTableCapacity); - const SecurityLevel security_level = - (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; - EXPECT_CALL(*device_files_, - RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) - .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), - SetArgPointee<1>(usage_entries), SetArgPointee<2>(false), - Return(true))); - EXPECT_CALL(*crypto_session_, - LoadUsageTableHeader(security_level, kUsageTableHeader)) - .WillOnce(Return(NO_ERROR)); - // Try to create a new entry, and fail. + // Expectations for capacity check: + // 1) Open a new crypto session. + // 2) Creating a new usage entry within OEMCrypto fails EXPECT_CALL(*crypto_session_, Open(security_level)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce(Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR)); - // Expect clean up. - EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); - EXPECT_CALL(*device_files_, DeleteAllUsageInfo()).WillOnce(Return(true)); - EXPECT_CALL(*device_files_, DeleteUsageTableInfo()).WillOnce(Return(true)); - // Expect recreation of usage table. + + // Expectations for create: + // 1) Existing table is destroyed (files etc.) + // 2) Create new header within OEMCrypto succeeds + // 3) Storing the table header fails + ExpectToDeleteUsageTableFiles(); EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(security_level, NotNull())) .WillOnce( - DoAll(SetArgPointee<1>(kEmptyUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + 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_)); } +// 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, - UsageEntriesOverCapacity_AddInvalidateEntrySucceeds) { - // Capacity +2. - std::vector usage_entries = kOverFullUsageEntryInfoVector; - usage_entries.push_back(kDummyUsageEntryInfo); - - const SecurityLevel security_level = - (GetParam() == kSecurityLevelL3) ? kLevel3 : kLevelDefault; + RestoreUsageTable_NoCapacity_AboveMinimum_InvalidateEntryFails) { + crypto_session_->SetMaximumUsageTableEntries(0); // Unlimited. + ASSERT_LT(kDefaultTableCapacity, kOverFullUsageEntryInfoVector.size()); + const size_t kTableStartSize = 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_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), @@ -846,30 +963,42 @@ TEST_P(UsageTableHeaderInitializationTest, LoadUsageTableHeader(security_level, kUsageTableHeader)) .WillOnce(Return(NO_ERROR)); - // Expectations for AddEntry - const uint32_t expect_usage_entry_number = - kOverFullUsageEntryInfoVector.size(); + // 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) 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())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) .WillOnce( DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kAnotherUsageTableHeader, usage_entries)) - .WillOnce(Return(true)); + // Called twice due to defrag. + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, + 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. - EXPECT_CALL(*crypto_session_, Open(security_level)) - .WillOnce(Return(NO_ERROR)); + // 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_, - ShrinkUsageTableHeader(security_level, usage_entries.size() - 1, - NotNull())) - .WillOnce(DoAll(SetArgPointee<2>(kYetAnotherUsageTableHeader), + CreateUsageTableHeader(security_level, NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(kYetAnotherUsageTableHeader), Return(NO_ERROR))); - EXPECT_CALL(*device_files_, - StoreUsageTableInfo(kYetAnotherUsageTableHeader, - SizeIs(kOverFullUsageEntryInfoVector.size()))) + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kYetAnotherUsageTableHeader, + kEmptyUsageEntryInfoVector)) .WillOnce(Return(true)); 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) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number; - uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); - + const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR))); + uint32_t usage_entry_number = 0; EXPECT_NE(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, @@ -898,13 +1026,12 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed_UnknownError) { TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number; - uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size() - 1; - + const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size() - 1; EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + uint32_t usage_entry_number = 0; EXPECT_NE(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, @@ -916,11 +1043,9 @@ TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) { TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number; - uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); + const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); std::vector expect_usage_entry_info_vector = kUsageEntryInfoVector; - expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1); expect_usage_entry_info_vector[expect_usage_entry_number] = kUsageEntryInfoOfflineLicense2; @@ -937,6 +1062,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { UnorderedElementsAreArray(expect_usage_entry_info_vector))) .WillOnce(Return(true)); + uint32_t usage_entry_number = 0; EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, @@ -949,8 +1075,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number; - uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); + const uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); std::vector expect_usage_entry_info_vector = kUsageEntryInfoVector; @@ -970,6 +1095,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { UnorderedElementsAreArray(expect_usage_entry_info_vector))) .WillOnce(Return(true)); + uint32_t usage_entry_number = 0; EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, @@ -982,8 +1108,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number; - uint32_t next_usage_entry_number = kUsageEntryInfoVector.size(); + const uint32_t next_usage_entry_number = kUsageEntryInfoVector.size(); size_t skip_usage_entries = 3; uint32_t expect_usage_entry_number = next_usage_entry_number + skip_usage_entries; @@ -1006,6 +1131,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2))) .WillOnce(Return(true)); + uint32_t usage_entry_number = 0; EXPECT_EQ(NO_ERROR, usage_table_header_->AddEntry( crypto_session_, @@ -1123,7 +1249,7 @@ TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailsEveryTime) { TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { 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( crypto_session_, kUsageEntry, usage_entry_number)); @@ -1131,7 +1257,7 @@ TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) { 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)) .WillOnce(Return(LOAD_USAGE_ENTRY_GENERATION_SKEW)); @@ -1142,7 +1268,7 @@ TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) { TEST_F(UsageTableHeaderTest, LoadEntry) { 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)) .WillOnce(Return(NO_ERROR)); @@ -1184,7 +1310,7 @@ TEST_F(UsageTableHeaderTest, UpdateEntry) { TEST_F(UsageTableHeaderTest, InvalidateEntry_InvalidUsageEntryNumber) { Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); - uint32_t usage_entry_number = kUsageEntryInfoVector.size(); + const uint32_t usage_entry_number = kUsageEntryInfoVector.size(); metrics::CryptoMetrics metrics; 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, sizeof(usage_entry_info_array)); + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), @@ -3211,14 +3340,12 @@ TEST_F(UsageTableHeaderTest, StaleHeader) { EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kLevelDefault, kUsageTableHeader)) .WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW)); + ExpectToDeleteUsageTableFiles(); EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(kLevelDefault, 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, + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kAnotherUsageTableHeader, kEmptyUsageEntryInfoVector)) .WillOnce(Return(true)); @@ -3302,6 +3429,9 @@ TEST_F(UsageTableHeaderTest, Shrink_MoreThanTable) { // the table header and entries are configured for LRU. // 2. No upgrading action is taken. TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_NoAction) { + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .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 // determine appropriate expiry and last_used times. TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_Succeed) { + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), @@ -3395,6 +3528,9 @@ TEST_F(UsageTableHeaderTest, upgraded_usage_entry_info_list[2].storage_type = kStorageTypeUnknown; // Load table expectations. + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .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.offline_license_expiry_time = 0; + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(kUpgradableUsageTableHeader), @@ -3510,6 +3649,9 @@ TEST_F(UsageTableHeaderTest, // that the table has not been configured for upgrade. // 2. None of the entries can have their license info loaded. TEST_F(UsageTableHeaderTest, LruUsageTableUpgrade_AllFailure) { + EXPECT_CALL(*crypto_session_, + GetNumberOfOpenSessions(kLevelDefault, NotNull())) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull(), NotNull())) .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 // usage table header. - EXPECT_CALL(*device_files_, DeleteAllLicenses()); - EXPECT_CALL(*device_files_, DeleteAllUsageInfo()); - EXPECT_CALL(*device_files_, DeleteUsageTableInfo()); + ExpectToDeleteUsageTableFiles(); EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(kLevelDefault, NotNull())) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(*device_files_, StoreUsageTableInfo(_, _)); + .WillOnce(DoAll(SetArgPointee<1>(kUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .WillOnce(Return(true)); EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); } TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) { - // General setup. - EXPECT_CALL(*device_files_, - 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_)); + Init(kSecurityLevelL1, kUpgradedUsageTableHeader, + kUpgradedUsageEntryInfoList); // Expected values. const uint32_t expected_usage_entry_number = @@ -3598,17 +3731,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateLicenseEntry) { } TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) { - // General setup. - EXPECT_CALL(*device_files_, - 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_)); + Init(kSecurityLevelL1, kUpgradedUsageTableHeader, + kUpgradedUsageEntryInfoList); // Expected values. const uint32_t expected_usage_entry_number = @@ -3646,17 +3770,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_CreateUsageInfoEntry) { } TEST_F(UsageTableHeaderTest, LruLastUsedTime_UpdateEntry) { - // General setup. - EXPECT_CALL(*device_files_, - 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_)); + Init(kSecurityLevelL1, kUpgradedUsageTableHeader, + kUpgradedUsageEntryInfoList); std::vector expected_usage_info_list = kUpgradedUsageEntryInfoList; @@ -3689,17 +3804,8 @@ TEST_F(UsageTableHeaderTest, LruLastUsedTime_UpdateEntry) { } TEST_F(UsageTableHeaderTest, LruLastUsedTime_LoadEntry) { - // General setup. - EXPECT_CALL(*device_files_, - 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_)); + Init(kSecurityLevelL1, kUpgradedUsageTableHeader, + kUpgradedUsageEntryInfoList); std::vector expected_usage_info_list = kUpgradedUsageEntryInfoList; @@ -4091,5 +4197,4 @@ TEST_F(UsageTableHeaderTest, PotentialTableCapacity_Available) { EXPECT_EQ(usage_table_header_->potential_table_capacity(), kTableCapacity); EXPECT_FALSE(usage_table_header_->HasUnlimitedTableCapacity()); } - } // namespace wvcdm