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:
Rahul Frias
2014-07-02 13:03:09 -07:00
parent 9b4da994ec
commit 7a933ee48e
18 changed files with 1308 additions and 692 deletions

View File

@@ -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();