Revert "Core CDM: Removed secure stop support."

This reverts commit b039f31b27.

Reason for revert: Feature rejected by Android

Bug: 242289743
Change-Id: I8cd6014b4e2de93b3c574d407d6c8885863fed4f
This commit is contained in:
Alex Dale
2022-12-03 00:46:22 +00:00
committed by Android (Google) Code Review
parent b039f31b27
commit 16a4c2690a
6 changed files with 660 additions and 139 deletions

View File

@@ -127,10 +127,10 @@ class CdmEngine {
// stored offline data associated with the session. // stored offline data associated with the session.
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id); virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
// This method removes all offline data associated with the session. It // This method removes all offline data associated with the session, such as
// should be used with care, as it deletes the info immediately and // offline keys and usage info. It should be used with care, as it deletes the
// without using a release message, so the server is not able to // info immediately and without using a release message, so the server is not
// receive usage info or track releases for offline licenses. // able to receive usage info or track releases for offline licenses.
virtual CdmResponseType RemoveLicense(const CdmSessionId& session_id); virtual CdmResponseType RemoveLicense(const CdmSessionId& session_id);
// Construct valid renewal request for the current session keys. // Construct valid renewal request for the current session keys.
@@ -235,32 +235,38 @@ class CdmEngine {
virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id, virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id,
CdmSecurityLevel security_level); CdmSecurityLevel security_level);
// Usage related methods for streaming licenses with persistent usage // Usage related methods for streaming licenses
// information (deprecated). // 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, virtual CdmResponseType GetUsageInfo(const std::string& app_id,
int* error_detail, int* error_detail,
CdmUsageInfo* usage_info); 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, virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid, const CdmSecureStopId& ssid,
int* error_detail, int* error_detail,
CdmUsageInfo* usage_info); CdmUsageInfo* usage_info);
// Retrieve usage info for a given security level and whose PST is // Retrieve usage info for a given security level and whose
// specified by |ssid| (deprecated). // 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, virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid, const CdmSecureStopId& ssid,
RequestedSecurityLevel security_level, RequestedSecurityLevel security_level,
int* error_detail, int* error_detail,
CdmUsageInfo* usage_info); 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, virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id,
CdmSecurityLevel security_level); CdmSecurityLevel security_level);
// Remove all usage records for the current origin. Span all // 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 RemoveAllUsageInfo(const std::string& app_id);
virtual CdmResponseType RemoveUsageInfo( virtual CdmResponseType RemoveUsageInfo(
@@ -400,6 +406,9 @@ class CdmEngine {
CdmSessionId* session_id); CdmSessionId* session_id);
bool ValidateKeySystem(const CdmKeySystem& key_system); 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); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
@@ -434,11 +443,13 @@ class CdmEngine {
std::string spoid_; std::string spoid_;
uint32_t user_id_; uint32_t user_id_;
// Tracks the time of the last update to usage info across all // Usage related variables
// actively managed CDM sessions with loaded usage entries. Used // Used to isolate a single active usage information license. Loading,
// to periodically update a usage entry associated with an active // creating or releasing a different usage licenses through the engine
// license. // API will release the handle to previously active secure stop license.
int64_t last_usage_info_update_time_ = 0; 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. // Protect release_key_sets_ from non-thread-safe operations.
std::mutex release_key_sets_lock_; std::mutex release_key_sets_lock_;

View File

@@ -75,6 +75,12 @@ class CdmSession {
virtual CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id, virtual CdmResponseType RestoreOfflineSession(const CdmKeySetId& key_set_id,
CdmLicenseType license_type, CdmLicenseType license_type,
int* error_detail); 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 CdmSessionId& session_id() { return session_id_; }
virtual const CdmKeySetId& key_set_id() { return key_set_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_initial_usage_update() { return is_initial_usage_update_; }
virtual bool is_usage_update_needed() { return is_usage_update_needed_; } 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_initial_usage_update_ = false;
is_usage_update_needed_ = false; is_usage_update_needed_ = false;
} }
@@ -153,19 +159,12 @@ class CdmSession {
virtual bool is_offline() { return is_offline_; } virtual bool is_offline() { return is_offline_; }
virtual bool is_temporary() { return is_temporary_; } virtual bool is_temporary() { return is_temporary_; }
virtual bool license_received() { return license_received_; } virtual bool license_received() { return license_received_; }
virtual bool has_provider_session_token() {
virtual bool HasUsageEntry() { return (license_parser_ &&
// The PST is only set if a usage entry has been loaded. license_parser_->provider_session_token().size() > 0);
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 SupportsUsageEntries() const { virtual bool supports_usage_info() const {
return usage_table_header_ != nullptr; return usage_table_header_ != nullptr;
} }
@@ -244,6 +243,8 @@ class CdmSession {
bool StoreLicense(CdmOfflineLicenseState state, int* error_detail); bool StoreLicense(CdmOfflineLicenseState state, int* error_detail);
bool UpdateUsageInfo();
CdmResponseType GenerateKeyRequestInternal( CdmResponseType GenerateKeyRequestInternal(
const InitializationData& init_data, CdmLicenseType license_type, const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request); const CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request);
@@ -304,15 +305,14 @@ class CdmSession {
bool last_decrypt_failed_ = false; bool last_decrypt_failed_ = false;
// Usage related flags and data // Usage related flags and data
bool is_initial_usage_update_ = true; bool is_initial_usage_update_;
bool is_usage_update_needed_ = false; bool is_usage_update_needed_;
// Only assign |usage_table_header_| if capable of supporting usage // Only assign |usage_table_header_| if capable of supporting usage
// information. // information.
UsageTableHeader* usage_table_header_ = nullptr; UsageTableHeader* usage_table_header_ = nullptr;
uint32_t usage_entry_number_ = 0; uint32_t usage_entry_number_;
CdmUsageEntry usage_entry_; CdmUsageEntry usage_entry_;
// This PST should only be set if the session has a usage entry. std::string usage_provider_session_token_;
std::string provider_session_token_;
// information useful for offline and usage scenarios // information useful for offline and usage scenarios
CdmKeyMessage key_request_; CdmKeyMessage key_request_;

View File

@@ -31,8 +31,8 @@
namespace wvcdm { namespace wvcdm {
namespace { namespace {
constexpr uint64_t kReleaseSessionTimeToLive = 60; // seconds const uint64_t kReleaseSessionTimeToLive = 60; // seconds
constexpr uint32_t kUpdateUsageInfoPeriod = 60; // seconds const uint32_t kUpdateUsageInformationPeriod = 60; // seconds
} // namespace } // namespace
class UsagePropertySet : public CdmClientPropertySet { class UsagePropertySet : public CdmClientPropertySet {
@@ -67,12 +67,16 @@ CdmEngine::CdmEngine(wvutil::FileSystem* file_system,
: metrics_(metrics), : metrics_(metrics),
cert_provisioning_(), cert_provisioning_(),
file_system_(file_system), file_system_(file_system),
spoid_(EMPTY_SPOID) { spoid_(EMPTY_SPOID),
usage_session_(),
usage_property_set_(),
last_usage_information_update_time_(0) {
assert(file_system); assert(file_system);
Properties::Init(); Properties::Init();
} }
CdmEngine::~CdmEngine() { CdmEngine::~CdmEngine() {
usage_session_.reset();
std::unique_lock<std::recursive_mutex> lock(session_map_lock_); std::unique_lock<std::recursive_mutex> lock(session_map_lock_);
session_map_.Terminate(); session_map_.Terminate();
} }
@@ -411,7 +415,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id,
} }
if (key_set_id != nullptr) { 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(); *key_set_id = session->key_set_id();
LOGI("key_set_id = %s", IdPtrToString(key_set_id)); LOGI("key_set_id = %s", IdPtrToString(key_set_id));
} else { } else {
@@ -1279,21 +1284,50 @@ CdmResponseType CdmEngine::ListStoredLicenses(
CdmResponseType CdmEngine::ListUsageIds( CdmResponseType CdmEngine::ListUsageIds(
const std::string& app_id, CdmSecurityLevel security_level, const std::string& app_id, CdmSecurityLevel security_level,
std::vector<std::string>* /* ksids */, std::vector<std::string>* ksids,
std::vector<std::string>* /* provider_session_tokens */) { std::vector<std::string>* provider_session_tokens) {
LOGI("app_id = %s, security_level = %s", IdToString(app_id), if (!ksids && !provider_session_tokens) {
CdmSecurityLevelToString(security_level)); LOGE("Outputs |ksids| and |provider_session_tokens| are null");
LOGW("API not supported"); return INVALID_PARAMETERS_ENG_23;
return NOT_IMPLEMENTED_ERROR; }
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, CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
CdmSecurityLevel security_level, CdmSecurityLevel security_level,
const std::string& key_set_id) { const std::string& key_set_id) {
LOGI("app_id = %s, security_level = %s, key_set_id = %s", IdToString(app_id), LOGI("app_id = %s, key_set_id = %s", IdToString(app_id),
CdmSecurityLevelToString(security_level), IdToString(key_set_id)); IdToString(key_set_id));
LOGW("API not supported"); if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) {
return NOT_IMPLEMENTED_ERROR; 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( CdmResponseType CdmEngine::GetOfflineLicenseState(
@@ -1363,7 +1397,7 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
} }
} else if (sts == LICENSE_USAGE_ENTRY_MISSING) { } else if (sts == LICENSE_USAGE_ENTRY_MISSING) {
// It is possible that the CDM is tracking a key set ID, but has // 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; // it will no longer be possible to load the license for release;
// and the file should simply be deleted. // and the file should simply be deleted.
LOGW("License usage entry is missing, deleting license file"); LOGW("License usage entry is missing, deleting license file");
@@ -1381,66 +1415,415 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid, const CdmSecureStopId& ssid,
int* /* error_detail */, int* error_detail,
CdmUsageInfo* /* usage_info */) { CdmUsageInfo* usage_info) {
LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid)); // Try to find usage info at the default security level. If the
LOGE("API not supported"); // security level is unprovisioned or we are unable to find it,
return NOT_IMPLEMENTED_ERROR; // 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, CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid, const CdmSecureStopId& ssid,
RequestedSecurityLevel security_level, RequestedSecurityLevel security_level,
int* /* error_detail */, int* error_detail,
CdmUsageInfo* /* usage_info */) { CdmUsageInfo* usage_info) {
LOGI("app_id = %s, ssid = %s, security_level = %s", IdToString(app_id), LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid));
IdToString(ssid), RequestedSecurityLevelToString(security_level)); if (!usage_property_set_) {
LOGE("API not supported"); usage_property_set_.reset(new UsagePropertySet());
return NOT_IMPLEMENTED_ERROR; }
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, CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
int* /* error_detail */, int* error_detail,
CdmUsageInfo* /* usage_info */) { CdmUsageInfo* usage_info) {
LOGI("app_id = %s", IdToString(app_id)); LOGI("app_id = %s", IdToString(app_id));
LOGE("API not supported"); if (usage_info == nullptr) {
return NOT_IMPLEMENTED_ERROR; 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( CdmResponseType CdmEngine::RemoveAllUsageInfo(
const std::string& app_id, CdmSecurityLevel cdm_security_level) { const std::string& app_id, CdmSecurityLevel cdm_security_level) {
LOGI("app_id = %s, security_level = %s", IdToString(app_id), LOGI("app_id = %s, security_level = %s", IdToString(app_id),
CdmSecurityLevelToString(cdm_security_level)); CdmSecurityLevelToString(cdm_security_level));
LOGE("API not supported"); if (!usage_property_set_) {
return NOT_IMPLEMENTED_ERROR; 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) { CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) {
LOGI("app_id = %s", IdToString(app_id)); LOGI("app_id = %s", IdToString(app_id));
LOGE("API not supported"); const CdmResponseType status_l1 =
return NOT_IMPLEMENTED_ERROR; 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( CdmResponseType CdmEngine::RemoveUsageInfo(
const std::string& app_id, const CdmSecureStopId& provider_session_token) { const std::string& app_id, const CdmSecureStopId& provider_session_token) {
LOGI("app_id = %s, pst = %s", IdToString(app_id), LOGI("app_id = %s, pst = %s", IdToString(app_id),
IdToString(provider_session_token)); IdToString(provider_session_token));
LOGE("API not supported"); if (!usage_property_set_) {
return NOT_IMPLEMENTED_ERROR; 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( CdmResponseType CdmEngine::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) { const CdmUsageInfoReleaseMessage& message) {
LOGI("message_size = %zu", message.size()); LOGI("message_size = %zu", message.size());
LOGE("API not supported"); if (!usage_session_) {
return NOT_IMPLEMENTED_ERROR; 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( CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
const CdmKeySetId& key_set_id, CdmKeyMessage* /* release_message */) { CdmKeyMessage* release_message) {
LOGI("key_set_id = %s", IdToString(key_set_id)); LOGI("key_set_id = %s", IdToString(key_set_id));
LOGE("API not supported"); // This method is currently only used by the CE CDM, in which all session IDs
return NOT_IMPLEMENTED_ERROR; // 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( CdmResponseType CdmEngine::DecryptV16(
@@ -1689,7 +2072,6 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id,
bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width, bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height) { uint32_t height) {
std::unique_lock<std::recursive_mutex> lock(session_map_lock_);
std::shared_ptr<CdmSession> session; std::shared_ptr<CdmSession> session;
if (session_map_.FindSession(session_id, &session)) { if (session_map_.FindSession(session_id, &session)) {
session->NotifyResolution(width, height); session->NotifyResolution(width, height);
@@ -1706,9 +2088,10 @@ void CdmEngine::OnTimerEvent() {
wvutil::Clock clock; wvutil::Clock clock;
const uint64_t current_time = clock.GetCurrentTime(); const uint64_t current_time = clock.GetCurrentTime();
bool usage_update_period_expired = false; 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; 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; bool is_initial_usage_update = false;
@@ -1718,24 +2101,27 @@ void CdmEngine::OnTimerEvent() {
CdmSessionList sessions; CdmSessionList sessions;
session_map_.GetSessionList(sessions); session_map_.GetSessionList(sessions);
for (auto& session : sessions) { while (!sessions.empty()) {
is_initial_usage_update = is_initial_usage_update = is_initial_usage_update ||
is_initial_usage_update || session->is_initial_usage_update(); sessions.front()->is_initial_usage_update();
is_usage_update_needed = is_usage_update_needed =
is_usage_update_needed || session->is_usage_update_needed(); is_usage_update_needed || sessions.front()->is_usage_update_needed();
session->OnTimerEvent(usage_update_period_expired);
sessions.front()->OnTimerEvent(usage_update_period_expired);
sessions.pop_front();
} }
if (is_usage_update_needed && if (is_usage_update_needed &&
(usage_update_period_expired || is_initial_usage_update)) { (usage_update_period_expired || is_initial_usage_update)) {
// Session list may have changed. Rebuild. // Session list may have changed. Rebuild.
sessions.clear();
session_map_.GetSessionList(sessions); session_map_.GetSessionList(sessions);
for (auto& session : sessions) { for (CdmSessionList::iterator iter = sessions.begin();
session->ResetUsageFlags(); iter != sessions.end(); ++iter) {
if (session->SupportsUsageEntries() && session->HasUsageEntry()) { (*iter)->reset_usage_flags();
session->UpdateUsageEntryInformation(); if ((*iter)->supports_usage_info() &&
(*iter)->has_provider_session_token()) {
(*iter)->UpdateUsageEntryInformation();
} }
} }
} }

View File

@@ -78,6 +78,10 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
is_temporary_(false), is_temporary_(false),
security_level_(kSecurityLevelUninitialized), security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault), 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_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) { mock_policy_engine_in_use_(false) {
assert(metrics_); // metrics_ must not be null. assert(metrics_); // metrics_ must not be null.
@@ -87,7 +91,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
} }
CdmSession::~CdmSession() { CdmSession::~CdmSession() {
if (HasUsageEntry() && !is_release_) { if (has_provider_session_token() && supports_usage_info() && !is_release_) {
UpdateUsageEntryInformation(); UpdateUsageEntryInformation();
} }
@@ -265,9 +269,9 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
return GET_RELEASED_LICENSE_ERROR; return GET_RELEASED_LICENSE_ERROR;
} }
std::string provider_session_token;
bool sign_fake_request = false; // TODO(b/169483174): remove this variable. bool sign_fake_request = false; // TODO(b/169483174): remove this variable.
if (SupportsUsageEntries()) { if (supports_usage_info()) {
std::string provider_session_token;
if (!license_parser_->ExtractProviderSessionToken( if (!license_parser_->ExtractProviderSessionToken(
key_response_, &provider_session_token)) { key_response_, &provider_session_token)) {
provider_session_token.clear(); provider_session_token.clear();
@@ -284,7 +288,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
IdToString(key_set_id)); IdToString(key_set_id));
return USAGE_ENTRY_ALREADY_LOADED; return USAGE_ENTRY_ALREADY_LOADED;
} }
provider_session_token_ = std::move(provider_session_token);
if (sts != NO_ERROR) { if (sts != NO_ERROR) {
LOGE("Failed to load usage entry: status = %d", static_cast<int>(sts)); LOGE("Failed to load usage entry: status = %d", static_cast<int>(sts));
return sts; return sts;
@@ -293,10 +296,6 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
} else { } else {
sign_fake_request = true; // TODO(b/169483174): remove this block. 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 // 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 // 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 // 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( CdmResponseType sts = usage_table_header_->UpdateEntry(
usage_entry_number_, crypto_session_.get(), &usage_entry_); usage_entry_number_, crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) { if (sts != NO_ERROR) {
@@ -341,16 +340,74 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
return sts; return sts;
} }
if (!StoreLicense(license_data.state, error_detail)) { if (!StoreLicense(license_data.state, error_detail)) {
LOGW("Unable to save updated license"); LOGW("Unable to save updated usage info");
} }
} }
license_received_ = true; license_received_ = true;
is_offline_ = true;
is_release_ = license_type == kLicenseTypeRelease; is_release_ = license_type == kLicenseTypeRelease;
has_license_been_restored_ = true; has_license_been_restored_ = true;
return KEY_ADDED; 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. // This is a thin wrapper that initiates the latency metric.
CdmResponseType CdmSession::GenerateKeyRequest( CdmResponseType CdmSession::GenerateKeyRequest(
const InitializationData& init_data, CdmLicenseType license_type, 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 see if it has a provider session token. If so a new entry needs
// to be created. // to be created.
CdmResponseType sts; CdmResponseType sts;
if (SupportsUsageEntries()) { std::string provider_session_token;
std::string provider_session_token; if (supports_usage_info()) {
if (license_parser_->ExtractProviderSessionToken(key_response, if (license_parser_->ExtractProviderSessionToken(key_response,
&provider_session_token) && &provider_session_token) &&
!provider_session_token.empty()) { !provider_session_token.empty()) {
if (!is_offline_) { std::string app_id;
LOGE("CDM does not support secure stop licenses"); GetApplicationId(&app_id);
return ADD_KEY_ERROR;
}
sts = usage_table_header_->AddEntry( sts = usage_table_header_->AddEntry(
crypto_session_.get(), /* is_persistent */ true, key_set_id_, crypto_session_.get(), is_offline_, key_set_id_,
/* usage_info_filename */ "", key_response, &usage_entry_number_); DeviceFiles::GetUsageInfoFileName(app_id), key_response,
&usage_entry_number_);
crypto_metrics_->usage_table_header_add_entry_.Increment(sts); crypto_metrics_->usage_table_header_add_entry_.Increment(sts);
if (sts != NO_ERROR) return sts; if (sts != NO_ERROR) return sts;
provider_session_token_ = std::move(provider_session_token);
} }
} }
sts = sts =
@@ -502,14 +557,13 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
version_info.license_service_version()); version_info.license_service_version());
// Update or invalidate entry if usage table header+entries are supported // 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) { if (sts != KEY_ADDED) {
const CdmResponseType invalidate_sts = const CdmResponseType invalidate_sts =
usage_table_header_->InvalidateEntry( usage_table_header_->InvalidateEntry(
usage_entry_number_, true, file_handle_.get(), crypto_metrics_); usage_entry_number_, true, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment( crypto_metrics_->usage_table_header_delete_entry_.Increment(
invalidate_sts); invalidate_sts);
provider_session_token_.clear();
if (invalidate_sts != NO_ERROR) { if (invalidate_sts != NO_ERROR) {
LOGW("Invalidate usage entry failed: status = %d", LOGW("Invalidate usage entry failed: status = %d",
static_cast<int>(invalidate_sts)); static_cast<int>(invalidate_sts));
@@ -526,11 +580,15 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
IdToString(license_parser_->provider_session_token()), IdToString(license_parser_->provider_session_token()),
license_parser_->provider_session_token().size()); license_parser_->provider_session_token().size());
if (is_offline_ && !is_temporary_) { if ((is_offline_ || has_provider_session_token()) && !is_temporary_) {
if (HasUsageEntry()) { if (has_provider_session_token() && supports_usage_info()) {
usage_table_header_->UpdateEntry(usage_entry_number_, usage_table_header_->UpdateEntry(usage_entry_number_,
crypto_session_.get(), &usage_entry_); crypto_session_.get(), &usage_entry_);
} }
if (!is_offline_)
usage_provider_session_token_ = license_parser_->provider_session_token();
sts = StoreLicense(); sts = StoreLicense();
if (sts != NO_ERROR) return sts; if (sts != NO_ERROR) return sts;
} }
@@ -642,7 +700,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParametersV16& params) {
} }
has_decrypted_since_last_report_ = true; has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) { if (!is_usage_update_needed_) {
is_usage_update_needed_ = HasUsageEntry(); is_usage_update_needed_ = has_provider_session_token();
} }
last_decrypt_failed_ = false; last_decrypt_failed_ = false;
} else { } else {
@@ -728,7 +786,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
if (KEY_MESSAGE != status) return status; if (KEY_MESSAGE != status) return status;
if (HasUsageEntry()) { if (has_provider_session_token() && supports_usage_info()) {
status = usage_table_header_->UpdateEntry( status = usage_table_header_->UpdateEntry(
usage_entry_number_, crypto_session_.get(), &usage_entry_); 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 (is_offline_) { // Mark license as being released
if (!StoreLicense(kLicenseStateReleasing, nullptr)) if (!StoreLicense(kLicenseStateReleasing, nullptr))
return RELEASE_KEY_REQUEST_ERROR; 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; key_request_type_ = key_request->type;
@@ -771,7 +833,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
LOGE("CDM session not initialized"); LOGE("CDM session not initialized");
return NOT_INITIALIZED_ERROR; return NOT_INITIALIZED_ERROR;
} }
if (!SupportsUsageEntries()) { if (!supports_usage_info()) {
LOGE("Cannot delete entry, usage table not supported"); LOGE("Cannot delete entry, usage table not supported");
return INCORRECT_USAGE_SUPPORT_TYPE_1; return INCORRECT_USAGE_SUPPORT_TYPE_1;
} }
@@ -800,7 +862,6 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
sts = usage_table_header_->InvalidateEntry( sts = usage_table_header_->InvalidateEntry(
usage_entry_number, true, file_handle_.get(), crypto_metrics_); usage_entry_number, true, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(sts); crypto_metrics_->usage_table_header_delete_entry_.Increment(sts);
provider_session_token_.clear();
return sts; return sts;
} }
@@ -847,23 +908,54 @@ bool CdmSession::GenerateKeySetId(bool atsc_mode_enabled,
} }
CdmResponseType CdmSession::StoreLicense() { CdmResponseType CdmSession::StoreLicense() {
if (is_temporary_ || !is_offline_) { if (is_temporary_) {
LOGE("Session type prohibits storage"); LOGE("Session type prohibits storage");
return STORAGE_PROHIBITED; return STORAGE_PROHIBITED;
} }
if (key_set_id_.empty()) {
LOGE("No key set ID"); if (is_offline_) {
return EMPTY_KEYSET_ID; 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()) { std::string app_id;
LOGE("License policy prohibits storage"); GetApplicationId(&app_id);
return OFFLINE_LICENSE_PROHIBITED; 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)) { return STORE_USAGE_INFO_ERROR;
LOGE("Unable to store license");
return STORE_LICENSE_ERROR_1;
} }
return NO_ERROR; return NO_ERROR;
} }
@@ -907,8 +999,8 @@ CdmResponseType CdmSession::RemoveKeys() {
} }
CdmResponseType CdmSession::RemoveLicense() { CdmResponseType CdmSession::RemoveLicense() {
if (is_offline_) { if (is_offline_ || has_provider_session_token()) {
if (HasUsageEntry()) { if (has_provider_session_token() && supports_usage_info()) {
DeleteUsageEntry(usage_entry_number_); DeleteUsageEntry(usage_entry_number_);
} }
DeleteLicenseFile(); DeleteLicenseFile();
@@ -917,8 +1009,17 @@ CdmResponseType CdmSession::RemoveLicense() {
} }
bool CdmSession::DeleteLicenseFile() { bool CdmSession::DeleteLicenseFile() {
if (!is_offline_) return false; if (!is_offline_ && !has_provider_session_token()) return false;
return file_handle_->DeleteLicense(key_set_id_);
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) { void CdmSession::NotifyResolution(uint32_t width, uint32_t height) {
@@ -949,14 +1050,16 @@ void CdmSession::GetApplicationId(std::string* app_id) {
} }
CdmResponseType CdmSession::UpdateUsageEntryInformation() { CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (!HasUsageEntry()) { if (!has_provider_session_token() || !supports_usage_info()) {
LOGE("Session does not have a usage entry"); 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; return INCORRECT_USAGE_SUPPORT_TYPE_2;
} }
CdmResponseType sts = NO_ERROR; CdmResponseType sts = NO_ERROR;
// TODO(blueeyes): Add measurements to all UpdateEntry calls in a way that // 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. // UpdateUsageEntryInformation.
M_TIME(sts = usage_table_header_->UpdateEntry( M_TIME(sts = usage_table_header_->UpdateEntry(
usage_entry_number_, crypto_session_.get(), &usage_entry_), usage_entry_number_, crypto_session_.get(), &usage_entry_),
@@ -964,8 +1067,11 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (sts != NO_ERROR) return sts; if (sts != NO_ERROR) return sts;
StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive, if (is_offline_)
nullptr); StoreLicense(is_release_ ? kLicenseStateReleasing : kLicenseStateActive,
nullptr);
else if (!usage_provider_session_token_.empty())
UpdateUsageInfo();
return NO_ERROR; return NO_ERROR;
} }
@@ -1032,6 +1138,23 @@ CdmResponseType CdmSession::GetDecryptHashError(std::string* error_string) {
return crypto_session_->GetDecryptHashError(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) { void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) {
if (sts == KEY_ADDED && license_request_latency_.IsStarted()) { if (sts == KEY_ADDED && license_request_latency_.IsStarted()) {
metrics_->cdm_session_license_request_latency_ms_.Record( metrics_->cdm_session_license_request_latency_ms_.Record(

View File

@@ -452,7 +452,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
} }
// TODO(rfrias): Refactor to avoid needing to call CdmSession // 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(); const CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return status; if (NO_ERROR != status) return status;
} }
@@ -796,7 +796,7 @@ CdmResponseType CdmLicense::RestoreOfflineLicense(
} }
if (!provider_session_token_.empty()) { 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(); const CdmResponseType status = cdm_session->UpdateUsageEntryInformation();
if (NO_ERROR != status) return sts; if (NO_ERROR != status) return sts;
} }

View File

@@ -303,12 +303,13 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) {
// Set up mocks and expectations for the UpdateUsageEntryInformation call. // Set up mocks and expectations for the UpdateUsageEntryInformation call.
EXPECT_CALL(*crypto_session_, HasUsageInfoSupport(_)) EXPECT_CALL(*crypto_session_, HasUsageInfoSupport(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(true), Return(true))); .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())) EXPECT_CALL(usage_table_header_, UpdateEntry(_, NotNull(), NotNull()))
.WillRepeatedly(Return(NO_ERROR)); .WillRepeatedly(Return(NO_ERROR));
EXPECT_EQ(NO_ERROR, cdm_session_->Init(nullptr)); EXPECT_EQ(NO_ERROR, cdm_session_->Init(nullptr));
cdm_session_->SetProviderSessionTokenForTest("Mock provider session token"); EXPECT_TRUE(cdm_session_->supports_usage_info());
EXPECT_TRUE(cdm_session_->SupportsUsageEntries());
EXPECT_EQ(NO_ERROR, cdm_session_->UpdateUsageEntryInformation()); EXPECT_EQ(NO_ERROR, cdm_session_->UpdateUsageEntryInformation());
// Verify the UsageEntry metric is set. // Verify the UsageEntry metric is set.