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

@@ -53,6 +53,7 @@ adb push $OUT/system/bin/policy_engine_unittest /system/bin
adb push $OUT/system/bin/libwvdrmmediacrypto_test /system/bin
adb push $OUT/system/bin/libwvdrmdrmplugin_test /system/bin
adb push $OUT/system/bin/cdm_engine_test /system/bin
adb push $OUT/system/bin/cdm_session_unittest /system/bin
adb push $OUT/system/bin/file_store_unittest /system/bin
adb push $OUT/system/bin/device_files_unittest /system/bin
adb push $OUT/system/bin/timer_unittest /system/bin

View File

@@ -31,11 +31,6 @@ class CdmSession {
virtual CdmResponseType RestoreUsageSession(
const CdmKeyMessage& key_request, const CdmKeyResponse& key_response);
virtual void set_key_system(const CdmKeySystem& ksystem) {
key_system_ = ksystem;
}
virtual const CdmKeySystem& key_system() { return key_system_; }
virtual const CdmSessionId& session_id() { return session_id_; }
virtual CdmResponseType GenerateKeyRequest(
@@ -85,7 +80,7 @@ class CdmSession {
virtual bool AttachEventListener(WvCdmEventListener* listener);
virtual bool DetachEventListener(WvCdmEventListener* listener);
virtual void OnTimerEvent();
virtual void OnTimerEvent(bool update_usage);
virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id);
virtual SecurityLevel GetRequestedSecurityLevel();
@@ -99,6 +94,11 @@ class CdmSession {
}
private:
// Internal constructor
void Create(CdmLicense* license_parser, CryptoSession* crypto_session,
PolicyEngine* policy_engine, DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set);
// Generate unique ID for each new session.
CdmSessionId GenerateSessionId();
bool GenerateKeySetId(CdmKeySetId* key_set_id);
@@ -108,16 +108,19 @@ class CdmSession {
bool DeleteLicense();
// instance variables
const CdmSessionId session_id_;
CdmKeySystem key_system_;
CdmLicense license_parser_;
bool initialized_;
CdmSessionId session_id_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;
PolicyEngine policy_engine_;
scoped_ptr<PolicyEngine> policy_engine_;
scoped_ptr<DeviceFiles> file_handle_;
bool license_received_;
bool is_offline_;
bool is_release_;
bool is_usage_update_needed_;
bool is_initial_decryption_;
bool has_decrypted_recently_;
CdmSecurityLevel security_level_;
// information useful for offline and usage scenarios
CdmKeyMessage key_request_;
@@ -134,6 +137,16 @@ class CdmSession {
std::set<WvCdmEventListener*> listeners_;
// For testing only
// Takes ownership of license_parser, crypto_session, policy_engine
// and device_files
CdmSession(CdmLicense* license_parser, CryptoSession* crypto_session,
PolicyEngine* policy_engine, DeviceFiles* file_handle,
const CdmClientPropertySet* cdm_client_property_set);
#if defined(UNIT_TEST)
friend class CdmSessionTest;
#endif
CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession);
};

View File

@@ -18,6 +18,24 @@ typedef std::map<CryptoKeyId, CryptoKey*> CryptoKeyMap;
class CryptoSession {
public:
// This enum should be kept in sync with the values specified for
// HDCP capabilities in OEMCryptoCENC.h. (See comments for
// OEMCrypto_GetHDCPCapability)
typedef enum {
kOemCryptoHdcpNotSupported = 0,
kOemCryptoHdcpVersion1 = 1,
kOemCryptoHdcpVersion2 = 2,
kOemCryptoHdcpVersion2_1 = 3,
kOemCryptoHdcpVersion2_2 = 4,
kOemCryptoNoHdcpDeviceAttached = 0xff,
} OemCryptoHdcpVersion;
typedef enum {
kUsageDurationsInvalid = 0,
kUsageDurationPlaybackNotBegun = 1,
kUsageDurationsValid = 2,
} UsageDurationStatus;
CryptoSession();
virtual ~CryptoSession();
@@ -37,7 +55,7 @@ class CryptoSession {
virtual CryptoSessionId oec_session_id() { return oec_session_id_; }
// Key request/response
virtual void GenerateRequestId(std::string& req_id_str);
virtual bool GenerateRequestId(std::string* req_id_str);
virtual bool PrepareRequest(const std::string& key_deriv_message,
bool is_provisioning, std::string* signature);
virtual bool PrepareRenewalRequest(const std::string& message,
@@ -66,13 +84,20 @@ class CryptoSession {
// Media data path
virtual CdmResponseType Decrypt(const CdmDecryptionParameters& parameters);
virtual bool UsageInformationSupport(bool* has_support);
virtual CdmResponseType UpdateUsageInformation();
virtual CdmResponseType DeactivateUsageInformation(
const std::string& provider_session_token);
virtual CdmResponseType GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report);
const std::string& provider_session_token, std::string* usage_report,
UsageDurationStatus* usage_duration_status,
int64_t* seconds_since_started, int64_t* seconds_since_last_played);
virtual CdmResponseType ReleaseUsageInformation(
const std::string& message, const std::string& signature,
const std::string& provider_session_token);
virtual bool GetHdcpCapabilities(OemCryptoHdcpVersion* current,
OemCryptoHdcpVersion* max);
virtual bool GetRandom(size_t data_length, uint8_t* random_data);
private:

View File

@@ -3,6 +3,7 @@
#ifndef WVCDM_CORE_DEVICE_FILES_H_
#define WVCDM_CORE_DEVICE_FILES_H_
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
#if defined(UNIT_TEST)
@@ -21,11 +22,13 @@ class DeviceFiles {
kLicenseStateUnknown,
} LicenseState;
DeviceFiles(): file_(NULL), security_level_(kSecurityLevelUninitialized),
initialized_(false), test_file_(false) {}
DeviceFiles();
virtual ~DeviceFiles();
virtual bool Init(CdmSecurityLevel security_level);
virtual bool Reset(CdmSecurityLevel security_level) {
return Init(security_level);
}
virtual bool StoreCertificate(const std::string& certificate,
const std::string& wrapped_private_key);
@@ -39,7 +42,9 @@ class DeviceFiles {
const CdmKeyResponse& key_response,
const CdmKeyMessage& key_renewal_request,
const CdmKeyResponse& key_renewal_response,
const std::string& release_server_url);
const std::string& release_server_url,
int64_t playback_start_time,
int64_t last_playback_time);
virtual bool RetrieveLicense(const std::string& key_set_id,
LicenseState* state,
CdmInitData* pssh_data,
@@ -47,7 +52,9 @@ class DeviceFiles {
CdmKeyResponse* key_response,
CdmKeyMessage* key_renewal_request,
CdmKeyResponse* key_renewal_response,
std::string* release_server_url);
std::string* release_server_url,
int64_t* playback_start_time,
int64_t* last_playback_time);
virtual bool DeleteLicense(const std::string& key_set_id);
virtual bool DeleteAllFiles();
virtual bool DeleteAllLicenses();
@@ -93,7 +100,7 @@ class DeviceFiles {
FRIEND_TEST(WvCdmUsageInfoTest, DISABLED_UsageInfo);
#endif
File* file_;
scoped_ptr<File> file_;
CdmSecurityLevel security_level_;
bool initialized_;

View File

@@ -6,6 +6,7 @@
#include <set>
#include "initialization_data.h"
#include "scoped_ptr.h"
#include "wv_cdm_types.h"
namespace video_widevine_server {
@@ -16,14 +17,15 @@ class SignedMessage;
namespace wvcdm {
class Clock;
class CryptoSession;
class PolicyEngine;
class CdmLicense {
public:
CdmLicense() : session_(NULL), initialized_(false) {}
virtual ~CdmLicense() {}
CdmLicense();
virtual ~CdmLicense();
virtual bool Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine);
@@ -42,7 +44,9 @@ class CdmLicense {
virtual bool RestoreOfflineLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response);
const CdmKeyResponse& license_renewal_response,
int64_t playback_start_time,
int64_t last_playback_time);
virtual bool RestoreUsageLicense(const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response);
virtual bool HasInitData() { return !stored_init_data_.empty(); }
@@ -76,6 +80,14 @@ class CdmLicense {
// Used for certificate based licensing
CdmKeyMessage key_request_;
scoped_ptr<Clock> clock_;
// For testing
CdmLicense(Clock* clock);
#if defined(UNIT_TEST)
friend class CdmLicenseTest;
#endif
CORE_DISALLOW_COPY_AND_ASSIGN(CdmLicense);
};

View File

@@ -44,6 +44,7 @@ class PolicyEngine {
// Call this on first decrypt to set the start of playback.
virtual void BeginDecryption(void);
virtual void DecryptionEvent(void);
// UpdateLicense is used in handling a license response for a renewal request.
// The response may only contain any policy fields that have changed. In this
@@ -60,9 +61,21 @@ class PolicyEngine {
bool IsLicenseDurationExpired(int64_t current_time);
bool IsPlaybackDurationExpired(int64_t current_time);
bool GetSecondsSinceStarted(int64_t* seconds_since_started);
bool GetSecondsSinceLastPlayed(int64_t* seconds_since_started);
// for offline save and restore
int64_t GetPlaybackStartTime() { return playback_start_time_; }
int64_t GetLastPlaybackTime() { return last_playback_time_; }
void RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time);
bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; }
private:
typedef enum {
kLicenseStateInitial,
kLicenseStatePending, // if license is issued for sometime in the future
kLicenseStateCanPlay,
kLicenseStateNeedRenewal,
kLicenseStateWaitingLicenseUpdate,
@@ -92,18 +105,15 @@ class PolicyEngine {
// from the license server we will get an updated id field.
video_widevine_server::sdk::LicenseIdentification license_id_;
// This is the license start time that gets sent from the server in each
// license request or renewal.
// The server returns the license start time in the license/license renewal
// response based off the request time sent by the client in the
// license request/renewal
int64_t license_start_time_;
// This is the time at which the license was received and playback was
// started. These times are based off the local clock in case there is a
// discrepency between local and server time.
int64_t license_received_time_;
int64_t playback_start_time_;
int64_t last_playback_time_;
// This is used as a reference point for policy management. This value
// represents an offset from license_received_time_. This is used to
// represents an offset from license_start_time_. This is used to
// calculate the time where renewal retries should occur.
int64_t next_renewal_time_;
int64_t policy_max_duration_seconds_;

View File

@@ -87,6 +87,14 @@ class Properties {
security_level_path_backward_compatibility_support_ = flag;
}
#if defined(UNIT_TEST)
FRIEND_TEST(CdmSessionTest, InitWithCertificate);
FRIEND_TEST(CdmSessionTest, InitWithKeybox);
FRIEND_TEST(CdmSessionTest, ReInitFail);
FRIEND_TEST(CdmSessionTest, InitFailCryptoError);
FRIEND_TEST(CdmSessionTest, InitNeedsProvisioning);
#endif
private:
static bool oem_crypto_use_secure_buffers_;
static bool oem_crypto_use_fifo_;

View File

@@ -702,7 +702,7 @@ void CdmEngine::OnTimerEvent() {
for (CdmSessionMap::iterator iter = sessions_.begin();
iter != sessions_.end(); ++iter) {
iter->second->OnTimerEvent();
iter->second->OnTimerEvent(update_usage_information);
if (update_usage_information && iter->second->is_usage_update_needed()) {
// usage is updated for all sessions so this needs to be

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

View File

@@ -169,13 +169,8 @@ bool CryptoSession::GetApiVersion(uint32_t* version) {
if (!initialized_) {
return false;
}
CdmSecurityLevel level = GetSecurityLevel();
SecurityLevel security_level = kLevelDefault;
if (kSecurityLevelL3 == level) security_level = kLevel3;
LOGV("CryptoSession::GetApiVersion: Lock");
AutoLock auto_lock(crypto_lock_);
*version = OEMCrypto_APIVersion(security_level);
*version = OEMCrypto_APIVersion(
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
return true;
}
@@ -267,13 +262,19 @@ void CryptoSession::Close() {
}
}
void CryptoSession::GenerateRequestId(std::string& req_id_str) {
bool CryptoSession::GenerateRequestId(std::string* req_id_str) {
LOGV("CryptoSession::GenerateRequestId: Lock");
AutoLock auto_lock(crypto_lock_);
req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_),
sizeof(request_id_base_)) +
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_),
sizeof(request_id_index_));
if (!req_id_str) {
LOGE("CryptoSession::GenerateRequestId: No output destination provided.");
return false;
}
*req_id_str = HexEncode(reinterpret_cast<uint8_t*>(&request_id_base_),
sizeof(request_id_base_)) +
HexEncode(reinterpret_cast<uint8_t*>(&request_id_index_),
sizeof(request_id_index_));
return true;
}
bool CryptoSession::PrepareRequest(const std::string& message,
@@ -662,19 +663,31 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
}
}
CdmResponseType CryptoSession::UpdateUsageInformation() {
return (OEMCrypto_UpdateUsageTable() == OEMCrypto_SUCCESS) ? NO_ERROR
: UNKNOWN_ERROR;
bool CryptoSession::UsageInformationSupport(bool* has_support) {
LOGV("UsageInformationSupport: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return false;
*has_support = OEMCrypto_SupportsUsageTable(
kSecurityLevelL3 == GetSecurityLevel() ? kLevel3 : kLevelDefault);
return true;
}
CdmResponseType CryptoSession::GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report) {
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
CdmResponseType CryptoSession::UpdateUsageInformation() {
LOGV("UpdateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
AutoLock auto_lock(crypto_lock_);
if (!initialized_) return UNKNOWN_ERROR;
if (NULL == usage_report) {
LOGE("usage_report parameter is null");
OEMCryptoResult status = OEMCrypto_UpdateUsageTable();
if (status != OEMCrypto_SUCCESS) {
LOGE("CryptoSession::UsageUsageInformation: error=%ld", status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
CdmResponseType CryptoSession::DeactivateUsageInformation(
const std::string& provider_session_token) {
LOGV("DeactivateUsageInformation: id=%ld", (uint32_t)oec_session_id_);
AutoLock auto_lock(crypto_lock_);
uint8_t* pst = reinterpret_cast<uint8_t*>(
@@ -684,19 +697,41 @@ CdmResponseType CryptoSession::GenerateUsageReport(
OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length());
if (OEMCrypto_SUCCESS != status) {
LOGE("CryptoSession::GenerateUsageReport: Deactivate Usage Entry error=%ld",
status);
LOGE("CryptoSession::DeactivateUsageInformation: Deactivate Usage Entry "
" error=%ld",
status);
return UNKNOWN_ERROR;
}
return NO_ERROR;
}
CdmResponseType CryptoSession::GenerateUsageReport(
const std::string& provider_session_token, std::string* usage_report,
UsageDurationStatus* usage_duration_status, int64_t* seconds_since_started,
int64_t* seconds_since_last_played) {
LOGV("GenerateUsageReport: id=%ld", (uint32_t)oec_session_id_);
if (NULL == usage_report) {
LOGE("CryptoSession::GenerateUsageReport: usage_report parameter is null");
return UNKNOWN_ERROR;
}
size_t usage_length = 0;
status = OEMCrypto_ReportUsage(oec_session_id_, pst,
provider_session_token.length(), NULL,
&usage_length);
AutoLock auto_lock(crypto_lock_);
uint8_t* pst = reinterpret_cast<uint8_t*>(
const_cast<char*>(provider_session_token.data()));
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status);
return UNKNOWN_ERROR;
size_t usage_length = 0;
OEMCryptoResult status =
OEMCrypto_ReportUsage(oec_session_id_, pst,
provider_session_token.length(), NULL,
&usage_length);
if (OEMCrypto_SUCCESS != status) {
if (OEMCrypto_ERROR_SHORT_BUFFER != status) {
LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld",
status);
return UNKNOWN_ERROR;
}
}
usage_report->resize(usage_length);
@@ -711,10 +746,27 @@ CdmResponseType CryptoSession::GenerateUsageReport(
return UNKNOWN_ERROR;
}
if (usage_length < usage_report->length()) {
if (usage_length != usage_report->length()) {
usage_report->resize(usage_length);
}
OEMCrypto_PST_Report pst_report;
*usage_duration_status = kUsageDurationsInvalid;
if (usage_length < sizeof(pst_report)) {
LOGE("CryptoSession::GenerateUsageReport: usage report too small=%ld",
usage_length);
return NO_ERROR; // usage report available but no duration information
}
memcpy(&pst_report, usage_report->data(), sizeof(pst_report));
if (kUnused == pst_report.status) {
*usage_duration_status = kUsageDurationPlaybackNotBegun;
return NO_ERROR;
}
*usage_duration_status = kUsageDurationsValid;
*seconds_since_started = pst_report.seconds_since_first_decrypt;
*seconds_since_last_played = pst_report.seconds_since_last_decrypt;
return NO_ERROR;
}
@@ -822,6 +874,24 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message,
return true;
}
bool CryptoSession::GetHdcpCapabilities(
OemCryptoHdcpVersion* current_version,
OemCryptoHdcpVersion* max_version) {
LOGV("GetHdcpCapabilities: id=%ld", (uint32_t)oec_session_id_);
if (!initialized_) return UNKNOWN_ERROR;
OEMCrypto_HDCP_Capability current, max;
OEMCryptoResult status = OEMCrypto_GetHDCPCapability(&current, &max);
if (OEMCrypto_SUCCESS != status) {
LOGW("OEMCrypto_GetHDCPCapability fails with %d", status);
return false;
}
*current_version = static_cast<OemCryptoHdcpVersion>(current);
*max_version = static_cast<OemCryptoHdcpVersion>(max);
return true;
}
bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) {
if (random_data == NULL) {
LOGE("CryptoSession::GetRandom: random data destination not provided");

View File

@@ -55,8 +55,13 @@ bool Hash(const std::string& data, std::string* hash) {
namespace wvcdm {
DeviceFiles::DeviceFiles()
: file_(NULL), security_level_(kSecurityLevelUninitialized),
initialized_(false), test_file_(false) {
}
DeviceFiles::~DeviceFiles() {
if (!file_ && !test_file_) delete file_;
if (test_file_) file_.release();
}
bool DeviceFiles::Init(CdmSecurityLevel security_level) {
@@ -69,7 +74,7 @@ bool DeviceFiles::Init(CdmSecurityLevel security_level) {
LOGW("DeviceFiles::Init: Unsupported security level %d", security_level);
return false;
}
file_ = new File();
if (!test_file_) file_.reset(new File());
security_level_ = security_level;
initialized_ = true;
return true;
@@ -148,7 +153,9 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
const CdmKeyResponse& license_message,
const CdmKeyMessage& license_renewal_request,
const CdmKeyResponse& license_renewal,
const std::string& release_server_url) {
const std::string& release_server_url,
int64_t playback_start_time,
int64_t last_playback_time) {
if (!initialized_) {
LOGW("DeviceFiles::StoreLicense: not initialized");
return false;
@@ -179,6 +186,8 @@ bool DeviceFiles::StoreLicense(const std::string& key_set_id,
license->set_renewal_request(license_renewal_request);
license->set_renewal(license_renewal);
license->set_release_server_url(release_server_url);
license->set_playback_start_time(playback_start_time);
license->set_last_playback_time(last_playback_time);
std::string serialized_file;
file.SerializeToString(&serialized_file);
@@ -193,7 +202,9 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
CdmKeyResponse* license_message,
CdmKeyMessage* license_renewal_request,
CdmKeyResponse* license_renewal,
std::string* release_server_url) {
std::string* release_server_url,
int64_t* playback_start_time,
int64_t* last_playback_time) {
if (!initialized_) {
LOGW("DeviceFiles::RetrieveLicense: not initialized");
return false;
@@ -245,6 +256,8 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
*license_renewal_request = license.renewal_request();
*license_renewal = license.renewal();
*release_server_url = license.release_server_url();
*playback_start_time = license.playback_start_time();
*last_playback_time = license.last_playback_time();
return true;
}
@@ -454,7 +467,7 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector<
bool DeviceFiles::StoreFile(const char* name,
const std::string& serialized_file) {
if (!file_) {
if (!file_.get()) {
LOGW("DeviceFiles::StoreFile: Invalid file handle");
return false;
}
@@ -514,7 +527,7 @@ bool DeviceFiles::StoreFile(const char* name,
}
bool DeviceFiles::RetrieveFile(const char* name, std::string* serialized_file) {
if (!file_) {
if (!file_.get()) {
LOGW("DeviceFiles::RetrieveFile: Invalid file handle");
return false;
}
@@ -663,8 +676,7 @@ std::string DeviceFiles::GetUsageInfoFileName() {
}
void DeviceFiles::SetTestFile(File* file) {
if (file_) delete file_;
file_ = file;
file_.reset(file);
test_file_ = true;
}

View File

@@ -31,6 +31,8 @@ message License {
optional bytes renewal_request = 5;
optional bytes renewal = 6;
optional bytes release_server_url = 7;
optional int64 playback_start_time = 8 [default = 0];
optional int64 last_playback_time = 9 [default = 0];
}
message UsageInfo {

View File

@@ -2,8 +2,11 @@
#include "license.h"
#include <limits.h>
#include <vector>
#include "clock.h"
#include "crypto_key.h"
#include "crypto_session.h"
#include "device_files.h"
@@ -22,7 +25,6 @@ std::string kDeviceNameKey = "device_name";
std::string kProductNameKey = "product_name";
std::string kBuildInfoKey = "build_info";
std::string kDeviceIdKey = "device_id";
std::string kOemCryptoApiVersion = "oemcrypto_api_version";
const unsigned char kServiceCertificateCAPublicKey[] = {
0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81,
0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03,
@@ -80,6 +82,7 @@ namespace wvcdm {
// Protobuf generated classes.
using video_widevine_server::sdk::ClientIdentification;
using video_widevine_server::sdk::ClientIdentification_ClientCapabilities;
using video_widevine_server::sdk::ClientIdentification_NameValue;
using video_widevine_server::sdk::DeviceCertificate;
using video_widevine_server::sdk::EncryptedClientIdentification;
@@ -140,6 +143,24 @@ static std::vector<CryptoKey> ExtractContentKeys(const License& license) {
return key_array;
}
CdmLicense::CdmLicense()
: session_(NULL),
initialized_(false),
clock_(new Clock()) {
}
CdmLicense::CdmLicense(Clock* clock)
: session_(NULL),
initialized_(false) {
if (NULL == clock) {
LOGE("CdmLicense::CdmLicense: clock parameter not provided");
return;
}
clock_.reset(clock);
}
CdmLicense::~CdmLicense() {}
bool CdmLicense::Init(const std::string& token, CryptoSession* session,
PolicyEngine* policy_engine) {
if (token.size() == 0) {
@@ -206,7 +227,7 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
}
std::string request_id;
session_->GenerateRequestId(request_id);
session_->GenerateRequestId(&request_id);
LicenseRequest license_request;
ClientIdentification* client_id = license_request.mutable_client_id();
@@ -260,11 +281,54 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
client_info->set_name(kDeviceIdKey);
client_info->set_value(value);
}
ClientIdentification_ClientCapabilities* client_capabilities =
client_id->mutable_client_capabilities();
bool supports_usage_information;
if (session_->UsageInformationSupport(&supports_usage_information)) {
client_capabilities->set_session_token(supports_usage_information);
}
CryptoSession::OemCryptoHdcpVersion current_version, max_version;
if (session_->GetHdcpCapabilities(&current_version, &max_version)) {
switch (max_version) {
case CryptoSession::kOemCryptoHdcpNotSupported:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_NONE);
break;
case CryptoSession::kOemCryptoHdcpVersion1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V1);
break;
case CryptoSession::kOemCryptoHdcpVersion2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2);
break;
case CryptoSession::kOemCryptoHdcpVersion2_1:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_1);
break;
case CryptoSession::kOemCryptoHdcpVersion2_2:
client_capabilities->set_max_hdcp_version(
video_widevine_server::sdk::
ClientIdentification_ClientCapabilities_HdcpVersion_HDCP_V2_2);
break;
case CryptoSession::kOemCryptoNoHdcpDeviceAttached:
default:
LOGW(
"CdmLicense::PrepareKeyRequest: unexpected HDCP max capability "
"version %d",
max_version);
}
}
uint32_t version = 0;
if (session_->GetApiVersion(&version)) {
client_info = client_id->add_client_info();
client_info->set_name(kOemCryptoApiVersion);
client_info->set_value(UintToString(version));
client_capabilities->set_oem_crypto_api_version(version);
}
if (privacy_mode_enabled) {
@@ -359,12 +423,10 @@ bool CdmLicense::PrepareKeyRequest(const InitializationData& init_data,
return false;
}
// The time field will be updated once the cdm wrapper
// has been updated to pass us in the time.
license_request.set_request_time(0);
license_request.set_type(LicenseRequest::NEW);
license_request.set_request_time(clock_->GetCurrentTime());
// Get/set the nonce. This value will be reflected in the Key Control Block
// of the license response.
uint32_t nonce;
@@ -430,21 +492,47 @@ bool CdmLicense::PrepareKeyUpdateRequest(bool is_renewal,
else
license_request.set_type(LicenseRequest::RELEASE);
license_request.set_request_time(clock_->GetCurrentTime());
LicenseRequest_ContentIdentification_ExistingLicense* current_license =
license_request.mutable_content_id()->mutable_license();
LicenseIdentification license_id = policy_engine_->license_id();
current_license->mutable_license_id()->CopyFrom(license_id);
if (!is_renewal) {
if (license_id.has_provider_session_token()) {
std::string usage_report;
if (NO_ERROR !=
session_->GenerateUsageReport(license_id.provider_session_token(),
&usage_report)) {
int64_t seconds_since_started, seconds_since_last_played;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
if (!provider_session_token_.empty()) {
if (!is_renewal) {
CdmResponseType status =
session_->DeactivateUsageInformation(provider_session_token_);
if (NO_ERROR != status)
return false;
}
current_license->set_session_usage_table_entry(usage_report);
}
std::string usage_report;
CdmResponseType status =
session_->GenerateUsageReport(provider_session_token_,
&usage_report, &usage_duration_status,
&seconds_since_started,
&seconds_since_last_played);
if (!is_renewal) {
if (NO_ERROR == status)
current_license->set_session_usage_table_entry(usage_report);
else
return false;
}
}
if (CryptoSession::kUsageDurationsValid != usage_duration_status) {
if (policy_engine_->GetSecondsSinceStarted(&seconds_since_started) &&
policy_engine_->GetSecondsSinceLastPlayed(&seconds_since_last_played))
usage_duration_status = CryptoSession::kUsageDurationsValid;
}
if (CryptoSession::kUsageDurationsValid == usage_duration_status) {
current_license->set_seconds_since_started(seconds_since_started);
current_license->set_seconds_since_last_played(seconds_since_last_played);
}
// Get/set the nonce. This value will be reflected in the Key Control Block
@@ -664,7 +752,9 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
bool CdmLicense::RestoreOfflineLicense(
const CdmKeyMessage& license_request,
const CdmKeyResponse& license_response,
const CdmKeyResponse& license_renewal_response) {
const CdmKeyResponse& license_renewal_response,
int64_t playback_start_time,
int64_t last_playback_time) {
if (license_request.empty() || license_response.empty()) {
LOGE(
@@ -704,6 +794,36 @@ bool CdmLicense::RestoreOfflineLicense(
if (sts != KEY_ADDED) return false;
}
if (!provider_session_token_.empty()) {
std::string usage_report;
CryptoSession::UsageDurationStatus usage_duration_status =
CryptoSession::kUsageDurationsInvalid;
int64_t seconds_since_started, seconds_since_last_played;
sts = session_->GenerateUsageReport(
provider_session_token_, &usage_report, &usage_duration_status,
&seconds_since_started, &seconds_since_last_played);
if (NO_ERROR == sts) {
switch (usage_duration_status) {
case CryptoSession::kUsageDurationPlaybackNotBegun:
playback_start_time = 0;
last_playback_time = 0;
break;
case CryptoSession::kUsageDurationsValid: {
int64_t current_time = clock_->GetCurrentTime();
if (current_time - seconds_since_started > 0)
playback_start_time = current_time - seconds_since_started;
if (current_time - last_playback_time > 0)
last_playback_time = current_time - seconds_since_last_played;
break;
}
default:
break;
}
}
}
policy_engine_->RestorePlaybackTimes(playback_start_time, last_playback_time);
return true;
}

View File

@@ -287,12 +287,6 @@ message SignedMessage {
// This message is used to pass optional data on initial license issuance.
message SessionInit {
enum ReplayControl {
NO_SESSION_USAGE = 0;
NONCE_REQUIRED_AND_NEW_SESSION_USAGE = 1;
NONCE_REQUIRED_OR_EXISTING_SESSION_USAGE = 2;
}
optional bytes session_id = 1;
optional bytes purchase_id = 2;
// master_signing_key should be 128 bits in length.
@@ -312,8 +306,6 @@ message SessionInit {
// LicenseIdentfication::provider_session_token, and sent back in all
// license renewal and release requests for the session thereafter.
optional bytes provider_session_token = 7;
// Replay control indicator which will be encoded into V9+ KeyControl blocks.
optional ReplayControl replay_control = 8 [default = NO_SESSION_USAGE];
}
// This message is used by the server to preserve and restore session state.
@@ -415,6 +407,7 @@ message ClientIdentification {
optional bool session_token = 2 [default = false];
optional bool video_resolution_constraints = 3 [default = false];
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
optional uint32 oem_crypto_api_version = 5;
}
// Type of factory-provisioned device root of trust. Optional.

View File

@@ -95,7 +95,7 @@ OEMCryptoResult OEMCrypto_GetHDCPCapability(OEMCrypto_HDCP_Capability *current,
}
extern "C" bool OEMCrypto_SupportsUsageTable() {
return OEMCrypto_ERROR_NOT_IMPLEMENTED;
return false;
}
extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() {

View File

@@ -33,8 +33,8 @@ void PolicyEngine::Init(Clock* clock) {
license_state_ = kLicenseStateInitial;
can_decrypt_ = false;
license_start_time_ = 0;
license_received_time_ = 0;
playback_start_time_ = 0;
last_playback_time_ = 0;
next_renewal_time_ = 0;
policy_max_duration_seconds_ = 0;
clock_ = clock;
@@ -76,6 +76,14 @@ void PolicyEngine::OnTimerEvent(bool* event_occurred, CdmEventType* event) {
break;
}
case kLicenseStatePending: {
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
}
break;
}
case kLicenseStateInitial:
case kLicenseStateExpired: {
break;
@@ -115,13 +123,11 @@ void PolicyEngine::UpdateLicense(
policy_.MergeFrom(license.policy());
// some basic license validation
if (license_state_ == kLicenseStateInitial) {
// license start time needs to be present in the initial response
if (!license.has_license_start_time())
return;
}
else {
// if renewal, discard license if version has not been updated
// license start time needs to be specified in the initial response
if (!license.has_license_start_time()) return;
// if renewal, discard license if version has not been updated
if (license_state_ != kLicenseStateInitial) {
if (license.id().version() > license_id_.version())
license_id_.CopyFrom(license.id());
else
@@ -129,12 +135,8 @@ void PolicyEngine::UpdateLicense(
}
// Update time information
int64_t current_time = clock_->GetCurrentTime();
if (license.has_license_start_time())
license_start_time_ = license.license_start_time();
license_received_time_ = current_time;
next_renewal_time_ = current_time +
policy_.renewal_delay_seconds();
license_start_time_ = license.license_start_time();
next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds();
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
// will be set to the minimum of the following policies :
@@ -157,12 +159,18 @@ void PolicyEngine::UpdateLicense(
return;
}
int64_t current_time = clock_->GetCurrentTime();
if (IsLicenseDurationExpired(current_time)) return;
if (IsPlaybackDurationExpired(current_time)) return;
// Update state
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
} else {
license_state_ = kLicenseStatePending;
can_decrypt_ = false;
}
}
void PolicyEngine::BeginDecryption() {
@@ -172,12 +180,14 @@ void PolicyEngine::BeginDecryption() {
case kLicenseStateNeedRenewal:
case kLicenseStateWaitingLicenseUpdate:
playback_start_time_ = clock_->GetCurrentTime();
last_playback_time_ = playback_start_time_;
if (policy_.renew_with_usage()) {
license_state_ = kLicenseStateNeedRenewal;
}
break;
case kLicenseStateInitial:
case kLicenseStatePending:
case kLicenseStateExpired:
default:
break;
@@ -185,6 +195,10 @@ void PolicyEngine::BeginDecryption() {
}
}
void PolicyEngine::DecryptionEvent() {
last_playback_time_ = clock_->GetCurrentTime();
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
std::stringstream ss;
int64_t current_time = clock_->GetCurrentTime();
@@ -211,6 +225,27 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
return NO_ERROR;
}
bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) {
if (playback_start_time_ == 0) return false;
*seconds_since_started = clock_->GetCurrentTime() - playback_start_time_;
return (*seconds_since_started >= 0) ? true : false;
}
bool PolicyEngine::GetSecondsSinceLastPlayed(
int64_t* seconds_since_last_played) {
if (last_playback_time_ == 0) return false;
*seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_;
return (*seconds_since_last_played >= 0) ? true : false;
}
void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time,
int64_t last_playback_time) {
playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0;
last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0;
}
void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
license_state_ = kLicenseStateWaitingLicenseUpdate;
next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds();
@@ -221,7 +256,7 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) {
// will always return false if the value is 0.
bool PolicyEngine::IsLicenseDurationExpired(int64_t current_time) {
return policy_max_duration_seconds_ &&
license_received_time_ + policy_max_duration_seconds_ <=
license_start_time_ + policy_max_duration_seconds_ <=
current_time;
}
@@ -229,9 +264,12 @@ int64_t PolicyEngine::GetLicenseDurationRemaining(int64_t current_time) {
if (0 == policy_max_duration_seconds_) return LLONG_MAX;
int64_t remaining_time = policy_max_duration_seconds_
+ license_received_time_ - current_time;
+ license_start_time_ - current_time;
if (remaining_time < 0) remaining_time = 0;
if (remaining_time < 0)
remaining_time = 0;
else if (remaining_time > policy_max_duration_seconds_)
remaining_time = policy_max_duration_seconds_;
return remaining_time;
}
@@ -256,7 +294,7 @@ int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) {
bool PolicyEngine::IsRenewalDelayExpired(int64_t current_time) {
return policy_.can_renew() &&
(policy_.renewal_delay_seconds() > 0) &&
license_received_time_ + policy_.renewal_delay_seconds() <=
license_start_time_ + policy_.renewal_delay_seconds() <=
current_time;
}
@@ -264,7 +302,7 @@ bool PolicyEngine::IsRenewalRecoveryDurationExpired(
int64_t current_time) {
// NOTE: Renewal Recovery Duration is currently not used.
return (policy_.renewal_recovery_duration_seconds() > 0) &&
license_received_time_ + policy_.renewal_recovery_duration_seconds() <=
license_start_time_ + policy_.renewal_recovery_duration_seconds() <=
current_time;
}

File diff suppressed because it is too large Load Diff

View File

@@ -138,8 +138,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
policy->set_can_play(false);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5));
.WillOnce(Return(kLicenseStartTime + 1));
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->can_decrypt());
@@ -163,8 +162,8 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + min_duration + 1));
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration));
policy_engine_->SetLicense(license_);
@@ -220,8 +219,8 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + 1 + min_duration));
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration));
policy_engine_->SetLicense(license_);
@@ -248,8 +247,8 @@ TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + 1 + min_duration));
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration));
policy_engine_->SetLicense(license_);
@@ -277,10 +276,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay - 1))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay))
.WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay))
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration))
.WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration));
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration - 1))
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration));
policy_engine_->SetLicense(license_);
@@ -357,8 +356,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + 1 + min_duration));
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration));
policy_engine_->SetLicense(license_);
@@ -387,8 +386,8 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + kHighDuration))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 10));
.WillOnce(Return(kLicenseStartTime + kHighDuration - 1))
.WillOnce(Return(kLicenseStartTime + kHighDuration));
policy_engine_->SetLicense(license_);
@@ -406,6 +405,29 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_LicenseWithFutureStartTime) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime - 100))
.WillOnce(Return(kLicenseStartTime - 50))
.WillOnce(Return(kLicenseStartTime))
.WillOnce(Return(kLicenseStartTime + 10));
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_FALSE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
License_Policy* policy = license_.mutable_policy();
policy->set_can_renew(false);
@@ -480,6 +502,49 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess_WithFutureStartTime) {
int64_t license_renewal_delay =
GetLicenseRenewalDelay(kStreamingLicenseDuration);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay - 15))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 30))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 60));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
license_.set_license_start_time(kLicenseStartTime + license_renewal_delay +
50);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_FALSE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
int64_t license_renewal_delay =
GetLicenseRenewalDelay(kStreamingLicenseDuration);
@@ -790,7 +855,7 @@ TEST_F(PolicyEngineTest, QuerySuccess) {
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
ss.clear();
EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time);
EXPECT_EQ(kStreamingLicenseDuration - 100, remaining_time);
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration, remaining_time);
@@ -816,7 +881,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_PlaybackNotBegun) {
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 99, remaining_time);
EXPECT_EQ(kStreamingLicenseDuration - 100, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -832,7 +897,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_PlaybackNotBegun) {
ss.clear();
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time);
EXPECT_EQ(kStreamingLicenseDuration - 200, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -861,7 +926,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_PlaybackBegun) {
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 49, remaining_time);
EXPECT_EQ(kStreamingLicenseDuration - 50, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -885,7 +950,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_PlaybackBegun) {
ss.clear();
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 199, remaining_time);
EXPECT_EQ(kStreamingLicenseDuration - 200, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -931,7 +996,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kOfflineLicenseDuration - 299, remaining_time);
EXPECT_EQ(kOfflineLicenseDuration - 300, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -952,8 +1017,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_CanPlayFalse) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 100))
.WillOnce(Return(kLicenseStartTime + 200));
.WillOnce(Return(kLicenseStartTime + 100));
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->can_decrypt());
@@ -977,7 +1041,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_CanPlayFalse) {
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kOfflineLicenseDuration - 199, remaining_time);
EXPECT_EQ(kOfflineLicenseDuration - 100, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -995,8 +1059,8 @@ TEST_F(PolicyEngineTest, QuerySuccess_RentalDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + min_duration + 1))
.WillOnce(Return(kLicenseStartTime + min_duration + 5));
policy_engine_->SetLicense(license_);
@@ -1076,7 +1140,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_PlaybackDurationExpired) {
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kHighDuration - 10004 - min_duration, remaining_time);
EXPECT_EQ(kHighDuration - 10005 - min_duration, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
@@ -1092,9 +1156,9 @@ TEST_F(PolicyEngineTest, QuerySuccess_LicenseDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + 1 + min_duration))
.WillOnce(Return(kLicenseStartTime + 5 + min_duration));
.WillOnce(Return(kLicenseStartTime + min_duration + 5));
policy_engine_->SetLicense(license_);
@@ -1140,11 +1204,11 @@ TEST_F(PolicyEngineTest, QuerySuccess_RentalDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay - 1))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay))
.WillOnce(Return(kLicenseStartTime + 1 + license_renewal_delay))
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration - 1))
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration))
.WillOnce(Return(kLicenseStartTime + 1 + kStreamingLicenseDuration))
.WillOnce(Return(kLicenseStartTime + 5 + kStreamingLicenseDuration));
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 5));
policy_engine_->SetLicense(license_);
@@ -1275,9 +1339,9 @@ TEST_F(PolicyEngineTest, QuerySuccess_LicenseDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + min_duration - 1))
.WillOnce(Return(kLicenseStartTime + min_duration))
.WillOnce(Return(kLicenseStartTime + 1 + min_duration))
.WillOnce(Return(kLicenseStartTime + 5 + min_duration));
.WillOnce(Return(kLicenseStartTime + min_duration + 5));
policy_engine_->SetLicense(license_);
@@ -1325,7 +1389,7 @@ TEST_F(PolicyEngineTest, QuerySuccess_Durations0) {
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + kHighDuration))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 10))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 9))
.WillOnce(Return(kLicenseStartTime + kHighDuration + 15));
policy_engine_->SetLicense(license_);
@@ -1361,4 +1425,213 @@ TEST_F(PolicyEngineTest, QuerySuccess_Durations0) {
EXPECT_EQ(LLONG_MAX, remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
}
TEST_F(PolicyEngineTest, QuerySuccess_LicenseWithFutureStartTime) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime - 100))
.WillOnce(Return(kLicenseStartTime - 50))
.WillOnce(Return(kLicenseStartTime - 10))
.WillOnce(Return(kLicenseStartTime))
.WillOnce(Return(kLicenseStartTime + 10))
.WillOnce(Return(kLicenseStartTime + 25));
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_FALSE(policy_engine_->can_decrypt());
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration, remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
EXPECT_FALSE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->BeginDecryption();
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
ss.clear();
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 25, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration - 15, remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
}
TEST_F(PolicyEngineTest, QuerySuccess_Renew) {
int64_t license_renewal_delay =
GetLicenseRenewalDelay(kStreamingLicenseDuration);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay - 25))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 10))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 15));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
license_.set_license_start_time(kLicenseStartTime + license_renewal_delay +
15);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - kLicenseRenewalRetryInterval,
remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration + 5 - license_renewal_delay -
kLicenseRenewalRetryInterval - 15,
remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
}
TEST_F(PolicyEngineTest, QuerySuccess_RenewWithFutureStartTime) {
int64_t license_renewal_delay =
GetLicenseRenewalDelay(kStreamingLicenseDuration);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay - 25))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 10))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 20))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 10))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 20))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 30))
.WillOnce(Return(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 40));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
license_.set_license_start_time(kLicenseStartTime + license_renewal_delay +
kLicenseRenewalRetryInterval + 20);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_FALSE(policy_engine_->can_decrypt());
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration + 5 - license_renewal_delay -
kLicenseRenewalRetryInterval - 20,
remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
ss.clear();
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kStreamingLicenseDuration - 20, remaining_time);
ss.clear();
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(kPlaybackDuration + 5 - license_renewal_delay -
kLicenseRenewalRetryInterval - 40,
remaining_time);
EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]);
}
} // wvcdm