Unified State-Changing API for LicenseKeyStatus

(This is a merge of http://go/wvgerrit/31040)

Because the Policy Engine was only consulting the result of the Max-Res
Decode check when it was in kLicenseStateCanPlay and not in other states
that imply kKeyStatusUsable, like kLicenseStateWaitingLicenseUpdate, the
Max-Res Decode results would not be honored during the interval between
requesting a renewal and receiving the result. (Or until the key
expired.) This was particularly problematic for keys with renewal delays
less than ten seconds long, which would freeze the Max-Res state before
it had a chance to update for the first time, effectively disabling
Max-Res Decode until renewal was received.

Fixing this required changing how the Policy Engine and the
LicenseKeyStatus objects communicate about the changing usability state
of the LicenseKeyStatus objects. Before, a call to ApplyConstraints()
might calculate a Max-Res failure, but this failure would be pending
until the Policy Engine deigned to call ApplyStatusChange() again.
Without a call to ApplyStatusChange(), it could pend forever. This put a
burden on the PolicyEngine to poll the LicenseKeys with redundant
ApplyStatusChange() calls using the same CdmKeyStatus that the keys were
already in, just in case Max-Res had changed since the last necessary
call to ApplyStatusChange().

If the Policy Engine got the timing of these calls wrong, it would
result in Max-Res results being ignored. (as in the linked bug) If it
ever polled with the wrong CdmKeyStatus, it would update the
LicenseKeys' status when it did not mean to. It would be preferable if
this polling were not needed, so that the Policy Engine couldn't get it
wrong.

This patch changes the API between these classes so that when Max-Res
fails, the state change can be reported immediately instead of pending
until ApplyStatusChange() is called, eliminating the need for polling.
All state changes to the LicenseKeyStatus objects go through a unified
ApplyStatusChange() method that can update the CdmKeyStatus, resolution,
and/or HDCP level and report any resulting usability changes
immediately. This patch updates the unit tests to exercise this new API
instead of the old API.

Previously, the linked bug slipped past our unit tests because we only
test unrenewable, streaming licenses against Max-Res. This patch adds
several more variants to
policy_engine_constraints_unittest so that it tests six kinds of
license to provide better coverage.

Bug: 62393949
Test: build_and_run_all_unit_tests
Change-Id: I0dfdbf6b8ea39abb446089aef5f6ea0502e9b4c6
This commit is contained in:
John W. Bruce
2017-08-29 16:37:49 -07:00
parent c0133bf3a4
commit b8e31487a4
6 changed files with 264 additions and 115 deletions

View File

@@ -11,6 +11,8 @@
namespace wvcdm {
static const uint32_t kNoResolution = 0;
class LicenseKeyStatus;
// Holds all content and operator session keys for a session.
@@ -31,11 +33,17 @@ class LicenseKeys {
virtual bool GetAllowedUsage(const KeyId& key_id,
CdmKeyAllowedUsage* allowed_usage);
// Applies a new status to each content key.
// Returns true if any statuses changed, and sets new_usable_keys to
// true if the status changes resulted in keys becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Applies a new status, resolution, and/or HDCP level to each content key,
// updating their usability under their constraints. Returns true if any keys
// have changed usability, and sets new_usable_keys to true if the 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);
// Populates the CdmKeyStatusMap with the current content keys.
virtual void ExtractKeyStatuses(CdmKeyStatusMap* content_keys);
@@ -45,11 +53,6 @@ class LicenseKeys {
// to the key, returns true.
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
// querying usage and constraint settings.
virtual void SetFromLicense(
@@ -85,21 +88,24 @@ class LicenseKeyStatus {
// Returns the current status of the key.
virtual CdmKeyStatus GetKeyStatus() const { return key_status_; }
// Applies a new status to this key.
// Returns true if the status changed, and sets new_usable_keys to
// true if the status changes resulted in the key becoming usable.
virtual bool ApplyStatusChange(CdmKeyStatus new_status,
bool* new_usable_keys);
// Applies a new status, resolution, and/or HDCP level, updating the key's
// usability under its constraints. Returns true if this results in a change
// to the key's usability, and sets newly_usable to true if the status
// changes resulted in the key becoming usable.
//
// 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
// may change due to calls to ApplyConstraints().
// Note: this will return true until the first call to ApplyConstraints().
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:
typedef ::video_widevine::License::KeyContainer KeyContainer;
typedef KeyContainer::OperatorSessionKeyPermissions
@@ -121,11 +127,14 @@ class LicenseKeyStatus {
bool HasConstraints() {
return is_content_key_ && constraints_.size() != 0;
}
void SetConstraints(const ConstraintList& constraints);
void ApplyConstraints();
bool is_content_key_;
CdmKeyStatus key_status_;
uint32_t resolution_;
CryptoSession::HdcpCapability hdcp_level_;
bool can_check_constraints_;
bool meets_constraints_;
CdmKeyAllowedUsage allowed_usage_;
CryptoSession::HdcpCapability default_hdcp_level_;

View File

@@ -108,6 +108,10 @@ class PolicyEngine {
friend class PolicyEngineConstraintsTest;
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 SetDeviceResolution(uint32_t width, uint32_t height) {
@@ -143,9 +147,14 @@ class PolicyEngine {
void UpdateRenewalRequest(int64_t current_time);
// Notifies updates in keys information and fire OnKeysChange event if
// key changes.
void NotifyKeysChange(CdmKeyStatus new_status);
// Updates the keys' status to |new_status|. Calls NotifyIfKeysChanged() to
// 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
// expiry time changes.