Files
android/libwvdrmengine/cdm/core/src/cdm_engine.cpp
Jeff Tinker 4cf8594a87 Replaces staging provisioning server url with production server url
The default provisioning server url now points to the production server.
Also switches to the real field provisioning system ID that works
only on the production servers, and updates the unit tests to work
properly with the prod servers.

Bug: 8724358

Merge of:
  https://widevine-internal-review.googlesource.com/#/c/5270/
  https://widevine-internal-review.googlesource.com/#/c/5550/
  https://widevine-internal-review.googlesource.com/#/c/5321/
  https://widevine-internal-review.googlesource.com/#/c/5501/
from the Widevine CDM repository

Change-Id: Iff1d7349c6a84bf30c6cdd534933ae747d5cff55
2013-05-09 15:54:33 -07:00

810 lines
25 KiB
C++

// Copyright 2013 Google Inc. All Rights Reserved.
#include "cdm_engine.h"
#include <iostream>
#include <sstream>
#include "buffer_reader.h"
#include "cdm_session.h"
#include "crypto_engine.h"
#include "device_files.h"
#include "license_protocol.pb.h"
#include "log.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#ifndef CDM_POLICY_TIMER_DURATION_SECONDS
#define CDM_POLICY_TIMER_DURATION_SECONDS 1
#endif
namespace {
const std::string kDefaultProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
}
namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ProvisioningRequest;
using video_widevine_server::sdk::ProvisioningResponse;
using video_widevine_server::sdk::SignedProvisioningMessage;
typedef std::map<CdmSessionId,CdmSession*>::const_iterator CdmSessionIter;
CdmEngine::CdmEngine() : provisioning_session_(NULL) {
Properties::Init();
}
CdmEngine::~CdmEngine() {
CancelSessions();
CdmSessionMap::iterator i(sessions_.begin());
for (; i != sessions_.end(); ++i)
delete i->second;
sessions_.clear();
}
CdmResponseType CdmEngine::OpenSession(
const CdmKeySystem& key_system,
CdmSessionId* session_id) {
LOGI("CdmEngine::OpenSession");
if (!ValidateKeySystem(key_system)) {
LOGI("CdmEngine::OpenSession: invalid key_system = %s", key_system.c_str());
return KEY_ERROR;
}
if (!session_id) {
LOGE("CdmEngine::OpenSession: no session ID destination provided");
return KEY_ERROR;
}
// TODO(edwinwong, rfrias): Save key_system in session for validation checks
CdmSession* new_session = new CdmSession();
if (!new_session) {
return KEY_ERROR;
}
if (new_session->session_id().empty()) {
LOGE("CdmEngine::OpenSession: failure to generate session ID");
delete(new_session);
return UNKNOWN_ERROR;
}
CdmSessionId new_session_id = new_session->session_id();
CdmResponseType sts = new_session->Init();
if (sts != NO_ERROR) {
LOGE("CdmEngine::OpenSession: bad session init");
delete(new_session);
return sts;
}
sessions_[new_session_id] = new_session;
*session_id = new_session_id;
return NO_ERROR;
}
CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) {
LOGI("CdmEngine::CloseSession");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str());
return KEY_ERROR;
}
sessions_.erase(session_id);
if (sessions_.size() == 0)
DisablePolicyTimer();
iter->second->DestroySession();
delete iter->second;
return NO_ERROR;
}
CdmResponseType CdmEngine::GenerateKeyRequest(
const CdmSessionId& session_id,
bool is_key_system_present,
const CdmKeySystem& key_system,
const CdmInitData& init_data,
const CdmLicenseType license_type,
CdmAppParameterMap& app_parameters,
CdmKeyMessage* key_request,
std::string* server_url) {
LOGI("CdmEngine::GenerateKeyRequest");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
if (is_key_system_present) {
// TODO(edwinwong, rfrias): validate key_system has not changed
}
if (!key_request) {
LOGE("CdmEngine::GenerateKeyRequest: no key request destination provided");
return KEY_ERROR;
}
key_request->clear();
// TODO(edwinwong, rfrias): need to pass in license type and app parameters
CdmResponseType sts = iter->second->GenerateKeyRequest(init_data,
license_type,
app_parameters,
key_request,
server_url);
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateKeyRequest: key request generation failed, sts=%d",
(int)sts);
return sts;
}
// TODO(edwinwong, rfrias): persist init_data, license_type, app_parameters
// in session
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::AddKey(
const CdmSessionId& session_id,
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::AddKey");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::AddKey: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
if (key_data.empty()) {
LOGE("CdmEngine::AddKey: no key_data");
return KEY_ERROR;
}
CdmResponseType sts = iter->second->AddKey(key_data);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::AddKey: keys not added, result = %d", (int)sts);
}
EnablePolicyTimer();
return sts;
}
CdmResponseType CdmEngine::CancelKeyRequest(
const CdmSessionId& session_id,
bool is_key_system_present,
const CdmKeySystem& key_system) {
LOGI("CdmEngine::CancelKeyRequest");
//TODO(gmorgan): Issue: what is semantics of canceling a key request. Should
//this call cancel all keys for the session?
// TODO(jfore): We should disable the policy timer here if there are no
// active sessions. Sessions are currently not being destroyed here. We can
// add this logic once the semantics of canceling the key is worked out.
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::CancelKeyRequest: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
if (is_key_system_present) {
// TODO(edwinwong, rfrias): validate key_system has not changed
}
// TODO(edwinwong, rfrias): unload keys here
DisablePolicyTimer();
return NO_ERROR;
}
CdmResponseType CdmEngine::GenerateRenewalRequest(
const CdmSessionId& session_id,
CdmKeyMessage* key_request,
std::string* server_url) {
LOGI("CdmEngine::GenerateRenewalRequest");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
if (!key_request) {
LOGE("CdmEngine::GenerateRenewalRequest: no key request destination provided");
return KEY_ERROR;
}
key_request->clear();
CdmResponseType sts = iter->second->GenerateRenewalRequest(key_request,
server_url);
if (KEY_MESSAGE != sts) {
LOGE("CdmEngine::GenerateRenewalRequest: key request generation failed, sts=%d",
(int)sts);
return sts;
}
return KEY_MESSAGE;
}
CdmResponseType CdmEngine::RenewKey(
const CdmSessionId& session_id,
const CdmKeyResponse& key_data) {
LOGI("CdmEngine::RenewKey");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
if (key_data.empty()) {
LOGE("CdmEngine::RenewKey: no key_data");
return KEY_ERROR;
}
CdmResponseType sts = iter->second->RenewKey(key_data);
if (KEY_ADDED != sts) {
LOGE("CdmEngine::RenewKey: keys not added, sts=%d", (int)sts);
return sts;
}
return KEY_ADDED;
}
CdmResponseType CdmEngine::QueryStatus(CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryStatus");
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
return KEY_ERROR;
}
switch (crypto_engine->GetSecurityLevel()) {
case CryptoEngine::kSecurityLevelL1:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;
break;
case CryptoEngine::kSecurityLevelL2:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L2;
break;
case CryptoEngine::kSecurityLevelL3:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3;
break;
case CryptoEngine::kSecurityLevelUnknown:
(*key_info)[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_Unknown;
break;
default:
return KEY_ERROR;
}
std::string deviceId;
bool success = crypto_engine->GetDeviceUniqueId(&deviceId);
if (success) {
(*key_info)[QUERY_KEY_DEVICE_ID] = deviceId;
}
uint32_t system_id;
success = crypto_engine->GetSystemId(&system_id);
if (success) {
std::ostringstream system_id_stream;
system_id_stream << system_id;
(*key_info)[QUERY_KEY_SYSTEM_ID] = system_id_stream.str();
}
return NO_ERROR;
}
CdmResponseType CdmEngine::QueryKeyStatus(
const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyStatus");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
return iter->second->QueryKeyStatus(key_info);
}
CdmResponseType CdmEngine::QueryKeyControlInfo(
const CdmSessionId& session_id,
CdmQueryMap* key_info) {
LOGI("CdmEngine::QueryKeyControlInfo");
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGE("CdmEngine::QueryKeyControlInfo: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
return iter->second->QueryKeyControlInfo(key_info);
}
/*
* The certificate provisioning process creates a cdm and a crypto session.
* The lives of these sessions are short and therefore, not added to the
* CdmSessionMap. We need to explicitly delete these objects when error occurs
* or when we are done with provisioning.
*/
void CdmEngine::CleanupProvisioningSession() {
if (provisioning_session_) {
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (crypto_engine) {
CdmSessionId cdm_session_id = provisioning_session_->session_id();
CryptoSession* crypto_session =
crypto_engine->FindSession(cdm_session_id);
if (crypto_session) {
LOGV("delete crypto session for id=%s", cdm_session_id.c_str());
delete crypto_session;
} else {
LOGE("CleanupProvisioningSession: cannot find crypto_session");
}
}
delete provisioning_session_;
provisioning_session_ = NULL;
}
}
/*
* This function converts SignedProvisioningRequest into base64 string.
* It then wraps it in JSON format expected by the Apiary frontend.
* Apiary requires the base64 encoding to replace '+' with minus '-',
* and '/' with underscore '_'; opposite to stubby's.
*
* Returns the JSON formated string in *request. The JSON string will be
* appended as a query parameter, i.e. signedRequest=<base 64 encoded
* SignedProvisioningRequest>. All base64 '=' padding chars must be removed.
*
* The JSON formated request takes the following format:
*
* base64 encoded message
*/
void CdmEngine::ComposeJsonRequestAsQueryString(
const std::string& message,
CdmProvisioningRequest* request) {
// performs base64 encoding for message
std::vector<uint8_t> message_vector(message.begin(), message.end());
std::string message_b64 = Base64SafeEncode(message_vector);
// removes trailing '=' padding characters;
// the encoded string can have at most 2 '=' padding chars, so start
// searching at the end minus four characters
size_t found_pos = message_b64.find("=", message_b64.size() - 4);
if (std::string::npos != found_pos) {
message_b64.resize(found_pos);
}
request->assign(message_b64);
}
/*
* Composes a device provisioning request and output the request in JSON format
* in *request. It also returns the default url for the provisioning server
* in *default_url.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CdmEngine::GetProvisioningRequest(
CdmProvisioningRequest* request,
std::string* default_url) {
if (!request || !default_url) {
LOGE("GetProvisioningRequest: invalid input parameters");
return UNKNOWN_ERROR;
}
default_url->assign(kDefaultProvisioningServerUrl);
if (provisioning_session_) {
CleanupProvisioningSession();
}
//
//---------------------------------------------------------------------------
// This function can be called before a cdm session is created.
// First creates a cdm session, then creates a crypto session.
//
CdmSession* cdm_session = new CdmSession();
if (!cdm_session) {
LOGE("GetProvisioningRequest: fails to create a cdm session");
return UNKNOWN_ERROR;
}
if (cdm_session->session_id().empty()) {
LOGE("GetProvisioningRequest: fails to generate session ID");
delete cdm_session;
return UNKNOWN_ERROR;
}
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
LOGE("GetProvisioningRequest: fails to create a crypto engine");
delete cdm_session;
return UNKNOWN_ERROR;
}
CdmSessionId cdm_session_id = cdm_session->session_id();
CryptoSession* crypto_session = crypto_engine->CreateSession(cdm_session_id);
if (!crypto_session) {
LOGE("GetProvisioningRequest: fails to create a crypto session");
delete cdm_session;
return UNKNOWN_ERROR;
}
// TODO(edwinwong): replace this cdm session pointer with crypto session
// pointer if feasible
provisioning_session_ = cdm_session;
LOGV("provisioning session id=%s", cdm_session_id.c_str());
//
//---------------------------------------------------------------------------
// Prepares device provisioning request.
//
ProvisioningRequest provisioning_request;
ClientIdentification* client_id = provisioning_request.mutable_client_id();
client_id->set_type(ClientIdentification::KEYBOX);
std::string token;
if (!crypto_engine->GetToken(&token)) {
LOGE("GetProvisioningRequest: fails to get token");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
client_id->set_token(token);
uint32_t nonce;
if (!crypto_session->GenerateNonce(&nonce)) {
LOGE("GetProvisioningRequest: fails to generate a nonce");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
// The provisioning server does not convert the nonce to uint32_t, it just
// passes the binary data to the response message.
std::string the_nonce(reinterpret_cast<char*>(&nonce), sizeof(nonce));
provisioning_request.set_nonce(the_nonce);
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
// Derives signing and encryption keys and constructs signature.
std::string request_signature;
if (!crypto_session->PrepareRequest(serialized_message,
&request_signature, true)) {
request->clear();
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (request_signature.empty()) {
request->clear();
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
signed_provisioning_msg.set_signature(request_signature);
std::string serialized_request;
signed_provisioning_msg.SerializeToString(&serialized_request);
// converts request into JSON string
ComposeJsonRequestAsQueryString(serialized_request, request);
return NO_ERROR;
}
/*
* Parses the input json_str and locates substring using start_substr and
* end_stubstr. The found base64 substring is then decoded and returns
* in *result.
*
* Returns true for success and false if fails.
*/
bool CdmEngine::ParseJsonResponse(
const CdmProvisioningResponse& json_str,
const std::string& start_substr,
const std::string& end_substr,
std::string* result) {
std::string b64_string;
size_t start = json_str.find(start_substr);
if (start == json_str.npos) {
LOGE("ParseJsonResponse: cannot find start substring");
return false;
} else {
size_t end = json_str.find(end_substr, start + start_substr.length());
if (end == json_str.npos) {
LOGE("ParseJsonResponse cannot locate end substring");
return false;
}
size_t b64_string_size = end - start - start_substr.length();
b64_string.assign(json_str, start + start_substr.length(), b64_string_size);
}
// Decodes base64 substring and returns it in *result
std::vector<uint8_t> result_vector = Base64SafeDecode(b64_string);
result->assign(result_vector.begin(), result_vector.end());
return true;
}
/*
* The response message consists of a device certificate and the device RSA key.
* The device RSA key is stored in the T.E.E. The device certificate is stored
* in the device.
*
* Returns NO_ERROR for success and UNKNOWN_ERROR if fails.
*/
CdmResponseType CdmEngine::HandleProvisioningResponse(
CdmProvisioningResponse& response) {
if (response.empty()) {
LOGE("Empty provisioning response.");
return UNKNOWN_ERROR;
}
//---------------------------------------------------------------------------
// Extracts signed response from JSON string, decodes base64 signed response
const std::string kMessageStart = "\"signedResponse\": \"";
const std::string kMessageEnd = "\"";
std::string serialized_signed_response;
if (!ParseJsonResponse(response, kMessageStart, kMessageEnd,
&serialized_signed_response)) {
LOGE("Fails to extract signed serialized response from JSON response");
return UNKNOWN_ERROR;
}
//
//---------------------------------------------------------------------------
// Creates a crypto session using provisioning_session_.
//
if (!provisioning_session_) {
LOGE("HandleProvisioningResponse: invalid provisioning session");
return UNKNOWN_ERROR;
}
CryptoEngine* crypto_engine = CryptoEngine::GetInstance();
if (!crypto_engine) {
LOGE("HandleProvisioningResponse: fails to create a crypto engine");
return UNKNOWN_ERROR;
}
CdmSessionId cdm_session_id = provisioning_session_->session_id();
CryptoSession* crypto_session = crypto_engine->FindSession(cdm_session_id);
if (!crypto_session) {
LOGE("HandleProvisioningResponse: fails to find %s",
cdm_session_id.c_str());
return UNKNOWN_ERROR;
}
//---------------------------------------------------------------------------
// Authenticates provisioning response using D1s (server key derived from
// the provisioing request's input). Validate provisioning response and
// stores private device RSA key and certificate.
SignedProvisioningMessage signed_response;
if (!signed_response.ParseFromString(serialized_signed_response)) {
LOGE("Fails to parse signed serialized response");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (!signed_response.has_signature() || !signed_response.has_message()) {
LOGE("Invalid response - signature or message not found");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& signed_message = signed_response.message();
ProvisioningResponse provisioning_response;
if (!provisioning_response.ParseFromString(signed_message)) {
LOGE("Fails to parse signed message");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
if (!provisioning_response.has_device_rsa_key()) {
LOGE("Invalid response - key not found");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& enc_rsa_key = provisioning_response.device_rsa_key();
const std::string& nonce = provisioning_response.nonce();
const std::string& rsa_key_iv = provisioning_response.device_rsa_key_iv();
const std::string& signature = signed_response.signature();
std::string wrapped_rsa_key;
if (!crypto_session->RewrapDeviceRSAKey(signed_message,
signature,
nonce.data(),
enc_rsa_key,
enc_rsa_key.size(),
rsa_key_iv,
&wrapped_rsa_key)) {
LOGE("HandleProvisioningResponse: RewrapDeviceRSAKey fails");
CleanupProvisioningSession();
return UNKNOWN_ERROR;
}
const std::string& device_certificate = provisioning_response.device_certificate();
DeviceFiles::StoreCertificate(device_certificate, wrapped_rsa_key);
//
//---------------------------------------------------------------------------
// Deletes cdm and crypto sessions created for provisioning.
//
CleanupProvisioningSession();
return NO_ERROR;
}
CdmResponseType CdmEngine::GetSecureStops(
CdmSecureStops* secure_stops) {
// TODO(edwinwong, rfrias): add implementation
return NO_ERROR;
}
CdmResponseType CdmEngine::ReleaseSecureStops(
const CdmSecureStopReleaseMessage& message) {
// TODO(edwinwong, rfrias): add implementation
return NO_ERROR;
}
CdmResponseType CdmEngine::Decrypt(
const CdmSessionId& session_id,
bool is_encrypted,
bool is_secure,
const KeyId& key_id,
const uint8_t* encrypt_buffer,
size_t encrypt_length,
const std::vector<uint8_t>& iv,
size_t block_offset,
void* decrypt_buffer,
size_t decrypt_buffer_offset,
bool is_video) {
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
LOGW("CdmEngine::Decrypt: session_id not found = %s", session_id.c_str());
return KEY_ERROR;
}
return iter->second->Decrypt(is_encrypted, is_secure, key_id, encrypt_buffer,
encrypt_length, iv, block_offset,
decrypt_buffer, decrypt_buffer_offset,
is_video);
}
bool CdmEngine::IsKeyValid(const KeyId& key_id) {
for (CdmSessionIter iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
if (iter->second->IsKeyValid(key_id)) {
return true;
}
}
return false;
}
bool CdmEngine::AttachEventListener(
const CdmSessionId& session_id,
WvCdmEventListener* listener) {
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
return iter->second->AttachEventListener(listener);
}
bool CdmEngine::DetachEventListener(
const CdmSessionId& session_id,
WvCdmEventListener* listener) {
CdmSessionIter iter = sessions_.find(session_id);
if (iter == sessions_.end()) {
return false;
}
return iter->second->DetachEventListener(listener);
}
bool CdmEngine::ValidateKeySystem(const CdmKeySystem& key_system) {
return (key_system.find("widevine") != std::string::npos);
}
bool CdmEngine::CancelSessions() {
// TODO(gmorgan) Implement CancelSessions()
return true;
}
// Parse a blob of multiple concatenated PSSH atoms to extract the first
// widevine pssh
// TODO(kqyang): temporary workaround - remove after b/7928472 is resolved
bool CdmEngine::ExtractWidevinePssh(
const CdmInitData& init_data, CdmInitData* output) {
BufferReader reader(
reinterpret_cast<const uint8_t*>(init_data.data()), init_data.length());
// TODO(kqyang): Extracted from an actual init_data;
// Need to find out where it comes from.
static const uint8_t kWidevineSystemId[] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED,
};
// one PSSH blob consists of:
// 4 byte size of the PSSH atom, inclusive
// "pssh"
// 4 byte flags, value 0
// 16 byte system id
// 4 byte size of PSSH data, exclusive
while (1) {
// size of PSSH atom, used for skipping
uint32_t size;
if (!reader.Read4(&size)) return false;
// "pssh"
std::vector<uint8_t> pssh;
if (!reader.ReadVec(&pssh, 4)) return false;
if (memcmp(&pssh[0], "pssh", 4)) return false;
// flags
uint32_t flags;
if (!reader.Read4(&flags)) return false;
if (flags != 0) return false;
// system id
std::vector<uint8_t> system_id;
if (!reader.ReadVec(&system_id, sizeof(kWidevineSystemId))) return false;
if (memcmp(&system_id[0], kWidevineSystemId,
sizeof(kWidevineSystemId))) {
// skip the remaining contents of the atom,
// after size field, atom name, flags and system id
if (!reader.SkipBytes(
size - 4 - 4 - 4 - sizeof(kWidevineSystemId))) return false;
continue;
}
// size of PSSH box
uint32_t pssh_length;
if (!reader.Read4(&pssh_length)) return false;
output->clear();
if (!reader.ReadString(output, pssh_length)) return false;
return true;
}
// we did not find a matching record
return false;
}
void CdmEngine::EnablePolicyTimer() {
if (!policy_timer_.IsRunning())
policy_timer_.Start(this, CDM_POLICY_TIMER_DURATION_SECONDS);
}
void CdmEngine::DisablePolicyTimer() {
if (policy_timer_.IsRunning())
policy_timer_.Stop();
}
void CdmEngine::OnTimerEvent() {
for (CdmSessionIter iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent();
}
}
} // namespace wvcdm