Files
android/libwvdrmengine/cdm/core/src/cdm_session.cpp

1004 lines
31 KiB
C++

// Copyright 2012 Google Inc. All Rights Reserved.
#include "cdm_session.h"
#include <assert.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include "cdm_engine.h"
#include "clock.h"
#include "file_store.h"
#include "log.h"
#include "metrics_front_end.h"
#include "properties.h"
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#include "usage_table_header.h"
namespace {
const size_t kKeySetIdLength = 14;
} // namespace
namespace wvcdm {
CdmSession::CdmSession(FileSystem* file_system) :
initialized_(false),
crypto_session_(new CryptoSession(&metrics_)),
file_handle_(new DeviceFiles(file_system)),
license_received_(false),
is_offline_(false),
is_release_(false),
is_temporary_(false),
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_decryption_(true),
has_decrypted_since_last_report_(false),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
usage_support_type_(kNonSecureUsageSupport),
usage_table_header_(NULL),
usage_entry_number_(0),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
CdmResponseType sts =
crypto_session_->GetUsageSupportType(&usage_support_type_);
if (sts != NO_ERROR) {
LOGW("CdmSession::CdmSession: Failed to get usage support type");
}
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_ = UsageTableHeader::GetInstance(file_system,
crypto_session_.get());
life_span_.Start();
}
CdmSession::~CdmSession() {
if (!key_set_id_.empty()) {
// Unreserve the license ID.
file_handle_->UnreserveLicenseId(key_set_id_);
}
Properties::RemoveSessionPropertySet(session_id_);
M_RECORD(&metrics_, cdm_session_life_span_, life_span_.AsMs());
}
CdmResponseType CdmSession::Init(
CdmClientPropertySet* cdm_client_property_set) {
return Init(cdm_client_property_set, NULL, NULL);
}
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
const CdmSessionId* forced_session_id,
WvCdmEventListener* event_listener) {
if (initialized_) {
LOGE("CdmSession::Init: Failed due to previous initialization");
return SESSION_INIT_ERROR_2;
}
if (cdm_client_property_set &&
cdm_client_property_set->security_level() ==
QUERY_VALUE_SECURITY_LEVEL_L3) {
requested_security_level_ = kLevel3;
security_level_ = kSecurityLevelL3;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->Open(
requested_security_level_),
&metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (NO_ERROR != sts) return sts;
M_TIME(
security_level_ = crypto_session_->GetSecurityLevel(),
&metrics_,
crypto_session_get_security_level_,
security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("CdmSession::Init: Unable to initialize file handle");
return SESSION_FILE_HANDLE_INIT_ERROR;
}
// Device Provisioning state is not yet known.
// If not using certificates, then Keybox is client token for license
// requests.
// Otherwise, try to fetch device certificate. If not successful and
// provisioning is supported, return NEED_PROVISIONING. Otherwise, return
// an error.
std::string client_token;
CdmClientTokenType client_token_type =
crypto_session_->GetPreProvisionTokenType();
if ((client_token_type == kClientTokenKeybox) &&
(!Properties::use_certificates_as_identification())) {
// Keybox is client token.
LOGW("CdmSession::Init: Properties::use_certificates_as_identification() "
"is not set - using Keybox for license requests (not recommended).");
bool get_client_token_sts;
M_TIME(
get_client_token_sts = crypto_session_->GetClientToken(
&client_token),
&metrics_,
crypto_session_get_token_,
get_client_token_sts);
if (!get_client_token_sts) {
return SESSION_INIT_ERROR_1;
}
} else {
// License server client ID token is a stored certificate. Stage it or
// indicate that provisioning is needed. Get token from stored certificate
std::string wrapped_key;
if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key)) {
return NEED_PROVISIONING;
}
bool load_cert_sts;
M_TIME(
load_cert_sts = crypto_session_->LoadCertificatePrivateKey(
wrapped_key),
&metrics_,
crypto_session_load_certificate_private_key_,
load_cert_sts);
if(!load_cert_sts) {
return NEED_PROVISIONING;
}
client_token_type = kClientTokenDrmCert;
}
if (forced_session_id) {
key_set_id_ = *forced_session_id;
} else {
bool ok = GenerateKeySetId(&key_set_id_);
(void)ok; // ok is now used when assertions are turned off.
assert(ok);
}
session_id_ =
Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId();
if (session_id_.empty()) {
LOGE("CdmSession::Init: empty session ID");
return EMPTY_SESSION_ID;
}
if (cdm_client_property_set)
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
if (!mock_license_parser_in_use_)
license_parser_.reset(new CdmLicense(session_id_));
if (!mock_policy_engine_in_use_)
policy_engine_.reset(new PolicyEngine(
session_id_, event_listener, crypto_session_.get()));
if (!license_parser_->Init(client_token, client_token_type,
crypto_session_.get(), policy_engine_.get()))
return LICENSE_PARSER_INIT_ERROR;
license_received_ = false;
is_initial_decryption_ = true;
initialized_ = true;
return NO_ERROR;
}
CdmResponseType CdmSession::RestoreOfflineSession(
const CdmKeySetId& key_set_id, const CdmLicenseType license_type) {
if (!key_set_id_.empty()) {
file_handle_->UnreserveLicenseId(key_set_id_);
}
key_set_id_ = key_set_id;
DeviceFiles::LicenseState license_state;
int64_t playback_start_time;
int64_t last_playback_time;
int64_t grace_period_end_time;
if (!file_handle_->RetrieveLicense(
key_set_id, &license_state, &offline_init_data_, &key_request_,
&key_response_, &offline_key_renewal_request_,
&offline_key_renewal_response_, &offline_release_server_url_,
&playback_start_time, &last_playback_time, &grace_period_end_time,
&app_parameters_, &usage_entry_, &usage_entry_number_)) {
LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. "
"key set id = %s", key_set_id.c_str());
return GET_LICENSE_ERROR;
}
// Do not restore a released offline license, unless a release retry
if (!(license_type == kLicenseTypeRelease ||
license_state == DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = "
"%d, type = %d", license_state, license_type);
return GET_RELEASED_LICENSE_ERROR;
}
if (usage_support_type_ == kUsageEntrySupport) {
CdmResponseType sts = usage_table_header_->LoadEntry(crypto_session_.get(),
usage_entry_,
usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = %d",
sts);
return sts;
}
}
if (license_type == kLicenseTypeRelease) {
if (!license_parser_->RestoreLicenseForRelease(key_request_,
key_response_)) {
return RELEASE_LICENSE_ERROR_1;
}
} else {
if (!license_parser_->RestoreOfflineLicense(
key_request_, key_response_, offline_key_renewal_response_,
playback_start_time, last_playback_time, grace_period_end_time)) {
return RESTORE_OFFLINE_LICENSE_ERROR_2;
}
}
if (usage_support_type_ == kUsageEntrySupport) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = "
"%d", sts);
return sts;
}
if (!StoreLicense(license_state)) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = true;
is_release_ = license_type == kLicenseTypeRelease;
return KEY_ADDED;
}
CdmResponseType CdmSession::RestoreUsageSession(
const DeviceFiles::CdmUsageData& usage_data) {
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;
if (usage_support_type_ == kUsageEntrySupport) {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
sts);
return sts;
}
}
if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) {
return RELEASE_LICENSE_ERROR_2;
}
if (usage_support_type_ == kUsageEntrySupport) {
CdmResponseType sts =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d",
sts);
return sts;
}
if (!UpdateUsageInfo()) {
LOGW("CdmSession::RestoreUsageSession: unable to save updated usage "
"info");
}
}
license_received_ = true;
is_offline_ = false;
is_release_ = true;
return KEY_ADDED;
}
CdmResponseType CdmSession::GenerateKeyRequest(
const InitializationData& init_data, CdmLicenseType license_type,
const CdmAppParameterMap& app_parameters,
CdmKeyRequest* key_request) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::GenerateKeyRequest: Invalid crypto session");
return INVALID_CRYPTO_SESSION_1;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::GenerateKeyRequest: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_1;
}
if (!key_request) {
LOGE("CdmSession::GenerateKeyRequest: No output destination provided");
return INVALID_PARAMETERS_ENG_5;
}
switch (license_type) {
case kLicenseTypeTemporary:
is_temporary_ = true;
break;
case kLicenseTypeStreaming:
is_offline_ = false;
break;
case kLicenseTypeOffline:
is_offline_ = true;
break;
case kLicenseTypeRelease:
is_release_ = true;
break;
case kLicenseTypeDeferred:
// If you're going to pass Deferred, you must have empty init data in
// this call and stored init data from the previous call.
if (!init_data.IsEmpty() || !license_parser_->HasInitData()) {
return INVALID_LICENSE_TYPE;
}
// The arguments check out.
// The is_release_ and is_offline_ flags were already set last time based
// on the original license type. Do not change them, and use them to
// re-derive the original license type.
if (is_release_) {
license_type = kLicenseTypeRelease;
} else if (is_offline_) {
license_type = kLicenseTypeOffline;
} else if (is_temporary_) {
license_type = kLicenseTypeTemporary;
} else {
license_type = kLicenseTypeStreaming;
}
break;
default:
LOGE("CdmSession::GenerateKeyRequest: unrecognized license type: %ld",
license_type);
return INVALID_LICENSE_TYPE;
}
if (is_release_) {
return GenerateReleaseRequest(key_request);
} else if (license_received_) { // renewal
return GenerateRenewalRequest(key_request);
} else {
key_request->type = kKeyRequestTypeInitial;
if (!license_parser_->HasInitData()) {
if (!init_data.is_supported()) {
LOGW("CdmSession::GenerateKeyRequest: unsupported init data type (%s)",
init_data.type().c_str());
return UNSUPPORTED_INIT_DATA;
}
if (init_data.IsEmpty()) {
LOGW("CdmSession::GenerateKeyRequest: init data absent");
return INIT_DATA_NOT_FOUND;
}
}
if (is_offline_ && key_set_id_.empty()) {
LOGE("CdmSession::GenerateKeyRequest: Unable to generate key set ID");
return KEY_REQUEST_ERROR_1;
}
app_parameters_ = app_parameters;
CdmResponseType status = license_parser_->PrepareKeyRequest(
init_data, license_type,
app_parameters, &key_request->message,
&key_request->url);
if (KEY_MESSAGE != status) return status;
key_request_ = key_request->message;
if (is_offline_) {
offline_init_data_ = init_data.data();
offline_release_server_url_ = key_request->url;
}
return KEY_MESSAGE;
}
}
// AddKey() - Accept license response and extract key info.
CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::AddKey: Invalid crypto session");
return INVALID_CRYPTO_SESSION_2;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::AddKey: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_2;
}
if (is_release_) {
CdmResponseType sts = ReleaseKey(key_response);
return (sts == NO_ERROR) ? KEY_ADDED : sts;
} else if (license_received_) { // renewal
return RenewKey(key_response);
} else {
// If usage table header+entries are supported, preprocess the license
// to see if it has a provider session token. If so a new entry needs
// to be created.
CdmResponseType sts;
std::string provider_session_token;
if (usage_support_type_ == kUsageEntrySupport) {
if (license_parser_->ExtractProviderSessionToken(
key_response, &provider_session_token) &&
!provider_session_token.empty()) {
std::string app_id;
GetApplicationId(&app_id);
sts = usage_table_header_->AddEntry(
crypto_session_.get(), is_offline_, key_set_id_,
DeviceFiles::GetUsageInfoFileName(app_id), &usage_entry_number_);
if (sts != NO_ERROR) return sts;
}
}
sts = license_parser_->HandleKeyResponse(key_response);
// Update or delete entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty()) {
if (sts != KEY_ADDED) {
CdmResponseType sts =
usage_table_header_->DeleteEntry(usage_entry_number_);
if (sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d", sts);
}
}
}
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts;
license_received_ = true;
key_response_ = key_response;
if (is_offline_ || has_provider_session_token()) {
if (usage_support_type_ == kUsageEntrySupport)
usage_table_header_->UpdateEntry(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;
}
return KEY_ADDED;
}
}
CdmResponseType CdmSession::QueryStatus(CdmQueryMap* query_response) {
if (crypto_session_.get() == NULL) {
LOGE("CdmSession::QueryStatus: Invalid crypto session");
return INVALID_CRYPTO_SESSION_3;
}
if (!crypto_session_->IsOpen()) {
LOGE("CdmSession::QueryStatus: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_3;
}
switch (security_level_) {
case kSecurityLevelL1:
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L1;
break;
case kSecurityLevelL2:
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L2;
break;
case kSecurityLevelL3:
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_L3;
break;
case kSecurityLevelUninitialized:
case kSecurityLevelUnknown:
(*query_response)[QUERY_KEY_SECURITY_LEVEL] =
QUERY_VALUE_SECURITY_LEVEL_UNKNOWN;
break;
default:
return INVALID_QUERY_KEY;
}
return NO_ERROR;
}
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* query_response) {
return policy_engine_->Query(query_response);
}
CdmResponseType CdmSession::QueryKeyAllowedUsage(
const std::string& key_id, CdmKeyAllowedUsage* key_usage) {
return policy_engine_->QueryKeyAllowedUsage(key_id, key_usage);
}
CdmResponseType CdmSession::QueryOemCryptoSessionId(
CdmQueryMap* query_response) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::QueryOemCryptoSessionId: Invalid crypto session");
return INVALID_CRYPTO_SESSION_4;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::QueryOemCryptoSessionId: Crypto session not open");
return CRYPTO_SESSION_OPEN_ERROR_4;
}
std::stringstream ss;
ss << crypto_session_->oec_session_id();
(*query_response)[QUERY_KEY_OEMCRYPTO_SESSION_ID] = ss.str();
return NO_ERROR;
}
// Decrypt() - Accept encrypted buffer and return decrypted data.
CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (crypto_session_.get() == NULL) {
LOGW("CdmSession::Decrypt: Invalid crypto session");
return INVALID_CRYPTO_SESSION_5;
}
if (!crypto_session_->IsOpen()) {
LOGW("CdmSession::Decrypt: Crypto session not open");
return DECRYPT_NOT_READY;
}
// Playback may not begin until either the start time passes or the license
// is updated, so we treat this Decrypt call as invalid.
if (params.is_encrypted &&
!policy_engine_->CanDecryptContent(*params.key_id)) {
return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY;
}
if (!policy_engine_->CanUseKey(*params.key_id, security_level_))
return KEY_PROHIBITED_FOR_SECURITY_LEVEL;
CdmResponseType status = crypto_session_->Decrypt(params);
if (status == NO_ERROR) {
if (is_initial_decryption_) {
policy_engine_->BeginDecryption();
is_initial_decryption_ = false;
}
has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) {
is_usage_update_needed_ = has_provider_session_token();
}
} else {
Clock clock;
int64_t current_time = clock.GetCurrentTime();
if (policy_engine_->HasLicenseOrPlaybackDurationExpired(current_time)) {
return NEED_KEY;
}
}
return status;
}
// License renewal
// GenerateRenewalRequest() - Construct valid renewal request for the current
// session keys.
CdmResponseType CdmSession::GenerateRenewalRequest(
CdmKeyRequest* key_request) {
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
true, app_parameters_, &key_request->message, &key_request->url);
key_request->type = kKeyRequestTypeRenewal;
if (KEY_MESSAGE != status) return status;
if (is_offline_) {
offline_key_renewal_request_ = key_request->message;
}
return KEY_MESSAGE;
}
// RenewKey() - Accept renewal response and update key info.
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(true, key_response);
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RENEW_KEY_ERROR_1 : sts;
if (is_offline_) {
offline_key_renewal_response_ = key_response;
if (!StoreLicense(DeviceFiles::kLicenseStateActive))
return RENEW_KEY_ERROR_2;
}
return KEY_ADDED;
}
CdmResponseType CdmSession::GenerateReleaseRequest(
CdmKeyRequest* key_request) {
is_release_ = true;
CdmResponseType status = license_parser_->PrepareKeyUpdateRequest(
false, app_parameters_, &key_request->message,
&key_request->url);
key_request->type = kKeyRequestTypeRelease;
if (KEY_MESSAGE != status) return status;
if (usage_support_type_ == kUsageEntrySupport) {
status = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (status != NO_ERROR) {
LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = "
"%d", status);
return status;
}
}
if (is_offline_) { // Mark license as being released
if (!StoreLicense(DeviceFiles::kLicenseStateReleasing))
return RELEASE_KEY_REQUEST_ERROR;
} else if (!usage_provider_session_token_.empty()) {
if (usage_support_type_ == kUsageEntrySupport) {
if (!UpdateUsageInfo())
return RELEASE_USAGE_INFO_FAILED;
}
}
return KEY_MESSAGE;
}
// ReleaseKey() - Accept release response and release license.
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
CdmResponseType sts =
license_parser_->HandleKeyUpdateResponse(false, key_response);
if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts;
if (is_offline_ || has_provider_session_token()) {
DeleteLicense();
// Deletion of usage entry cannot occur while in use by a crypto session.
// So close and reopen after deletion.
if (usage_support_type_ == kUsageEntrySupport) {
crypto_session_->Close();
CdmResponseType sts = usage_table_header_->DeleteEntry(usage_entry_number_);
if (sts != NO_ERROR) return sts;
M_TIME(
sts = crypto_session_->Open(requested_security_level_),
&metrics_,
crypto_session_open_,
sts,
requested_security_level_);
if (NO_ERROR != sts) return sts;
}
}
return NO_ERROR;
}
CdmResponseType CdmSession::DeleteUsageEntry(
const DeviceFiles::CdmUsageData& usage_data) {
if (usage_support_type_ != kUsageEntrySupport) {
LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d",
usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
return usage_table_header_->DeleteEntry(usage_data.usage_entry_number);
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
return license_parser_->IsKeyLoaded(key_id);
}
int64_t CdmSession::GetDurationRemaining() {
if (policy_engine_->IsLicenseForFuture()) return 0;
return policy_engine_->GetLicenseOrPlaybackDurationRemaining();
}
CdmSessionId CdmSession::GenerateSessionId() {
static int session_num = 1;
return SESSION_ID_PREFIX + IntToString(++session_num);
}
bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
if (!key_set_id) {
LOGW("CdmSession::GenerateKeySetId: key set id destination not provided");
return false;
}
std::vector<uint8_t> random_data(
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
while (key_set_id->empty()) {
if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) {
return false;
}
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
// key set collision
if (file_handle_->LicenseExists(*key_set_id)) {
key_set_id->clear();
}
}
// Reserve the license ID to avoid collisions.
file_handle_->ReserveLicenseId(*key_set_id);
return true;
}
CdmResponseType CdmSession::StoreLicense() {
if (is_temporary_) {
LOGE("CdmSession::StoreLicense: Session type prohibits storage.");
return STORAGE_PROHIBITED;
}
if (is_offline_) {
if (key_set_id_.empty()) {
LOGE("CdmSession::StoreLicense: No key set ID");
return EMPTY_KEYSET_ID;
}
if (!license_parser_->is_offline()) {
LOGE("CdmSession::StoreLicense: License policy prohibits storage.");
return OFFLINE_LICENSE_PROHIBITED;
}
if (!StoreLicense(DeviceFiles::kLicenseStateActive)) {
LOGE("CdmSession::StoreLicense: 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("CdmSession::StoreLicense: No provider session token and not offline");
return STORE_LICENSE_ERROR_2;
}
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_)) {
LOGE("CdmSession::StoreLicense: Unable to store usage info");
return STORE_USAGE_INFO_ERROR;
}
return NO_ERROR;
}
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
return file_handle_->StoreLicense(
key_set_id_, state, offline_init_data_, key_request_, key_response_,
offline_key_renewal_request_, offline_key_renewal_response_,
offline_release_server_url_, policy_engine_->GetPlaybackStartTime(),
policy_engine_->GetLastPlaybackTime(),
policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry_,
usage_entry_number_);
}
CdmResponseType CdmSession::ReleaseCrypto() {
crypto_session_->Close();
return NO_ERROR;
}
bool CdmSession::DeleteLicense() {
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) {
policy_engine_->NotifyResolution(width, height);
}
void CdmSession::OnTimerEvent(bool update_usage) {
if (update_usage && has_decrypted_since_last_report_) {
policy_engine_->DecryptionEvent();
has_decrypted_since_last_report_ = false;
if (is_offline_ && !is_release_) {
StoreLicense(DeviceFiles::kLicenseStateActive);
}
}
policy_engine_->OnTimerEvent();
}
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
if (key_set_id_ == key_set_id) {
policy_engine_->NotifySessionExpiration();
}
}
void CdmSession::GetApplicationId(std::string* app_id) {
if (app_id && !Properties::GetApplicationId(session_id_, app_id)) {
*app_id = "";
}
}
CdmResponseType CdmSession::DeleteMultipleUsageInformation(
const std::vector<std::string>& provider_session_tokens) {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->DeleteMultipleUsageInformation(
provider_session_tokens),
&metrics_,
crypto_session_delete_multiple_usage_information_,
sts);
return sts;
}
CdmResponseType CdmSession::UpdateUsageTableInformation() {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->UpdateUsageInformation(),
&metrics_,
crypto_session_update_usage_information_,
sts);
return sts;
}
CdmResponseType CdmSession::UpdateUsageEntryInformation() {
if (usage_support_type_ != kUsageEntrySupport) {
LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected usage type "
"supported: %d", usage_support_type_);
return INCORRECT_USAGE_SUPPORT_TYPE_2;
}
CdmResponseType sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_);
if (sts != NO_ERROR) return sts;
if (is_offline_)
StoreLicense(is_release_
? DeviceFiles::kLicenseStateReleasing
: DeviceFiles::kLicenseStateActive);
else if (!usage_provider_session_token_.empty())
UpdateUsageInfo();
return NO_ERROR;
}
CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
if (!out_buffer) {
LOGE("CdmSession::GenericEncrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_6;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericEncrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
&metrics_,
crypto_session_generic_encrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer,
const std::string& key_id,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* out_buffer) {
if (!out_buffer) {
LOGE("CdmSession::GenericDecrypt: No output destination provided");
return INVALID_PARAMETERS_ENG_7;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericDecrypt(
in_buffer,
key_id,
iv,
algorithm,
out_buffer),
&metrics_,
crypto_session_generic_decrypt_,
sts,
metrics::Pow2Bucket(in_buffer.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericSign(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
std::string* signature) {
if (!signature) {
LOGE("CdmSession::GenericSign: No output destination provided");
return INVALID_PARAMETERS_ENG_8;
}
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericSign(
message,
key_id,
algorithm,
signature),
&metrics_,
crypto_session_generic_sign_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
return sts;
}
CdmResponseType CdmSession::GenericVerify(const std::string& message,
const std::string& key_id,
CdmSigningAlgorithm algorithm,
const std::string& signature) {
CdmResponseType sts;
M_TIME(
sts = crypto_session_->GenericVerify(
message,
key_id,
algorithm,
signature),
&metrics_,
crypto_session_generic_verify_,
sts,
metrics::Pow2Bucket(message.size()),
algorithm);
return sts;
}
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);
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {
license_parser_.reset(license_parser);
mock_license_parser_in_use_ = true;
}
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
crypto_session_.reset(crypto_session);
}
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {
policy_engine_.reset(policy_engine);
mock_policy_engine_in_use_ = true;
}
void CdmSession::set_file_handle(DeviceFiles* file_handle) {
file_handle_.reset(file_handle);
}
} // namespace wvcdm