Files
android/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp
Aaron Vaage edb9f00df7 Widevine Metrics System
This change is the complete Widevine metrics system. It will
measure and record runtime information about what is happening
in the CDM - such as errors and throughput.

Bug: 33745339
Bug: 26027857
Change-Id: Ic9a82074f1e2b72c72d751b235f8ae361232787d
2017-01-27 16:59:17 -08:00

509 lines
15 KiB
C++

// Copyright 2013 Google Inc. All Rights Reserved.
#include "wv_content_decryption_module.h"
#include "ami_adapter.h"
#include "cdm_client_property_set.h"
#include "cdm_engine.h"
#include "initialization_data.h"
#include "license.h"
#include "log.h"
#include "metrics_front_end.h"
#include "properties.h"
#include "service_certificate.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
namespace {
const int kCdmPolicyTimerDurationSeconds = 1;
}
namespace wvcdm {
Lock WvContentDecryptionModule::session_sharing_id_generation_lock_;
WvContentDecryptionModule::WvContentDecryptionModule() {
report_root_ = new AmiAdapter();
front_end_ = new metrics::MetricsFrontEnd(report_root_);
metrics::MetricsFrontEnd::OverrideInstance(front_end_);
}
WvContentDecryptionModule::~WvContentDecryptionModule() {
DisablePolicyTimer(true);
metrics::MetricsFrontEnd::OverrideInstance(NULL);
delete front_end_;
delete report_root_;
front_end_ = NULL;
report_root_ = NULL;
}
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();
}
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()) {
AutoLock 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;
M_TIME(
sts = cdm_engine->OpenSession(
key_system,
property_set,
event_listener,
session_id),
cdm_engine->GetMetrics(),
cdm_engine_open_session_,
sts);
if (sts == NO_ERROR) {
cdm_by_session_id_[*session_id] = cdm_engine;
}
return sts;
}
CdmResponseType WvContentDecryptionModule::CloseSession(
const CdmSessionId& session_id) {
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
// TODO(rfrias): Avoid reusing the error codes from CdmEngine.
if (!cdm_engine) return SESSION_NOT_FOUND_1;
CdmResponseType sts;
M_TIME(
sts = cdm_engine->CloseSession(
session_id),
cdm_engine->GetMetrics(),
cdm_engine_close_session_,
sts);
if (sts == NO_ERROR) {
cdm_by_session_id_.erase(session_id);
}
DisablePolicyTimer(false);
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) {
M_TIME(
sts = cdm_engine->OpenKeySetSession(
key_set_id,
property_set,
NULL),
cdm_engine->GetMetrics(),
cdm_engine_open_key_set_session_,
sts);
if (sts != NO_ERROR) return sts;
cdm_by_session_id_[key_set_id] = cdm_engine;
}
InitializationData initialization_data(init_data_type, init_data);
M_TIME(
sts = cdm_engine->GenerateKeyRequest(
session_id,
key_set_id,
initialization_data,
license_type,
app_parameters,
key_request),
cdm_engine->GetMetrics(),
cdm_engine_generate_key_request_,
sts);
switch(license_type) {
case kLicenseTypeRelease:
if (sts != KEY_MESSAGE) {
M_TIME(
cdm_engine->CloseKeySetSession(
key_set_id),
cdm_engine->GetMetrics(),
cdm_engine_close_key_set_session_);
cdm_by_session_id_.erase(key_set_id);
}
break;
default:
if (sts == KEY_MESSAGE)
EnablePolicyTimer();
break;
}
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;
CdmResponseType sts;
M_TIME(
sts = cdm_engine->AddKey(
session_id,
key_data,
key_set_id),
cdm_engine->GetMetrics(),
cdm_engine_add_key_,
sts);
if (sts == KEY_ADDED && session_id.empty()) { // license type release
M_TIME(
cdm_engine->CloseKeySetSession(
*key_set_id),
cdm_engine->GetMetrics(),
cdm_engine_close_key_set_session_);
cdm_by_session_id_.erase(*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;
M_TIME(
sts = cdm_engine->RestoreKey(
session_id,
key_set_id),
cdm_engine->GetMetrics(),
cdm_engine_restore_key_,
sts);
if (sts == KEY_ADDED)
EnablePolicyTimer();
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;
M_TIME(
sts = cdm_engine->RemoveKeys(
session_id),
cdm_engine->GetMetrics(),
cdm_engine_remove_keys_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::QueryStatus(
SecurityLevel security_level,
const std::string& key,
std::string* value) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->QueryStatus(
security_level,
key,
value),
cdm_engine->GetMetrics(),
cdm_engine_query_status_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::QuerySessionStatus(
const CdmSessionId& session_id, CdmQueryMap* key_info) {
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
if (!cdm_engine) return SESSION_NOT_FOUND_8;
CdmResponseType sts;
M_TIME(
sts = cdm_engine->QuerySessionStatus(
session_id,
key_info),
cdm_engine->GetMetrics(),
cdm_engine_query_session_status_,
sts);
return sts;
}
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;
M_TIME(
sts = cdm_engine->QueryKeyStatus(
session_id,
key_info),
cdm_engine->GetMetrics(),
cdm_engine_query_key_status_,
sts);
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;
CdmResponseType sts;
M_TIME(
sts = cdm_engine->QueryOemCryptoSessionId(
session_id,
response),
cdm_engine->GetMetrics(),
cdm_engine_query_oemcrypto_session_id_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::GetProvisioningRequest(
CdmCertificateType cert_type,
const std::string& cert_authority,
const CdmIdentifier& identifier,
CdmProvisioningRequest* request,
std::string* default_url) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->GetProvisioningRequest(
cert_type,
cert_authority,
request,
default_url),
cdm_engine->GetMetrics(),
cdm_engine_get_provisioning_request_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse(
const CdmIdentifier& identifier,
CdmProvisioningResponse& response,
std::string* cert,
std::string* wrapped_key) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->HandleProvisioningResponse(
response,
cert,
wrapped_key),
cdm_engine->GetMetrics(),
cdm_engine_handle_provisioning_response_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::Unprovision(
CdmSecurityLevel level, const CdmIdentifier& identifier) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->Unprovision(
level),
cdm_engine->GetMetrics(),
cdm_engine_unprovision_,
sts,
level);
return sts;
}
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
const std::string& app_id, CdmUsageInfo* usage_info) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->GetUsageInfo(
app_id,
usage_info),
cdm_engine->GetMetrics(),
cdm_engine_get_usage_info_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
const std::string& app_id,
const CdmSecureStopId& ssid,
CdmUsageInfo* usage_info) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->GetUsageInfo(
app_id,
ssid,
usage_info),
cdm_engine->GetMetrics(),
cdm_engine_get_usage_info_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::ReleaseAllUsageInfo(
const std::string& app_id) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->ReleaseAllUsageInfo(
app_id),
cdm_engine->GetMetrics(),
cdm_engine_release_all_usage_info_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo(
const CdmUsageInfoReleaseMessage& message) {
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
CdmResponseType sts;
M_TIME(
sts = cdm_engine->ReleaseUsageInfo(
message),
cdm_engine->GetMetrics(),
cdm_engine_release_usage_info_,
sts);
return sts;
}
CdmResponseType WvContentDecryptionModule::Decrypt(
const CdmSessionId& session_id,
bool validate_key_id,
const CdmDecryptionParameters& 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) return SESSION_NOT_FOUND_FOR_DECRYPT;
CdmSessionId local_session_id = session_id;
if (validate_key_id &&
Properties::GetSessionSharingId(session_id) != 0) {
bool status;
M_TIME(
status = cdm_engine->FindSessionForKey(
*parameters.key_id,
&local_session_id),
cdm_engine->GetMetrics(),
cdm_engine_find_session_for_key_,
status);
if (!status) {
LOGE("WvContentDecryptionModule::Decrypt: unable to find session");
return SESSION_NOT_FOUND_FOR_DECRYPT;
}
}
CdmResponseType sts;
M_TIME(
sts = cdm_engine->Decrypt(
local_session_id,
parameters),
cdm_engine->GetMetrics(),
cdm_engine_decrypt_,
sts);
return sts;
}
void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id,
uint32_t width,
uint32_t height) {
CdmEngine* cdm_engine = GetCdmForSessionId(session_id);
if (!cdm_engine) return;
M_TIME(
cdm_engine->NotifyResolution(session_id, width, height),
cdm_engine->GetMetrics(),
cdm_engine_notify_resolution_);
}
bool WvContentDecryptionModule::IsValidServiceCertificate(
const std::string& certificate) {
return ServiceCertificate::VerifySignedServiceCertificate(certificate) ==
NO_ERROR;
}
WvContentDecryptionModule::CdmInfo::CdmInfo()
: cdm_engine(new CdmEngine(&file_system)) {}
CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier(
const CdmIdentifier& identifier) {
AutoLock auto_lock(cdms_lock_);
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.SetOrigin(identifier.origin);
cdms_[identifier].file_system.SetIdentifier(
identifier.spoid + identifier.origin);
}
return cdms_[identifier].cdm_engine.get();
}
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 NULL;
return it->second;
}
void WvContentDecryptionModule::EnablePolicyTimer() {
AutoLock auto_lock(policy_timer_lock_);
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, kCdmPolicyTimerDurationSeconds);
}
void WvContentDecryptionModule::DisablePolicyTimer(bool force) {
bool has_sessions = false;
{
AutoLock auto_lock(cdms_lock_);
for (auto it = cdms_.begin(); it != cdms_.end();) {
if (it->second.cdm_engine->SessionSize() != 0) {
has_sessions = true;
++it;
} else {
// The CDM is no longer used for this identifier, delete it.
it = cdms_.erase(it);
}
}
}
AutoLock auto_lock(policy_timer_lock_);
if ((!has_sessions || force) && policy_timer_.IsRunning())
policy_timer_.Stop();
}
void WvContentDecryptionModule::OnTimerEvent() {
AutoLock auto_lock(cdms_lock_);
for (auto it = cdms_.begin(); it != cdms_.end(); ++it) {
it->second.cdm_engine->OnTimerEvent();
}
}
uint32_t WvContentDecryptionModule::GenerateSessionSharingId() {
static int next_session_sharing_id = 0;
return ++next_session_sharing_id;
}
} // namespace wvcdm