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.
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_;

View File

@@ -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_;

View File

@@ -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();
}
}
}

View File

@@ -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(

View File

@@ -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;
}

View File

@@ -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.