[ Merge of http://go/wvgerrit/101443 ] The WVDrmPlugin has a single CdmIdentifier. The CdmIdentifier contains a SPOID that is calculated from the device ID (keybox or OEM cert), an application reverse domain name and possibly an origin. The CdmIdentifier is set and SPOID calculated on certain calls into WVDrmPlugin. Once it is set, it will not be recalculated. We prevent certain operations such as modifying the origin once the CdmIdentifier has been set as this will require recalculating the SPOID. Recalculating the SPOID may affect open sessions or calls in progress. In a similar way, modifying the security level, will affect the Device ID value and in turn the SPOID. The security level cannot be modified if any sessions are open. This does leave open the possibility that the SPOID may be calculated at one security level, sessions are then closed, and the security level is then changed without an error being flagged. The provisioning certificate file name is based on the SPOID. When the SPOID does not match the security level, either the provisioning information may not be found even though that security level has been provisionined or the provisioning information may be stored in an incorrect location if provisioning occurs. The correct solution is to prevent modifications to the security level once the CdmIdentifier is set. This is a behavior change and might impact apps. We will reevaluate this for the next release. For now, we will work around this. When the CdmIdentifier is set for L3, we will calculate SPOIDs with both L1 and L3 device IDs and check if provisioning previously occurred with SPOIDs calculated for that level. If so, use that level, otherwise use L3. Bug: 147703382 Test: Android unit/integration tests, GtsMediaDrmTests Change-Id: Ia64adfc5848e431ee3876af03eebdb4b6eb83116
587 lines
22 KiB
C++
587 lines
22 KiB
C++
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
|
|
// source code may only be used and distributed under the Widevine Master
|
|
// License Agreement.
|
|
|
|
#include "wv_content_decryption_module.h"
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
#include "cdm_client_property_set.h"
|
|
#include "cdm_engine.h"
|
|
#include "cdm_engine_factory.h"
|
|
#include "initialization_data.h"
|
|
#include "license.h"
|
|
#include "log.h"
|
|
#include "metrics.pb.h"
|
|
#include "properties.h"
|
|
#include "service_certificate.h"
|
|
#include "string_conversions.h"
|
|
#include "wv_cdm_constants.h"
|
|
#include "wv_cdm_event_listener.h"
|
|
|
|
namespace {
|
|
const int kCdmTimerDurationSeconds = 1;
|
|
}
|
|
|
|
namespace wvcdm {
|
|
|
|
std::mutex WvContentDecryptionModule::session_sharing_id_generation_lock_;
|
|
|
|
WvContentDecryptionModule::WvContentDecryptionModule() {}
|
|
|
|
WvContentDecryptionModule::~WvContentDecryptionModule() {
|
|
CryptoSession::DisableDelayedTermination();
|
|
CloseAllCdms();
|
|
CryptoSession::TryTerminate();
|
|
DisableTimerAndWaitForExit();
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) {
|
|
return InitializationData(init_data_type).is_supported();
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsCenc(const std::string& init_data_type) {
|
|
return InitializationData(init_data_type).is_cenc();
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsWebm(const std::string& init_data_type) {
|
|
return InitializationData(init_data_type).is_webm();
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsHls(const std::string& init_data_type) {
|
|
return InitializationData(init_data_type).is_hls();
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsAudio(const std::string& init_data_type) {
|
|
return InitializationData(init_data_type).is_audio();
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::OpenSession(
|
|
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
|
const CdmIdentifier& identifier, WvCdmEventListener* event_listener,
|
|
CdmSessionId* session_id) {
|
|
if (property_set && property_set->is_session_sharing_enabled()) {
|
|
std::unique_lock<std::mutex> auto_lock(session_sharing_id_generation_lock_);
|
|
if (property_set->session_sharing_id() == 0)
|
|
property_set->set_session_sharing_id(GenerateSessionSharingId());
|
|
}
|
|
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
CdmResponseType sts = cdm_engine->OpenSession(key_system, property_set,
|
|
event_listener, session_id);
|
|
if (sts == NO_ERROR) {
|
|
cdm_by_session_id_[*session_id] = cdm_engine;
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::CloseSession(
|
|
const CdmSessionId& session_id) {
|
|
LOGV("Closing session ID: %s", session_id.c_str());
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
// TODO(rfrias): Avoid reusing the error codes from CdmEngine.
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_1;
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
CdmResponseType sts = cdm_engine->CloseSession(session_id);
|
|
if (sts == NO_ERROR) {
|
|
cdm_by_session_id_.erase(session_id);
|
|
}
|
|
|
|
return sts;
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsOpenSession(const CdmSessionId& session_id) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
return cdm_engine && cdm_engine->IsOpenSession(session_id);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GenerateKeyRequest(
|
|
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
|
|
const std::string& init_data_type, const CdmInitData& init_data,
|
|
const CdmLicenseType license_type, CdmAppParameterMap& app_parameters,
|
|
CdmClientPropertySet* property_set, const CdmIdentifier& identifier,
|
|
CdmKeyRequest* key_request) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
CdmResponseType sts;
|
|
if (license_type == kLicenseTypeRelease) {
|
|
sts = cdm_engine->OpenKeySetSession(key_set_id, property_set, nullptr);
|
|
if (sts != NO_ERROR) return sts;
|
|
cdm_by_session_id_[key_set_id] = cdm_engine;
|
|
}
|
|
|
|
const SecurityLevel requested_security_level =
|
|
property_set && property_set->security_level().compare(
|
|
wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0
|
|
? wvcdm::kLevel3
|
|
: wvcdm::kLevelDefault;
|
|
|
|
std::string oec_version;
|
|
sts = cdm_engine->QueryStatus(requested_security_level,
|
|
QUERY_KEY_OEMCRYPTO_API_VERSION, &oec_version);
|
|
if (sts != NO_ERROR) {
|
|
return sts;
|
|
}
|
|
InitializationData initialization_data(init_data_type, init_data,
|
|
oec_version);
|
|
|
|
sts = cdm_engine->GenerateKeyRequest(session_id, key_set_id,
|
|
initialization_data, license_type,
|
|
app_parameters, key_request);
|
|
if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) {
|
|
cdm_engine->CloseKeySetSession(key_set_id);
|
|
cdm_by_session_id_.erase(key_set_id);
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::AddKey(
|
|
const CdmSessionId& session_id, const CdmKeyResponse& key_data,
|
|
CdmKeySetId* key_set_id) {
|
|
CdmEngine* cdm_engine = session_id.empty() ? GetCdmForSessionId(*key_set_id)
|
|
: GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_3;
|
|
// Save key_set_id, as CDM will return an empty key_set_id on release
|
|
CdmKeySetId release_key_set_id;
|
|
if (session_id.empty() && key_set_id != nullptr) {
|
|
release_key_set_id = *key_set_id;
|
|
}
|
|
CdmResponseType sts;
|
|
CdmLicenseType license_type;
|
|
sts = cdm_engine->AddKey(session_id, key_data, &license_type, key_set_id);
|
|
// Empty session id indicates license type release.
|
|
if (sts == KEY_ADDED && session_id.empty()) {
|
|
cdm_engine->CloseKeySetSession(release_key_set_id);
|
|
cdm_by_session_id_.erase(release_key_set_id);
|
|
}
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::RestoreKey(
|
|
const CdmSessionId& session_id, const CdmKeySetId& key_set_id) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_4;
|
|
CdmResponseType sts;
|
|
sts = cdm_engine->RestoreKey(session_id, key_set_id);
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::RemoveKeys(
|
|
const CdmSessionId& session_id) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_5;
|
|
CdmResponseType sts = cdm_engine->RemoveKeys(session_id);
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::QueryStatus(
|
|
SecurityLevel security_level, const std::string& key, std::string* value) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
|
|
return cdm_engine->QueryStatus(security_level, key, value);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::QuerySessionStatus(
|
|
const CdmSessionId& session_id, CdmQueryMap* key_info) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_8;
|
|
return cdm_engine->QuerySessionStatus(session_id, key_info);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::QueryKeyStatus(
|
|
const CdmSessionId& session_id, CdmQueryMap* key_info) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_9;
|
|
CdmResponseType sts;
|
|
sts = cdm_engine->QueryKeyStatus(session_id, key_info);
|
|
return sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::QueryOemCryptoSessionId(
|
|
const CdmSessionId& session_id, CdmQueryMap* response) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return SESSION_NOT_FOUND_10;
|
|
return cdm_engine->QueryOemCryptoSessionId(session_id, response);
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsSecurityLevelSupported(
|
|
CdmSecurityLevel level) {
|
|
return CdmEngine::IsSecurityLevelSupported(level);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetProvisioningRequest(
|
|
CdmCertificateType cert_type, const std::string& cert_authority,
|
|
const CdmIdentifier& identifier, const std::string& service_certificate,
|
|
SecurityLevel requested_security_level, CdmProvisioningRequest* request,
|
|
std::string* default_url) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->GetProvisioningRequest(
|
|
cert_type, cert_authority, service_certificate, requested_security_level,
|
|
request, default_url);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
|
|
const CdmIdentifier& identifier, CdmProvisioningResponse& response,
|
|
SecurityLevel requested_security_level, std::string* cert,
|
|
std::string* wrapped_key) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->HandleProvisioningResponse(
|
|
response, requested_security_level, cert, wrapped_key);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::Unprovision(
|
|
CdmSecurityLevel level, const CdmIdentifier& identifier) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
// Persistent state is deleted on unprovisioning. The L3 OEMCrypto device
|
|
// key may however remain in memory until |OEMCrypto_Terminate| is called.
|
|
// It is not regenerated until |OEMCrypto_Initialize| is called.
|
|
// Enable immediate OEMCrypto termination and re-initalization on
|
|
// unprovisioning.
|
|
CryptoSession::DisableDelayedTermination();
|
|
return cdm_engine->Unprovision(level);
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsProvisioned(CdmSecurityLevel security_level,
|
|
const std::string& origin,
|
|
const std::string& spoid,
|
|
bool atsc_mode_enabled) {
|
|
FileSystem file_system;
|
|
file_system.set_origin(origin);
|
|
file_system.set_identifier(spoid + origin);
|
|
DeviceFiles device_files(&file_system);
|
|
device_files.Init(security_level);
|
|
return device_files.HasCertificate(atsc_mode_enabled);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
|
|
const std::string& app_id, const CdmIdentifier& identifier,
|
|
CdmUsageInfo* usage_info) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
int error_detail = NO_ERROR;
|
|
return cdm_engine->GetUsageInfo(app_id, &error_detail, usage_info);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
|
|
const std::string& app_id, const CdmSecureStopId& ssid,
|
|
const CdmIdentifier& identifier, CdmUsageInfo* usage_info) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
int error_detail = NO_ERROR;
|
|
return cdm_engine->GetUsageInfo(app_id, ssid, &error_detail, usage_info);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::RemoveAllUsageInfo(
|
|
const std::string& app_id, const CdmIdentifier& identifier) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->RemoveAllUsageInfo(app_id);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::RemoveUsageInfo(
|
|
const std::string& app_id, const CdmIdentifier& identifier,
|
|
const CdmSecureStopId& secure_stop_id) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->RemoveUsageInfo(app_id, secure_stop_id);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo(
|
|
const CdmUsageInfoReleaseMessage& message,
|
|
const CdmIdentifier& identifier) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->ReleaseUsageInfo(message);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetSecureStopIds(
|
|
const std::string& app_id, const CdmIdentifier& identifier,
|
|
std::vector<CdmSecureStopId>* ssids) {
|
|
if (ssids == nullptr) {
|
|
LOGE("Secure stop IDs destination not provided");
|
|
return PARAMETER_NULL;
|
|
}
|
|
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
CdmResponseType sts =
|
|
cdm_engine->ListUsageIds(app_id, kSecurityLevelL1, nullptr, ssids);
|
|
std::vector<CdmSecureStopId> secure_stop_ids;
|
|
CdmResponseType sts_l3 = cdm_engine->ListUsageIds(app_id, kSecurityLevelL3,
|
|
nullptr, &secure_stop_ids);
|
|
ssids->insert(ssids->end(), secure_stop_ids.begin(), secure_stop_ids.end());
|
|
return sts_l3 != NO_ERROR ? sts_l3 : sts;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::Decrypt(
|
|
const CdmSessionId& session_id, bool validate_key_id,
|
|
const CdmDecryptionParameters& parameters) {
|
|
return DecryptV16(session_id, validate_key_id,
|
|
CdmDecryptionParametersV16::from_v15(parameters));
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::DecryptV16(
|
|
const CdmSessionId& session_id, bool validate_key_id,
|
|
const CdmDecryptionParametersV16& parameters) {
|
|
// First find the CdmEngine that has the given session_id. If we are using
|
|
// key sharing, the shared session will still be in the same CdmEngine.
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) {
|
|
LOGE("Decrypt session ID not found: %s", session_id.c_str());
|
|
return SESSION_NOT_FOUND_18;
|
|
}
|
|
|
|
CdmSessionId local_session_id = session_id;
|
|
if (validate_key_id && Properties::GetSessionSharingId(session_id) != 0) {
|
|
bool status =
|
|
cdm_engine->FindSessionForKey(parameters.key_id, &local_session_id);
|
|
if (!status) {
|
|
bool is_any_protected = std::any_of(
|
|
std::begin(parameters.samples), std::end(parameters.samples),
|
|
[](const CdmDecryptionSample& sample) -> bool {
|
|
return std::any_of(
|
|
std::begin(sample.subsamples), std::end(sample.subsamples),
|
|
[](const CdmDecryptionSubsample& subsample) -> bool {
|
|
return subsample.protected_bytes > 0;
|
|
});
|
|
});
|
|
|
|
// A key does not need to be loaded if
|
|
// (a) the content consists of one or more samples and is entirely
|
|
// clear data
|
|
// (b) (legacy) the content consists of clear lead/frame in a single
|
|
// subsample. In earlier releases content was presented for decryption
|
|
// as individual subsamples. If the clear frame is broken up across
|
|
// multiple subsamples decryption, requests should be rejected
|
|
// (b/110251447)
|
|
// TODO(b/149524614): Remove support for the legacy case
|
|
if (is_any_protected ||
|
|
(parameters.observe_legacy_fields && parameters.samples.size() == 1 &&
|
|
parameters.samples[0].subsamples.size() == 1 &&
|
|
!(parameters.samples[0].subsamples[0].flags &
|
|
OEMCrypto_FirstSubsample &&
|
|
parameters.samples[0].subsamples[0].flags &
|
|
OEMCrypto_LastSubsample))) {
|
|
return KEY_NOT_FOUND_IN_SESSION;
|
|
}
|
|
}
|
|
}
|
|
return cdm_engine->DecryptV16(local_session_id, parameters);
|
|
}
|
|
|
|
void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id,
|
|
uint32_t width,
|
|
uint32_t height) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
if (!cdm_engine) return;
|
|
cdm_engine->NotifyResolution(session_id, width, height);
|
|
}
|
|
|
|
bool WvContentDecryptionModule::IsValidServiceCertificate(
|
|
const std::string& certificate) {
|
|
ServiceCertificate cert;
|
|
CdmResponseType status = cert.Init(certificate);
|
|
if (status != NO_ERROR) return false;
|
|
return cert.has_certificate();
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetMetrics(
|
|
const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) {
|
|
if (!metrics) {
|
|
return PARAMETER_NULL;
|
|
}
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
auto it = cdms_.find(identifier);
|
|
if (it == cdms_.end()) {
|
|
LOGE("Cdm Identifier not found");
|
|
// TODO(blueeyes): Add a better error.
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
return it->second.cdm_engine->GetMetricsSnapshot(metrics) ? NO_ERROR
|
|
: UNKNOWN_ERROR;
|
|
}
|
|
|
|
WvContentDecryptionModule::CdmInfo::CdmInfo()
|
|
: cdm_engine(CdmEngineFactory::CreateCdmEngine(&file_system)) {}
|
|
|
|
CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier(
|
|
const CdmIdentifier& identifier) {
|
|
CdmEngine* cdm_engine;
|
|
bool enable_timer = false;
|
|
{
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
if (cdms_.size() == 0) enable_timer = true;
|
|
if (cdms_.find(identifier) == cdms_.end()) {
|
|
// Accessing the map entry will create a new instance using the default
|
|
// constructor. We then need to provide it with two pieces of info: The
|
|
// origin provided by the app and an identifier that uniquely identifies
|
|
// this CDM. We concatenate all pieces of the CdmIdentifier in order to
|
|
// create an ID that is unique to that identifier.
|
|
cdms_[identifier].file_system.set_origin(identifier.origin);
|
|
cdms_[identifier].file_system.set_identifier(identifier.spoid +
|
|
identifier.origin);
|
|
cdms_[identifier].cdm_engine->SetAppPackageName(
|
|
identifier.app_package_name);
|
|
cdms_[identifier].cdm_engine->SetSpoid(identifier.spoid);
|
|
}
|
|
cdm_engine = cdms_[identifier].cdm_engine.get();
|
|
}
|
|
// Do not enable timer while holding on to the |cdms_lock_|
|
|
if (enable_timer) EnableTimer();
|
|
|
|
return cdm_engine;
|
|
}
|
|
|
|
CdmEngine* WvContentDecryptionModule::GetCdmForSessionId(
|
|
const std::string& session_id) {
|
|
// Use find to avoid creating empty entries when not found.
|
|
auto it = cdm_by_session_id_.find(session_id);
|
|
if (it == cdm_by_session_id_.end()) return nullptr;
|
|
return it->second;
|
|
}
|
|
|
|
void WvContentDecryptionModule::CloseAllCdms() {
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
|
|
for (auto it = cdms_.begin(); it != cdms_.end();) {
|
|
it = cdms_.erase(it);
|
|
}
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::CloseCdm(
|
|
const CdmIdentifier& cdm_identifier) {
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
auto it = cdms_.find(cdm_identifier);
|
|
if (it == cdms_.end()) {
|
|
LOGE("Cdm Identifier not found");
|
|
// TODO(blueeyes): Create a better error.
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
// Remove any sessions that point to this engine.
|
|
for (auto session_it = cdm_by_session_id_.begin();
|
|
session_it != cdm_by_session_id_.end();) {
|
|
if (session_it->second == it->second.cdm_engine.get()) {
|
|
session_it = cdm_by_session_id_.erase(session_it);
|
|
} else {
|
|
++session_it;
|
|
}
|
|
}
|
|
cdms_.erase(it);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::SetDecryptHash(
|
|
const std::string& hash_data, CdmSessionId* id) {
|
|
if (id == nullptr) {
|
|
LOGE("Cdm session ID not provided");
|
|
return PARAMETER_NULL;
|
|
}
|
|
|
|
uint32_t frame_number;
|
|
std::string hash;
|
|
CdmResponseType res =
|
|
CdmEngine::ParseDecryptHashString(hash_data, id, &frame_number, &hash);
|
|
|
|
if (res != NO_ERROR) return res;
|
|
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(*id);
|
|
|
|
if (!cdm_engine) {
|
|
LOGE("Unable to find CdmEngine");
|
|
return SESSION_NOT_FOUND_20;
|
|
}
|
|
|
|
res = cdm_engine->SetDecryptHash(*id, frame_number, hash);
|
|
return res;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetDecryptHashError(
|
|
const CdmSessionId& session_id, std::string* hash_error_string) {
|
|
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
|
|
|
|
if (!cdm_engine) {
|
|
LOGE("Unable to find CdmEngine");
|
|
return SESSION_NOT_FOUND_20;
|
|
}
|
|
return cdm_engine->GetDecryptHashError(session_id, hash_error_string);
|
|
}
|
|
|
|
void WvContentDecryptionModule::EnableTimer() {
|
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
|
if (!timer_.IsRunning()) timer_.Start(this, kCdmTimerDurationSeconds);
|
|
}
|
|
|
|
void WvContentDecryptionModule::DisableTimer() {
|
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
|
if (timer_.IsRunning()) {
|
|
timer_.Stop(false);
|
|
}
|
|
}
|
|
|
|
void WvContentDecryptionModule::DisableTimerAndWaitForExit() {
|
|
std::unique_lock<std::mutex> auto_lock(timer_lock_);
|
|
if (timer_.IsRunning()) {
|
|
timer_.Stop(true);
|
|
}
|
|
}
|
|
|
|
void WvContentDecryptionModule::OnTimerEvent() {
|
|
bool disable_timer = false;
|
|
{
|
|
std::unique_lock<std::mutex> auto_lock(cdms_lock_);
|
|
for (auto it = cdms_.begin(); it != cdms_.end(); ++it) {
|
|
it->second.cdm_engine->OnTimerEvent();
|
|
}
|
|
if (cdms_.empty()) {
|
|
if (CryptoSession::TryTerminate()) {
|
|
// If CryptoSession is in a state to be terminated, try acquiring the
|
|
// |timer_lock_| before deciding whether to disable the timer. If the
|
|
// lock cannot be acquired, there is no need to disable the timer.
|
|
// The |timer_lock_| is either being held by
|
|
// * EnableTimer - This does not appear possible, but if it did
|
|
// happen |OnTimerEvent| will be called again. The timer can be
|
|
// disabled during a future call.
|
|
// * DisableTimer - If so, allow that call to disable the timer. No
|
|
// need to call to disable it here.
|
|
// * DisableTimerAndWaitForExit - If so, allow that call to disable
|
|
// the timer. No need to call to disable it here. Note that
|
|
// |DisableTimerAndWaitForExit| will try to stop the timer but
|
|
// wait for it to exit. This might kick off the timer thread one
|
|
// last time, which will call into OnTimerEvent. Calling
|
|
// |DisableTimer| here will result in deadlock.
|
|
if (timer_lock_.try_lock()) {
|
|
disable_timer = true;
|
|
timer_lock_.unlock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Release |cdms_lock_| before attempting to disable the timer
|
|
if (disable_timer) {
|
|
DisableTimer();
|
|
}
|
|
}
|
|
|
|
uint32_t WvContentDecryptionModule::GenerateSessionSharingId() {
|
|
static int next_session_sharing_id = 0;
|
|
return ++next_session_sharing_id;
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::ListStoredLicenses(
|
|
CdmSecurityLevel security_level, const CdmIdentifier& identifier,
|
|
std::vector<CdmKeySetId>* key_set_ids) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->ListStoredLicenses(security_level, key_set_ids);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::GetOfflineLicenseState(
|
|
const CdmKeySetId& key_set_id, CdmSecurityLevel security_level,
|
|
const CdmIdentifier& identifier, CdmOfflineLicenseState* license_state) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->GetOfflineLicenseState(key_set_id, security_level,
|
|
license_state);
|
|
}
|
|
|
|
CdmResponseType WvContentDecryptionModule::RemoveOfflineLicense(
|
|
const CdmKeySetId& key_set_id, CdmSecurityLevel security_level,
|
|
const CdmIdentifier& identifier) {
|
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
|
return cdm_engine->RemoveOfflineLicense(key_set_id, security_level);
|
|
}
|
|
|
|
} // namespace wvcdm
|