Merge "Revert "Core CDM: Removed secure stop support.""
This commit is contained in:
@@ -127,10 +127,10 @@ class CdmEngine {
|
||||
// stored offline data associated with the session.
|
||||
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
|
||||
|
||||
// This method removes all offline data associated with the session. It
|
||||
// should be used with care, as it deletes the info immediately and
|
||||
// without using a release message, so the server is not able to
|
||||
// receive usage info or track releases for offline licenses.
|
||||
// This method removes all offline data associated with the session, such as
|
||||
// offline keys and usage info. It should be used with care, as it deletes the
|
||||
// info immediately and without using a release message, so the server is not
|
||||
// able to receive usage info or track releases for offline licenses.
|
||||
virtual CdmResponseType RemoveLicense(const CdmSessionId& session_id);
|
||||
|
||||
// Construct valid renewal request for the current session keys.
|
||||
@@ -235,32 +235,38 @@ class CdmEngine {
|
||||
virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id,
|
||||
CdmSecurityLevel security_level);
|
||||
|
||||
// Usage related methods for streaming licenses with persistent usage
|
||||
// information (deprecated).
|
||||
// Usage related methods for streaming licenses
|
||||
// Retrieve a random usage info from the list of all usage infos for this app
|
||||
// id. If |error_detail| is not null, an additional error code may be provided
|
||||
// in the event of an error.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info);
|
||||
|
||||
// Retrieve usage info whose PST is specified by |ssid| (deprecated).
|
||||
// Retrieve usage info whose PST is specified by |ssid|
|
||||
// If |error_detail| is not null, an additional error code may be provided
|
||||
// in the event of an error.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info);
|
||||
|
||||
// Retrieve usage info for a given security level and whose PST is
|
||||
// specified by |ssid| (deprecated).
|
||||
// Retrieve usage info for a given security level and whose
|
||||
// PST is specified by |ssid|.
|
||||
// If |error_detail| is not null, an additional error code may be provided
|
||||
// in the event of an error.
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
RequestedSecurityLevel security_level,
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info);
|
||||
|
||||
// Remove all usage records for the current origin (deprecated).
|
||||
// Remove all usage records for the current origin.
|
||||
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id,
|
||||
CdmSecurityLevel security_level);
|
||||
|
||||
// Remove all usage records for the current origin. Span all
|
||||
// security levels (deprecated).
|
||||
// security levels.
|
||||
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id);
|
||||
|
||||
virtual CdmResponseType RemoveUsageInfo(
|
||||
@@ -400,6 +406,9 @@ class CdmEngine {
|
||||
CdmSessionId* session_id);
|
||||
|
||||
bool ValidateKeySystem(const CdmKeySystem& key_system);
|
||||
CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
RequestedSecurityLevel requested_security_level,
|
||||
int* error_detail, CdmUsageInfo* usage_info);
|
||||
|
||||
void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
|
||||
|
||||
@@ -434,11 +443,13 @@ class CdmEngine {
|
||||
std::string spoid_;
|
||||
uint32_t user_id_;
|
||||
|
||||
// Tracks the time of the last update to usage info across all
|
||||
// actively managed CDM sessions with loaded usage entries. Used
|
||||
// to periodically update a usage entry associated with an active
|
||||
// license.
|
||||
int64_t last_usage_info_update_time_ = 0;
|
||||
// Usage related variables
|
||||
// Used to isolate a single active usage information license. Loading,
|
||||
// creating or releasing a different usage licenses through the engine
|
||||
// API will release the handle to previously active secure stop license.
|
||||
std::unique_ptr<CdmSession> usage_session_;
|
||||
std::unique_ptr<UsagePropertySet> usage_property_set_;
|
||||
int64_t last_usage_information_update_time_;
|
||||
|
||||
// Protect release_key_sets_ from non-thread-safe operations.
|
||||
std::mutex release_key_sets_lock_;
|
||||
|
||||
@@ -75,6 +75,12 @@ class CdmSession {
|
||||
virtual CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
CdmLicenseType license_type,
|
||||
int* error_detail);
|
||||
// Restores an usage session from the provided |usage_data|.
|
||||
// The |error_detail| will be filled with an internal error code. The
|
||||
// |error_detail| may be a CdmResponseType or other error code type. It is
|
||||
// only suitable for additional logging or debugging.
|
||||
virtual CdmResponseType RestoreUsageSession(
|
||||
const DeviceFiles::CdmUsageData& usage_data, int* error_detail);
|
||||
|
||||
virtual const CdmSessionId& session_id() { return session_id_; }
|
||||
virtual const CdmKeySetId& key_set_id() { return key_set_id_; }
|
||||
@@ -144,7 +150,7 @@ class CdmSession {
|
||||
|
||||
virtual bool is_initial_usage_update() { return is_initial_usage_update_; }
|
||||
virtual bool is_usage_update_needed() { return is_usage_update_needed_; }
|
||||
virtual void ResetUsageFlags() {
|
||||
virtual void reset_usage_flags() {
|
||||
is_initial_usage_update_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
}
|
||||
@@ -153,19 +159,12 @@ class CdmSession {
|
||||
virtual bool is_offline() { return is_offline_; }
|
||||
virtual bool is_temporary() { return is_temporary_; }
|
||||
virtual bool license_received() { return license_received_; }
|
||||
|
||||
virtual bool HasUsageEntry() {
|
||||
// The PST is only set if a usage entry has been loaded.
|
||||
return provider_session_token_.size() > 0;
|
||||
}
|
||||
virtual const std::string& provider_session_token() {
|
||||
return provider_session_token_;
|
||||
}
|
||||
void SetProviderSessionTokenForTest(const std::string& pst) {
|
||||
provider_session_token_ = pst;
|
||||
virtual bool has_provider_session_token() {
|
||||
return (license_parser_ &&
|
||||
license_parser_->provider_session_token().size() > 0);
|
||||
}
|
||||
|
||||
virtual bool SupportsUsageEntries() const {
|
||||
virtual bool supports_usage_info() const {
|
||||
return usage_table_header_ != nullptr;
|
||||
}
|
||||
|
||||
@@ -244,6 +243,8 @@ class CdmSession {
|
||||
|
||||
bool StoreLicense(CdmOfflineLicenseState state, int* error_detail);
|
||||
|
||||
bool UpdateUsageInfo();
|
||||
|
||||
CdmResponseType GenerateKeyRequestInternal(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
|
||||
@@ -304,15 +305,14 @@ class CdmSession {
|
||||
bool last_decrypt_failed_ = false;
|
||||
|
||||
// Usage related flags and data
|
||||
bool is_initial_usage_update_ = true;
|
||||
bool is_usage_update_needed_ = false;
|
||||
bool is_initial_usage_update_;
|
||||
bool is_usage_update_needed_;
|
||||
// Only assign |usage_table_header_| if capable of supporting usage
|
||||
// information.
|
||||
UsageTableHeader* usage_table_header_ = nullptr;
|
||||
uint32_t usage_entry_number_ = 0;
|
||||
uint32_t usage_entry_number_;
|
||||
CdmUsageEntry usage_entry_;
|
||||
// This PST should only be set if the session has a usage entry.
|
||||
std::string provider_session_token_;
|
||||
std::string usage_provider_session_token_;
|
||||
|
||||
// information useful for offline and usage scenarios
|
||||
CdmKeyMessage key_request_;
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
|
||||
namespace wvcdm {
|
||||
namespace {
|
||||
constexpr uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
constexpr uint32_t kUpdateUsageInfoPeriod = 60; // seconds
|
||||
const uint64_t kReleaseSessionTimeToLive = 60; // seconds
|
||||
const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
|
||||
} // namespace
|
||||
|
||||
class UsagePropertySet : public CdmClientPropertySet {
|
||||
@@ -67,12 +67,16 @@ CdmEngine::CdmEngine(wvutil::FileSystem* file_system,
|
||||
: metrics_(metrics),
|
||||
cert_provisioning_(),
|
||||
file_system_(file_system),
|
||||
spoid_(EMPTY_SPOID) {
|
||||
spoid_(EMPTY_SPOID),
|
||||
usage_session_(),
|
||||
usage_property_set_(),
|
||||
last_usage_information_update_time_(0) {
|
||||
assert(file_system);
|
||||
Properties::Init();
|
||||
}
|
||||
|
||||
CdmEngine::~CdmEngine() {
|
||||
usage_session_.reset();
|
||||
std::unique_lock<std::recursive_mutex> lock(session_map_lock_);
|
||||
session_map_.Terminate();
|
||||
}
|
||||
@@ -411,7 +415,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
|
||||
}
|
||||
|
||||
if (key_set_id != nullptr) {
|
||||
if ((session->is_offline()) && !license_type_release) {
|
||||
if ((session->is_offline() || session->has_provider_session_token()) &&
|
||||
!license_type_release) {
|
||||
*key_set_id = session->key_set_id();
|
||||
LOGI("key_set_id = %s", IdPtrToString(key_set_id));
|
||||
} else {
|
||||
@@ -1279,21 +1284,50 @@ CdmResponseType CdmEngine::ListStoredLicenses(
|
||||
|
||||
CdmResponseType CdmEngine::ListUsageIds(
|
||||
const std::string& app_id, CdmSecurityLevel security_level,
|
||||
std::vector<std::string>* /* ksids */,
|
||||
std::vector<std::string>* /* provider_session_tokens */) {
|
||||
LOGI("app_id = %s, security_level = %s", IdToString(app_id),
|
||||
CdmSecurityLevelToString(security_level));
|
||||
LOGW("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
std::vector<std::string>* ksids,
|
||||
std::vector<std::string>* provider_session_tokens) {
|
||||
if (!ksids && !provider_session_tokens) {
|
||||
LOGE("Outputs |ksids| and |provider_session_tokens| are null");
|
||||
return INVALID_PARAMETERS_ENG_23;
|
||||
}
|
||||
if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) {
|
||||
LOGD("OKP fallback to L3");
|
||||
security_level = kSecurityLevelL3;
|
||||
}
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("Unable to initialize device files");
|
||||
return LIST_USAGE_ERROR_1;
|
||||
}
|
||||
if (!handle.ListUsageIds(app_id, ksids, provider_session_tokens)) {
|
||||
LOGE("Failed: app_id = %s, security_level = %s", IdToString(app_id),
|
||||
CdmSecurityLevelToString(security_level));
|
||||
return LIST_USAGE_ERROR_2;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
|
||||
CdmSecurityLevel security_level,
|
||||
const std::string& key_set_id) {
|
||||
LOGI("app_id = %s, security_level = %s, key_set_id = %s", IdToString(app_id),
|
||||
CdmSecurityLevelToString(security_level), IdToString(key_set_id));
|
||||
LOGW("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
LOGI("app_id = %s, key_set_id = %s", IdToString(app_id),
|
||||
IdToString(key_set_id));
|
||||
if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) {
|
||||
LOGD("OKP fallback to L3");
|
||||
security_level = kSecurityLevelL3;
|
||||
}
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(security_level)) {
|
||||
LOGE("Unable to initialize device files");
|
||||
return DELETE_USAGE_ERROR_1;
|
||||
}
|
||||
std::string provider_session_token;
|
||||
if (!handle.GetProviderSessionToken(app_id, key_set_id,
|
||||
&provider_session_token)) {
|
||||
LOGE("GetProviderSessionToken failed");
|
||||
return DELETE_USAGE_ERROR_2;
|
||||
}
|
||||
return RemoveUsageInfo(app_id, provider_session_token);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetOfflineLicenseState(
|
||||
@@ -1363,7 +1397,7 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
|
||||
}
|
||||
} else if (sts == LICENSE_USAGE_ENTRY_MISSING) {
|
||||
// It is possible that the CDM is tracking a key set ID, but has
|
||||
// removed the usage entry associated with it. In this case,
|
||||
// removed the usage information associated with it. In this case,
|
||||
// it will no longer be possible to load the license for release;
|
||||
// and the file should simply be deleted.
|
||||
LOGW("License usage entry is missing, deleting license file");
|
||||
@@ -1381,66 +1415,415 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
int* /* error_detail */,
|
||||
CdmUsageInfo* /* usage_info */) {
|
||||
LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info) {
|
||||
// Try to find usage info at the default security level. If the
|
||||
// security level is unprovisioned or we are unable to find it,
|
||||
// try L3.
|
||||
CdmResponseType status =
|
||||
GetUsageInfo(app_id, ssid, kLevelDefault, error_detail, usage_info);
|
||||
switch (status) {
|
||||
case NEED_PROVISIONING:
|
||||
case GET_USAGE_INFO_ERROR_1:
|
||||
case GET_USAGE_INFO_ERROR_2:
|
||||
case USAGE_INFO_NOT_FOUND:
|
||||
status = GetUsageInfo(app_id, ssid, kLevel3, error_detail, usage_info);
|
||||
return status;
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
const CdmSecureStopId& ssid,
|
||||
RequestedSecurityLevel security_level,
|
||||
int* /* error_detail */,
|
||||
CdmUsageInfo* /* usage_info */) {
|
||||
LOGI("app_id = %s, ssid = %s, security_level = %s", IdToString(app_id),
|
||||
IdToString(ssid), RequestedSecurityLevelToString(security_level));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info) {
|
||||
LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid));
|
||||
if (!usage_property_set_) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
if (usage_info == nullptr) {
|
||||
LOGE("Output |usage_info| is null");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
usage_property_set_->set_security_level(security_level);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("Session init error: status = %d", static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("Device file init error");
|
||||
return GET_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
|
||||
DeviceFiles::CdmUsageData usage_data;
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), ssid,
|
||||
&usage_data)) {
|
||||
usage_property_set_->set_security_level(kLevel3);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("Session init error: status = %d", static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
if (!handle.Reset(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("Device file init error");
|
||||
return GET_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
ssid, &usage_data)) {
|
||||
// No entry found for that ssid.
|
||||
return USAGE_INFO_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
status = usage_session_->RestoreUsageSession(usage_data, error_detail);
|
||||
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("RestoreUsageSession failed: status = %d", static_cast<int>(status));
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmKeyRequest request;
|
||||
status = usage_session_->GenerateReleaseRequest(&request);
|
||||
|
||||
usage_info->clear();
|
||||
usage_info->push_back(request.message);
|
||||
|
||||
if (KEY_MESSAGE != status) {
|
||||
LOGE("GenerateReleaseRequest failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
return KEY_MESSAGE;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||
int* /* error_detail */,
|
||||
CdmUsageInfo* /* usage_info */) {
|
||||
int* error_detail,
|
||||
CdmUsageInfo* usage_info) {
|
||||
LOGI("app_id = %s", IdToString(app_id));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
if (usage_info == nullptr) {
|
||||
LOGE("Output |usage_info| is null");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
// Return a random usage report from a random security level
|
||||
RequestedSecurityLevel security_level =
|
||||
wvutil::CdmRandom::RandomBool() ? kLevelDefault : kLevel3;
|
||||
CdmResponseType status = UNKNOWN_ERROR;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, error_detail, usage_info);
|
||||
if (KEY_MESSAGE == status && !usage_info->empty()) {
|
||||
return status;
|
||||
}
|
||||
} while (KEY_CANCELED == status);
|
||||
|
||||
security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3;
|
||||
do {
|
||||
status = GetUsageInfo(app_id, security_level, error_detail, usage_info);
|
||||
if (NEED_PROVISIONING == status)
|
||||
return NO_ERROR; // Valid scenario that one of the security
|
||||
// levels has not been provisioned
|
||||
} while (KEY_CANCELED == status);
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::GetUsageInfo(
|
||||
const std::string& app_id, RequestedSecurityLevel requested_security_level,
|
||||
int* error_detail, CdmUsageInfo* usage_info) {
|
||||
LOGI("app_id = %s, security_level = %s", IdToString(app_id),
|
||||
RequestedSecurityLevelToString(requested_security_level));
|
||||
if (usage_info == nullptr) {
|
||||
LOGE("Output |usage_info| is null");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
if (requested_security_level == kLevelDefault && OkpIsInFallbackMode()) {
|
||||
LOGD("OKP fallback to L3");
|
||||
requested_security_level = kLevel3;
|
||||
}
|
||||
if (!usage_property_set_) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
usage_property_set_->set_security_level(requested_security_level);
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
|
||||
CdmResponseType status = usage_session_->Init(usage_property_set_.get());
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("Session init error");
|
||||
return status;
|
||||
}
|
||||
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(usage_session_->GetSecurityLevel())) {
|
||||
LOGE("Unable to initialize device files");
|
||||
return GET_USAGE_INFO_ERROR_3;
|
||||
}
|
||||
|
||||
std::vector<DeviceFiles::CdmUsageData> usage_data;
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&usage_data)) {
|
||||
LOGE("Unable to read usage information");
|
||||
return GET_USAGE_INFO_ERROR_4;
|
||||
}
|
||||
|
||||
if (usage_data.empty()) {
|
||||
usage_info->clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
const size_t index = wvutil::CdmRandom::RandomInRange(usage_data.size() - 1);
|
||||
status = usage_session_->RestoreUsageSession(usage_data[index], error_detail);
|
||||
if (KEY_ADDED != status) {
|
||||
// TODO(b/141704872): Make multiple attempts.
|
||||
LOGE("RestoreUsageSession failed: index = %zu, status = %d", index,
|
||||
static_cast<int>(status));
|
||||
usage_info->clear();
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmKeyRequest request;
|
||||
status = usage_session_->GenerateReleaseRequest(&request);
|
||||
|
||||
usage_info->clear();
|
||||
usage_info->push_back(request.message);
|
||||
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
break;
|
||||
case KEY_CANCELED: // usage information not present in
|
||||
usage_session_->DeleteLicenseFile(); // OEMCrypto, delete and try again
|
||||
usage_info->clear();
|
||||
break;
|
||||
default:
|
||||
LOGE("GenerateReleaseRequest failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
usage_info->clear();
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveAllUsageInfo(
|
||||
const std::string& app_id, CdmSecurityLevel cdm_security_level) {
|
||||
LOGI("app_id = %s, security_level = %s", IdToString(app_id),
|
||||
CdmSecurityLevelToString(cdm_security_level));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
if (!usage_property_set_) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
|
||||
CdmResponseType status = NO_ERROR;
|
||||
DeviceFiles handle(file_system_);
|
||||
if (handle.Init(cdm_security_level)) {
|
||||
const RequestedSecurityLevel security_level =
|
||||
cdm_security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
|
||||
usage_property_set_->set_security_level(security_level);
|
||||
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
|
||||
usage_session_->Init(usage_property_set_.get());
|
||||
|
||||
if (usage_session_->supports_usage_info()) {
|
||||
std::vector<DeviceFiles::CdmUsageData> usage_data;
|
||||
// Retrieve all usage information but delete only one before
|
||||
// refetching. This is because deleting the usage entry
|
||||
// might cause other entries to be shifted and information updated.
|
||||
do {
|
||||
if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&usage_data)) {
|
||||
LOGW("Failed to retrieve usage info");
|
||||
break;
|
||||
}
|
||||
|
||||
if (usage_data.empty()) break;
|
||||
|
||||
CdmResponseType res =
|
||||
usage_session_->DeleteUsageEntry(usage_data[0].usage_entry_number);
|
||||
|
||||
if (res != NO_ERROR) {
|
||||
LOGW("Failed to delete usage entry: status = %d",
|
||||
static_cast<int>(res));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
usage_data[0].provider_session_token)) {
|
||||
LOGW("Failed to delete usage info");
|
||||
break;
|
||||
}
|
||||
} while (!usage_data.empty());
|
||||
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&provider_session_tokens)) {
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_5;
|
||||
}
|
||||
}
|
||||
}
|
||||
usage_session_.reset();
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) {
|
||||
LOGI("app_id = %s", IdToString(app_id));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
const CdmResponseType status_l1 =
|
||||
RemoveAllUsageInfo(app_id, kSecurityLevelL1);
|
||||
const CdmResponseType status_l3 =
|
||||
RemoveAllUsageInfo(app_id, kSecurityLevelL3);
|
||||
// Prioritizing L1 status.
|
||||
if (status_l1 != NO_ERROR) {
|
||||
return status_l1;
|
||||
}
|
||||
return status_l3;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::RemoveUsageInfo(
|
||||
const std::string& app_id, const CdmSecureStopId& provider_session_token) {
|
||||
LOGI("app_id = %s, pst = %s", IdToString(app_id),
|
||||
IdToString(provider_session_token));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
if (!usage_property_set_) {
|
||||
usage_property_set_.reset(new UsagePropertySet());
|
||||
}
|
||||
usage_property_set_->set_app_id(app_id);
|
||||
|
||||
CdmResponseType status = NO_ERROR;
|
||||
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
|
||||
DeviceFiles handle(file_system_);
|
||||
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
|
||||
RequestedSecurityLevel security_level =
|
||||
static_cast<CdmSecurityLevel>(j) == kSecurityLevelL3 ? kLevel3
|
||||
: kLevelDefault;
|
||||
usage_property_set_->set_security_level(security_level);
|
||||
usage_session_.reset(
|
||||
new CdmSession(file_system_, metrics_->AddSession()));
|
||||
usage_session_->Init(usage_property_set_.get());
|
||||
|
||||
CdmKeyMessage license_request;
|
||||
CdmKeyResponse license_response;
|
||||
CdmUsageEntry usage_entry;
|
||||
uint32_t usage_entry_number;
|
||||
std::string drm_certificate;
|
||||
CryptoWrappedKey wrapped_private_key;
|
||||
|
||||
if (!handle.RetrieveUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), provider_session_token,
|
||||
&license_request, &license_response, &usage_entry,
|
||||
&usage_entry_number, &drm_certificate, &wrapped_private_key)) {
|
||||
// Try other security level
|
||||
continue;
|
||||
}
|
||||
|
||||
if (usage_session_->supports_usage_info()) {
|
||||
status = usage_session_->DeleteUsageEntry(usage_entry_number);
|
||||
if (!handle.DeleteUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
provider_session_token)) {
|
||||
status = REMOVE_USAGE_INFO_ERROR_1;
|
||||
}
|
||||
usage_session_.reset();
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
LOGE("Failed to initialize L%d device files", j);
|
||||
status = REMOVE_USAGE_INFO_ERROR_2;
|
||||
}
|
||||
}
|
||||
usage_session_.reset();
|
||||
return REMOVE_USAGE_INFO_ERROR_3;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::ReleaseUsageInfo(
|
||||
const CdmUsageInfoReleaseMessage& message) {
|
||||
LOGI("message_size = %zu", message.size());
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
if (!usage_session_) {
|
||||
LOGE("Usage session not initialized");
|
||||
return RELEASE_USAGE_INFO_ERROR;
|
||||
}
|
||||
const CdmResponseType status = usage_session_->ReleaseKey(message);
|
||||
usage_session_.reset();
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("ReleaseKey failed: status = %d", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::LoadUsageSession(
|
||||
const CdmKeySetId& key_set_id, CdmKeyMessage* /* release_message */) {
|
||||
CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
|
||||
CdmKeyMessage* release_message) {
|
||||
LOGI("key_set_id = %s", IdToString(key_set_id));
|
||||
LOGE("API not supported");
|
||||
return NOT_IMPLEMENTED_ERROR;
|
||||
// This method is currently only used by the CE CDM, in which all session IDs
|
||||
// are key set IDs.
|
||||
assert(Properties::AlwaysUseKeySetIds());
|
||||
if (key_set_id.empty()) {
|
||||
LOGE("Invalid key set ID");
|
||||
return EMPTY_KEYSET_ID_ENG_5;
|
||||
}
|
||||
if (release_message == nullptr) {
|
||||
LOGE("Output |release_message| is null");
|
||||
return PARAMETER_NULL;
|
||||
}
|
||||
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (!session_map_.FindSession(key_set_id, &session)) {
|
||||
LOGE("Session not found: key_set_id = %s", IdToString(key_set_id));
|
||||
return SESSION_NOT_FOUND_11;
|
||||
}
|
||||
|
||||
DeviceFiles handle(file_system_);
|
||||
if (!handle.Init(session->GetSecurityLevel())) {
|
||||
LOGE("Unable to initialize device files");
|
||||
return LOAD_USAGE_INFO_FILE_ERROR;
|
||||
}
|
||||
|
||||
std::string app_id;
|
||||
session->GetApplicationId(&app_id);
|
||||
|
||||
DeviceFiles::CdmUsageData usage_data;
|
||||
if (!handle.RetrieveUsageInfoByKeySetId(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id,
|
||||
&(usage_data.provider_session_token), &(usage_data.license_request),
|
||||
&(usage_data.license), &(usage_data.usage_entry),
|
||||
&(usage_data.usage_entry_number), &(usage_data.drm_certificate),
|
||||
&(usage_data.wrapped_private_key))) {
|
||||
LOGE("Unable to find usage information");
|
||||
return LOAD_USAGE_INFO_MISSING;
|
||||
}
|
||||
|
||||
int error_detail = NO_ERROR;
|
||||
usage_data.key_set_id = key_set_id;
|
||||
CdmResponseType status =
|
||||
session->RestoreUsageSession(usage_data, &error_detail);
|
||||
session->GetMetrics()->cdm_session_restore_usage_session_.Increment(
|
||||
status, error_detail);
|
||||
if (KEY_ADDED != status) {
|
||||
LOGE("Restore failed: key_set_id = %s, status = %d", IdToString(key_set_id),
|
||||
static_cast<int>(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmKeyRequest request;
|
||||
status = session->GenerateReleaseRequest(&request);
|
||||
*release_message = std::move(request.message);
|
||||
switch (status) {
|
||||
case KEY_MESSAGE:
|
||||
break;
|
||||
case KEY_CANCELED:
|
||||
// usage information not present in OEMCrypto, delete and try again
|
||||
session->DeleteLicenseFile();
|
||||
break;
|
||||
default:
|
||||
LOGE("GenerateReleaseRequest failed: status = %d",
|
||||
static_cast<int>(status));
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::DecryptV16(
|
||||
@@ -1689,7 +2072,6 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
|
||||
|
||||
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
|
||||
uint32_t height) {
|
||||
std::unique_lock<std::recursive_mutex> lock(session_map_lock_);
|
||||
std::shared_ptr<CdmSession> session;
|
||||
if (session_map_.FindSession(session_id, &session)) {
|
||||
session->NotifyResolution(width, height);
|
||||
@@ -1706,9 +2088,10 @@ void CdmEngine::OnTimerEvent() {
|
||||
wvutil::Clock clock;
|
||||
const uint64_t current_time = clock.GetCurrentTime();
|
||||
bool usage_update_period_expired = false;
|
||||
if (current_time - last_usage_info_update_time_ > kUpdateUsageInfoPeriod) {
|
||||
if (current_time - last_usage_information_update_time_ >
|
||||
kUpdateUsageInformationPeriod) {
|
||||
usage_update_period_expired = true;
|
||||
last_usage_info_update_time_ = current_time;
|
||||
last_usage_information_update_time_ = current_time;
|
||||
}
|
||||
|
||||
bool is_initial_usage_update = false;
|
||||
@@ -1718,24 +2101,27 @@ void CdmEngine::OnTimerEvent() {
|
||||
CdmSessionList sessions;
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
for (auto& session : sessions) {
|
||||
is_initial_usage_update =
|
||||
is_initial_usage_update || session->is_initial_usage_update();
|
||||
while (!sessions.empty()) {
|
||||
is_initial_usage_update = is_initial_usage_update ||
|
||||
sessions.front()->is_initial_usage_update();
|
||||
is_usage_update_needed =
|
||||
is_usage_update_needed || session->is_usage_update_needed();
|
||||
session->OnTimerEvent(usage_update_period_expired);
|
||||
is_usage_update_needed || sessions.front()->is_usage_update_needed();
|
||||
|
||||
sessions.front()->OnTimerEvent(usage_update_period_expired);
|
||||
sessions.pop_front();
|
||||
}
|
||||
|
||||
if (is_usage_update_needed &&
|
||||
(usage_update_period_expired || is_initial_usage_update)) {
|
||||
// Session list may have changed. Rebuild.
|
||||
sessions.clear();
|
||||
session_map_.GetSessionList(sessions);
|
||||
|
||||
for (auto& session : sessions) {
|
||||
session->ResetUsageFlags();
|
||||
if (session->SupportsUsageEntries() && session->HasUsageEntry()) {
|
||||
session->UpdateUsageEntryInformation();
|
||||
for (CdmSessionList::iterator iter = sessions.begin();
|
||||
iter != sessions.end(); ++iter) {
|
||||
(*iter)->reset_usage_flags();
|
||||
if ((*iter)->supports_usage_info() &&
|
||||
(*iter)->has_provider_session_token()) {
|
||||
(*iter)->UpdateUsageEntryInformation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
|
||||
is_temporary_(false),
|
||||
security_level_(kSecurityLevelUninitialized),
|
||||
requested_security_level_(kLevelDefault),
|
||||
is_initial_usage_update_(true),
|
||||
is_usage_update_needed_(false),
|
||||
usage_table_header_(nullptr),
|
||||
usage_entry_number_(0),
|
||||
mock_license_parser_in_use_(false),
|
||||
mock_policy_engine_in_use_(false) {
|
||||
assert(metrics_); // metrics_ must not be null.
|
||||
@@ -87,7 +91,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
|
||||
}
|
||||
|
||||
CdmSession::~CdmSession() {
|
||||
if (HasUsageEntry() && !is_release_) {
|
||||
if (has_provider_session_token() && supports_usage_info() && !is_release_) {
|
||||
UpdateUsageEntryInformation();
|
||||
}
|
||||
|
||||
@@ -265,9 +269,9 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
return GET_RELEASED_LICENSE_ERROR;
|
||||
}
|
||||
|
||||
std::string provider_session_token;
|
||||
bool sign_fake_request = false; // TODO(b/169483174): remove this variable.
|
||||
if (SupportsUsageEntries()) {
|
||||
std::string provider_session_token;
|
||||
if (supports_usage_info()) {
|
||||
if (!license_parser_->ExtractProviderSessionToken(
|
||||
key_response_, &provider_session_token)) {
|
||||
provider_session_token.clear();
|
||||
@@ -284,7 +288,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
IdToString(key_set_id));
|
||||
return USAGE_ENTRY_ALREADY_LOADED;
|
||||
}
|
||||
provider_session_token_ = std::move(provider_session_token);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("Failed to load usage entry: status = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
@@ -293,10 +296,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
} else {
|
||||
sign_fake_request = true; // TODO(b/169483174): remove this block.
|
||||
}
|
||||
// Must be set before updating the usage entry. CdmLicense will attempt
|
||||
// to update the usage info when restoring.
|
||||
is_offline_ = true;
|
||||
|
||||
// TODO(b/169483174): remove this code in v17. For OEMCrypto v16, an offline
|
||||
// license would not work because the rental clock in OEMCrypto is only
|
||||
// started when the license request is signed. We will sign a fake license
|
||||
@@ -333,7 +332,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (HasUsageEntry()) {
|
||||
if (!provider_session_token.empty() && supports_usage_info()) {
|
||||
CdmResponseType sts = usage_table_header_->UpdateEntry(
|
||||
usage_entry_number_, crypto_session_.get(), &usage_entry_);
|
||||
if (sts != NO_ERROR) {
|
||||
@@ -341,16 +340,74 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
||||
return sts;
|
||||
}
|
||||
if (!StoreLicense(license_data.state, error_detail)) {
|
||||
LOGW("Unable to save updated license");
|
||||
LOGW("Unable to save updated usage info");
|
||||
}
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
is_offline_ = true;
|
||||
is_release_ = license_type == kLicenseTypeRelease;
|
||||
has_license_been_restored_ = true;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RestoreUsageSession(
|
||||
const DeviceFiles::CdmUsageData& usage_data, int* error_detail) {
|
||||
if (!initialized_) {
|
||||
LOGE("CDM session not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
}
|
||||
if (!key_set_id_.empty()) {
|
||||
file_handle_->UnreserveLicenseId(key_set_id_);
|
||||
}
|
||||
key_set_id_ = usage_data.key_set_id;
|
||||
key_request_ = usage_data.license_request;
|
||||
key_response_ = usage_data.license;
|
||||
usage_entry_ = usage_data.usage_entry;
|
||||
usage_entry_number_ = usage_data.usage_entry_number;
|
||||
usage_provider_session_token_ = usage_data.provider_session_token;
|
||||
|
||||
CdmResponseType status = LoadPrivateOrLegacyKey(
|
||||
usage_data.drm_certificate, usage_data.wrapped_private_key);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
CdmResponseType sts = NO_ERROR;
|
||||
if (supports_usage_info()) {
|
||||
sts = usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_,
|
||||
usage_entry_number_);
|
||||
crypto_metrics_->usage_table_header_load_entry_.Increment(sts);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("Failed to load usage entry: status = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
|
||||
sts = license_parser_->RestoreLicenseForRelease(usage_data.drm_certificate,
|
||||
key_request_, key_response_);
|
||||
|
||||
if (sts != NO_ERROR) {
|
||||
SetErrorDetail(error_detail, sts);
|
||||
return RELEASE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (supports_usage_info()) {
|
||||
sts = usage_table_header_->UpdateEntry(
|
||||
usage_entry_number_, crypto_session_.get(), &usage_entry_);
|
||||
if (sts != NO_ERROR) {
|
||||
LOGE("Failed to update usage entry: status = %d", static_cast<int>(sts));
|
||||
return sts;
|
||||
}
|
||||
if (!UpdateUsageInfo()) {
|
||||
LOGW("Unable to save updated usage info");
|
||||
}
|
||||
}
|
||||
|
||||
license_received_ = true;
|
||||
is_offline_ = false;
|
||||
is_release_ = true;
|
||||
return KEY_ADDED;
|
||||
}
|
||||
|
||||
// This is a thin wrapper that initiates the latency metric.
|
||||
CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
const InitializationData& init_data, CdmLicenseType license_type,
|
||||
@@ -475,21 +532,19 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
// to see if it has a provider session token. If so a new entry needs
|
||||
// to be created.
|
||||
CdmResponseType sts;
|
||||
if (SupportsUsageEntries()) {
|
||||
std::string provider_session_token;
|
||||
std::string provider_session_token;
|
||||
if (supports_usage_info()) {
|
||||
if (license_parser_->ExtractProviderSessionToken(key_response,
|
||||
&provider_session_token) &&
|
||||
!provider_session_token.empty()) {
|
||||
if (!is_offline_) {
|
||||
LOGE("CDM does not support secure stop licenses");
|
||||
return ADD_KEY_ERROR;
|
||||
}
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
sts = usage_table_header_->AddEntry(
|
||||
crypto_session_.get(), /* is_persistent */ true, key_set_id_,
|
||||
/* usage_info_filename */ "", key_response, &usage_entry_number_);
|
||||
crypto_session_.get(), is_offline_, key_set_id_,
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), key_response,
|
||||
&usage_entry_number_);
|
||||
crypto_metrics_->usage_table_header_add_entry_.Increment(sts);
|
||||
if (sts != NO_ERROR) return sts;
|
||||
provider_session_token_ = std::move(provider_session_token);
|
||||
}
|
||||
}
|
||||
sts =
|
||||
@@ -502,14 +557,13 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
version_info.license_service_version());
|
||||
|
||||
// Update or invalidate entry if usage table header+entries are supported
|
||||
if (HasUsageEntry()) {
|
||||
if (!provider_session_token.empty() && supports_usage_info()) {
|
||||
if (sts != KEY_ADDED) {
|
||||
const CdmResponseType invalidate_sts =
|
||||
usage_table_header_->InvalidateEntry(
|
||||
usage_entry_number_, true, file_handle_.get(), crypto_metrics_);
|
||||
crypto_metrics_->usage_table_header_delete_entry_.Increment(
|
||||
invalidate_sts);
|
||||
provider_session_token_.clear();
|
||||
if (invalidate_sts != NO_ERROR) {
|
||||
LOGW("Invalidate usage entry failed: status = %d",
|
||||
static_cast<int>(invalidate_sts));
|
||||
@@ -526,11 +580,15 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
|
||||
IdToString(license_parser_->provider_session_token()),
|
||||
license_parser_->provider_session_token().size());
|
||||
|
||||
if (is_offline_ && !is_temporary_) {
|
||||
if (HasUsageEntry()) {
|
||||
if ((is_offline_ || has_provider_session_token()) && !is_temporary_) {
|
||||
if (has_provider_session_token() && supports_usage_info()) {
|
||||
usage_table_header_->UpdateEntry(usage_entry_number_,
|
||||
crypto_session_.get(), &usage_entry_);
|
||||
}
|
||||
|
||||
if (!is_offline_)
|
||||
usage_provider_session_token_ = license_parser_->provider_session_token();
|
||||
|
||||
sts = StoreLicense();
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
@@ -642,7 +700,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParametersV16& params) {
|
||||
}
|
||||
has_decrypted_since_last_report_ = true;
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ = HasUsageEntry();
|
||||
is_usage_update_needed_ = has_provider_session_token();
|
||||
}
|
||||
last_decrypt_failed_ = false;
|
||||
} else {
|
||||
@@ -728,7 +786,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
|
||||
|
||||
if (KEY_MESSAGE != status) return status;
|
||||
|
||||
if (HasUsageEntry()) {
|
||||
if (has_provider_session_token() && supports_usage_info()) {
|
||||
status = usage_table_header_->UpdateEntry(
|
||||
usage_entry_number_, crypto_session_.get(), &usage_entry_);
|
||||
|
||||
@@ -741,6 +799,10 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
|
||||
if (is_offline_) { // Mark license as being released
|
||||
if (!StoreLicense(kLicenseStateReleasing, nullptr))
|
||||
return RELEASE_KEY_REQUEST_ERROR;
|
||||
} else if (!usage_provider_session_token_.empty()) {
|
||||
if (supports_usage_info()) {
|
||||
if (!UpdateUsageInfo()) return RELEASE_USAGE_INFO_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
key_request_type_ = key_request->type;
|
||||
@@ -771,7 +833,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
|
||||
LOGE("CDM session not initialized");
|
||||
return NOT_INITIALIZED_ERROR;
|
||||
}
|
||||
if (!SupportsUsageEntries()) {
|
||||
if (!supports_usage_info()) {
|
||||
LOGE("Cannot delete entry, usage table not supported");
|
||||
return INCORRECT_USAGE_SUPPORT_TYPE_1;
|
||||
}
|
||||
@@ -800,7 +862,6 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
|
||||
sts = usage_table_header_->InvalidateEntry(
|
||||
usage_entry_number, true, file_handle_.get(), crypto_metrics_);
|
||||
crypto_metrics_->usage_table_header_delete_entry_.Increment(sts);
|
||||
provider_session_token_.clear();
|
||||
return sts;
|
||||
}
|
||||
|
||||
@@ -847,23 +908,54 @@ bool CdmSession::GenerateKeySetId(bool atsc_mode_enabled,
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::StoreLicense() {
|
||||
if (is_temporary_ || !is_offline_) {
|
||||
if (is_temporary_) {
|
||||
LOGE("Session type prohibits storage");
|
||||
return STORAGE_PROHIBITED;
|
||||
}
|
||||
if (key_set_id_.empty()) {
|
||||
LOGE("No key set ID");
|
||||
return EMPTY_KEYSET_ID;
|
||||
|
||||
if (is_offline_) {
|
||||
if (key_set_id_.empty()) {
|
||||
LOGE("No key set ID");
|
||||
return EMPTY_KEYSET_ID;
|
||||
}
|
||||
|
||||
if (!license_parser_->is_offline()) {
|
||||
LOGE("License policy prohibits storage");
|
||||
return OFFLINE_LICENSE_PROHIBITED;
|
||||
}
|
||||
|
||||
if (!StoreLicense(kLicenseStateActive, nullptr)) {
|
||||
LOGE("Unable to store license");
|
||||
return STORE_LICENSE_ERROR_1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
} // if (is_offline_)
|
||||
|
||||
std::string provider_session_token =
|
||||
license_parser_->provider_session_token();
|
||||
if (provider_session_token.empty()) {
|
||||
LOGE("No provider session token and not offline");
|
||||
return STORE_LICENSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!license_parser_->is_offline()) {
|
||||
LOGE("License policy prohibits storage");
|
||||
return OFFLINE_LICENSE_PROHIBITED;
|
||||
}
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
if (!file_handle_->StoreUsageInfo(
|
||||
provider_session_token, key_request_, key_response_,
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), key_set_id_, usage_entry_,
|
||||
usage_entry_number_, drm_certificate_, wrapped_private_key_)) {
|
||||
LOGE("Unable to store usage info");
|
||||
// Usage info file is corrupt. Delete current usage entry and file.
|
||||
if (supports_usage_info()) {
|
||||
DeleteUsageEntry(usage_entry_number_);
|
||||
} else {
|
||||
LOGW("Cannot store, usage table not supported");
|
||||
}
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
file_handle_->DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), &provider_session_tokens);
|
||||
|
||||
if (!StoreLicense(kLicenseStateActive, nullptr)) {
|
||||
LOGE("Unable to store license");
|
||||
return STORE_LICENSE_ERROR_1;
|
||||
return STORE_USAGE_INFO_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -907,8 +999,8 @@ CdmResponseType CdmSession::RemoveKeys() {
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::RemoveLicense() {
|
||||
if (is_offline_) {
|
||||
if (HasUsageEntry()) {
|
||||
if (is_offline_ || has_provider_session_token()) {
|
||||
if (has_provider_session_token() && supports_usage_info()) {
|
||||
DeleteUsageEntry(usage_entry_number_);
|
||||
}
|
||||
DeleteLicenseFile();
|
||||
@@ -917,8 +1009,17 @@ CdmResponseType CdmSession::RemoveLicense() {
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicenseFile() {
|
||||
if (!is_offline_) return false;
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
if (!is_offline_ && !has_provider_session_token()) return false;
|
||||
|
||||
if (is_offline_) {
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
} else {
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
return file_handle_->DeleteUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
license_parser_->provider_session_token());
|
||||
}
|
||||
}
|
||||
|
||||
void CdmSession::NotifyResolution(uint32_t width, uint32_t height) {
|
||||
@@ -949,14 +1050,16 @@ void CdmSession::GetApplicationId(std::string* app_id) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
|
||||
if (!HasUsageEntry()) {
|
||||
LOGE("Session does not have a usage entry");
|
||||
if (!has_provider_session_token() || !supports_usage_info()) {
|
||||
LOGE("Unexpected state: usage_support = %s, PST present = %s, ",
|
||||
supports_usage_info() ? "true" : "false",
|
||||
has_provider_session_token() ? "yes" : "no");
|
||||
return INCORRECT_USAGE_SUPPORT_TYPE_2;
|
||||
}
|
||||
|
||||
CdmResponseType sts = NO_ERROR;
|
||||
// TODO(blueeyes): Add measurements to all UpdateEntry calls in a way that
|
||||
// allows us to isolate this particular use case within
|
||||
// allos us to isolate this particular use case within
|
||||
// UpdateUsageEntryInformation.
|
||||
M_TIME(sts = usage_table_header_->UpdateEntry(
|
||||
usage_entry_number_, crypto_session_.get(), &usage_entry_),
|
||||
@@ -964,8 +1067,11 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
|
||||
|
||||
if (sts != NO_ERROR) return sts;
|
||||
|
||||
StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive,
|
||||
nullptr);
|
||||
if (is_offline_)
|
||||
StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive,
|
||||
nullptr);
|
||||
else if (!usage_provider_session_token_.empty())
|
||||
UpdateUsageInfo();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
@@ -1032,6 +1138,23 @@ CdmResponseType CdmSession::GetDecryptHashError(std::string* error_string) {
|
||||
return crypto_session_->GetDecryptHashError(error_string);
|
||||
}
|
||||
|
||||
bool CdmSession::UpdateUsageInfo() {
|
||||
std::string app_id;
|
||||
GetApplicationId(&app_id);
|
||||
|
||||
DeviceFiles::CdmUsageData usage_data;
|
||||
usage_data.provider_session_token = usage_provider_session_token_;
|
||||
usage_data.license_request = key_request_;
|
||||
usage_data.license = key_response_;
|
||||
usage_data.key_set_id = key_set_id_;
|
||||
usage_data.usage_entry = usage_entry_;
|
||||
usage_data.usage_entry_number = usage_entry_number_;
|
||||
|
||||
return file_handle_->UpdateUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id), usage_provider_session_token_,
|
||||
usage_data);
|
||||
}
|
||||
|
||||
void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) {
|
||||
if (sts == KEY_ADDED && license_request_latency_.IsStarted()) {
|
||||
metrics_->cdm_session_license_request_latency_ms_.Record(
|
||||
|
||||
@@ -452,7 +452,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
}
|
||||
|
||||
// TODO(rfrias): Refactor to avoid needing to call CdmSession
|
||||
if (cdm_session && cdm_session->SupportsUsageEntries()) {
|
||||
if (cdm_session && cdm_session->supports_usage_info()) {
|
||||
const CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
|
||||
if (NO_ERROR != status) return status;
|
||||
}
|
||||
@@ -796,7 +796,7 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
|
||||
}
|
||||
|
||||
if (!provider_session_token_.empty()) {
|
||||
if (cdm_session && cdm_session->SupportsUsageEntries()) {
|
||||
if (cdm_session && cdm_session->supports_usage_info()) {
|
||||
const CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
|
||||
if (NO_ERROR != status) return sts;
|
||||
}
|
||||
|
||||
@@ -303,12 +303,13 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) {
|
||||
// Set up mocks and expectations for the UpdateUsageEntryInformation call.
|
||||
EXPECT_CALL(*crypto_session_, HasUsageInfoSupport(_))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<0>(true), Return(true)));
|
||||
EXPECT_CALL(*license_parser_, provider_session_token())
|
||||
.WillRepeatedly(Return("Mock provider session token"));
|
||||
EXPECT_CALL(usage_table_header_, UpdateEntry(_, NotNull(), NotNull()))
|
||||
.WillRepeatedly(Return(NO_ERROR));
|
||||
|
||||
EXPECT_EQ(NO_ERROR, cdm_session_->Init(nullptr));
|
||||
cdm_session_->SetProviderSessionTokenForTest("Mock provider session token");
|
||||
EXPECT_TRUE(cdm_session_->SupportsUsageEntries());
|
||||
EXPECT_TRUE(cdm_session_->supports_usage_info());
|
||||
EXPECT_EQ(NO_ERROR, cdm_session_->UpdateUsageEntryInformation());
|
||||
|
||||
// Verify the UsageEntry metric is set.
|
||||
|
||||
Reference in New Issue
Block a user