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
This commit is contained in:
@@ -35,6 +35,7 @@ LOCAL_SRC_FILES := \
|
|||||||
$(CORE_SRC_DIR)/policy_engine.cpp \
|
$(CORE_SRC_DIR)/policy_engine.cpp \
|
||||||
$(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \
|
$(CORE_SRC_DIR)/privacy_crypto_openssl.cpp \
|
||||||
$(CORE_SRC_DIR)/service_certificate.cpp \
|
$(CORE_SRC_DIR)/service_certificate.cpp \
|
||||||
|
$(CORE_SRC_DIR)/usage_table_header.cpp \
|
||||||
$(SRC_DIR)/ami_adapter.cpp \
|
$(SRC_DIR)/ami_adapter.cpp \
|
||||||
$(SRC_DIR)/wv_content_decryption_module.cpp \
|
$(SRC_DIR)/wv_content_decryption_module.cpp \
|
||||||
$(METRICS_SRC_DIR)/distribution.cpp \
|
$(METRICS_SRC_DIR)/distribution.cpp \
|
||||||
|
|||||||
@@ -62,6 +62,10 @@ class CdmLicense {
|
|||||||
return is_offline_;
|
return is_offline_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ExtractProviderSessionToken(
|
||||||
|
const CdmKeyResponse& license_response,
|
||||||
|
std::string* provider_session_token);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
CdmResponseType HandleKeyErrorResponse(
|
CdmResponseType HandleKeyErrorResponse(
|
||||||
|
|||||||
109
libwvdrmengine/cdm/core/include/usage_table_header.h
Normal file
109
libwvdrmengine/cdm/core/include/usage_table_header.h
Normal file
@@ -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 <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<DeviceFiles> file_handle_;
|
||||||
|
CdmSecurityLevel security_level_;
|
||||||
|
|
||||||
|
CdmUsageTableHeader usage_table_header_;
|
||||||
|
std::vector<CdmUsageEntryInfo> 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_
|
||||||
@@ -279,6 +279,17 @@ enum CdmResponseType {
|
|||||||
INVALID_PARAMETERS_ENG_23,
|
INVALID_PARAMETERS_ENG_23,
|
||||||
USAGE_INFORMATION_SUPPORT_FAILED,
|
USAGE_INFORMATION_SUPPORT_FAILED,
|
||||||
USAGE_SUPPORT_GET_API_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 {
|
enum CdmKeyStatus {
|
||||||
|
|||||||
@@ -793,6 +793,43 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) {
|
|||||||
return loaded_keys_.find(key_id) != loaded_keys_.end();
|
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(
|
CdmResponseType CdmLicense::HandleKeyErrorResponse(
|
||||||
const SignedMessage& signed_message) {
|
const SignedMessage& signed_message) {
|
||||||
LicenseError license_error;
|
LicenseError license_error;
|
||||||
|
|||||||
564
libwvdrmengine/cdm/core/src/usage_table_header.cpp
Normal file
564
libwvdrmengine/cdm/core/src/usage_table_header.cpp
Normal file
@@ -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<std::string> 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<std::string> 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<DeviceFiles::CdmUsageData> 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
|
||||||
@@ -528,6 +528,32 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
|||||||
case USAGE_SUPPORT_GET_API_FAILED:
|
case USAGE_SUPPORT_GET_API_FAILED:
|
||||||
*os << "USAGE_SUPPORT_GET_API_FAILED";
|
*os << "USAGE_SUPPORT_GET_API_FAILED";
|
||||||
break;
|
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:
|
default:
|
||||||
*os << "Unknown CdmResponseType";
|
*os << "Unknown CdmResponseType";
|
||||||
|
|||||||
@@ -244,10 +244,21 @@ enum {
|
|||||||
kInvalidParametersEng23 = ERROR_DRM_VENDOR_MIN + 230,
|
kInvalidParametersEng23 = ERROR_DRM_VENDOR_MIN + 230,
|
||||||
kUsageInformationSupportFailed = ERROR_DRM_VENDOR_MIN + 231,
|
kUsageInformationSupportFailed = ERROR_DRM_VENDOR_MIN + 231,
|
||||||
kUsageSupportGetApiFailed = ERROR_DRM_VENDOR_MIN + 232,
|
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.
|
// This should always follow the last error code.
|
||||||
// The offset value should be updated each time a new error code is added.
|
// 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
|
// Used by crypto test mode
|
||||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||||
|
|||||||
@@ -479,6 +479,28 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
return kUsageInformationSupportFailed;
|
return kUsageInformationSupportFailed;
|
||||||
case wvcdm::USAGE_SUPPORT_GET_API_FAILED:
|
case wvcdm::USAGE_SUPPORT_GET_API_FAILED:
|
||||||
return kUsageSupportGetApiFailed;
|
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_1:
|
||||||
case wvcdm::UNUSED_2:
|
case wvcdm::UNUSED_2:
|
||||||
|
|||||||
Reference in New Issue
Block a user