diff --git a/libwvdrmengine/build_and_run_all_unit_tests.sh b/libwvdrmengine/build_and_run_all_unit_tests.sh index a0df822a..9afdddd4 100755 --- a/libwvdrmengine/build_and_run_all_unit_tests.sh +++ b/libwvdrmengine/build_and_run_all_unit_tests.sh @@ -88,6 +88,7 @@ try_adb_push $OUT/vendor/bin/license_unittest try_adb_push $OUT/vendor/bin/license_keys_unittest try_adb_push $OUT/vendor/bin/initialization_data_unittest try_adb_push $OUT/vendor/bin/device_files_unittest +try_adb_push $OUT/vendor/bin/usage_table_header_unittest try_adb_push $OUT/vendor/bin/service_certificate_unittest try_adb_push $OUT/vendor/bin/timer_unittest try_adb_push $OUT/vendor/bin/libwvdrmengine_test diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index dd3d1d90..4321ca76 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -82,8 +82,7 @@ class CdmSession { // ReleaseKey() - Accept response and release key. virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); - virtual CdmResponseType DeleteUsageEntry( - const DeviceFiles::CdmUsageData& usage_data); + virtual CdmResponseType DeleteUsageEntry(uint32_t usage_entry_number); virtual bool IsKeyLoaded(const KeyId& key_id); virtual int64_t GetDurationRemaining(); diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index b61ddc05..b6214789 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -17,6 +17,8 @@ namespace wvcdm { class CryptoKey; +class UsageTableHeader; + typedef std::map CryptoKeyMap; class CryptoSession { @@ -142,6 +144,9 @@ class CryptoSession { const std::string& signature); // Usage table header and usage entry related methods + virtual UsageTableHeader* GetUsageTableHeader() { + return usage_table_header_; + } virtual CdmResponseType GetUsageSupportType(CdmUsageSupportType* type); virtual CdmResponseType CreateUsageTableHeader( CdmUsageTableHeader* usage_table_header); @@ -217,6 +222,9 @@ class CryptoSession { bool is_usage_support_type_valid_; CdmUsageSupportType usage_support_type_; + UsageTableHeader* usage_table_header_; + static UsageTableHeader* usage_table_header_l1_; + static UsageTableHeader* usage_table_header_l3_; uint64_t request_id_base_; static uint64_t request_id_index_; diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 3f7c4f95..ef15142b 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -21,6 +21,7 @@ namespace wvcdm { class Clock; class CryptoSession; class PolicyEngine; +class CdmSession; class CdmLicense { public: @@ -37,7 +38,8 @@ class CdmLicense { std::string* server_url); virtual CdmResponseType PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, - CdmKeyMessage* signed_request, std::string* server_url); + CdmSession* cdm_session, CdmKeyMessage* signed_request, + std::string* server_url); virtual CdmResponseType HandleKeyResponse( const CdmKeyResponse& license_response); virtual CdmResponseType HandleKeyUpdateResponse( diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h index 8276cfd4..3357a3c8 100644 --- a/libwvdrmengine/cdm/core/include/usage_table_header.h +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -7,36 +7,48 @@ #include #include "device_files.h" +#include "file_store.h" #include "lock.h" #include "metrics_group.h" #include "scoped_ptr.h" -#include "timer_metric.h" #include "wv_cdm_types.h" namespace wvcdm { -class FileSystem; class CryptoSession; -// The UsageTableHeader class is a singleton that CDM sessions will share. -// A separate object will be created for each security level. -// The class synchronizes access to usage table header and associated -// data-structures and controls when they are read in or written out to -// non-secure persistent storage. +// Offline licenses/secure stops may be securely tracked using usage +// tables (OEMCrypto v9-12) or usage table headers+usage entries +// (OEMCrypto v13+). This class assists with the latter, synchronizing +// access to usage table header and associated data-structures and controlling +// when they are read in or written out to non-secure persistent storage. +// +// Each OEMCrypto (for each security level) will maintain its own usage table +// header. Each license will have an associated usage entry that is also +// stored in persistent memory and is noted in the usage table header. +// Usage entry information will be verified when licenses are loaded. +// +// OEMCrypto for each security level have their own usage table +// headers. They are loaded on initialization and written out periodically. +// The lifecycle of this class is tied to when OEMCrypto is +// initialized/terminated. +// +// Sessions and licenses are however handled by CdmSession and so most +// calls to maniplate the usage table header related to usage entries +// are by CdmSession. +// // Upgrades from a fixed size usage table (supported by previous // versions of the OEMCrypto API v9-12) are handled by this class. // |usage_entry| and |usage_entry_number|s need to be saved in the license // and usage info records by the caller. class UsageTableHeader { public: - // This methods instantiates or retrieves a usage table header singleton of - // appropriate security level as specified by the |crypto_session| - // object. + UsageTableHeader(); + virtual ~UsageTableHeader() {} + // |crypto_session| is used to create or load a usage master table and // not cached beyound this call. - static UsageTableHeader* GetInstance(FileSystem* file_system, - CryptoSession* crypto_session_); - virtual ~UsageTableHeader() {} + bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session); // |persistent_license| false indicates usage info record CdmResponseType AddEntry(CryptoSession* crypto_session, @@ -53,54 +65,67 @@ class UsageTableHeader { // The licenses or usage info records specified by |usage_entry_number| // should not be in use by any open CryptoSession objects when calls // to DeleteEntry and MoveEntry are made. - CdmResponseType DeleteEntry(uint32_t usage_entry_number); - CdmResponseType MoveEntry(uint32_t from_usage_entry_number, - const CdmUsageEntry& from_usage_entry, - uint32_t to_usage_entry_number); + CdmResponseType DeleteEntry(uint32_t usage_entry_number, DeviceFiles* handle, + metrics::MetricsGroup* metrics); private: - UsageTableHeader(FileSystem* file_system, CryptoSession* crypto_session, - CdmSecurityLevel security_level); + CdmResponseType MoveEntry(uint32_t from /* usage entry number */, + const CdmUsageEntry& from_usage_entry, + uint32_t to /* usage entry number */, + DeviceFiles* handle, + metrics::MetricsGroup* metrics); - CdmResponseType GetEntry(uint32_t usage_entry_number, + CdmResponseType GetEntry(uint32_t usage_entry_number, DeviceFiles* handle, CdmUsageEntry* usage_entry); - CdmResponseType StoreEntry(uint32_t usage_entry_number, + CdmResponseType StoreEntry(uint32_t usage_entry_number, DeviceFiles* handle, const CdmUsageEntry& usage_entry); - bool DeleteLastEntry(); + CdmResponseType Shrink(metrics::MetricsGroup* metrics, + uint32_t number_of_usage_entries_to_delete); - CdmResponseType UpgradeFromUsageTable(); - bool UpgradeLicensesFromUsageTable(); - bool UpgradeUsageInfoFromUsageTable(); + CdmResponseType UpgradeFromUsageTable(DeviceFiles* handle, + metrics::MetricsGroup* metrics); + bool UpgradeLicensesFromUsageTable(DeviceFiles* handle, + metrics::MetricsGroup* metrics); + bool UpgradeUsageInfoFromUsageTable(DeviceFiles* handle, + metrics::MetricsGroup* metrics); virtual bool is_inited() { return is_inited_; } - SecurityLevel GetSecurityLevel() { - return security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; - } - - static UsageTableHeader* usage_table_header_l1_; - static UsageTableHeader* usage_table_header_l3_; - + // This handle and file system is only to be used when accessing + // usage_table_header. Usage entries should use the file system provided + // by CdmSession. scoped_ptr file_handle_; + scoped_ptr file_system_; CdmSecurityLevel security_level_; + SecurityLevel requested_security_level_; CdmUsageTableHeader usage_table_header_; std::vector usage_entry_info_; - metrics::MetricsGroup metrics_; - metrics::TimerMetric life_span_; - // 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_; - static Lock initialization_lock_; // Synchonizes access to the Usage Table Header and bookkeeping // data-structures Lock usage_table_header_lock_; + // Test related declarations + friend class UsageTableHeaderTest; + + // These setters are for testing only. Takes ownership of the pointers. + void SetDeviceFiles(DeviceFiles* device_files) { + file_handle_.reset(device_files); + } + void SetCryptoSession(CryptoSession* crypto_session) { + test_crypto_session_.reset(crypto_session); + } + + // Test related data members + scoped_ptr test_crypto_session_; + CORE_DISALLOW_COPY_AND_ASSIGN(UsageTableHeader); }; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index f69cb20f..2570336a 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -297,6 +297,7 @@ enum CdmResponseType { INCORRECT_USAGE_SUPPORT_TYPE_2, KEY_PROHIBITED_FOR_SECURITY_LEVEL, /* 255 */ KEY_NOT_FOUND_IN_SESSION, + NO_USAGE_ENTRIES, }; enum CdmKeyStatus { @@ -392,6 +393,12 @@ struct CdmUsageEntryInfo { CdmUsageEntryStorageType storage_type; CdmKeySetId key_set_id; std::string usage_info_file_name; + bool operator==(const CdmUsageEntryInfo& other) const { + return storage_type == other.storage_type && + key_set_id == other.key_set_id && + (storage_type != kStorageUsageInfo || + usage_info_file_name == other.usage_info_file_name); + } }; class CdmKeyAllowedUsage { diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index e761c9c8..556b5f94 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1132,7 +1132,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { } else { for (size_t k = 0; k < usage_data.size(); ++k) { CdmResponseType status2 = - usage_session_->DeleteUsageEntry(usage_data[k]); + usage_session_->DeleteUsageEntry( + usage_data[k].usage_entry_number); if (status == NO_ERROR && status2 != NO_ERROR) status = status2; } diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 2e7fcb60..0731b465 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -44,15 +44,6 @@ CdmSession::CdmSession(FileSystem* file_system) : usage_entry_number_(0), mock_license_parser_in_use_(false), mock_policy_engine_in_use_(false) { - CdmResponseType sts = - crypto_session_->GetUsageSupportType(&usage_support_type_); - - if (sts != NO_ERROR) { - LOGW("CdmSession::CdmSession: Failed to get usage support type"); - } - if (usage_support_type_ == kUsageEntrySupport) - usage_table_header_ = UsageTableHeader::GetInstance(file_system, - crypto_session_.get()); life_span_.Start(); } @@ -104,6 +95,13 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set, return SESSION_FILE_HANDLE_INIT_ERROR; } + if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) { + if (usage_support_type_ == kUsageEntrySupport) + usage_table_header_ = crypto_session_->GetUsageTableHeader(); + } else { + usage_support_type_ = kNonSecureUsageSupport; + } + // Device Provisioning state is not yet known. // If not using certificates, then Keybox is client token for license // requests. @@ -447,7 +445,8 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { !provider_session_token.empty()) { if (sts != KEY_ADDED) { CdmResponseType sts = - usage_table_header_->DeleteEntry(usage_entry_number_); + usage_table_header_->DeleteEntry(usage_entry_number_, + file_handle_.get(), &metrics_); if (sts != NO_ERROR) { LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts); } @@ -589,7 +588,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { CdmResponseType CdmSession::GenerateRenewalRequest( CdmKeyRequest* key_request) { CdmResponseType status = license_parser_->PrepareKeyUpdateRequest( - true, app_parameters_, &key_request->message, &key_request->url); + true, app_parameters_, NULL, &key_request->message, &key_request->url); key_request->type = kKeyRequestTypeRenewal; @@ -619,8 +618,8 @@ CdmResponseType CdmSession::GenerateReleaseRequest( CdmKeyRequest* key_request) { is_release_ = true; CdmResponseType status = license_parser_->PrepareKeyUpdateRequest( - false, app_parameters_, &key_request->message, - &key_request->url); + false, app_parameters_, usage_table_header_ == NULL ? NULL : this, + &key_request->message, &key_request->url); key_request->type = kKeyRequestTypeRelease; @@ -657,34 +656,50 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { if (is_offline_ || has_provider_session_token()) { DeleteLicense(); - // Deletion of usage entry cannot occur while in use by a crypto session. - // So close and reopen after deletion. if (usage_support_type_ == kUsageEntrySupport) { - crypto_session_->Close(); - CdmResponseType sts = usage_table_header_->DeleteEntry(usage_entry_number_); - if (sts != NO_ERROR) return sts; - - M_TIME( - sts = crypto_session_->Open(requested_security_level_), - &metrics_, - crypto_session_open_, - sts, - requested_security_level_); + sts = DeleteUsageEntry(usage_entry_number_); if (NO_ERROR != sts) return sts; } } return NO_ERROR; } -CdmResponseType CdmSession::DeleteUsageEntry( - const DeviceFiles::CdmUsageData& usage_data) { +CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) { if (usage_support_type_ != kUsageEntrySupport) { LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d", usage_support_type_); return INCORRECT_USAGE_SUPPORT_TYPE_1; } - return usage_table_header_->DeleteEntry(usage_data.usage_entry_number); + // The usage entry cannot be deleted if it has a crypto session handling + // it, so close and reopen session. + CdmResponseType sts; + crypto_session_->Close(); + crypto_session_.reset(new CryptoSession(&metrics_)); + M_TIME( + sts = crypto_session_->Open(requested_security_level_), + &metrics_, + crypto_session_open_, + sts, + requested_security_level_); + if (sts != NO_ERROR) return sts; + + usage_table_header_ = NULL; + if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) { + if (usage_support_type_ == kUsageEntrySupport) + usage_table_header_ = crypto_session_->GetUsageTableHeader(); + } else { + usage_support_type_ = kNonSecureUsageSupport; + } + + if (usage_table_header_ == NULL) { + LOGE("CdmSession::DeleteUsageEntry: Usage table header unavailable"); + return INCORRECT_USAGE_SUPPORT_TYPE_1; + } + + return usage_table_header_->DeleteEntry(usage_entry_number, + file_handle_.get(), + &metrics_); } bool CdmSession::IsKeyLoaded(const KeyId& key_id) { diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index f62cd347..29b34dd3 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -17,6 +17,7 @@ #include "properties.h" #include "pst_report.h" #include "string_conversions.h" +#include "usage_table_header.h" #include "wv_cdm_constants.h" namespace { @@ -41,6 +42,8 @@ Lock CryptoSession::crypto_lock_; bool CryptoSession::initialized_ = false; int CryptoSession::session_count_ = 0; uint64_t CryptoSession::request_id_index_ = 0; +UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL; +UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL; CryptoSession::CryptoSession(metrics::MetricsGroup* metrics) : metrics_(metrics), @@ -50,6 +53,7 @@ CryptoSession::CryptoSession(metrics::MetricsGroup* metrics) requested_security_level_(kLevelDefault), is_usage_support_type_valid_(false), usage_support_type_(kNonSecureUsageSupport), + usage_table_header_(NULL), request_id_base_(0), cipher_mode_(kCipherModeCtr) { Init(); @@ -120,6 +124,16 @@ void CryptoSession::Terminate() { if (OEMCrypto_SUCCESS != sts) { LOGE("OEMCrypto_Terminate failed: %d", sts); } + + if (usage_table_header_l1_ != NULL) { + delete usage_table_header_l1_; + usage_table_header_l1_ = NULL; + } + if (usage_table_header_l3_ != NULL) { + delete usage_table_header_l3_; + usage_table_header_l3_ = NULL; + } + initialized_ = false; } @@ -210,8 +224,7 @@ bool CryptoSession::GetProvisioningToken(std::string* token) { } CdmSecurityLevel CryptoSession::GetSecurityLevel() { - LOGV("CryptoSession::GetSecurityLevel: Lock"); - AutoLock auto_lock(crypto_lock_); + LOGV("CryptoSession::GetSecurityLevel"); if (!initialized_) { return kSecurityLevelUninitialized; } @@ -421,7 +434,8 @@ uint8_t CryptoSession::GetSecurityPatchLevel() { } CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { - LOGV("CryptoSession::Open: Lock"); + LOGV("CryptoSession::Open: Lock: requested_security_level: %d", + requested_security_level); AutoLock auto_lock(crypto_lock_); if (!initialized_) return UNKNOWN_ERROR; if (open_) return NO_ERROR; @@ -461,6 +475,35 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { random_sts, metrics::Pow2Bucket(sizeof(request_id_base_))); ++request_id_index_; + + CdmUsageSupportType usage_support_type; + if (GetUsageSupportType(&usage_support_type) == NO_ERROR) { + if (usage_support_type == kUsageEntrySupport) { + CdmSecurityLevel security_level = GetSecurityLevel(); + if (security_level == kSecurityLevelL1 || + security_level == kSecurityLevelL3) { + UsageTableHeader* header = security_level == kSecurityLevelL1 ? + usage_table_header_l1_ : usage_table_header_l3_; + if (header == NULL) { + 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. + if (!header->Init(security_level, this)) { + delete header; + usage_table_header_ = NULL; + return NO_ERROR; + } + if (security_level == kSecurityLevelL1) + usage_table_header_l1_ = header; + else + usage_table_header_l3_ = header; + } + usage_table_header_ = header; + } + } + } + return NO_ERROR; } @@ -1085,9 +1128,7 @@ CdmResponseType CryptoSession::UpdateUsageInformation() { AutoLock auto_lock(crypto_lock_); if (!initialized_) return UNKNOWN_ERROR; - CdmUsageSupportType usage_support_type; - if (GetUsageSupportType(&usage_support_type) == NO_ERROR && - usage_support_type == kUsageEntrySupport) { + if (usage_table_header_ != NULL) { LOGV("UpdateUsageInformation: deprecated for OEMCrypto v13+"); return NO_ERROR; } @@ -1248,6 +1289,11 @@ CdmResponseType CryptoSession::ReleaseUsageInformation( LOGV("ReleaseUsageInformation: id=%ld", (uint32_t)oec_session_id_); { AutoLock auto_lock(crypto_lock_); + if (usage_table_header_ != NULL) { + LOGW("ReleaseUsageInformation: deprecated for OEMCrypto v13+"); + return NO_ERROR; + } + const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* sig = reinterpret_cast(signature.data()); const uint8_t* pst = msg + GetOffset(message, provider_session_token); diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 276369bb..d1b659b2 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -7,6 +7,7 @@ #include #include "clock.h" +#include "cdm_session.h" #include "crypto_key.h" #include "crypto_session.h" #include "device_files.h" @@ -287,7 +288,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest( CdmResponseType CdmLicense::PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, - CdmKeyMessage* signed_request, std::string* server_url) { + CdmSession* cdm_session, CdmKeyMessage* signed_request, + std::string* server_url) { if (!initialized_) { LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_1; @@ -346,6 +348,12 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest( if (NO_ERROR != status) return status; } + // TODO(rfrias): Refactor to avoid needing to call CdmSession + if (cdm_session) { + CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); + if (NO_ERROR != status) return status; + } + std::string usage_report; CdmResponseType status = crypto_session_->GenerateUsageReport( provider_session_token_, &usage_report, &usage_duration_status, diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp index 11420d29..790aeffb 100644 --- a/libwvdrmengine/cdm/core/src/usage_table_header.cpp +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -5,6 +5,7 @@ #include "crypto_session.h" #include "license.h" #include "log.h" +#include "metrics_group.h" namespace { std::string kEmptyString; @@ -12,78 +13,66 @@ std::string kEmptyString; namespace wvcdm { -UsageTableHeader* UsageTableHeader::usage_table_header_l1_ = NULL; -UsageTableHeader* UsageTableHeader::usage_table_header_l3_ = NULL; - -Lock UsageTableHeader::initialization_lock_; - -UsageTableHeader* UsageTableHeader::GetInstance(FileSystem* file_system, - CryptoSession* crypto_session) { - LOGV("UsageTableHeader::GetInstance"); - AutoLock auto_lock(initialization_lock_); - CdmSecurityLevel security_level = crypto_session->GetSecurityLevel(); - switch (security_level) { - case kSecurityLevelL1: - if (usage_table_header_l1_ != NULL) - return usage_table_header_l1_; - break; - case kSecurityLevelL3: - if (usage_table_header_l3_ != NULL) - return usage_table_header_l3_; - break; - default: - LOGE("UsageTableHeader::GetInstance: unsupported security level: %d", - security_level); - return NULL; - } - - UsageTableHeader* header = - new UsageTableHeader(file_system, crypto_session, security_level); - if (!header->is_inited()) { - delete header; - return NULL; - } - - if (security_level == kSecurityLevelL1) - usage_table_header_l1_ = header; - else - usage_table_header_l3_ = header; - return header; +UsageTableHeader::UsageTableHeader() + : security_level_(kSecurityLevelUninitialized), + requested_security_level_(kLevelDefault), + is_inited_(false) { + file_system_.reset(new FileSystem()); + file_handle_.reset(new DeviceFiles(file_system_.get())); } -UsageTableHeader::UsageTableHeader(FileSystem* file_system, - CryptoSession* crypto_session, - CdmSecurityLevel security_level) - : file_handle_(new DeviceFiles(file_system)), - security_level_(security_level), - is_inited_(false) { - LOGV("UsageTableHeader::UsageTablerHeader: security level: %d", - security_level); +bool UsageTableHeader::Init(CdmSecurityLevel security_level, + CryptoSession* crypto_session) { + LOGV("UsageTableHeader::Init: security level: %d", security_level); + if (crypto_session == NULL) { + LOGE("UsageTableHeader::Init: no crypto session provided"); + return false; + } + + switch (security_level) { + case kSecurityLevelL1: + case kSecurityLevelL3: + break; + default: + LOGE("UsageTableHeader::Init: invalid security level provided: %d", + security_level); + return false; + } + + security_level_ = security_level; + + if (!file_handle_->Init(security_level)) { + LOGE("UsageTableHeader::Init: device files initialization failed"); + return false; + } if (!file_handle_->RetrieveUsageTableInfo(&usage_table_header_, &usage_entry_info_)) { CdmResponseType status = crypto_session->CreateUsageTableHeader(&usage_table_header_); - if (status != NO_ERROR) return; + if (status != NO_ERROR) return false; + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); } else { CdmResponseType status = crypto_session->LoadUsageTableHeader(usage_table_header_); if (status != NO_ERROR) { LOGE( - "UsageTableHeader::UsageTablerHeader: load usage table failed, " - "security level: %d", + "UsageTableHeader::Init: load usage table failed, security level: %d", security_level); - return; + return false; } } + requested_security_level_ = + security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; is_inited_ = true; + return true; } CdmResponseType UsageTableHeader::AddEntry( CryptoSession* crypto_session, bool persistent_license, const CdmKeySetId& key_set_id, const std::string& usage_info_file_name, uint32_t* usage_entry_number) { - LOGV("UsageTableHeader::AddEntry"); + LOGV("UsageTableHeader::AddEntry: Lock"); AutoLock auto_lock(usage_table_header_lock_); CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number); @@ -120,15 +109,17 @@ CdmResponseType UsageTableHeader::AddEntry( return NO_ERROR; } -CdmResponseType UsageTableHeader::LoadEntry( - CryptoSession* crypto_session, const CdmUsageEntry& usage_entry, - uint32_t usage_entry_number) { - LOGV("UsageTableHeader::LoadEntry"); +CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number) { + LOGV("UsageTableHeader::LoadEntry: Lock"); AutoLock auto_lock(usage_table_header_lock_); if (usage_entry_number >= usage_entry_info_.size()) { - LOGE("UsageTableHeader::LoadEntry: usage entry number %d larger than table size: %d", - usage_entry_number, usage_entry_info_.size()); + LOGE( + "UsageTableHeader::LoadEntry: usage entry number %d larger than table " + "size: %d", + usage_entry_number, usage_entry_info_.size()); return USAGE_INVALID_LOAD_ENTRY; } return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); @@ -136,20 +127,21 @@ CdmResponseType UsageTableHeader::LoadEntry( CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session, CdmUsageEntry* usage_entry) { - LOGV("UsageTableHeader::UpdateEntry"); + LOGV("UsageTableHeader::UpdateEntryL: Lock"); AutoLock auto_lock(usage_table_header_lock_); - CdmUsageTableHeader usage_table_header; CdmResponseType status = crypto_session->UpdateUsageEntry(&usage_table_header_, usage_entry); if (status != NO_ERROR) return status; - file_handle_->StoreUsageTableInfo(usage_table_header, usage_entry_info_); + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); return NO_ERROR; } -CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number) { - LOGV("UsageTableHeader::DeleteEntry"); +CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number, + DeviceFiles* handle, + metrics::MetricsGroup* metrics) { + LOGV("UsageTableHeader::DeleteEntry: Lock"); AutoLock auto_lock(usage_table_header_lock_); if (usage_entry_number >= usage_entry_info_.size()) return USAGE_INVALID_PARAMETERS_1; @@ -159,12 +151,12 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number) { CdmUsageEntry swap_usage_entry; bool swap_usage_entry_valid = false; - for (; !swap_usage_entry_valid && swap_entry_number > usage_entry_number; - --swap_entry_number) { + while (!swap_usage_entry_valid && swap_entry_number > usage_entry_number) { switch (usage_entry_info_[swap_entry_number].storage_type) { case kStorageLicense: case kStorageUsageInfo: { - CdmResponseType status = GetEntry(swap_entry_number, &swap_usage_entry); + CdmResponseType status = + GetEntry(swap_entry_number, handle, &swap_usage_entry); if (status == NO_ERROR) swap_usage_entry_valid = true; break; } @@ -172,37 +164,54 @@ CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number) { default: break; } + if (!swap_usage_entry_valid) --swap_entry_number; } - uint32_t new_usage_table_size = 0; + uint32_t number_of_entries_to_be_deleted = + usage_entry_info_.size() - usage_entry_number; if (swap_usage_entry_valid) { - MoveEntry(swap_entry_number, swap_usage_entry, usage_entry_number); - new_usage_table_size = swap_entry_number; - } else { - // No valid usage entries before entry to be deleted - new_usage_table_size = usage_entry_number; + CdmResponseType status = MoveEntry(swap_entry_number, swap_usage_entry, + usage_entry_number, handle, metrics); + // If unable to move entry, unset storage type of entry to be deleted and + // resize |usage_entry_info_| so that swap usage entry is the last entry. + if (status != NO_ERROR) { + usage_entry_info_[usage_entry_number].storage_type = kStorageUnknown; + usage_entry_info_[usage_entry_number].key_set_id.clear(); + if (usage_entry_info_.size() - 1 == swap_entry_number) { + file_handle_->StoreUsageTableInfo(usage_table_header_, + usage_entry_info_); + } else { + Shrink(metrics, usage_entry_info_.size() - swap_entry_number - 1); + } + return NO_ERROR; + } + number_of_entries_to_be_deleted = + usage_entry_info_.size() - swap_entry_number; } - usage_entry_info_.resize(new_usage_table_size); - CryptoSession crypto_session(&metrics_); - crypto_session.Open(GetSecurityLevel()); - crypto_session.ShrinkUsageTableHeader(new_usage_table_size, - &usage_table_header_); - file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); - return NO_ERROR; + + return Shrink(metrics, number_of_entries_to_be_deleted); } CdmResponseType UsageTableHeader::MoveEntry( uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, - uint32_t to_usage_entry_number) { + uint32_t to_usage_entry_number, DeviceFiles* handle, + metrics::MetricsGroup* metrics) { LOGV("UsageTableHeader::MoveEntry"); - AutoLock auto_lock(usage_table_header_lock_); - CryptoSession crypto_session(&metrics_); - crypto_session.Open(GetSecurityLevel()); + // crypto_session points to an object whose scope is this method or a test + // object whose scope is the lifetime of this class + scoped_ptr scoped_crypto_session; + CryptoSession* crypto_session = test_crypto_session_.get(); + if (crypto_session == NULL) { + scoped_crypto_session.reset((new CryptoSession(metrics))); + crypto_session = scoped_crypto_session.get(); + } + + crypto_session->Open(requested_security_level_); CdmResponseType status = - crypto_session.LoadUsageEntry(from_usage_entry_number, from_usage_entry); + crypto_session->LoadUsageEntry(from_usage_entry_number, from_usage_entry); if (status != NO_ERROR) { LOGE("UsageTableHeader::MoveEntry: Failed to load usage entry: %d", @@ -210,7 +219,7 @@ CdmResponseType UsageTableHeader::MoveEntry( return status; } - status = crypto_session.MoveUsageEntry(to_usage_entry_number); + status = crypto_session->MoveUsageEntry(to_usage_entry_number); if (status != NO_ERROR) { LOGE("UsageTableHeader::MoveEntry: Failed to move usage entry: %d->%d", @@ -222,7 +231,7 @@ CdmResponseType UsageTableHeader::MoveEntry( usage_entry_info_[from_usage_entry_number]; CdmUsageEntry usage_entry; - status = crypto_session.UpdateUsageEntry(&usage_table_header_, &usage_entry); + status = crypto_session->UpdateUsageEntry(&usage_table_header_, &usage_entry); if (status != NO_ERROR) { LOGE("UsageTableHeader::MoveEntry: Failed to update usage entry: %d", @@ -232,12 +241,13 @@ CdmResponseType UsageTableHeader::MoveEntry( file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); - StoreEntry(to_usage_entry_number, usage_entry); + StoreEntry(to_usage_entry_number, handle, usage_entry); return NO_ERROR; } CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, + DeviceFiles* handle, CdmUsageEntry* usage_entry) { uint32_t entry_number; switch (usage_entry_info_[usage_entry_number].storage_type) { @@ -247,7 +257,7 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, std::string key_renewal_response, release_server_url; int64_t playback_start_time, last_playback_time, grace_period_end_time; CdmAppParameterMap app_parameters; - if (!file_handle_->RetrieveLicense( + if (!handle->RetrieveLicense( usage_entry_info_[usage_entry_number].key_set_id, &license_state, &init_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, @@ -263,7 +273,7 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, CdmKeyMessage license_request; CdmKeyResponse license_response; - if (!file_handle_->RetrieveUsageInfoByKeySetId( + if (!handle->RetrieveUsageInfoByKeySetId( usage_entry_info_[usage_entry_number].usage_info_file_name, usage_entry_info_[usage_entry_number].key_set_id, &provider_session_token, &license_request, &license_response, @@ -293,6 +303,7 @@ CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, } CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, + DeviceFiles* handle, const CdmUsageEntry& usage_entry) { uint32_t entry_number; switch (usage_entry_info_[usage_entry_number].storage_type) { @@ -303,7 +314,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, int64_t playback_start_time, last_playback_time, grace_period_end_time; CdmAppParameterMap app_parameters; CdmUsageEntry entry; - if (!file_handle_->RetrieveLicense( + if (!handle->RetrieveLicense( usage_entry_info_[usage_entry_number].key_set_id, &license_state, &init_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, @@ -312,7 +323,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, LOGE("UsageTableHeader::StoreEntry: Failed to retrieve license"); return USAGE_RETRIEVE_LICENSE_FAILED; } - if (!file_handle_->StoreLicense( + if (!handle->StoreLicense( usage_entry_info_[usage_entry_number].key_set_id, license_state, init_data, key_request, key_response, key_renewal_request, key_renewal_response, release_server_url, playback_start_time, @@ -327,7 +338,7 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, CdmUsageEntry entry; std::string provider_session_token, init_data, key_request, key_response, key_renewal_request; - if (!file_handle_->RetrieveUsageInfoByKeySetId( + if (!handle->RetrieveUsageInfoByKeySetId( usage_entry_info_[usage_entry_number].usage_info_file_name, usage_entry_info_[usage_entry_number].key_set_id, &provider_session_token, &key_request, &key_response, &entry, @@ -337,10 +348,10 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, "information"); return USAGE_RETRIEVE_USAGE_INFO_FAILED; } - file_handle_->DeleteUsageInfo( + handle->DeleteUsageInfo( usage_entry_info_[usage_entry_number].usage_info_file_name, provider_session_token); - if (!file_handle_->StoreUsageInfo( + if (!handle->StoreUsageInfo( provider_session_token, key_request, key_response, usage_entry_info_[usage_entry_number].usage_info_file_name, usage_entry_info_[usage_entry_number].key_set_id, usage_entry, @@ -361,41 +372,56 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, return NO_ERROR; } -bool UsageTableHeader::DeleteLastEntry() { +CdmResponseType UsageTableHeader::Shrink( + metrics::MetricsGroup* metrics, + uint32_t number_of_usage_entries_to_delete) { if (usage_entry_info_.empty()) { - LOGW( - "UsageTableHeader::DeleteLastEntry: usage entry info table " - "unexpectedly empty"); - return false; + LOGE("UsageTableHeader::Shrink: usage entry info table unexpectedly empty"); + return NO_USAGE_ENTRIES; } - usage_entry_info_.resize(usage_entry_info_.size() - 1); + if (usage_entry_info_.size() < number_of_usage_entries_to_delete) { + LOGW( + "UsageTableHeader::Shrink: cannot delete %d entries when usage entry " + "table size is %d", number_of_usage_entries_to_delete, + usage_entry_info_.size()); + return NO_ERROR; + } + + if (number_of_usage_entries_to_delete == 0) return NO_ERROR; + + usage_entry_info_.resize(usage_entry_info_.size() - + number_of_usage_entries_to_delete); + + // crypto_session points to an object whose scope is this method or a test + // object whose scope is the lifetime of this class + scoped_ptr scoped_crypto_session; + CryptoSession* crypto_session = test_crypto_session_.get(); + if (crypto_session == NULL) { + scoped_crypto_session.reset((new CryptoSession(metrics))); + crypto_session = scoped_crypto_session.get(); + } + + CdmResponseType status = crypto_session->Open(requested_security_level_); + if (status != NO_ERROR) return status; + + status = crypto_session->ShrinkUsageTableHeader(usage_entry_info_.size(), + &usage_table_header_); + if (status != NO_ERROR) return status; - CryptoSession crypto_session(&metrics_); - crypto_session.Open(GetSecurityLevel()); - crypto_session.ShrinkUsageTableHeader(usage_entry_info_.size(), - &usage_table_header_); file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); - return true; -} - -CdmResponseType UsageTableHeader::UpgradeFromUsageTable() { - CryptoSession crypto_session(&metrics_); - CdmResponseType status = crypto_session.Open(GetSecurityLevel()); - - if (status != NO_ERROR) return status; - - status = crypto_session.CreateUsageTableHeader(&usage_table_header_); - if (status != NO_ERROR) return status; - - crypto_session.Close(); - - UpgradeLicensesFromUsageTable(); - UpgradeUsageInfoFromUsageTable(); return NO_ERROR; } -bool UsageTableHeader::UpgradeLicensesFromUsageTable() { +CdmResponseType UsageTableHeader::UpgradeFromUsageTable( + DeviceFiles* handle, metrics::MetricsGroup* metrics) { + UpgradeLicensesFromUsageTable(handle, metrics); + UpgradeUsageInfoFromUsageTable(handle, metrics); + return NO_ERROR; +} + +bool UsageTableHeader::UpgradeLicensesFromUsageTable( + DeviceFiles* handle, metrics::MetricsGroup* metrics) { // Fetch the key set IDs for each offline license. For each license // * retrieve the provider session token, // * create a new usage entry @@ -404,7 +430,7 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { // * save the usage table header and store the usage entry number and // usage entry along with the license to persistent memory std::vector key_set_ids; - if (file_handle_->ListLicenses(&key_set_ids)) { + if (handle->ListLicenses(&key_set_ids)) { LOGW( "UpgradeUsageTableHeader::UpgradeLicensesFromUsageTable: unable to " "retrieve list of licenses"); @@ -419,7 +445,7 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { CdmAppParameterMap app_parameters; CdmUsageEntry usage_entry; uint32_t usage_entry_number; - if (!file_handle_->RetrieveLicense( + if (!handle->RetrieveLicense( key_set_ids[i], &license_state, &init_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, &last_playback_time, @@ -442,8 +468,8 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { if (provider_session_token.empty()) continue; - CryptoSession crypto_session(&metrics_); - CdmResponseType status = crypto_session.Open(GetSecurityLevel()); + CryptoSession crypto_session(metrics); + CdmResponseType status = crypto_session.Open(requested_security_level_); if (status != NO_ERROR) continue; @@ -456,7 +482,7 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { if (status != NO_ERROR) { crypto_session.Close(); - DeleteLastEntry(); + Shrink(metrics, 1); continue; } @@ -464,16 +490,18 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { if (status != NO_ERROR) { crypto_session.Close(); - DeleteLastEntry(); + Shrink(metrics, 1); continue; } - if (!file_handle_->StoreLicense( + if (!handle->StoreLicense( key_set_ids[i], license_state, init_data, key_request, key_response, key_renewal_request, key_renewal_response, release_server_url, playback_start_time, last_playback_time, grace_period_end_time, app_parameters, usage_entry, usage_entry_number)) { - LOGE("UsageTableHeader::StoreEntry: Failed to store license"); + LOGE( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to store " + "license"); continue; } } @@ -481,7 +509,8 @@ bool UsageTableHeader::UpgradeLicensesFromUsageTable() { return NO_ERROR; } -bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { +bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( + DeviceFiles* handle, metrics::MetricsGroup* metrics) { // Fetch all usage files. For each file retrieve all the usage info records // within the file. For each piece of usage information // * create a new usage entry @@ -492,7 +521,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { // information to persistent memory along with usage entry number and usage // entry. std::vector usage_info_file_names; - if (file_handle_->ListUsageInfoFiles(&usage_info_file_names)) { + if (handle->ListUsageInfoFiles(&usage_info_file_names)) { LOGW( "UpgradeUsageTableHeader::UpgradeUsageInfoFromUsageTable: Unable to " "retrieve list of usage info file names"); @@ -501,8 +530,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { for (size_t i = 0; i < usage_info_file_names.size(); ++i) { std::vector usage_data; - if (!file_handle_->RetrieveUsageInfo(usage_info_file_names[i], - &usage_data)) { + if (!handle->RetrieveUsageInfo(usage_info_file_names[i], &usage_data)) { LOGW( "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Failed to " "retrieve usage records from %s", @@ -518,8 +546,8 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { continue; } - CryptoSession crypto_session(&metrics_); - CdmResponseType status = crypto_session.Open(GetSecurityLevel()); + CryptoSession crypto_session(metrics); + CdmResponseType status = crypto_session.Open(requested_security_level_); if (status != NO_ERROR) continue; @@ -536,7 +564,7 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { if (status != NO_ERROR) { crypto_session.Close(); - DeleteLastEntry(); + Shrink(metrics, 1); continue; } @@ -544,12 +572,12 @@ bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { if (status != NO_ERROR) { crypto_session.Close(); - DeleteLastEntry(); + Shrink(metrics, 1); continue; } } - if (!file_handle_->StoreUsageInfo(usage_info_file_names[i], usage_data)) { + if (!handle->StoreUsageInfo(usage_info_file_names[i], usage_data)) { LOGE( "UsageTableHeader::StoreUsageInfo: Failed to store usage records to " "%s", diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 3a511785..101f0ce0 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -574,6 +574,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case KEY_NOT_FOUND_IN_SESSION: *os << "KEY_NOT_FOUND_IN_SESSION"; break; + case NO_USAGE_ENTRIES: + *os << "NO_USAGE_ENTRIES"; + break; default: *os << "Unknown CdmResponseType"; diff --git a/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp new file mode 100644 index 00000000..0c81b4cc --- /dev/null +++ b/libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp @@ -0,0 +1,1563 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#include "usage_table_header.h" +#include +#include +#include +#include "crypto_session.h" +#include "device_files.h" +#include "file_store.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +namespace { + +const std::string kEmptyString; + +const CdmUsageTableHeader kEmptyUsageTableHeader; +const CdmUsageTableHeader kUsageTableHeader = "some usage table header"; +const CdmUsageTableHeader kAnotherUsageTableHeader = + "another usage table header"; +const CdmUsageEntry kUsageEntry = "usage entry"; +const CdmUsageEntry kAnotherUsageEntry = "another usage entry"; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense1 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_1", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense2 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_2", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense3 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_3", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop1 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_1", + .usage_info_file_name = "usage_info_file_1"}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop2 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_2", + .usage_info_file_name = "usage_info_file_2"}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop3 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_3", + .usage_info_file_name = "usage_info_file_3"}; +const CdmUsageEntryInfo kUsageEntryInfoStorageTypeUnknown = { + .storage_type = kStorageUnknown, + .key_set_id = "", + .usage_info_file_name = ""}; +const std::vector kEmptyUsageEntryInfoVector; +const std::vector kUsageEntryInfoVector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown}; +const DeviceFiles::LicenseState kActiveLicenseState = + DeviceFiles::kLicenseStateActive; +const CdmInitData kPsshData = "pssh data"; +const CdmKeyMessage kKeyRequest = "key request"; +const CdmKeyResponse kKeyResponse = "key response"; +const CdmKeyMessage kKeyRenewalRequest = "key renewal request"; +const CdmKeyResponse kKeyRenewalResponse = "key renewal response"; +const std::string kReleaseServerUrl = "some url"; +const std::string kProviderSessionToken = "provider session token"; +const CdmAppParameterMap kEmptyAppParameters; +int64_t kPlaybackStartTime = 1030005; +int64_t kPlaybackDuration = 300; +int64_t kGracePeriodEndTime = 60; + +class MockDeviceFiles : public DeviceFiles { + public: + MockDeviceFiles() : DeviceFiles(&file_system_) { Init(kSecurityLevelL1); } + + MOCK_METHOD2(RetrieveUsageTableInfo, + bool(CdmUsageTableHeader*, std::vector*)); + MOCK_METHOD2(StoreUsageTableInfo, + bool(const CdmUsageTableHeader&, + const std::vector&)); + MOCK_METHOD2(DeleteUsageInfo, bool(const std::string&, const std::string&)); + MOCK_METHOD7(RetrieveUsageInfoByKeySetId, + bool(const std::string&, const std::string&, std::string*, + CdmKeyMessage*, CdmKeyResponse*, CdmUsageEntry*, + uint32_t*)); + MOCK_METHOD7(StoreUsageInfo, + bool(const std::string&, const CdmKeyMessage&, + const CdmKeyResponse&, const std::string&, + const std::string&, const CdmUsageEntry&, uint32_t)); + MOCK_METHOD2(RetrieveUsageInfo, + bool(const std::string&, std::vector*)); + + private: + FileSystem file_system_; +}; + +class MockCryptoSession : public CryptoSession { + public: + MockCryptoSession() : CryptoSession(NULL) {} + MOCK_METHOD1(Open, CdmResponseType(SecurityLevel)); + MOCK_METHOD1(LoadUsageTableHeader, + CdmResponseType(const CdmUsageTableHeader&)); + MOCK_METHOD1(CreateUsageTableHeader, CdmResponseType(CdmUsageTableHeader*)); + MOCK_METHOD1(CreateUsageEntry, CdmResponseType(uint32_t*)); + MOCK_METHOD2(LoadUsageEntry, CdmResponseType(uint32_t, const CdmUsageEntry&)); + MOCK_METHOD2(UpdateUsageEntry, + CdmResponseType(CdmUsageTableHeader*, CdmUsageEntry*)); + MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t)); + MOCK_METHOD2(ShrinkUsageTableHeader, + CdmResponseType(uint32_t, CdmUsageTableHeader*)); +}; + +} // namespace + +// gmock methods +using ::testing::_; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +class UsageTableHeaderTest : public ::testing::Test { + protected: + virtual void SetUp() { + // UsageTableHeader will take ownership of the pointer + device_files_ = new MockDeviceFiles(); + crypto_session_ = new MockCryptoSession(); + usage_table_header_ = new UsageTableHeader(); + + // usage_table_header_ object takes ownership of these objects + usage_table_header_->SetDeviceFiles(device_files_); + usage_table_header_->SetCryptoSession(crypto_session_); + } + + virtual void TearDown() { + if (usage_table_header_ != NULL) delete usage_table_header_; + } + + void Init(CdmSecurityLevel security_level, + const CdmUsageTableHeader& usage_table_header, + const std::vector& usage_entry_info_vector) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(usage_table_header), + SetArgPointee<1>(usage_entry_info_vector), + Return(true))); + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(usage_table_header)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_TRUE(usage_table_header_->Init(security_level, crypto_session_)); + } + + MockDeviceFiles* device_files_; + MockCryptoSession* crypto_session_; + UsageTableHeader* usage_table_header_; +}; + +TEST_F(UsageTableHeaderTest, InitError) { + EXPECT_FALSE( + usage_table_header_->Init(kSecurityLevelUninitialized, crypto_session_)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL2, crypto_session_)); + EXPECT_FALSE( + usage_table_header_->Init(kSecurityLevelUnknown, crypto_session_)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL1, NULL)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL2, NULL)); +} + +class UsageTableHeaderInitializationTest + : public UsageTableHeaderTest, + public ::testing::WithParamInterface {}; + +TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) { + std::vector empty_usage_entry_info; + + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(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) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntryInfoVector), Return(true))); + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +INSTANTIATE_TEST_CASE_P(Cdm, UsageTableHeaderInitializationTest, + ::testing::Values(kSecurityLevelL1, kSecurityLevelL3)); + +TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + 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))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense1.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense1.usage_info_file_name, + &usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + 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))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense1.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense1.usage_info_file_name, + &usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + 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; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAreArray(expect_usage_entry_info_vector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense2.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense2.key_set_id, + kUsageEntryInfoOfflineLicense2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + 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] = + kUsageEntryInfoSecureStop2; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAreArray(expect_usage_entry_info_vector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoSecureStop2.storage_type == kStorageLicense, + kUsageEntryInfoSecureStop2.key_set_id, + kUsageEntryInfoSecureStop2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + 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; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoSecureStop2.storage_type == kStorageLicense, + kUsageEntryInfoSecureStop2.key_set_id, + kUsageEntryInfoSecureStop2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = kUsageEntryInfoVector.size() + 3; + + EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = 1; + + EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) + .WillOnce(Return(LOAD_USAGE_ENTRY_GENERATION_SKEW)); + + EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, LoadEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = 1; + + EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, UpdateEntry_CryptoSessionError) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + CdmUsageEntry usage_entry; + + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntry), + Return(UPDATE_USAGE_ENTRY_UNKNOWN_ERROR))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->UpdateEntry(crypto_session_, &usage_entry)); +} + +TEST_F(UsageTableHeaderTest, UpdateEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + CdmUsageEntry usage_entry; + + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntry), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kUsageTableHeader, + UnorderedElementsAreArray(kUsageEntryInfoVector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->UpdateEntry(crypto_session_, &usage_entry)); +} + +TEST_F(UsageTableHeaderTest, DeleteEntry_InvalidUsageEntryNumber) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = kUsageEntryInfoVector.size(); + metrics::MetricsGroup metrics; + + EXPECT_NE(NO_ERROR, usage_table_header_->DeleteEntry( + usage_entry_number, device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is an Offline license. +// When attempting to delete the entry a crypto session error +// will occur. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will not be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Offline License 2 3 3 +// +// # of usage entries 4 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_CryptoSessionError) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense2 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce(Return(SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR)); + + EXPECT_NE(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is an Offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Offline License 2 3 Deleted +// +// # of usage entries 4 3 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastOfflineEntry) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense2 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Secure Stop 2 3 Deleted +// +// # of usage entries 4 3 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastSecureStopEntry) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop2 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are offline licenses, but have license files +// missing from persistent storage. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Offline entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 Deleted +// Offline License 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastOfflineEntriesHaveMissingLicenses) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + metrics::MetricsGroup metrics; + + device_files_->DeleteAllLicenses(); + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are secure stops, but have entries +// missing from usage info file in persistent storage. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Secure stops in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 Deleted +// Secure stop 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastSecureStopEntriesAreMissing) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop2.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(Return(false)); + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(Return(false)); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are offline licenses, but have incorrect usage +// entry number stored in persistent file store. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Offline entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 Deleted +// Offline License 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastOfflineEntriesHaveIncorrectUsageEntryNumber) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + metrics::MetricsGroup metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[usage_entry_info_vector.size() - 1].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + usage_entry_info_vector.size() - 2)); + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[usage_entry_info_vector.size() - 2].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + usage_entry_info_vector.size() - 3)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are secure stops, but have incorrect usage +// entry number stored in persistent file store. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Secure stops entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 Deleted +// Secure stop 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastSecureStopEntriesHaveIncorrectUsageEntryNumber) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t usage_entry_number_after_deleted_entry = + usage_entry_number_to_be_deleted + 1; + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop2.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll( + SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(usage_entry_number_to_be_deleted), Return(true))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), + SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(usage_entry_number_after_deleted_entry), + Return(true))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are of storage type unknown. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 2 +// Offline License 2 3 3 +// Offline License 3 4 Deleted +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreStorageTypeUnknown) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoOfflineLicense2, kUsageEntryInfoOfflineLicense3, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// b. The last offline usage entry will not be deleted/moved if the +// OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted/storage type unknown +// Offline License 2 3 3 +// Offline License 3 4 4 +// +// # of usage entries 5 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntryIsOffline_MoveOfflineEntryFailed) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an secure stop and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// b. The last secure stop usage entry will not be deleted/moved if the +// OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted/storage type unknown +// Secure stop 2 3 3 +// Secure stop 3 4 4 +// +// # of usage entries 5 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntryIsSecureStop_MoveSecureStopEntryFailed) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop3 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), + SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_usage_entry_number), Return(true))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The offline usage entry that preceeds the entries in (a) will +// not be deleted/moved if the OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted/storage type unknown +// Offline License 2 3 3 +// Offline License 3 4 4 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntriesAreOfflineAndUnknown_MoveOfflineEntryFailed) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_valid_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_valid_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number + 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The offline usage entry that preceeds the entries in (a) will +// not be deleted/moved if the OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted/storage type unknown +// Secure stop 2 3 3 +// Secure stop 3 4 4 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntriesAreSecureStopAndUnknown_MoveOfflineEntryFailed) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_valid_usage_entry_number), Return(true))); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number + 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be replaced with the last +// entry. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 3 +// Offline License 3 4 2 +// +// # of usage entries 5 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntryIsOffline) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); + + DeviceFiles::LicenseState license_state = DeviceFiles::kLicenseStateUnknown; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; + int64_t playback_start_time; + int64_t last_playback_time; + int64_t grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number = ~0; + + EXPECT_TRUE(device_files_->RetrieveLicense( + kUsageEntryInfoOfflineLicense3.key_set_id, &license_state, &pssh_data, + &key_request, &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url, &playback_start_time, &last_playback_time, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); + EXPECT_EQ(kActiveLicenseState, license_state); + EXPECT_EQ(kPsshData, pssh_data); + EXPECT_EQ(kKeyRequest, key_request); + EXPECT_EQ(kKeyResponse, key_response); + EXPECT_EQ(kKeyRenewalRequest, key_renewal_request); + EXPECT_EQ(kKeyRenewalResponse, key_renewal_response); + EXPECT_EQ(kReleaseServerUrl, release_server_url); + EXPECT_EQ(kPlaybackStartTime, playback_start_time); + EXPECT_EQ(kPlaybackStartTime + kPlaybackDuration, last_playback_time); + EXPECT_EQ(kGracePeriodEndTime, grace_period_end_time); + EXPECT_EQ(kEmptyAppParameters.size(), app_parameters.size()); + EXPECT_EQ(kAnotherUsageEntry, usage_entry); + EXPECT_EQ(usage_entry_number_to_be_deleted, usage_entry_number); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be replaced with the last +// entry. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 3 +// Secure stop 3 4 2 +// +// # of usage entries 5 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntryIsSecureStop) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop3 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_usage_entry_number), Return(true))); + + EXPECT_CALL(*device_files_, + DeleteUsageInfo(kUsageEntryInfoSecureStop3.usage_info_file_name, + kProviderSessionToken)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageInfo(kProviderSessionToken, kKeyRequest, kKeyResponse, + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, kAnotherUsageEntry, + usage_entry_number_to_be_deleted)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry being deleted and replaced with the offline entry in (3). +// b. The entries with unknown storage type in (2) will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 3 +// Offline License 3 4 2 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreOfflineAndUnknknown) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::MetricsGroup metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_valid_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_valid_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); + + DeviceFiles::LicenseState license_state = DeviceFiles::kLicenseStateUnknown; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; + int64_t playback_start_time; + int64_t last_playback_time; + int64_t grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number = ~0; + + EXPECT_TRUE(device_files_->RetrieveLicense( + kUsageEntryInfoOfflineLicense3.key_set_id, &license_state, &pssh_data, + &key_request, &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url, &playback_start_time, &last_playback_time, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); + EXPECT_EQ(kActiveLicenseState, license_state); + EXPECT_EQ(kPsshData, pssh_data); + EXPECT_EQ(kKeyRequest, key_request); + EXPECT_EQ(kKeyResponse, key_response); + EXPECT_EQ(kKeyRenewalRequest, key_renewal_request); + EXPECT_EQ(kKeyRenewalResponse, key_renewal_response); + EXPECT_EQ(kReleaseServerUrl, release_server_url); + EXPECT_EQ(kPlaybackStartTime, playback_start_time); + EXPECT_EQ(kPlaybackStartTime + kPlaybackDuration, last_playback_time); + EXPECT_EQ(kGracePeriodEndTime, grace_period_end_time); + EXPECT_EQ(kEmptyAppParameters.size(), app_parameters.size()); + EXPECT_EQ(kAnotherUsageEntry, usage_entry); + EXPECT_EQ(usage_entry_number_to_be_deleted, usage_entry_number); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry being deleted and replaced with the secure stop entry in (3). +// b. The entries with unknown storage type in (2) will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 3 +// Secure stop 3 4 2 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreSecureStopAndUnknknown) { + const std::vector usage_entry_info_vector = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoSecureStop1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop3 + metrics::MetricsGroup metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_valid_usage_entry_number), Return(true))); + + EXPECT_CALL(*device_files_, + DeleteUsageInfo(kUsageEntryInfoSecureStop3.usage_info_file_name, + kProviderSessionToken)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageInfo(kProviderSessionToken, kKeyRequest, kKeyResponse, + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, kAnotherUsageEntry, + usage_entry_number_to_be_deleted)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/Android.mk b/libwvdrmengine/cdm/test/Android.mk index 32778eff..4dcc72c3 100644 --- a/libwvdrmengine/cdm/test/Android.mk +++ b/libwvdrmengine/cdm/test/Android.mk @@ -71,6 +71,10 @@ test_name := timer_unittest test_src_dir := . include $(LOCAL_PATH)/unit-test.mk +test_name := usage_table_header_unittest +test_src_dir := ../core/test +include $(LOCAL_PATH)/unit-test.mk + test_name := distribution_test test_src_dir := ../metrics/test include $(LOCAL_PATH)/unit-test.mk diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 2d2a7a13..a7145ae6 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -2617,7 +2617,12 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { key_id.append(1, ch); GenerateKeyRequest(key_id, kLicenseTypeStreaming, property_set); - VerifyUsageKeyRequestResponse(g_license_server, g_client_auth); + + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + else + VerifyUsageKeyRequestResponse(g_license_server, g_client_auth); std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( @@ -2693,7 +2698,11 @@ TEST_F(WvCdmRequestLicenseTest, UsageReleaseAllTest) { key_id.append(1, ch); GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set); - VerifyUsageKeyRequestResponse(g_license_server, g_client_auth); + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(g_license_server, g_client_auth, false); + else + VerifyUsageKeyRequestResponse(g_license_server, g_client_auth); std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index e14d6066..87edca1f 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -259,10 +259,11 @@ enum { kReleaseUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 246, kIncorrectUsageSupportType1 = ERROR_DRM_VENDOR_MIN + 247, kIncorrectUsageSupportType2 = ERROR_DRM_VENDOR_MIN + 248, + kNoUsageEntries = ERROR_DRM_VENDOR_MIN + 249, // This should always follow the last error code. // The offset value should be updated each time a new error code is added. - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 248, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 249, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index f235819e..09e78a13 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -503,6 +503,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kIncorrectUsageSupportType1; case wvcdm::INCORRECT_USAGE_SUPPORT_TYPE_2: return kIncorrectUsageSupportType2; + case wvcdm::NO_USAGE_ENTRIES: + return kNoUsageEntries; case wvcdm::UNUSED_1: case wvcdm::UNUSED_2: diff --git a/libwvdrmengine/run_all_unit_tests.sh b/libwvdrmengine/run_all_unit_tests.sh index bb23ff2f..b6ca8b10 100755 --- a/libwvdrmengine/run_all_unit_tests.sh +++ b/libwvdrmengine/run_all_unit_tests.sh @@ -82,6 +82,7 @@ adb_shell_run license_unittest adb_shell_run license_keys_unittest adb_shell_run initialization_data_unittest adb_shell_run device_files_unittest +adb_shell_run usage_table_header_unittest adb_shell_run service_certificate_unittest adb_shell_run timer_unittest adb_shell_run buffer_reader_test