From 0db3a137e90b3278a4e23b1a541b9d521ab3d552 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Wed, 8 Feb 2017 14:56:16 -0800 Subject: [PATCH] Introduce UsageTableHeader class [ Merge of http://go/wvgerrit/23820 ] 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. Upgrades from a fixed size usage table (supported by previous versions of the OEMCrypto API v9-12) are handled by this class. b/34327459 Test: Verified by unit/integration tests on angler Change-Id: Ifc5996985e76bc260c01e55bc12aab1248389a80 --- libwvdrmengine/cdm/Android.mk | 1 + libwvdrmengine/cdm/core/include/license.h | 4 + .../cdm/core/include/usage_table_header.h | 109 ++++ .../cdm/core/include/wv_cdm_types.h | 11 + libwvdrmengine/cdm/core/src/license.cpp | 37 ++ .../cdm/core/src/usage_table_header.cpp | 564 ++++++++++++++++++ .../cdm/core/test/test_printers.cpp | 26 + libwvdrmengine/include/WVErrors.h | 13 +- libwvdrmengine/include/mapErrors-inl.h | 22 + 9 files changed, 786 insertions(+), 1 deletion(-) create mode 100644 libwvdrmengine/cdm/core/include/usage_table_header.h create mode 100644 libwvdrmengine/cdm/core/src/usage_table_header.cpp diff --git a/libwvdrmengine/cdm/Android.mk b/libwvdrmengine/cdm/Android.mk index 01994f68..7ed9968f 100644 --- a/libwvdrmengine/cdm/Android.mk +++ b/libwvdrmengine/cdm/Android.mk @@ -35,6 +35,7 @@ LOCAL_SRC_FILES := \ $(CORE_SRC_DIR)/policy_engine.cpp \ $(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \ $(CORE_SRC_DIR)/service_certificate.cpp \ + $(CORE_SRC_DIR)/usage_table_header.cpp \ $(SRC_DIR)/ami_adapter.cpp \ $(SRC_DIR)/wv_content_decryption_module.cpp \ $(METRICS_SRC_DIR)/distribution.cpp \ diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index 4a8554d8..3f7c4f95 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -62,6 +62,10 @@ class CdmLicense { return is_offline_; } + static bool ExtractProviderSessionToken( + const CdmKeyResponse& license_response, + std::string* provider_session_token); + private: CdmResponseType HandleKeyErrorResponse( diff --git a/libwvdrmengine/cdm/core/include/usage_table_header.h b/libwvdrmengine/cdm/core/include/usage_table_header.h new file mode 100644 index 00000000..8276cfd4 --- /dev/null +++ b/libwvdrmengine/cdm/core/include/usage_table_header.h @@ -0,0 +1,109 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#ifndef WVCDM_CORE_USAGE_TABLE_HEADER_H_ +#define WVCDM_CORE_USAGE_TABLE_HEADER_H_ + +#include +#include + +#include "device_files.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. +// 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. + // |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() {} + + // |persistent_license| false indicates usage info record + CdmResponseType AddEntry(CryptoSession* crypto_session, + bool persistent_license, + const CdmKeySetId& key_set_id, + const std::string& usage_info_filename, + uint32_t* usage_entry_number); + CdmResponseType LoadEntry(CryptoSession* crypto_session, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number); + CdmResponseType UpdateEntry(CryptoSession* crypto_session, + CdmUsageEntry* usage_entry); + + // 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); + + private: + UsageTableHeader(FileSystem* file_system, CryptoSession* crypto_session, + CdmSecurityLevel security_level); + + CdmResponseType GetEntry(uint32_t usage_entry_number, + CdmUsageEntry* usage_entry); + CdmResponseType StoreEntry(uint32_t usage_entry_number, + const CdmUsageEntry& usage_entry); + + bool DeleteLastEntry(); + + CdmResponseType UpgradeFromUsageTable(); + bool UpgradeLicensesFromUsageTable(); + bool UpgradeUsageInfoFromUsageTable(); + + 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_; + + scoped_ptr file_handle_; + CdmSecurityLevel 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_; + + CORE_DISALLOW_COPY_AND_ASSIGN(UsageTableHeader); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_USAGE_TABLE_HEADER_H_ diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 500cf616..62be1d78 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -279,6 +279,17 @@ enum CdmResponseType { INVALID_PARAMETERS_ENG_23, USAGE_INFORMATION_SUPPORT_FAILED, USAGE_SUPPORT_GET_API_FAILED, + UNEXPECTED_EMPTY_USAGE_ENTRY, /* 240 */ + INVALID_USAGE_ENTRY_NUMBER_MODIFICATION, + USAGE_INVALID_NEW_ENTRY, + USAGE_INVALID_PARAMETERS_1, + USAGE_RETRIEVE_LICENSE_FAILED, + USAGE_RETRIEVE_USAGE_INFO_FAILED, /* 245 */ + USAGE_RETRIEVE_INVALID_STORAGE_TYPE, + USAGE_ENTRY_NUMBER_MISMATCH, + USAGE_STORE_LICENSE_FAILED, + USAGE_STORE_USAGE_INFO_FAILED, + USAGE_INVALID_LOAD_ENTRY, /* 250 */ }; enum CdmKeyStatus { diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index 8131d2c0..46f11615 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -793,6 +793,43 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { return loaded_keys_.find(key_id) != loaded_keys_.end(); } +bool CdmLicense::ExtractProviderSessionToken( + const CdmKeyResponse& license_response, + std::string* provider_session_token) { + if (license_response.empty()) { + LOGW("CdmLicense::ExtractProviderSessionToken: empty license response"); + return false; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(license_response)) { + LOGW( + "CdmLicense::ExtractProviderSessionToken: unable to parse signed " + "license response"); + return false; + } + + if (signed_response.type() != SignedMessage::LICENSE) { + LOGW("CdmLicense::ExtractProviderSessionToken: unrecognized signed message " + "type: %d", signed_response.type()); + return false; + } + + License license; + if (!license.ParseFromString(signed_response.msg())) { + LOGE("CdmLicense::ExtractProviderSessionToken: unable to parse license " + "response"); + return false; + } + + if (license.id().has_provider_session_token()) { + *provider_session_token = license.id().provider_session_token(); + return true; + } + + return false; +} + CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { LicenseError license_error; diff --git a/libwvdrmengine/cdm/core/src/usage_table_header.cpp b/libwvdrmengine/cdm/core/src/usage_table_header.cpp new file mode 100644 index 00000000..11420d29 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/usage_table_header.cpp @@ -0,0 +1,564 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#include "usage_table_header.h" + +#include "crypto_session.h" +#include "license.h" +#include "log.h" + +namespace { +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(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); + + if (!file_handle_->RetrieveUsageTableInfo(&usage_table_header_, + &usage_entry_info_)) { + CdmResponseType status = + crypto_session->CreateUsageTableHeader(&usage_table_header_); + if (status != NO_ERROR) return; + } else { + CdmResponseType status = + crypto_session->LoadUsageTableHeader(usage_table_header_); + if (status != NO_ERROR) { + LOGE( + "UsageTableHeader::UsageTablerHeader: load usage table failed, " + "security level: %d", + security_level); + return; + } + } + is_inited_ = 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"); + AutoLock auto_lock(usage_table_header_lock_); + CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number); + + if (status != NO_ERROR) return status; + + if (*usage_entry_number < usage_entry_info_.size()) { + LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d", + *usage_entry_number, usage_entry_info_.size()); + return USAGE_INVALID_NEW_ENTRY; + } + + if (*usage_entry_number > usage_entry_info_.size()) { + LOGW("UsageTableHeader::AddEntry: new entry %d larger than table size: %d", + *usage_entry_number, usage_entry_info_.size()); + size_t number_of_entries = usage_entry_info_.size(); + usage_entry_info_.resize(*usage_entry_number + 1); + for (size_t i = number_of_entries; i < usage_entry_info_.size() - 1; ++i) { + usage_entry_info_[i].storage_type = kStorageUnknown; + usage_entry_info_[i].key_set_id.clear(); + usage_entry_info_[i].usage_info_file_name.clear(); + } + } else /* *usage_entry_number == usage_entry_info_.size() */ { + usage_entry_info_.resize(*usage_entry_number + 1); + } + + usage_entry_info_[*usage_entry_number].storage_type = + persistent_license ? kStorageLicense : kStorageUsageInfo; + usage_entry_info_[*usage_entry_number].key_set_id = key_set_id; + if (!persistent_license) + usage_entry_info_[*usage_entry_number].usage_info_file_name = + usage_info_file_name; + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::LoadEntry( + CryptoSession* crypto_session, const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number) { + LOGV("UsageTableHeader::LoadEntry"); + 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()); + return USAGE_INVALID_LOAD_ENTRY; + } + return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); +} + +CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session, + CdmUsageEntry* usage_entry) { + LOGV("UsageTableHeader::UpdateEntry"); + 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_); + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number) { + LOGV("UsageTableHeader::DeleteEntry"); + AutoLock auto_lock(usage_table_header_lock_); + if (usage_entry_number >= usage_entry_info_.size()) + return USAGE_INVALID_PARAMETERS_1; + + // Find the last valid entry number, in order to swap + size_t swap_entry_number = usage_entry_info_.size() - 1; + CdmUsageEntry swap_usage_entry; + bool swap_usage_entry_valid = false; + + for (; !swap_usage_entry_valid && swap_entry_number > usage_entry_number; + --swap_entry_number) { + switch (usage_entry_info_[swap_entry_number].storage_type) { + case kStorageLicense: + case kStorageUsageInfo: { + CdmResponseType status = GetEntry(swap_entry_number, &swap_usage_entry); + if (status == NO_ERROR) swap_usage_entry_valid = true; + break; + } + case kStorageUnknown: + default: + break; + } + } + + uint32_t new_usage_table_size = 0; + + 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; + } + 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; +} + +CdmResponseType UsageTableHeader::MoveEntry( + uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, + uint32_t to_usage_entry_number) { + LOGV("UsageTableHeader::MoveEntry"); + AutoLock auto_lock(usage_table_header_lock_); + + CryptoSession crypto_session(&metrics_); + crypto_session.Open(GetSecurityLevel()); + + CdmResponseType status = + crypto_session.LoadUsageEntry(from_usage_entry_number, from_usage_entry); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to load usage entry: %d", + from_usage_entry_number); + return status; + } + + status = crypto_session.MoveUsageEntry(to_usage_entry_number); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to move usage entry: %d->%d", + from_usage_entry_number, to_usage_entry_number); + return status; + } + + usage_entry_info_[to_usage_entry_number] = + usage_entry_info_[from_usage_entry_number]; + + CdmUsageEntry usage_entry; + status = crypto_session.UpdateUsageEntry(&usage_table_header_, &usage_entry); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to update usage entry: %d", + to_usage_entry_number); + return status; + } + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + + StoreEntry(to_usage_entry_number, usage_entry); + + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, + CdmUsageEntry* usage_entry) { + uint32_t entry_number; + switch (usage_entry_info_[usage_entry_number].storage_type) { + case kStorageLicense: { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + 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( + 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, + &last_playback_time, &grace_period_end_time, &app_parameters, + usage_entry, &entry_number)) { + LOGE("UsageTableHeader::GetEntry: Failed to retrieve license"); + return USAGE_RETRIEVE_LICENSE_FAILED; + } + break; + } + case kStorageUsageInfo: { + std::string provider_session_token; + CdmKeyMessage license_request; + CdmKeyResponse license_response; + + if (!file_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, + usage_entry, &entry_number)) { + LOGE( + "UsageTableHeader::GetEntry: Failed to retrieve usage information"); + return USAGE_RETRIEVE_USAGE_INFO_FAILED; + } + break; + } + case kStorageUnknown: + default: + LOGE( + "UsageTableHeader::GetEntry: Attempting to retrieve usage " + "information from unknown storage type: %d", + usage_entry_info_[usage_entry_number].storage_type); + return USAGE_RETRIEVE_INVALID_STORAGE_TYPE; + } + + if (usage_entry_number != entry_number) { + LOGE("UsageTableHeader::GetEntry: entry number mismatch: (%d, %d)", + usage_entry_number, entry_number); + return USAGE_ENTRY_NUMBER_MISMATCH; + } + + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, + const CdmUsageEntry& usage_entry) { + uint32_t entry_number; + switch (usage_entry_info_[usage_entry_number].storage_type) { + case kStorageLicense: { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + std::string key_renewal_response, release_server_url; + int64_t playback_start_time, last_playback_time, grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry entry; + if (!file_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, + &last_playback_time, &grace_period_end_time, &app_parameters, + &entry, &entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to retrieve license"); + return USAGE_RETRIEVE_LICENSE_FAILED; + } + if (!file_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, + last_playback_time, grace_period_end_time, app_parameters, + usage_entry, usage_entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to store license"); + return USAGE_STORE_LICENSE_FAILED; + } + break; + } + case kStorageUsageInfo: { + CdmUsageEntry entry; + std::string provider_session_token, init_data, key_request, key_response, + key_renewal_request; + if (!file_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, + &entry_number)) { + LOGE( + "UsageTableHeader::StoreEntry: Failed to retrieve usage " + "information"); + return USAGE_RETRIEVE_USAGE_INFO_FAILED; + } + file_handle_->DeleteUsageInfo( + usage_entry_info_[usage_entry_number].usage_info_file_name, + provider_session_token); + if (!file_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, + usage_entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to store usage information"); + return USAGE_STORE_USAGE_INFO_FAILED; + } + break; + } + case kStorageUnknown: + default: + LOGE( + "UsageTableHeader::GetUsageEntry: Attempting to retrieve usage " + "information from unknown storage type: %d", + usage_entry_info_[usage_entry_number].storage_type); + return USAGE_RETRIEVE_INVALID_STORAGE_TYPE; + } + return NO_ERROR; +} + +bool UsageTableHeader::DeleteLastEntry() { + if (usage_entry_info_.empty()) { + LOGW( + "UsageTableHeader::DeleteLastEntry: usage entry info table " + "unexpectedly empty"); + return false; + } + + usage_entry_info_.resize(usage_entry_info_.size() - 1); + + 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() { + // Fetch the key set IDs for each offline license. For each license + // * retrieve the provider session token, + // * create a new usage entry + // * copy over the entry from the usage table + // * update the usage header table and entry numbers + // * 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)) { + LOGW( + "UpgradeUsageTableHeader::UpgradeLicensesFromUsageTable: unable to " + "retrieve list of licenses"); + return false; + } + + for (size_t i = 0; i < key_set_ids.size(); ++i) { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + std::string key_renewal_response, release_server_url; + int64_t playback_start_time, last_playback_time, grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number; + if (!file_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, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)) { + LOGW( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to " + "retrieve license"); + continue; + } + + std::string provider_session_token; + if (!CdmLicense::ExtractProviderSessionToken(key_response, + &provider_session_token)) { + LOGW( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to " + "retrieve provider session token"); + continue; + } + + if (provider_session_token.empty()) continue; + + CryptoSession crypto_session(&metrics_); + CdmResponseType status = crypto_session.Open(GetSecurityLevel()); + + if (status != NO_ERROR) continue; + + status = AddEntry(&crypto_session, true /* persistent license */, + key_set_ids[i], kEmptyString, &usage_entry_number); + + if (status != NO_ERROR) continue; + + status = crypto_session.CopyOldUsageEntry(provider_session_token); + + if (status != NO_ERROR) { + crypto_session.Close(); + DeleteLastEntry(); + continue; + } + + status = UpdateEntry(&crypto_session, &usage_entry); + + if (status != NO_ERROR) { + crypto_session.Close(); + DeleteLastEntry(); + continue; + } + + if (!file_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"); + continue; + } + } + + return NO_ERROR; +} + +bool UsageTableHeader::UpgradeUsageInfoFromUsageTable() { + // 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 + // * copy over the entry from the usage table and + // * update the usage header table and entry numbers + // * save the usage table header + // * once done processing all the usage records from a file, save the usage + // 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)) { + LOGW( + "UpgradeUsageTableHeader::UpgradeUsageInfoFromUsageTable: Unable to " + "retrieve list of usage info file names"); + return false; + } + + 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)) { + LOGW( + "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Failed to " + "retrieve usage records from %s", + usage_info_file_names[i].c_str()); + continue; + } + + for (size_t j = 0; j < usage_data.size(); --j) { + if (usage_data[j].provider_session_token.empty()) { + LOGW( + "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Provider " + "session id empty"); + continue; + } + + CryptoSession crypto_session(&metrics_); + CdmResponseType status = crypto_session.Open(GetSecurityLevel()); + + if (status != NO_ERROR) continue; + + // TODO(rfrias): We need to fill in the app id, but it is hashed + // and we have no way to extract. Use the hased filename instead? + status = AddEntry(&crypto_session, false /* usage info */, + usage_data[j].key_set_id, kEmptyString, + &(usage_data[i].usage_entry_number)); + + if (status != NO_ERROR) continue; + + status = crypto_session.CopyOldUsageEntry( + usage_data[i].provider_session_token); + + if (status != NO_ERROR) { + crypto_session.Close(); + DeleteLastEntry(); + continue; + } + + status = UpdateEntry(&crypto_session, &(usage_data[i].usage_entry)); + + if (status != NO_ERROR) { + crypto_session.Close(); + DeleteLastEntry(); + continue; + } + } + + if (!file_handle_->StoreUsageInfo(usage_info_file_names[i], usage_data)) { + LOGE( + "UsageTableHeader::StoreUsageInfo: Failed to store usage records to " + "%s", + usage_info_file_names[i].c_str()); + continue; + } + } + + return NO_ERROR; +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index aea9bf6d..bed9f72a 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -528,6 +528,32 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case USAGE_SUPPORT_GET_API_FAILED: *os << "USAGE_SUPPORT_GET_API_FAILED"; break; + case UNEXPECTED_EMPTY_USAGE_ENTRY: + *os << "UNEXPECTED_EMPTY_USAGE_ENTRY"; + break; + case INVALID_USAGE_ENTRY_NUMBER_MODIFICATION: + *os << "INVALID_USAGE_ENTRY_NUMBER_MODIFICATION"; + break; + case USAGE_INVALID_NEW_ENTRY: *os << "USAGE_INVALID_NEW_ENTRY"; + break; + case USAGE_INVALID_PARAMETERS_1: *os << "USAGE_INVALID_PARAMETERS_1"; + break; + case USAGE_RETRIEVE_LICENSE_FAILED: *os << "USAGE_RETRIEVE_LICENSE_FAILED"; + break; + case USAGE_RETRIEVE_USAGE_INFO_FAILED: + *os << "USAGE_RETRIEVE_USAGE_INFO_FAILED"; + break; + case USAGE_RETRIEVE_INVALID_STORAGE_TYPE: + *os << "USAGE_RETRIEVE_INVALID_STORAGE_TYPE"; + break; + case USAGE_ENTRY_NUMBER_MISMATCH: *os << "USAGE_ENTRY_NUMBER_MISMATCH"; + break; + case USAGE_STORE_LICENSE_FAILED: *os << "USAGE_STORE_LICENSE_FAILED"; + break; + case USAGE_STORE_USAGE_INFO_FAILED: *os << "USAGE_STORE_USAGE_INFO_FAILED"; + break; + case USAGE_INVALID_LOAD_ENTRY: *os << "USAGE_INVALID_LOAD_ENTRY"; + break; default: *os << "Unknown CdmResponseType"; diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index 552979cd..e597525c 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -244,10 +244,21 @@ enum { kInvalidParametersEng23 = ERROR_DRM_VENDOR_MIN + 230, kUsageInformationSupportFailed = ERROR_DRM_VENDOR_MIN + 231, kUsageSupportGetApiFailed = ERROR_DRM_VENDOR_MIN + 232, + kUnexpectedEmptyUsageEntry = ERROR_DRM_VENDOR_MIN + 233, + kInvalidUsageEntryNumberModification = ERROR_DRM_VENDOR_MIN + 234, + kUsageInvalidNewEntry = ERROR_DRM_VENDOR_MIN + 235, + kUsageInvalidParameters1 = ERROR_DRM_VENDOR_MIN + 236, + kUsageRetrieveLicenseFailed = ERROR_DRM_VENDOR_MIN + 237, + kUsageRetrieveUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 238, + kUsageRetrieveInvalidStorageType = ERROR_DRM_VENDOR_MIN + 239, + kUsageEntryNumberMismatch = ERROR_DRM_VENDOR_MIN + 240, + kUsageStoreLicenseFailed = ERROR_DRM_VENDOR_MIN + 241, + kUsageStoreUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 242, + kUsageInvalidLoadEntry = ERROR_DRM_VENDOR_MIN + 243, // 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 + 232, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 243, // 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 d9ff873c..f18d13e9 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -479,6 +479,28 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kUsageInformationSupportFailed; case wvcdm::USAGE_SUPPORT_GET_API_FAILED: return kUsageSupportGetApiFailed; + case wvcdm::UNEXPECTED_EMPTY_USAGE_ENTRY: + return kUnexpectedEmptyUsageEntry; + case wvcdm::INVALID_USAGE_ENTRY_NUMBER_MODIFICATION: + return kInvalidUsageEntryNumberModification; + case wvcdm::USAGE_INVALID_NEW_ENTRY: + return kUsageInvalidNewEntry; + case wvcdm::USAGE_INVALID_PARAMETERS_1: + return kUsageInvalidParameters1; + case wvcdm::USAGE_RETRIEVE_LICENSE_FAILED: + return kUsageRetrieveLicenseFailed; + case wvcdm::USAGE_RETRIEVE_USAGE_INFO_FAILED: + return kUsageRetrieveUsageInfoFailed; + case wvcdm::USAGE_RETRIEVE_INVALID_STORAGE_TYPE: + return kUsageRetrieveInvalidStorageType; + case wvcdm::USAGE_ENTRY_NUMBER_MISMATCH: + return kUsageEntryNumberMismatch; + case wvcdm::USAGE_STORE_LICENSE_FAILED: + return kUsageStoreLicenseFailed; + case wvcdm::USAGE_STORE_USAGE_INFO_FAILED: + return kUsageStoreUsageInfoFailed; + case wvcdm::USAGE_INVALID_LOAD_ENTRY: + return kUsageInvalidLoadEntry; case wvcdm::UNUSED_1: case wvcdm::UNUSED_2: