Merge of usage reporting and license changes from WV CDM repo
* CdmSession unittest and license request time changes b/15914199 Merge of https://widevine-internal-review.googlesource.com/#/c/10597/ * Specify OEMCrypto API version in client capabilities b/15388863 Merge of https://widevine-internal-review.googlesource.com/#/c/10616/ * Report start and last play time in license request b/15995227 Merge of https://widevine-internal-review.googlesource.com/#/c/10617/ * Respect can_play flag b/15330338 Merge of https://widevine-internal-review.googlesource.com/#/c/10619/ * Restore offline session information b/16009274 Merge of https://widevine-internal-review.googlesource.com/#/c/10641/ Change-Id: I17fdc309efbc1d44385a86a368df11b1349b29c2
This commit is contained in:
@@ -25,46 +25,96 @@ namespace wvcdm {
|
||||
|
||||
typedef std::set<WvCdmEventListener*>::iterator CdmEventListenerIter;
|
||||
|
||||
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set)
|
||||
: session_id_(GenerateSessionId()),
|
||||
crypto_session_(NULL),
|
||||
license_received_(false),
|
||||
is_offline_(false),
|
||||
is_release_(false),
|
||||
is_usage_update_needed_(false),
|
||||
is_initial_decryption_(true) {
|
||||
CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(new CdmLicense(), new CryptoSession(), new PolicyEngine(),
|
||||
new DeviceFiles(), cdm_client_property_set);
|
||||
}
|
||||
|
||||
CdmSession::CdmSession(
|
||||
CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine,
|
||||
DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
Create(license_parser, crypto_session, policy_engine, file_handle,
|
||||
cdm_client_property_set);
|
||||
}
|
||||
|
||||
void CdmSession::Create(
|
||||
CdmLicense* license_parser,
|
||||
CryptoSession* crypto_session,
|
||||
PolicyEngine* policy_engine,
|
||||
DeviceFiles* file_handle,
|
||||
const CdmClientPropertySet* cdm_client_property_set) {
|
||||
// Just return on failures. Failures will be signaled in Init.
|
||||
if (NULL == license_parser) {
|
||||
LOGE("CdmSession::Create: License parser not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == crypto_session) {
|
||||
LOGE("CdmSession::Create: Crypto session not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == policy_engine) {
|
||||
LOGE("CdmSession::Create: Policy engine not provided");
|
||||
return;
|
||||
}
|
||||
if (NULL == file_handle) {
|
||||
LOGE("CdmSession::Create: Device files not provided");
|
||||
return;
|
||||
}
|
||||
initialized_ = false;
|
||||
session_id_ = GenerateSessionId();
|
||||
license_parser_.reset(license_parser);
|
||||
crypto_session_.reset(crypto_session);
|
||||
file_handle_.reset(file_handle);
|
||||
policy_engine_.reset(policy_engine);
|
||||
license_received_ = false;
|
||||
is_offline_ = false;
|
||||
is_release_ = false;
|
||||
is_usage_update_needed_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
has_decrypted_recently_ = false;
|
||||
if (cdm_client_property_set) {
|
||||
Properties::AddSessionPropertySet(session_id_, cdm_client_property_set);
|
||||
}
|
||||
security_level_ = GetRequestedSecurityLevel() == kLevel3
|
||||
? kSecurityLevelL3 : GetSecurityLevel();
|
||||
}
|
||||
|
||||
CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); }
|
||||
|
||||
CdmResponseType CdmSession::Init() {
|
||||
scoped_ptr<CryptoSession> session(new CryptoSession());
|
||||
|
||||
CdmResponseType sts = session->Open(GetRequestedSecurityLevel());
|
||||
if (session_id_.empty()) {
|
||||
LOGE("CdmSession::Init: Failed, session not properly constructed");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
CdmResponseType sts = crypto_session_->Open(GetRequestedSecurityLevel());
|
||||
if (NO_ERROR != sts) return sts;
|
||||
|
||||
std::string token;
|
||||
if (Properties::use_certificates_as_identification()) {
|
||||
DeviceFiles handle;
|
||||
std::string wrapped_key;
|
||||
if (!handle.Init(session.get()->GetSecurityLevel()) ||
|
||||
!handle.RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!session->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
if (!file_handle_->Init(security_level_) ||
|
||||
!file_handle_->RetrieveCertificate(&token, &wrapped_key) ||
|
||||
!crypto_session_->LoadCertificatePrivateKey(wrapped_key)) {
|
||||
return NEED_PROVISIONING;
|
||||
}
|
||||
} else {
|
||||
if (!session->GetToken(&token)) return UNKNOWN_ERROR;
|
||||
if (!crypto_session_->GetToken(&token)) return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_.Init(token, session.get(), &policy_engine_))
|
||||
if (!license_parser_->Init(token, crypto_session_.get(),
|
||||
policy_engine_.get()))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
crypto_session_.reset(session.release());
|
||||
license_received_ = false;
|
||||
is_initial_decryption_ = true;
|
||||
initialized_ = true;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -73,17 +123,21 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
key_set_id_ = key_set_id;
|
||||
|
||||
// Retrieve license information from persistent store
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_->GetSecurityLevel()))
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
DeviceFiles::LicenseState license_state;
|
||||
int64_t playback_start_time;
|
||||
int64_t last_playback_time;
|
||||
|
||||
if (!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_)) {
|
||||
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)) {
|
||||
LOGE("CdmSession::Init failed to retrieve license. key set id = %s",
|
||||
key_set_id.c_str());
|
||||
return UNKNOWN_ERROR;
|
||||
@@ -94,8 +148,10 @@ CdmResponseType CdmSession::RestoreOfflineSession(
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_.RestoreOfflineLicense(key_request_, key_response_,
|
||||
offline_key_renewal_response_)) {
|
||||
if (!license_parser_->RestoreOfflineLicense(key_request_, key_response_,
|
||||
offline_key_renewal_response_,
|
||||
playback_start_time,
|
||||
last_playback_time)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -111,7 +167,8 @@ CdmResponseType CdmSession::RestoreUsageSession(
|
||||
|
||||
key_request_ = key_request;
|
||||
key_response_ = key_response;
|
||||
if (!license_parser_.RestoreUsageLicense(key_request_, key_response_)) {
|
||||
|
||||
if (!license_parser_->RestoreUsageLicense(key_request_, key_response_)) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
@@ -155,14 +212,14 @@ CdmResponseType CdmSession::GenerateKeyRequest(
|
||||
init_data.type().c_str());
|
||||
return KEY_ERROR;
|
||||
}
|
||||
if (init_data.IsEmpty() && !license_parser_.HasInitData()) {
|
||||
if (init_data.IsEmpty() && !license_parser_->HasInitData()) {
|
||||
LOGW("CdmSession::GenerateKeyRequest: init data absent");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
if (!license_parser_.PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
if (!license_parser_->PrepareKeyRequest(init_data, license_type,
|
||||
app_parameters, session_id_,
|
||||
key_request, server_url)) {
|
||||
return KEY_ERROR;
|
||||
}
|
||||
|
||||
@@ -195,14 +252,14 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response,
|
||||
} else if (license_received_) { // renewal
|
||||
return RenewKey(key_response);
|
||||
} else {
|
||||
CdmResponseType sts = license_parser_.HandleKeyResponse(key_response);
|
||||
CdmResponseType sts = license_parser_->HandleKeyResponse(key_response);
|
||||
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
|
||||
license_received_ = true;
|
||||
key_response_ = key_response;
|
||||
|
||||
if (is_offline_ || !license_parser_.provider_session_token().empty()) {
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
sts = StoreLicense();
|
||||
if (sts != NO_ERROR) return sts;
|
||||
}
|
||||
@@ -245,7 +302,7 @@ CdmResponseType CdmSession::QueryStatus(CdmQueryMap* key_info) {
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyStatus(CdmQueryMap* key_info) {
|
||||
return policy_engine_.Query(key_info);
|
||||
return policy_engine_->Query(key_info);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::QueryKeyControlInfo(CdmQueryMap* key_info) {
|
||||
@@ -276,27 +333,35 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
if (params.is_encrypted && !policy_engine_->can_decrypt()) {
|
||||
return policy_engine_->IsLicenseForFuture() ? KEY_ERROR : NEED_KEY;
|
||||
}
|
||||
|
||||
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||
|
||||
switch (status) {
|
||||
case NO_ERROR:
|
||||
if (is_initial_decryption_) {
|
||||
policy_engine_.BeginDecryption();
|
||||
policy_engine_->BeginDecryption();
|
||||
is_initial_decryption_ = false;
|
||||
}
|
||||
has_decrypted_recently_ = true;
|
||||
if (!is_usage_update_needed_) {
|
||||
is_usage_update_needed_ =
|
||||
!license_parser_.provider_session_token().empty();
|
||||
!license_parser_->provider_session_token().empty();
|
||||
}
|
||||
break;
|
||||
case UNKNOWN_ERROR:
|
||||
case UNKNOWN_ERROR: {
|
||||
Clock clock;
|
||||
int64_t current_time = clock.GetCurrentTime();
|
||||
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
|
||||
policy_engine_.IsPlaybackDurationExpired(current_time)) {
|
||||
if (policy_engine_->IsLicenseDurationExpired(current_time) ||
|
||||
policy_engine_->IsPlaybackDurationExpired(current_time)) {
|
||||
return NEED_KEY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: //Ignore
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
@@ -307,7 +372,8 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
||||
// session keys.
|
||||
CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
if (!license_parser_.PrepareKeyUpdateRequest(true, key_request, server_url)) {
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(true, key_request,
|
||||
server_url)) {
|
||||
LOGE("CdmSession::GenerateRenewalRequest: ERROR on prepare");
|
||||
return KEY_ERROR;
|
||||
}
|
||||
@@ -321,7 +387,7 @@ CdmResponseType CdmSession::GenerateRenewalRequest(CdmKeyMessage* key_request,
|
||||
// RenewKey() - Accept renewal response and update key info.
|
||||
CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts =
|
||||
license_parser_.HandleKeyUpdateResponse(true, key_response);
|
||||
license_parser_->HandleKeyUpdateResponse(true, key_response);
|
||||
if (sts != KEY_ADDED) return sts;
|
||||
|
||||
if (is_offline_) {
|
||||
@@ -334,7 +400,7 @@ CdmResponseType CdmSession::RenewKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
std::string* server_url) {
|
||||
is_release_ = true;
|
||||
if (!license_parser_.PrepareKeyUpdateRequest(false, key_request, server_url))
|
||||
if (!license_parser_->PrepareKeyUpdateRequest(false, key_request, server_url))
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
if (is_offline_) { // Mark license as being released
|
||||
@@ -346,19 +412,19 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyMessage* key_request,
|
||||
|
||||
// ReleaseKey() - Accept release response and release license.
|
||||
CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) {
|
||||
CdmResponseType sts = license_parser_.HandleKeyUpdateResponse(false,
|
||||
CdmResponseType sts = license_parser_->HandleKeyUpdateResponse(false,
|
||||
key_response);
|
||||
if (KEY_ADDED != sts)
|
||||
return sts;
|
||||
|
||||
if (is_offline_ || !license_parser_.provider_session_token().empty()) {
|
||||
if (is_offline_ || !license_parser_->provider_session_token().empty()) {
|
||||
DeleteLicense();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
|
||||
return license_parser_.IsKeyLoaded(key_id);
|
||||
return license_parser_->IsKeyLoaded(key_id);
|
||||
}
|
||||
|
||||
CdmSessionId CdmSession::GenerateSessionId() {
|
||||
@@ -375,8 +441,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
std::vector<uint8_t> random_data(
|
||||
(kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0);
|
||||
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_->GetSecurityLevel()))
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return false;
|
||||
|
||||
while (key_set_id->empty()) {
|
||||
@@ -386,7 +451,7 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) {
|
||||
*key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data);
|
||||
|
||||
// key set collision
|
||||
if (handle.LicenseExists(*key_set_id)) {
|
||||
if (file_handle_->LicenseExists(*key_set_id)) {
|
||||
key_set_id->clear();
|
||||
}
|
||||
}
|
||||
@@ -414,20 +479,20 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
std::string provider_session_token = license_parser_.provider_session_token();
|
||||
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 UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_->GetSecurityLevel())) {
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to initialize device files");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!handle.StoreUsageInfo(provider_session_token, key_request_,
|
||||
key_response_)) {
|
||||
if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_,
|
||||
key_response_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store usage info");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@@ -435,31 +500,31 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
}
|
||||
|
||||
bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) {
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_->GetSecurityLevel()))
|
||||
if (!file_handle_->Reset(security_level_))
|
||||
return false;
|
||||
|
||||
return handle.StoreLicense(
|
||||
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_);
|
||||
offline_key_renewal_response_, offline_release_server_url_,
|
||||
policy_engine_->GetPlaybackStartTime(),
|
||||
policy_engine_->GetLastPlaybackTime());
|
||||
}
|
||||
|
||||
bool CdmSession::DeleteLicense() {
|
||||
if (!is_offline_ && license_parser_.provider_session_token().empty())
|
||||
if (!is_offline_ && license_parser_->provider_session_token().empty())
|
||||
return false;
|
||||
|
||||
DeviceFiles handle;
|
||||
if (!handle.Init(crypto_session_->GetSecurityLevel())) {
|
||||
if (!file_handle_->Reset(security_level_)) {
|
||||
LOGE("CdmSession::DeleteLicense: Unable to initialize device files");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_offline_)
|
||||
return handle.DeleteLicense(key_set_id_);
|
||||
return file_handle_->DeleteLicense(key_set_id_);
|
||||
else
|
||||
return handle.DeleteUsageInfo(
|
||||
license_parser_.provider_session_token());
|
||||
return file_handle_->DeleteUsageInfo(
|
||||
license_parser_->provider_session_token());
|
||||
}
|
||||
|
||||
bool CdmSession::AttachEventListener(WvCdmEventListener* listener) {
|
||||
@@ -471,11 +536,19 @@ bool CdmSession::DetachEventListener(WvCdmEventListener* listener) {
|
||||
return (listeners_.erase(listener) == 1);
|
||||
}
|
||||
|
||||
void CdmSession::OnTimerEvent() {
|
||||
void CdmSession::OnTimerEvent(bool update_usage) {
|
||||
bool event_occurred = false;
|
||||
CdmEventType event;
|
||||
|
||||
policy_engine_.OnTimerEvent(&event_occurred, &event);
|
||||
if (update_usage && has_decrypted_recently_) {
|
||||
policy_engine_->DecryptionEvent();
|
||||
has_decrypted_recently_ = false;
|
||||
}
|
||||
policy_engine_->OnTimerEvent(&event_occurred, &event);
|
||||
|
||||
if (is_offline_ && !is_release_) {
|
||||
StoreLicense(DeviceFiles::kLicenseStateActive);
|
||||
}
|
||||
|
||||
if (event_occurred) {
|
||||
for (CdmEventListenerIter iter = listeners_.begin();
|
||||
|
||||
Reference in New Issue
Block a user