From 5da8da58f62dbba219698e0f7813aee65a122465 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 22 May 2017 13:33:20 -0700 Subject: [PATCH] Corrections for big usage table support in L3 [ Merge of http://go/wvgerrit/26421 ] * Corrects usage_table_header lifetime management. Earlier the UsageTableHeader class was a singleton tied to the CdmEngine lifetime. With SPOIDs there might be multiple concurrent CdmEngine objects. The UsageTableHeader class is now associated with OEMCrypto lifetime. There are two UsageTableHeader objects one for each L1 and L3. These get allocated/deallocated on OEMCrypto Initialization/Termination respectively. * UsageTableHeader requires OEMCrypto, file read/writes and metric gathering to perform its required functionality. Because of the lifetime changes, CryptoSession, DeviceFiles and MetricsGroup objects need to passed to the methods rather than at Creation time. * Miscellaneous fixes, when moving or deleteing entries. * Adds usage_table_header_unittests. * Addresses failures with request_license_test with secure stop in L3. b/36858906 b/36855557 b/36048120 b/38341136 b/37100505 b/35946047 Test: Verified by unit and integration tests. Added new usage_table_header_unittests Change-Id: I20e396ab2c0afbd14372dd93b969e5b0f1ccd291 --- .../build_and_run_all_unit_tests.sh | 1 + libwvdrmengine/cdm/core/include/cdm_session.h | 3 +- .../cdm/core/include/crypto_session.h | 8 + libwvdrmengine/cdm/core/include/license.h | 4 +- .../cdm/core/include/usage_table_header.h | 97 +- .../cdm/core/include/wv_cdm_types.h | 7 + libwvdrmengine/cdm/core/src/cdm_engine.cpp | 3 +- libwvdrmengine/cdm/core/src/cdm_session.cpp | 71 +- .../cdm/core/src/crypto_session.cpp | 58 +- libwvdrmengine/cdm/core/src/license.cpp | 10 +- .../cdm/core/src/usage_table_header.cpp | 300 ++-- .../cdm/core/test/test_printers.cpp | 3 + .../core/test/usage_table_header_unittest.cpp | 1563 +++++++++++++++++ libwvdrmengine/cdm/test/Android.mk | 4 + .../cdm/test/request_license_test.cpp | 13 +- libwvdrmengine/include/WVErrors.h | 3 +- libwvdrmengine/include/mapErrors-inl.h | 2 + libwvdrmengine/run_all_unit_tests.sh | 1 + 18 files changed, 1937 insertions(+), 214 deletions(-) create mode 100644 libwvdrmengine/cdm/core/test/usage_table_header_unittest.cpp 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