Merge "Unified State-Changing API for LicenseKeyStatus" into oc-mr1-dev am: cd6178cf82

am: 753f6c6d27

Change-Id: Ic324a0bf46bc0d862de9c95072c5e4d53f157e51
This commit is contained in:
John W. Bruce
2017-09-01 00:52:24 +00:00
committed by android-build-merger
6 changed files with 264 additions and 115 deletions

View File

@@ -11,6 +11,8 @@
namespace wvcdm { namespace wvcdm {
static const uint32_t kNoResolution = 0;
class LicenseKeyStatus; class LicenseKeyStatus;
// Holds all content and operator session keys for a session. // Holds all content and operator session keys for a session.
@@ -31,10 +33,16 @@ class LicenseKeys {
virtual bool GetAllowedUsage(const KeyId& key_id, virtual bool GetAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* allowed_usage); CdmKeyAllowedUsage* allowed_usage);
// Applies a new status to each content key. // Applies a new status, resolution, and/or HDCP level to each content key,
// Returns true if any statuses changed, and sets new_usable_keys to // updating their usability under their constraints. Returns true if any keys
// true if the status changes resulted in keys becoming usable. // have changed usability, and sets new_usable_keys to true if the status
virtual bool ApplyStatusChange(CdmKeyStatus new_status, // changes resulted in at least one key becoming usable.
//
// Pass in NULL for any of the const-pointer arguments to leave that state
// unchanged.
virtual bool ApplyStatusChange(
const CdmKeyStatus* new_status, const uint32_t* new_resolution,
const CryptoSession::HdcpCapability* new_hdcp_level,
bool* new_usable_keys); bool* new_usable_keys);
// Populates the CdmKeyStatusMap with the current content keys. // Populates the CdmKeyStatusMap with the current content keys.
@@ -45,11 +53,6 @@ class LicenseKeys {
// to the key, returns true. // to the key, returns true.
virtual bool MeetsConstraints(const KeyId& key_id); virtual bool MeetsConstraints(const KeyId& key_id);
// Applies a resolution and/or hdcp change to each key, updating their
// useability under their constraints.
virtual void ApplyConstraints(uint32_t new_resolution,
CryptoSession::HdcpCapability new_hdcp_level);
// Extracts the keys from a license and makes them available for // Extracts the keys from a license and makes them available for
// querying usage and constraint settings. // querying usage and constraint settings.
virtual void SetFromLicense( virtual void SetFromLicense(
@@ -85,21 +88,24 @@ class LicenseKeyStatus {
// Returns the current status of the key. // Returns the current status of the key.
virtual CdmKeyStatus GetKeyStatus() const { return key_status_; } virtual CdmKeyStatus GetKeyStatus() const { return key_status_; }
// Applies a new status to this key. // Applies a new status, resolution, and/or HDCP level, updating the key's
// Returns true if the status changed, and sets new_usable_keys to // usability under its constraints. Returns true if this results in a change
// true if the status changes resulted in the key becoming usable. // to the key's usability, and sets newly_usable to true if the status
virtual bool ApplyStatusChange(CdmKeyStatus new_status, // changes resulted in the key becoming usable.
bool* new_usable_keys); //
// Pass in NULL for any of the const-pointer arguments to leave that state
// unchanged.
virtual bool ApplyStatusChange(
const CdmKeyStatus* maybe_new_status,
const uint32_t* maybe_new_resolution,
const CryptoSession::HdcpCapability* maybe_new_hdcp_level,
bool* newly_usable);
// Returns the current constraint status of this key. The result // Returns the current constraint status of this key. The result
// may change due to calls to ApplyConstraints(). // may change due to calls to ApplyConstraints().
// Note: this will return true until the first call to ApplyConstraints(). // Note: this will return true until the first call to ApplyConstraints().
virtual bool MeetsConstraints() const { return meets_constraints_; } virtual bool MeetsConstraints() const { return meets_constraints_; }
// Applies the given changes in resolution or HDCP settings.
virtual void ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level);
protected: protected:
typedef ::video_widevine::License::KeyContainer KeyContainer; typedef ::video_widevine::License::KeyContainer KeyContainer;
typedef KeyContainer::OperatorSessionKeyPermissions typedef KeyContainer::OperatorSessionKeyPermissions
@@ -121,11 +127,14 @@ class LicenseKeyStatus {
bool HasConstraints() { bool HasConstraints() {
return is_content_key_ && constraints_.size() != 0; return is_content_key_ && constraints_.size() != 0;
} }
void SetConstraints(const ConstraintList& constraints); void SetConstraints(const ConstraintList& constraints);
void ApplyConstraints();
bool is_content_key_; bool is_content_key_;
CdmKeyStatus key_status_; CdmKeyStatus key_status_;
uint32_t resolution_;
CryptoSession::HdcpCapability hdcp_level_;
bool can_check_constraints_;
bool meets_constraints_; bool meets_constraints_;
CdmKeyAllowedUsage allowed_usage_; CdmKeyAllowedUsage allowed_usage_;
CryptoSession::HdcpCapability default_hdcp_level_; CryptoSession::HdcpCapability default_hdcp_level_;

View File

@@ -108,6 +108,10 @@ class PolicyEngine {
friend class PolicyEngineConstraintsTest; friend class PolicyEngineConstraintsTest;
void InitDevice(CryptoSession* crypto_session); void InitDevice(CryptoSession* crypto_session);
// Checks the keys against the current state of the hardware in case the HDCP
// level no longer meets their requirements. Will only check once every
// |kHdcpCheckInterval| seconds. Calls NotifyIfKeysChanged() to propagate any
// resultant changes to the OnKeysChange event.
void CheckDevice(int64_t current_time); void CheckDevice(int64_t current_time);
void SetDeviceResolution(uint32_t width, uint32_t height) { void SetDeviceResolution(uint32_t width, uint32_t height) {
@@ -143,9 +147,14 @@ class PolicyEngine {
void UpdateRenewalRequest(int64_t current_time); void UpdateRenewalRequest(int64_t current_time);
// Notifies updates in keys information and fire OnKeysChange event if
// key changes. // Updates the keys' status to |new_status|. Calls NotifyIfKeysChanged() to
void NotifyKeysChange(CdmKeyStatus new_status); // propagate any resultant changes to the OnKeysChange event.
void UpdateKeyStatus(CdmKeyStatus new_status);
// Helper method that correctly fires the OnKeysChange event - if needed -
// depending on the parameters it is given.
void NotifyIfKeysChanged(bool keys_changed, bool new_usable_keys);
// Notifies updates in expiry time and fire OnExpirationUpdate event if // Notifies updates in expiry time and fire OnExpirationUpdate event if
// expiry time changes. // expiry time changes.

View File

@@ -86,14 +86,17 @@ bool LicenseKeys::GetAllowedUsage(const KeyId& key_id,
} }
} }
bool LicenseKeys::ApplyStatusChange(CdmKeyStatus new_status, bool LicenseKeys::ApplyStatusChange(
const CdmKeyStatus* new_status, const uint32_t* new_resolution,
const CryptoSession::HdcpCapability* new_hdcp_level,
bool* new_usable_keys) { bool* new_usable_keys) {
bool keys_changed = false; bool keys_changed = false;
bool newly_usable = false; bool newly_usable = false;
*new_usable_keys = false; *new_usable_keys = false;
for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) { for (LicenseKeyStatusIterator it = keys_.begin(); it != keys_.end(); ++it) {
bool usable; bool usable;
if (it->second->ApplyStatusChange(new_status, &usable)) { if (it->second->ApplyStatusChange(new_status, new_resolution,
new_hdcp_level, &usable)) {
newly_usable |= usable; newly_usable |= usable;
keys_changed = true; keys_changed = true;
} }
@@ -122,13 +125,6 @@ bool LicenseKeys::MeetsConstraints(const KeyId& key_id) {
} }
} }
void LicenseKeys::ApplyConstraints(
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) {
for (LicenseKeyStatusIterator i = keys_.begin(); i != keys_.end(); ++i) {
i->second->ApplyConstraints(new_resolution, new_hdcp_level);
}
}
void LicenseKeys::SetFromLicense( void LicenseKeys::SetFromLicense(
const video_widevine::License& license) { const video_widevine::License& license) {
this->Clear(); this->Clear();
@@ -145,6 +141,9 @@ void LicenseKeys::SetFromLicense(
LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) : LicenseKeyStatus::LicenseKeyStatus(const KeyContainer& key) :
is_content_key_(false), is_content_key_(false),
key_status_(kKeyStatusInternalError), key_status_(kKeyStatusInternalError),
resolution_(kNoResolution),
hdcp_level_(HDCP_NONE),
can_check_constraints_(false),
meets_constraints_(true), meets_constraints_(true),
default_hdcp_level_(HDCP_NONE) { default_hdcp_level_(HDCP_NONE) {
@@ -221,26 +220,55 @@ bool LicenseKeyStatus::GetAllowedUsage(CdmKeyAllowedUsage* allowed_usage) {
return true; return true;
} }
bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status, bool LicenseKeyStatus::ApplyStatusChange(
bool* new_usable_key) { const CdmKeyStatus* maybe_new_status, const uint32_t* maybe_new_resolution,
*new_usable_key = false; const CryptoSession::HdcpCapability* maybe_new_hdcp_level,
bool* newly_usable) {
*newly_usable = false;
if (!is_content_key_) { if (!is_content_key_) {
return false; return false;
} }
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) { // Most of this function is various work to calculate an updated value for
// the status. We start at the same value as the current status.
CdmKeyStatus updated_status = key_status_;
// Account for the new status, if provided.
if (maybe_new_status != NULL) {
const CdmKeyStatus& new_status = *maybe_new_status;
can_check_constraints_ = (new_status == kKeyStatusUsable);
updated_status = new_status;
}
// Account for the new resolution, if provided.
if (maybe_new_resolution != NULL) {
resolution_ = *maybe_new_resolution;
}
// Account for the new HDCP level, if provided.
if (maybe_new_hdcp_level != NULL) {
hdcp_level_ = *maybe_new_hdcp_level;
}
// If we can, check the current state against the constraints and update the
// status if needed.
if (can_check_constraints_) {
ApplyConstraints();
if (!MeetsConstraints()) { if (!MeetsConstraints()) {
updated_status = kKeyStatusOutputNotAllowed; updated_status = kKeyStatusOutputNotAllowed;
} }
} }
// Check if any of that work changed the key status.
if (key_status_ != updated_status) { if (key_status_ != updated_status) {
key_status_ = updated_status; key_status_ = updated_status;
if (updated_status == kKeyStatusUsable) { if (updated_status == kKeyStatusUsable) {
*new_usable_key = true; *newly_usable = true;
} }
return true; return true;
} } else {
return false; return false;
}
} }
// If the key has constraints, find the constraint that applies. // If the key has constraints, find the constraint that applies.
@@ -250,12 +278,16 @@ bool LicenseKeyStatus::ApplyStatusChange(CdmKeyStatus new_status,
// If the key has no constraints, or if the constraint has no HDCP // If the key has no constraints, or if the constraint has no HDCP
// requirement, use the key's default HDCP setting to check against the // requirement, use the key's default HDCP setting to check against the
// device's current HDCP level. // device's current HDCP level.
void LicenseKeyStatus::ApplyConstraints( void LicenseKeyStatus::ApplyConstraints() {
uint32_t new_resolution, CryptoSession::HdcpCapability new_hdcp_level) { if (resolution_ == kNoResolution) {
// Until a resolution has been detected, the constraints cannot be checked.
meets_constraints_ = true;
return;
}
VideoResolutionConstraint* current_constraint = NULL; VideoResolutionConstraint* current_constraint = NULL;
if (HasConstraints()) { if (HasConstraints()) {
current_constraint = GetConstraintForRes(new_resolution, constraints_); current_constraint = GetConstraintForRes(resolution_, constraints_);
if (NULL == current_constraint) { if (NULL == current_constraint) {
meets_constraints_ = false; meets_constraints_ = false;
return; return;
@@ -270,7 +302,7 @@ void LicenseKeyStatus::ApplyConstraints(
desired_hdcp_level = default_hdcp_level_; desired_hdcp_level = default_hdcp_level_;
} }
meets_constraints_ = (new_hdcp_level >= desired_hdcp_level); meets_constraints_ = (hdcp_level_ >= desired_hdcp_level);
} }
void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) { void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {

View File

@@ -17,7 +17,6 @@ using video_widevine::License;
namespace { namespace {
const int64_t kHdcpCheckInterval = 10; const int64_t kHdcpCheckInterval = 10;
const uint32_t kNoResolution = 0;
} // namespace } // namespace
@@ -71,7 +70,15 @@ void PolicyEngine::CheckDevice(int64_t current_time) {
if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) { if (!crypto_session_->GetHdcpCapabilities(&current_hdcp_level, &ignored)) {
current_hdcp_level = HDCP_NONE; current_hdcp_level = HDCP_NONE;
} }
license_keys_->ApplyConstraints(current_resolution_, current_hdcp_level);
bool new_usable_keys = false;
bool keys_changed =
license_keys_->ApplyStatusChange(NULL, // new_status
&current_resolution_,
&current_hdcp_level,
&new_usable_keys);
NotifyIfKeysChanged(keys_changed, new_usable_keys);
next_device_check_ = current_time + kHdcpCheckInterval; next_device_check_ = current_time + kHdcpCheckInterval;
} }
} }
@@ -89,21 +96,19 @@ void PolicyEngine::OnTimerEvent() {
if (HasLicenseOrPlaybackDurationExpired(current_time) && if (HasLicenseOrPlaybackDurationExpired(current_time) &&
license_state_ != kLicenseStateExpired) { license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired); UpdateKeyStatus(kKeyStatusExpired);
return; return;
} }
// Check device conditions that affect playability (HDCP, resolution) // Check device conditions that affect playability (HDCP, resolution)
CheckDevice(current_time); CheckDevice(current_time);
// Test to determine if renewal should be attempted.
bool renewal_needed = false; bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) { switch (license_state_) {
case kLicenseStateCanPlay: { case kLicenseStateCanPlay: {
if (HasRenewalDelayExpired(current_time)) renewal_needed = true; if (HasRenewalDelayExpired(current_time)) renewal_needed = true;
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break; break;
} }
@@ -120,7 +125,7 @@ void PolicyEngine::OnTimerEvent() {
case kLicenseStatePending: { case kLicenseStatePending: {
if (current_time >= license_start_time_) { if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay; license_state_ = kLicenseStateCanPlay;
NotifyKeysChange(kKeyStatusUsable); UpdateKeyStatus(kKeyStatusUsable);
} }
break; break;
} }
@@ -132,7 +137,7 @@ void PolicyEngine::OnTimerEvent() {
default: { default: {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusInternalError); UpdateKeyStatus(kKeyStatusInternalError);
break; break;
} }
} }
@@ -157,7 +162,7 @@ void PolicyEngine::SetLicenseForRelease(const License& license) {
policy_.Clear(); policy_.Clear();
// Expire any old keys. // Expire any old keys.
NotifyKeysChange(kKeyStatusExpired); UpdateKeyStatus(kKeyStatusExpired);
UpdateLicense(license); UpdateLicense(license);
} }
@@ -190,17 +195,17 @@ void PolicyEngine::UpdateLicense(const License& license) {
if (!policy_.can_play() || if (!policy_.can_play() ||
HasLicenseOrPlaybackDurationExpired(current_time)) { HasLicenseOrPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired); UpdateKeyStatus(kKeyStatusExpired);
return; return;
} }
// Update state // Update state
if (current_time >= license_start_time_) { if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay; license_state_ = kLicenseStateCanPlay;
NotifyKeysChange(kKeyStatusUsable); UpdateKeyStatus(kKeyStatusUsable);
} else { } else {
license_state_ = kLicenseStatePending; license_state_ = kLicenseStatePending;
NotifyKeysChange(kKeyStatusPending); UpdateKeyStatus(kKeyStatusPending);
} }
NotifyExpirationUpdate(current_time); NotifyExpirationUpdate(current_time);
} }
@@ -240,7 +245,7 @@ void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
void PolicyEngine::NotifySessionExpiration() { void PolicyEngine::NotifySessionExpiration() {
license_state_ = kLicenseStateExpired; license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired); UpdateKeyStatus(kKeyStatusExpired);
} }
CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) {
@@ -429,16 +434,23 @@ bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time; next_renewal_time_ <= current_time;
} }
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) { void PolicyEngine::UpdateKeyStatus(CdmKeyStatus new_status) {
bool keys_changed; bool new_usable_keys = false;
bool has_new_usable_key = false; bool keys_changed =
keys_changed = license_keys_->ApplyStatusChange(new_status, license_keys_->ApplyStatusChange(&new_status,
&has_new_usable_key); NULL, // new_resolution
NULL, // new_hdcp_level
&new_usable_keys);
NotifyIfKeysChanged(keys_changed, new_usable_keys);
}
void PolicyEngine::NotifyIfKeysChanged(bool keys_changed,
bool new_usable_keys) {
if (event_listener_ && keys_changed) { if (event_listener_ && keys_changed) {
CdmKeyStatusMap content_keys; CdmKeyStatusMap content_keys;
license_keys_->ExtractKeyStatuses(&content_keys); license_keys_->ExtractKeyStatuses(&content_keys);
event_listener_->OnSessionKeysChange(session_id_, content_keys, event_listener_->OnSessionKeysChange(session_id_, content_keys,
has_new_usable_key); new_usable_keys);
} }
} }

View File

@@ -392,14 +392,16 @@ TEST_F(LicenseKeysTest, CanDecrypt) {
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
bool new_usable_keys = false; bool new_usable_keys = false;
bool any_change = false; bool any_change = false;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, CdmKeyStatus status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
EXPECT_TRUE(license_keys_.CanDecryptContent(c_key)); EXPECT_TRUE(license_keys_.CanDecryptContent(c_key));
EXPECT_FALSE(license_keys_.CanDecryptContent(os_key)); EXPECT_FALSE(license_keys_.CanDecryptContent(os_key));
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired, status = kKeyStatusExpired;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -505,6 +507,7 @@ TEST_F(LicenseKeysTest, ExtractKeyStatuses) {
TEST_F(LicenseKeysTest, KeyStatusChanges) { TEST_F(LicenseKeysTest, KeyStatusChanges) {
bool new_usable_keys = false; bool new_usable_keys = false;
bool any_change = false; bool any_change = false;
CdmKeyStatus status;
CdmKeyStatusMap key_status_map; CdmKeyStatusMap key_status_map;
StageOperatorSessionKeys(); StageOperatorSessionKeys();
StageContentKeys(); StageContentKeys();
@@ -514,7 +517,8 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError); ExpectKeyStatusesEqual(key_status_map, kKeyStatusInternalError);
// change to pending // change to pending
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending, status = kKeyStatusPending;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -526,7 +530,8 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending); ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to pending (again) // change to pending (again)
any_change = license_keys_.ApplyStatusChange(kKeyStatusPending, status = kKeyStatusPending;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_FALSE(any_change); EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -538,7 +543,8 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending); ExpectKeyStatusesEqual(key_status_map, kKeyStatusPending);
// change to usable // change to usable
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -550,7 +556,8 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable); ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to usable (again) // change to usable (again)
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_FALSE(any_change); EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -562,7 +569,8 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable); ExpectKeyStatusesEqual(key_status_map, kKeyStatusUsable);
// change to expired // change to expired
any_change = license_keys_.ApplyStatusChange(kKeyStatusExpired, status = kKeyStatusExpired;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -577,10 +585,14 @@ TEST_F(LicenseKeysTest, KeyStatusChanges) {
TEST_F(LicenseKeysTest, HdcpChanges) { TEST_F(LicenseKeysTest, HdcpChanges) {
bool new_usable_keys = false; bool new_usable_keys = false;
bool any_change = false; bool any_change = false;
CdmKeyStatus status;
uint32_t resolution;
CryptoSession::HdcpCapability hdcp_level;
CdmKeyStatusMap key_status_map; CdmKeyStatusMap key_status_map;
StageHdcpKeys(); StageHdcpKeys();
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -598,9 +610,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT)); EXPECT_TRUE(license_keys_.MeetsConstraints(ck_sw_crypto_HDCP_NO_OUTPUT));
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT)); EXPECT_TRUE(license_keys_.MeetsConstraints(ck_hw_secure_HDCP_NO_OUTPUT));
license_keys_.ApplyConstraints(100, HDCP_NONE); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = 100;
&new_usable_keys); hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -623,9 +637,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1, ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_V2_1,
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V1); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = 100;
&new_usable_keys); hdcp_level = HDCP_V1;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_FALSE(any_change); EXPECT_FALSE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -648,9 +664,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1, ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_V2_1,
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_V2_2); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = 100;
&new_usable_keys); hdcp_level = HDCP_V2_2;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -674,9 +692,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT, ExpectKeyStatusEqual(key_status_map, ck_sw_crypto_HDCP_NO_OUTPUT,
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
license_keys_.ApplyConstraints(100, HDCP_NO_DIGITAL_OUTPUT); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = 100;
&new_usable_keys); hdcp_level = HDCP_NO_DIGITAL_OUTPUT;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -700,9 +720,11 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT, ExpectKeyStatusEqual(key_status_map, ck_hw_secure_HDCP_NO_OUTPUT,
kKeyStatusUsable); kKeyStatusUsable);
license_keys_.ApplyConstraints(100, HDCP_NONE); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = 100;
&new_usable_keys); hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -730,11 +752,15 @@ TEST_F(LicenseKeysTest, HdcpChanges) {
TEST_F(LicenseKeysTest, ConstraintChanges) { TEST_F(LicenseKeysTest, ConstraintChanges) {
bool new_usable_keys = false; bool new_usable_keys = false;
bool any_change = false; bool any_change = false;
CdmKeyStatus status;
uint32_t resolution;
CryptoSession::HdcpCapability hdcp_level;
CdmKeyStatusMap key_status_map; CdmKeyStatusMap key_status_map;
StageConstraintKeys(); StageConstraintKeys();
// No constraints set by device // No constraints set by device
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(&status, NULL, NULL,
&new_usable_keys); &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -749,9 +775,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res)); EXPECT_TRUE(license_keys_.MeetsConstraints(ck_NO_HDCP_dual_res));
// Low-res device, no HDCP support // Low-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_lo_res;
&new_usable_keys); hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);
@@ -771,9 +799,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
// Hi-res device, HDCP_V1 support // Hi-res device, HDCP_V1 support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_V1); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_hi_res;
&new_usable_keys); hdcp_level = HDCP_V1;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -793,9 +823,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
// Lo-res device, HDCP V2.2 support // Lo-res device, HDCP V2.2 support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_V2_2); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_lo_res;
&new_usable_keys); hdcp_level = HDCP_V2_2;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -816,9 +848,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
// Hi-res device, Maximal HDCP support // Hi-res device, Maximal HDCP support
license_keys_.ApplyConstraints(dev_hi_res, HDCP_NO_DIGITAL_OUTPUT); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_hi_res;
&new_usable_keys); hdcp_level = HDCP_NO_DIGITAL_OUTPUT;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -839,9 +873,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusUsable); kKeyStatusUsable);
// Lo-res device, no HDCP support // Lo-res device, no HDCP support
license_keys_.ApplyConstraints(dev_lo_res, HDCP_NONE); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_lo_res;
&new_usable_keys); hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_TRUE(new_usable_keys); EXPECT_TRUE(new_usable_keys);
@@ -862,9 +898,11 @@ TEST_F(LicenseKeysTest, ConstraintChanges) {
kKeyStatusOutputNotAllowed); kKeyStatusOutputNotAllowed);
// Too-high-res -- all keys rejected // Too-high-res -- all keys rejected
license_keys_.ApplyConstraints(dev_top_res, HDCP_NONE); status = kKeyStatusUsable;
any_change = license_keys_.ApplyStatusChange(kKeyStatusUsable, resolution = dev_top_res;
&new_usable_keys); hdcp_level = HDCP_NONE;
any_change = license_keys_.ApplyStatusChange(&status, &resolution,
&hdcp_level, &new_usable_keys);
EXPECT_TRUE(any_change); EXPECT_TRUE(any_change);
EXPECT_FALSE(new_usable_keys); EXPECT_FALSE(new_usable_keys);

View File

@@ -1,5 +1,8 @@
// Copyright 2016 Google Inc. All Rights Reserved. // Copyright 2016 Google Inc. All Rights Reserved.
#include <iostream>
#include <tuple>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "crypto_session.h" #include "crypto_session.h"
@@ -13,6 +16,8 @@
// protobuf generated classes. // protobuf generated classes.
using video_widevine::License; using video_widevine::License;
using video_widevine::License_Policy; using video_widevine::License_Policy;
using video_widevine::LicenseType;
using video_widevine::OFFLINE;
using video_widevine::STREAMING; using video_widevine::STREAMING;
namespace wvcdm { namespace wvcdm {
@@ -75,14 +80,47 @@ class MockCdmEventListener : public WvCdmEventListener {
int64_t new_expiry_time_seconds)); int64_t new_expiry_time_seconds));
}; };
struct LicenseTypeParam {
std::string name;
LicenseType license_type;
bool can_persist;
};
const LicenseTypeParam kStreamingParam{"Streaming", STREAMING, false};
const LicenseTypeParam kOfflineParam{"Offline", OFFLINE, true};
void PrintTo(const LicenseTypeParam& param, std::ostream* os) {
*os << param.name;
}
struct LicenseRenewalParam {
std::string name;
bool can_renew;
int64_t renewal_delay_seconds;
};
const LicenseRenewalParam kNoRenewalParam{"Cannot Renew", false, 0};
const LicenseRenewalParam kShortRenewalParam{"Short Renewal Delay", true, 1};
const LicenseRenewalParam kLongRenewalParam{"Long Renewal Delay", true, 60};
void PrintTo(const LicenseRenewalParam& param, std::ostream* os) {
*os << param.name;
}
} // namespace } // namespace
class PolicyEngineConstraintsTest : public Test { class PolicyEngineConstraintsTest
: public TestWithParam<std::tuple<LicenseTypeParam, LicenseRenewalParam>> {
protected: protected:
virtual void SetUp() { virtual void SetUp() {
mock_clock_ = new NiceMock<MockClock>(); mock_clock_ = new NiceMock<MockClock>();
current_time_ = 0; current_time_ = 0;
// mock_event_listener_ is a StrictMock, but we don't care about renewal
// calls for these tests and want to ignore them.
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_))
.Times(AtLeast(0));
policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_, policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_,
&crypto_session_)); &crypto_session_));
InjectMockClock(); InjectMockClock();
@@ -108,19 +146,25 @@ class PolicyEngineConstraintsTest : public Test {
} }
void SetupLicense() { void SetupLicense() {
LicenseTypeParam typeParam;
LicenseRenewalParam renewalParam;
std::tie(typeParam, renewalParam) = GetParam();
license_.set_license_start_time(current_time_); license_.set_license_start_time(current_time_);
LicenseIdentification* id = license_.mutable_id(); LicenseIdentification* id = license_.mutable_id();
id->set_version(1); id->set_version(1);
id->set_type(STREAMING); id->set_type(typeParam.license_type);
License_Policy* policy = license_.mutable_policy(); License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy(); policy = license_.mutable_policy();
policy->set_can_play(true); policy->set_can_play(true);
policy->set_can_persist(false); policy->set_can_persist(typeParam.can_persist);
policy->set_can_renew(renewalParam.can_renew);
policy->set_rental_duration_seconds(kRentalDuration); policy->set_rental_duration_seconds(kRentalDuration);
policy->set_playback_duration_seconds(kPlaybackDuration); policy->set_playback_duration_seconds(kPlaybackDuration);
policy->set_license_duration_seconds(kStreamingLicenseDuration); policy->set_license_duration_seconds(kStreamingLicenseDuration);
policy->set_renewal_delay_seconds(renewalParam.renewal_delay_seconds);
KeyList* keys = license_.mutable_key(); KeyList* keys = license_.mutable_key();
@@ -211,7 +255,7 @@ class PolicyEngineConstraintsTest : public Test {
License license_; License license_;
}; };
TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) { TEST_P(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2); EXPECT_CALL(*mock_clock_, GetCurrentTime()).Times(2);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _)); EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(kSessionId, _));
ExpectSessionKeysChange(kKeyStatusUsable, true); ExpectSessionKeysChange(kKeyStatusUsable, true);
@@ -227,7 +271,7 @@ TEST_F(PolicyEngineConstraintsTest, IsPermissiveWithoutAResolution) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
} }
TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) { TEST_P(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
{ {
Sequence time; Sequence time;
for (int i=0; i<4; ++i) { for (int i=0; i<4; ++i) {
@@ -279,7 +323,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesResolutionsBasedOnConstraints) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
} }
TEST_F(PolicyEngineConstraintsTest, TEST_P(PolicyEngineConstraintsTest,
RequestsHdcpImmediatelyAndOnlyAfterInterval) { RequestsHdcpImmediatelyAndOnlyAfterInterval) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()) EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0)) .WillOnce(Return(0))
@@ -310,7 +354,7 @@ TEST_F(PolicyEngineConstraintsTest,
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
} }
TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) { TEST_P(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
EXPECT_CALL(*mock_clock_, GetCurrentTime()) EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(0)); .WillOnce(Return(0));
EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0); EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _)).Times(0);
@@ -318,7 +362,7 @@ TEST_F(PolicyEngineConstraintsTest, DoesNotRequestHdcpWithoutALicense) {
policy_engine_->OnTimerEvent(); policy_engine_->OnTimerEvent();
} }
TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) { TEST_P(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
{ {
Sequence time; Sequence time;
for (int i=0; i<3; ++i) { for (int i=0; i<3; ++i) {
@@ -360,7 +404,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesConstraintOverridingHdcp) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
} }
TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) { TEST_P(PolicyEngineConstraintsTest, HandlesNoHdcp) {
{ {
Sequence time; Sequence time;
for (int i=0; i<3; ++i) { for (int i=0; i<3; ++i) {
@@ -407,7 +451,7 @@ TEST_F(PolicyEngineConstraintsTest, HandlesNoHdcp) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
} }
TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) { TEST_P(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
{ {
Sequence time; Sequence time;
for (int i=0; i<2; ++i) { for (int i=0; i<2; ++i) {
@@ -429,4 +473,9 @@ TEST_F(PolicyEngineConstraintsTest, IgnoresHdcpWithoutAResolution) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6)); EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId6));
} }
INSTANTIATE_TEST_CASE_P(Default, PolicyEngineConstraintsTest,
Combine(
Values(kStreamingParam, kOfflineParam),
Values(kNoRenewalParam, kShortRenewalParam, kLongRenewalParam)));
} // namespace wvcdm } // namespace wvcdm