From c2cee8406efbd3d2b6642fe07b99b7c4bc67edc2 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Sun, 9 Feb 2020 23:17:01 -0800 Subject: [PATCH] Policy Engine refactoring [ Merge of http://go/wvgerrit/93743 ] Reworks policy engine in preparation for changes to support timer and clock value handling by OEMCrypto core messages in OEMCrypto v16. No major functional changes have yet been introduced. Time and duration evaluation has been devolved to a new policy timer class. Policy specific to licenses that do not support OEMCrypto core messages is handled by a Policy Timer V15 class. This ensures backward compatibility. Backward compatibility may be needed if * OEMCrypto has not been upgraded to v16 * Licenses were persisted before the device was upgraded to v16 * License service does not yet support core messages Some minor changes to when the current time was retrieved required minor modification to test expectations. Bug: 139372190 Test: Android unit/integration tests Change-Id: I420fb181f656ed9a6bfe54f09e8b398c130d23da --- libwvdrmengine/cdm/Android.bp | 2 + .../cdm/core/include/policy_engine.h | 67 ++--- .../cdm/core/include/policy_timers.h | 112 +++++++++ .../cdm/core/include/policy_timers_v15.h | 85 +++++++ libwvdrmengine/cdm/core/src/policy_engine.cpp | 233 +++++------------- libwvdrmengine/cdm/core/src/policy_timers.cpp | 97 ++++++++ .../cdm/core/src/policy_timers_v15.cpp | 164 ++++++++++++ .../cdm/core/test/policy_engine_unittest.cpp | 4 +- 8 files changed, 541 insertions(+), 223 deletions(-) create mode 100644 libwvdrmengine/cdm/core/include/policy_timers.h create mode 100644 libwvdrmengine/cdm/core/include/policy_timers_v15.h create mode 100644 libwvdrmengine/cdm/core/src/policy_timers.cpp create mode 100644 libwvdrmengine/cdm/core/src/policy_timers_v15.cpp diff --git a/libwvdrmengine/cdm/Android.bp b/libwvdrmengine/cdm/Android.bp index ae262960..1ad45c5b 100644 --- a/libwvdrmengine/cdm/Android.bp +++ b/libwvdrmengine/cdm/Android.bp @@ -44,6 +44,8 @@ cc_library_static { CORE_SRC_DIR + "/license_key_status.cpp", CORE_SRC_DIR + "/oemcrypto_adapter_dynamic.cpp", CORE_SRC_DIR + "/policy_engine.cpp", + CORE_SRC_DIR + "/policy_timers.cpp", + CORE_SRC_DIR + "/policy_timers_v15.cpp", CORE_SRC_DIR + "/privacy_crypto_boringssl.cpp", CORE_SRC_DIR + "/service_certificate.cpp", CORE_SRC_DIR + "/usage_table_header.cpp", diff --git a/libwvdrmengine/cdm/core/include/policy_engine.h b/libwvdrmengine/cdm/core/include/policy_engine.h index d0a6759d..6a1088b4 100644 --- a/libwvdrmengine/cdm/core/include/policy_engine.h +++ b/libwvdrmengine/cdm/core/include/policy_engine.h @@ -9,6 +9,7 @@ #include #include +#include "clock.h" #include "disallow_copy_and_assign.h" #include "license_key_status.h" #include "license_protocol.pb.h" @@ -21,6 +22,7 @@ using video_widevine::WidevinePsshData_EntitledKey; class Clock; class CryptoSession; +class PolicyTimers; class WvCdmEventListener; // This acts as an oracle that basically says "Yes(true) you may still decrypt @@ -68,6 +70,7 @@ class PolicyEngine { // Call this on first decrypt to set the start of playback. virtual bool BeginDecryption(void); + // Call this periodically to update the most recent playback time. virtual void DecryptionEvent(void); // UpdateLicense is used in handling a license response for a renewal request. @@ -92,26 +95,20 @@ class PolicyEngine { 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_; } - int64_t GetGracePeriodEndTime() { return grace_period_end_time_; } + // For offline save and restore + int64_t GetPlaybackStartTime(); + int64_t GetLastPlaybackTime(); + int64_t GetGracePeriodEndTime(); void RestorePlaybackTimes(int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time); bool IsLicenseForFuture() { return license_state_ == kLicenseStatePending; } - bool HasPlaybackStarted(int64_t current_time) { - if (playback_start_time_ == 0) return false; - - const int64_t playback_time = current_time - playback_start_time_; - return playback_time >= policy_.play_start_grace_period_seconds(); - } bool HasLicenseOrPlaybackDurationExpired(int64_t current_time); int64_t GetLicenseOrPlaybackDurationRemaining(); - bool CanRenew() { return policy_.can_renew(); } + bool CanRenew() const; bool IsSufficientOutputProtection(const KeyId& key_id) { return license_keys_->MeetsConstraints(key_id); @@ -135,26 +132,6 @@ class PolicyEngine { kLicenseStateExpired } LicenseState; - // Gets the clock time that the license expires. This is the hard limit that - // all license types must obey at all times. - int64_t GetHardLicenseExpiryTime(); - // Gets the clock time that the rental duration will expire, using the license - // duration if one is not present. - int64_t GetRentalExpiryTime(); - // Gets the clock time that the license expires based on whether we have - // started playing. This takes into account GetHardLicenseExpiryTime. - int64_t GetExpiryTime(int64_t current_time, - bool ignore_soft_enforce_playback_duration); - - int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time); - int64_t GetPlaybackDurationRemaining(int64_t current_time); - - bool HasRenewalDelayExpired(int64_t current_time); - bool HasRenewalRecoveryDurationExpired(int64_t current_time); - bool HasRenewalRetryIntervalExpired(int64_t current_time); - - void UpdateRenewalRequest(int64_t current_time); - // Notifies updates in keys information and fire OnKeysChange event if // key changes. void NotifyKeysChange(CdmKeyStatus new_status); @@ -163,7 +140,8 @@ class PolicyEngine { // expiry time changes. void NotifyExpirationUpdate(int64_t current_time); - // Guard against clock rollbacks + void UpdateRenewalRequest(int64_t current_time); + int64_t GetCurrentTime(); // Test only methods @@ -174,31 +152,11 @@ class PolicyEngine { LicenseState license_state_; - // This is the current policy information for this license. This gets updated - // as license renewals occur. - video_widevine::License::Policy policy_; - // This is the license id field from server response. This data gets passed // back to the server in each renewal request. When we get a renewal response // from the license server we will get an updated id field. video_widevine::LicenseIdentification license_id_; - // 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_; - int64_t playback_start_time_; - int64_t last_playback_time_; - int64_t last_expiry_time_; - int64_t grace_period_end_time_; - bool last_expiry_time_set_; - bool was_expired_on_load_; - - // This is used as a reference point for policy management. This value - // represents an offset from license_start_time_. This is used to - // calculate the time where renewal retries should occur. - int64_t next_renewal_time_; - // to assist in clock rollback checks int64_t last_recorded_current_time_; @@ -210,11 +168,16 @@ class PolicyEngine { // and current status (CdmKeyStatus) std::unique_ptr license_keys_; + // This is the current policy information for this license. This gets updated + // as license renewals occur. + video_widevine::License::Policy policy_; + // Device checks int64_t next_device_check_; uint32_t current_resolution_; CryptoSession* crypto_session_; + std::unique_ptr policy_timers; std::unique_ptr clock_; CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine); diff --git a/libwvdrmengine/cdm/core/include/policy_timers.h b/libwvdrmengine/cdm/core/include/policy_timers.h new file mode 100644 index 00000000..059a332a --- /dev/null +++ b/libwvdrmengine/cdm/core/include/policy_timers.h @@ -0,0 +1,112 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#ifndef WVCDM_CORE_POLICY_TIMERS_H_ +#define WVCDM_CORE_POLICY_TIMERS_H_ + +#include +#include + +#include "disallow_copy_and_assign.h" +#include "license_protocol.pb.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +// This is driven by the Policy Engine and maintains timer related +// information from the policy such as duration windows and renewals. +// Timer expiration behavior has changed with the introduction of core +// messages in OEMCrypto v16. Handling of behavior that differs between +// a OEMCrypto v16 license with core messages and one without is left to +// a class that derives from this interface. + +class PolicyTimers { + public: + virtual ~PolicyTimers() {} + + // SetLicense is used in handling the initial license response. + virtual void SetLicense(const video_widevine::License& license); + + // UpdateLicense is used in handling a license response for a renewal request. + // The response may only contain policy fields that have changed. In this + // case an exact copy is not what we want to happen. + virtual bool UpdateLicense(int64_t current_time, + const video_widevine::License& license) = 0; + + // Call this on first decrypt to set the start of playback. + virtual void BeginDecryption(int64_t current_time) = 0; + // Call this periodically to update the most recent playback time. + virtual void DecryptionEvent(int64_t current_time); + + // For offline save and restore + virtual int64_t GetPlaybackStartTime() { return playback_start_time_; } + virtual int64_t GetLastPlaybackTime() { return last_playback_time_; } + virtual int64_t GetGracePeriodEndTime() = 0; + virtual void RestorePlaybackTimes(int64_t current_time, + int64_t playback_start_time, + int64_t last_playback_time, + int64_t grace_period_end_time) = 0; + + virtual bool HasPlaybackStarted(int64_t current_time) = 0; + virtual bool HasLicenseOrPlaybackDurationExpired(int64_t current_time) = 0; + virtual bool HasPassedGracePeriod(int64_t current_time) = 0; + + virtual int64_t GetLicenseOrPlaybackDurationRemaining( + int64_t current_time) = 0; + virtual int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time) = 0; + virtual int64_t GetPlaybackDurationRemaining(int64_t current_time) = 0; + + virtual bool GetSecondsSinceStarted(int64_t current_time, + int64_t* seconds_since_started); + virtual bool GetSecondsSinceLastPlayed(int64_t current_time, + int64_t* seconds_since_started); + + virtual bool IsLicenseForFuture(int64_t current_time); + + virtual bool UpdateExpirationTime(int64_t current_time, int64_t* expiry_time); + + // Renewal related methods + virtual bool HasRenewalDelayExpired(int64_t current_time); + virtual bool HasRenewalRetryIntervalExpired(int64_t current_time); + virtual void UpdateRenewalRequest(int64_t current_time); + virtual bool HasRenewalRecoveryDurationExpired(int64_t current_time); + + const video_widevine::License::Policy& get_policy() { return policy_; } + + protected: + PolicyTimers() + : license_start_time_(0), + playback_start_time_(0), + last_playback_time_(0), + last_expiry_time_(0), + last_expiry_time_set_(false), + next_renewal_time_(0), + was_expired_on_load_(false) {} + + // Gets the clock time that the license expires based on whether we have + // started playing. + virtual int64_t GetExpiryTime(int64_t current_time, + bool ignore_soft_enforce_playback_duration) = 0; + + // This is the current policy information for this license. This gets updated + // as license renewals occur. + video_widevine::License::Policy policy_; + + int64_t license_start_time_; + int64_t playback_start_time_; + int64_t last_playback_time_; + int64_t last_expiry_time_; + int64_t last_expiry_time_set_; + int64_t next_renewal_time_; + + // Indicates whether a persistent license was expired when loaded + bool was_expired_on_load_; + + private: + CORE_DISALLOW_COPY_AND_ASSIGN(PolicyTimers); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_POLICY_TIMERS_H_ diff --git a/libwvdrmengine/cdm/core/include/policy_timers_v15.h b/libwvdrmengine/cdm/core/include/policy_timers_v15.h new file mode 100644 index 00000000..2f641cbd --- /dev/null +++ b/libwvdrmengine/cdm/core/include/policy_timers_v15.h @@ -0,0 +1,85 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#ifndef WVCDM_CORE_POLICY_TIMERS_V15_H_ +#define WVCDM_CORE_POLICY_TIMERS_V15_H_ + +#include +#include + +#include "disallow_copy_and_assign.h" +#include "license_protocol.pb.h" +#include "policy_timers.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +// OEMCrypto v16 and core messages introduced changes to how duration values +// and clocks should be evaluated. This class provides backward compatibility +// for licenses that do not include a core message. Durations are handled +// in the same way as in earlier releases. +// +// Backward compatibility may be needed if +// * OEMCrypto has not been upgraded to v16 +// * Licenses were persisted before the device was upgraded to v16 +// * License service does not yet support core messages + +class PolicyTimersV15 : public PolicyTimers { + public: + PolicyTimersV15() : grace_period_end_time_(0) {} + + virtual ~PolicyTimersV15(){}; + + // 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 + // case an exact copy is not what we want to happen. + bool UpdateLicense(int64_t current_time, + const video_widevine::License& license) override; + + // Call this on first decrypt to set the start of playback. + void BeginDecryption(int64_t current_time) override; + + // for offline save and restore + int64_t GetGracePeriodEndTime() override { return grace_period_end_time_; } + + // for offline save and restore + void RestorePlaybackTimes(int64_t current_time, int64_t playback_start_time, + int64_t last_playback_time, + int64_t grace_period_end_time) override; + + bool HasPlaybackStarted(int64_t current_time) override; + bool HasLicenseOrPlaybackDurationExpired(int64_t current_time) override; + bool HasPassedGracePeriod(int64_t current_time) override; + + int64_t GetLicenseOrPlaybackDurationRemaining(int64_t current_time) override; + // This is only used in Query. This should return the time remaining on + // license_duration_seconds for streaming licenses and rental_duration_seconds + // for offline licenses. + int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time) override; + // This is only used in Query. This should return playback_duration_seconds, + // or the time remaining on it if playing. + int64_t GetPlaybackDurationRemaining(int64_t current_time) override; + + protected: + // Gets the clock time that the license expires based on whether we have + // started playing. This takes into account GetHardLicenseExpiryTime. + int64_t GetExpiryTime(int64_t current_time, + bool ignore_soft_enforce_playback_duration) override; + + private: + // Gets the clock time that the license expires. This is the hard limit that + // all license types must obey at all times. + int64_t GetHardLicenseExpiryTime(); + // Gets the clock time that the rental duration will expire, using the license + // duration if one is not present. + int64_t GetRentalExpiryTime(); + + int64_t grace_period_end_time_; + + CORE_DISALLOW_COPY_AND_ASSIGN(PolicyTimersV15); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_POLICY_TIMERS_V15_H_ diff --git a/libwvdrmengine/cdm/core/src/policy_engine.cpp b/libwvdrmengine/cdm/core/src/policy_engine.cpp index 28d23b0a..5e9c0fa0 100644 --- a/libwvdrmengine/cdm/core/src/policy_engine.cpp +++ b/libwvdrmengine/cdm/core/src/policy_engine.cpp @@ -4,12 +4,11 @@ #include "policy_engine.h" -#include - #include #include "clock.h" #include "log.h" +#include "policy_timers_v15.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -30,18 +29,11 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, WvCdmEventListener* event_listener, CryptoSession* crypto_session) : license_state_(kLicenseStateInitial), - license_start_time_(0), - playback_start_time_(0), - last_playback_time_(0), - last_expiry_time_(0), - grace_period_end_time_(0), - last_expiry_time_set_(false), - was_expired_on_load_(false), - next_renewal_time_(0), last_recorded_current_time_(0), session_id_(session_id), event_listener_(event_listener), license_keys_(new LicenseKeys(crypto_session->GetSecurityLevel())), + policy_timers(new PolicyTimersV15), clock_(new Clock) { InitDevice(crypto_session); } @@ -95,16 +87,15 @@ void PolicyEngine::CheckDeviceHdcpStatus() { void PolicyEngine::OnTimerEvent() { last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds; - int64_t current_time = GetCurrentTime(); + const int64_t current_time = GetCurrentTime(); // If we have passed the grace period, the expiration will update. - if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) { - grace_period_end_time_ = playback_start_time_; + if (policy_timers->HasPassedGracePeriod(current_time)) { NotifyExpirationUpdate(current_time); } // License expiration trumps all. - if (HasLicenseOrPlaybackDurationExpired(current_time) && + if (policy_timers->HasLicenseOrPlaybackDurationExpired(current_time) && license_state_ != kLicenseStateExpired) { license_state_ = kLicenseStateExpired; NotifyKeysChange(kKeyStatusExpired); @@ -119,7 +110,7 @@ void PolicyEngine::OnTimerEvent() { // Test to determine if renewal should be attempted. switch (license_state_) { case kLicenseStateCanPlay: { - if (HasRenewalDelayExpired(current_time)) { + if (policy_timers->HasRenewalDelayExpired(current_time)) { renewal_needed = true; } // HDCP may change, so force a check. @@ -133,14 +124,14 @@ void PolicyEngine::OnTimerEvent() { } case kLicenseStateWaitingLicenseUpdate: { - if (HasRenewalRetryIntervalExpired(current_time)) { + if (policy_timers->HasRenewalRetryIntervalExpired(current_time)) { renewal_needed = true; } break; } case kLicenseStatePending: { - if (current_time >= license_start_time_) { + if (!policy_timers->IsLicenseForFuture(current_time)) { license_state_ = kLicenseStateCanPlay; NotifyKeysChange(kKeyStatusUsable); } @@ -168,8 +159,8 @@ void PolicyEngine::OnTimerEvent() { void PolicyEngine::SetLicense(const License& license) { license_id_.Clear(); license_id_.CopyFrom(license.id()); - policy_.Clear(); license_keys_->SetFromLicense(license); + policy_timers->SetLicense(license); UpdateLicense(license); } @@ -181,10 +172,10 @@ void PolicyEngine::SetEntitledLicenseKeys( void PolicyEngine::SetLicenseForRelease(const License& license) { license_id_.Clear(); license_id_.CopyFrom(license.id()); - policy_.Clear(); // Expire any old keys. NotifyKeysChange(kKeyStatusExpired); + policy_timers->SetLicense(license); UpdateLicense(license); } @@ -195,8 +186,6 @@ void PolicyEngine::UpdateLicense(const License& license) { LOGD("Updating an expired license"); } - policy_.MergeFrom(license.policy()); - // some basic license validation // license start time needs to be specified in the initial response if (!license.has_license_start_time()) return; @@ -209,20 +198,19 @@ void PolicyEngine::UpdateLicense(const License& license) { return; } - // Update time information - license_start_time_ = license.license_start_time(); - next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds(); + const int64_t current_time = GetCurrentTime(); + policy_timers->UpdateLicense(current_time, license); - int64_t current_time = GetCurrentTime(); - if (!policy_.can_play() || - HasLicenseOrPlaybackDurationExpired(current_time)) { + // Update time information + if (!policy_timers->get_policy().can_play() || + policy_timers->HasLicenseOrPlaybackDurationExpired(current_time)) { license_state_ = kLicenseStateExpired; NotifyKeysChange(kKeyStatusExpired); return; } // Update state - if (current_time >= license_start_time_) { + if (!policy_timers->IsLicenseForFuture(current_time)) { license_state_ = kLicenseStateCanPlay; NotifyKeysChange(kKeyStatusUsable); } else { @@ -233,20 +221,18 @@ void PolicyEngine::UpdateLicense(const License& license) { } bool PolicyEngine::BeginDecryption() { - if (playback_start_time_ == 0) { + const int64_t current_time = GetCurrentTime(); + if (!policy_timers->HasPlaybackStarted(current_time)) { switch (license_state_) { case kLicenseStateCanPlay: case kLicenseStateNeedRenewal: case kLicenseStateWaitingLicenseUpdate: - playback_start_time_ = GetCurrentTime(); - last_playback_time_ = playback_start_time_; - if (policy_.play_start_grace_period_seconds() == 0) - grace_period_end_time_ = playback_start_time_; + policy_timers->BeginDecryption(current_time); - if (policy_.renew_with_usage()) { + if (policy_timers->get_policy().renew_with_usage()) { license_state_ = kLicenseStateNeedRenewal; } - NotifyExpirationUpdate(playback_start_time_); + NotifyExpirationUpdate(current_time); return true; case kLicenseStateInitial: case kLicenseStatePending: @@ -259,7 +245,9 @@ bool PolicyEngine::BeginDecryption() { } } -void PolicyEngine::DecryptionEvent() { last_playback_time_ = GetCurrentTime(); } +void PolicyEngine::DecryptionEvent() { + policy_timers->DecryptionEvent(GetCurrentTime()); +} void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) { SetDeviceResolution(width, height); @@ -271,7 +259,7 @@ void PolicyEngine::NotifySessionExpiration() { } CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { - int64_t current_time = GetCurrentTime(); + const int64_t current_time = GetCurrentTime(); if (license_state_ == kLicenseStateInitial) { query_response->clear(); @@ -282,17 +270,20 @@ CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { license_id_.type() == video_widevine::STREAMING ? QUERY_VALUE_STREAMING : QUERY_VALUE_OFFLINE; (*query_response)[QUERY_KEY_PLAY_ALLOWED] = - policy_.can_play() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + policy_timers->get_policy().can_play() ? QUERY_VALUE_TRUE + : QUERY_VALUE_FALSE; (*query_response)[QUERY_KEY_PERSIST_ALLOWED] = - policy_.can_persist() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + policy_timers->get_policy().can_persist() ? QUERY_VALUE_TRUE + : QUERY_VALUE_FALSE; (*query_response)[QUERY_KEY_RENEW_ALLOWED] = - policy_.can_renew() ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; - (*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = - std::to_string(GetLicenseOrRentalDurationRemaining(current_time)); + policy_timers->get_policy().can_renew() ? QUERY_VALUE_TRUE + : QUERY_VALUE_FALSE; + (*query_response)[QUERY_KEY_LICENSE_DURATION_REMAINING] = std::to_string( + policy_timers->GetLicenseOrRentalDurationRemaining(current_time)); (*query_response)[QUERY_KEY_PLAYBACK_DURATION_REMAINING] = - std::to_string(GetPlaybackDurationRemaining(current_time)); + std::to_string(policy_timers->GetPlaybackDurationRemaining(current_time)); (*query_response)[QUERY_KEY_RENEWAL_SERVER_URL] = - policy_.renewal_server_url(); + policy_timers->get_policy().renewal_server_url(); return NO_ERROR; } @@ -314,148 +305,54 @@ bool PolicyEngine::CanUseKeyForSecurityLevel(const KeyId& key_id) { } bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) { - if (playback_start_time_ == 0) return false; - - *seconds_since_started = GetCurrentTime() - playback_start_time_; - return (*seconds_since_started >= 0) ? true : false; + return policy_timers->GetSecondsSinceStarted(GetCurrentTime(), + seconds_since_started); } bool PolicyEngine::GetSecondsSinceLastPlayed( int64_t* seconds_since_last_played) { - if (last_playback_time_ == 0) return false; - - *seconds_since_last_played = GetCurrentTime() - last_playback_time_; - return (*seconds_since_last_played >= 0) ? true : false; + return policy_timers->GetSecondsSinceLastPlayed(GetCurrentTime(), + seconds_since_last_played); } int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() { - const int64_t current_time = GetCurrentTime(); - const int64_t expiry_time = - GetExpiryTime(current_time, - /* ignore_soft_enforce_playback_duration */ false); - if (expiry_time == NEVER_EXPIRES) return LLONG_MAX; - if (expiry_time < current_time) return 0; - return expiry_time - current_time; + return policy_timers->GetLicenseOrPlaybackDurationRemaining(GetCurrentTime()); +} + +bool PolicyEngine::CanRenew() const { + return policy_timers->get_policy().can_renew(); +} + +int64_t PolicyEngine::GetPlaybackStartTime() { + return policy_timers->GetPlaybackStartTime(); +} + +int64_t PolicyEngine::GetLastPlaybackTime() { + return policy_timers->GetLastPlaybackTime(); +} + +int64_t PolicyEngine::GetGracePeriodEndTime() { + return policy_timers->GetGracePeriodEndTime(); } void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time) { - playback_start_time_ = (playback_start_time > 0) ? playback_start_time : 0; - last_playback_time_ = (last_playback_time > 0) ? last_playback_time : 0; - grace_period_end_time_ = grace_period_end_time; - - if (policy_.play_start_grace_period_seconds() != 0) { - // If we are using grace period, we may need to override some of the values - // given to us by OEMCrypto. |grace_period_end_time| will be 0 if the grace - // period has not expired (effectively playback has not begun). Otherwise, - // |grace_period_end_time| contains the playback start time we should use. - playback_start_time_ = grace_period_end_time; - } - const int64_t current_time = GetCurrentTime(); - const int64_t expiry_time = - GetExpiryTime(current_time, - /* ignore_soft_enforce_playback_duration */ true); - was_expired_on_load_ = - expiry_time != NEVER_EXPIRES && expiry_time < current_time; + policy_timers->RestorePlaybackTimes(current_time, playback_start_time, + last_playback_time, + grace_period_end_time); NotifyExpirationUpdate(current_time); } void PolicyEngine::UpdateRenewalRequest(int64_t current_time) { license_state_ = kLicenseStateWaitingLicenseUpdate; - next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds(); + policy_timers->UpdateRenewalRequest(current_time); } bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) { - const int64_t expiry_time = - GetExpiryTime(current_time, - /* ignore_soft_enforce_playback_duration */ false); - return expiry_time != NEVER_EXPIRES && expiry_time <= current_time; -} - -// For the policy time fields checked in the following methods, a value of 0 -// indicates that there is no limit to the duration. If the fields are zero -// (including the hard limit) then these methods will return NEVER_EXPIRES. - -int64_t PolicyEngine::GetHardLicenseExpiryTime() { - return policy_.license_duration_seconds() > 0 - ? license_start_time_ + policy_.license_duration_seconds() - : NEVER_EXPIRES; -} - -int64_t PolicyEngine::GetRentalExpiryTime() { - const int64_t hard_limit = GetHardLicenseExpiryTime(); - if (policy_.rental_duration_seconds() == 0) return hard_limit; - const int64_t expiry_time = - license_start_time_ + policy_.rental_duration_seconds(); - if (hard_limit == NEVER_EXPIRES) return expiry_time; - return std::min(hard_limit, expiry_time); -} - -int64_t PolicyEngine::GetExpiryTime( - int64_t current_time, bool ignore_soft_enforce_playback_duration) { - if (!HasPlaybackStarted(current_time)) return GetRentalExpiryTime(); - - const int64_t hard_limit = GetHardLicenseExpiryTime(); - if (policy_.playback_duration_seconds() == 0) return hard_limit; - if (!ignore_soft_enforce_playback_duration && !was_expired_on_load_ && - policy_.soft_enforce_playback_duration()) { - return hard_limit; - } - const int64_t expiry_time = - playback_start_time_ + policy_.playback_duration_seconds(); - - if (hard_limit == NEVER_EXPIRES) return expiry_time; - return std::min(hard_limit, expiry_time); -} - -int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining( - int64_t current_time) { - // This is only used in Query. This should return the time remaining on - // license_duration_seconds for streaming licenses and rental_duration_seconds - // for offline licenses. - if (HasLicenseOrPlaybackDurationExpired(current_time)) return 0; - const int64_t license_expiry_time = GetRentalExpiryTime(); - if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX; - if (license_expiry_time < current_time) return 0; - const int64_t policy_license_duration = policy_.license_duration_seconds(); - if (policy_license_duration == NEVER_EXPIRES) - return license_expiry_time - current_time; - return std::min(license_expiry_time - current_time, policy_license_duration); -} - -int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { - // This is only used in Query. This should return playback_duration_seconds, - // or the time remaining on it if playing. - const int64_t playback_duration = policy_.playback_duration_seconds(); - if (playback_duration == 0) return LLONG_MAX; - if (playback_start_time_ == 0) return playback_duration; - - const int64_t playback_expiry_time = playback_duration + playback_start_time_; - if (playback_expiry_time < current_time) return 0; - const int64_t policy_playback_duration = policy_.playback_duration_seconds(); - return std::min(playback_expiry_time - current_time, - policy_playback_duration); -} - -bool PolicyEngine::HasRenewalDelayExpired(int64_t current_time) { - return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) && - license_start_time_ + policy_.renewal_delay_seconds() <= current_time; -} - -bool PolicyEngine::HasRenewalRecoveryDurationExpired(int64_t current_time) { - // NOTE: Renewal Recovery Duration is currently not used. - return (policy_.renewal_recovery_duration_seconds() > 0) && - license_start_time_ + policy_.renewal_recovery_duration_seconds() <= - current_time; -} - -bool PolicyEngine::HasRenewalRetryIntervalExpired(int64_t current_time) { - return policy_.can_renew() && - (policy_.renewal_retry_interval_seconds() > 0) && - next_renewal_time_ <= current_time; + return policy_timers->HasLicenseOrPlaybackDurationExpired(current_time); } // Apply a key status to the current keys. @@ -479,15 +376,11 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) { } void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) { - const int64_t expiry_time = - GetExpiryTime(current_time, - /* ignore_soft_enforce_playback_duration */ false); - if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) { - last_expiry_time_ = expiry_time; + int64_t expiry_time; + if (policy_timers->UpdateExpirationTime(current_time, &expiry_time)) { if (event_listener_) event_listener_->OnExpirationUpdate(session_id_, expiry_time); } - last_expiry_time_set_ = true; } int64_t PolicyEngine::GetCurrentTime() { diff --git a/libwvdrmengine/cdm/core/src/policy_timers.cpp b/libwvdrmengine/cdm/core/src/policy_timers.cpp new file mode 100644 index 00000000..2fa2a2af --- /dev/null +++ b/libwvdrmengine/cdm/core/src/policy_timers.cpp @@ -0,0 +1,97 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "policy_timers.h" + +#include + +#include + +#include "log.h" + +using video_widevine::License; + +namespace wvcdm { + +void PolicyTimers::SetLicense(const video_widevine::License& license) { + policy_.Clear(); + license_start_time_ = license.license_start_time(); +} + +void PolicyTimers::DecryptionEvent(int64_t current_time) { + last_playback_time_ = current_time; +} + +bool PolicyTimers::GetSecondsSinceStarted(int64_t current_time, + int64_t* seconds_since_started) { + if (seconds_since_started == nullptr) { + LOGE("|seconds_since_started| not provided"); + return false; + } + + if (playback_start_time_ == 0) return false; + + *seconds_since_started = current_time - playback_start_time_; + return (*seconds_since_started >= 0) ? true : false; +} + +bool PolicyTimers::GetSecondsSinceLastPlayed( + int64_t current_time, int64_t* seconds_since_last_played) { + if (seconds_since_last_played == nullptr) { + LOGE("|seconds_since_last_played| not provided"); + return false; + } + + if (last_playback_time_ == 0) return false; + + *seconds_since_last_played = current_time - last_playback_time_; + return (*seconds_since_last_played >= 0) ? true : false; +} + +bool PolicyTimers::IsLicenseForFuture(int64_t current_time) { + return current_time < license_start_time_; +} + +bool PolicyTimers::UpdateExpirationTime(int64_t current_time, + int64_t* expiry_time) { + if (expiry_time == nullptr) { + LOGE("|expiry_time| mot provided"); + return false; + } + + *expiry_time = + GetExpiryTime(current_time, + /* ignore_soft_enforce_playback_duration */ false); + const bool has_expiry_time_been_updated = + !last_expiry_time_set_ || *expiry_time != last_expiry_time_; + + if (has_expiry_time_been_updated) last_expiry_time_ = *expiry_time; + + last_expiry_time_set_ = true; + return has_expiry_time_been_updated; +} + +bool PolicyTimers::HasRenewalDelayExpired(int64_t current_time) { + return policy_.can_renew() && (policy_.renewal_delay_seconds() > 0) && + (license_start_time_ + policy_.renewal_delay_seconds() <= + current_time); +} + +bool PolicyTimers::HasRenewalRetryIntervalExpired(int64_t current_time) { + return policy_.can_renew() && + (policy_.renewal_retry_interval_seconds() > 0) && + (next_renewal_time_ <= current_time); +} + +void PolicyTimers::UpdateRenewalRequest(int64_t current_time) { + next_renewal_time_ = current_time + policy_.renewal_retry_interval_seconds(); +} + +bool PolicyTimers::HasRenewalRecoveryDurationExpired(int64_t current_time) { + return (policy_.renewal_recovery_duration_seconds() > 0) && + (license_start_time_ + policy_.renewal_recovery_duration_seconds() <= + current_time); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/policy_timers_v15.cpp b/libwvdrmengine/cdm/core/src/policy_timers_v15.cpp new file mode 100644 index 00000000..78ac81e6 --- /dev/null +++ b/libwvdrmengine/cdm/core/src/policy_timers_v15.cpp @@ -0,0 +1,164 @@ +// Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "policy_timers_v15.h" + +#include +#include + +#include "log.h" +#include "wv_cdm_constants.h" + +using video_widevine::License; + +namespace { + +const int64_t kTimeZero = 0; + +} // namespace + +namespace wvcdm { + +bool PolicyTimersV15::UpdateLicense(int64_t current_time, + const License& license) { + if (!license.has_policy()) return false; + + policy_.MergeFrom(license.policy()); + + // some basic license validation + // license start time needs to be specified in the initial response + if (!license.has_license_start_time()) return false; + + // Update time information + license_start_time_ = license.license_start_time(); + next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds(); + + if (!policy_.can_play() || HasLicenseOrPlaybackDurationExpired(current_time)) + return false; + + return true; +} + +void PolicyTimersV15::BeginDecryption(int64_t current_time) { + if (playback_start_time_ == 0) { + playback_start_time_ = current_time; + last_playback_time_ = current_time; + if (policy_.play_start_grace_period_seconds() == 0) + grace_period_end_time_ = current_time; + } +} + +void PolicyTimersV15::RestorePlaybackTimes(int64_t current_time, + int64_t playback_start_time, + int64_t last_playback_time, + int64_t grace_period_end_time) { + playback_start_time_ = std::max(playback_start_time, kTimeZero); + last_playback_time_ = std::max(last_playback_time, kTimeZero); + grace_period_end_time_ = grace_period_end_time; + + if (policy_.play_start_grace_period_seconds() != 0) { + // If we are using grace period, we may need to override some of the values + // given to us by OEMCrypto. |grace_period_end_time| will be 0 if the grace + // period has not expired (effectively playback has not begun). Otherwise, + // |grace_period_end_time| contains the playback start time we should use. + playback_start_time_ = grace_period_end_time; + } + + const int64_t expiry_time = GetExpiryTime( + current_time, /* ignore_soft_enforce_playback_duration */ true); + was_expired_on_load_ = + expiry_time != NEVER_EXPIRES && expiry_time < current_time; +} + +bool PolicyTimersV15::HasPlaybackStarted(int64_t current_time) { + if (playback_start_time_ == 0) return false; + + const int64_t playback_time = current_time - playback_start_time_; + return playback_time >= policy_.play_start_grace_period_seconds(); +} + +bool PolicyTimersV15::HasLicenseOrPlaybackDurationExpired( + int64_t current_time) { + const int64_t expiry_time = GetExpiryTime( + current_time, /* ignore_soft_enforce_playback_duration */ false); + return expiry_time != NEVER_EXPIRES && expiry_time <= current_time; +} + +bool PolicyTimersV15::HasPassedGracePeriod(int64_t current_time) { + if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) { + grace_period_end_time_ = playback_start_time_; + return true; + } + return false; +} + +int64_t PolicyTimersV15::GetLicenseOrPlaybackDurationRemaining( + int64_t current_time) { + const int64_t expiry_time = GetExpiryTime( + current_time, /* ignore_soft_enforce_playback_duration */ false); + if (expiry_time == NEVER_EXPIRES) return LLONG_MAX; + if (expiry_time < current_time) return 0; + return expiry_time - current_time; +} + +int64_t PolicyTimersV15::GetLicenseOrRentalDurationRemaining( + int64_t current_time) { + if (HasLicenseOrPlaybackDurationExpired(current_time)) return 0; + const int64_t license_expiry_time = GetRentalExpiryTime(); + if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX; + if (license_expiry_time < current_time) return 0; + const int64_t policy_license_duration = policy_.license_duration_seconds(); + if (policy_license_duration == NEVER_EXPIRES) + return license_expiry_time - current_time; + return std::min(license_expiry_time - current_time, policy_license_duration); +} + +int64_t PolicyTimersV15::GetPlaybackDurationRemaining(int64_t current_time) { + const int64_t playback_duration = policy_.playback_duration_seconds(); + if (playback_duration == 0) return LLONG_MAX; + if (playback_start_time_ == 0) return playback_duration; + + const int64_t playback_expiry_time = playback_duration + playback_start_time_; + if (playback_expiry_time < current_time) return 0; + const int64_t policy_playback_duration = policy_.playback_duration_seconds(); + return std::min(playback_expiry_time - current_time, + policy_playback_duration); +} + +// For the policy time fields checked in the following methods, a value of 0 +// indicates that there is no limit to the duration. If the fields are zero +// (including the hard limit) then these methods will return NEVER_EXPIRES. +int64_t PolicyTimersV15::GetHardLicenseExpiryTime() { + return policy_.license_duration_seconds() > 0 + ? license_start_time_ + policy_.license_duration_seconds() + : NEVER_EXPIRES; +} + +int64_t PolicyTimersV15::GetRentalExpiryTime() { + const int64_t hard_limit = GetHardLicenseExpiryTime(); + if (policy_.rental_duration_seconds() == 0) return hard_limit; + const int64_t expiry_time = + license_start_time_ + policy_.rental_duration_seconds(); + if (hard_limit == NEVER_EXPIRES) return expiry_time; + return std::min(hard_limit, expiry_time); +} + +int64_t PolicyTimersV15::GetExpiryTime( + int64_t current_time, bool ignore_soft_enforce_playback_duration) { + if (!HasPlaybackStarted(current_time)) return GetRentalExpiryTime(); + + const int64_t hard_limit = GetHardLicenseExpiryTime(); + if (policy_.playback_duration_seconds() == 0) return hard_limit; + if (!ignore_soft_enforce_playback_duration && !was_expired_on_load_ && + policy_.soft_enforce_playback_duration()) { + return hard_limit; + } + const int64_t expiry_time = + playback_start_time_ + policy_.playback_duration_seconds(); + + if (hard_limit == NEVER_EXPIRES) return expiry_time; + return std::min(hard_limit, expiry_time); +} + +} // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp index 649900ed..b55a9199 100644 --- a/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/policy_engine_unittest.cpp @@ -312,7 +312,8 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) .WillOnce(Return(kLicenseStartTime + 1)) - .WillOnce(Return(kLicenseStartTime + 5)); + .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 7)); ExpectSessionKeysChange(kKeyStatusExpired, false); @@ -2170,6 +2171,7 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_CanPlayFalse) { EXPECT_CALL(*mock_clock_, GetCurrentTime()) .WillOnce(Return(kLicenseStartTime + 1)) .WillOnce(Return(kLicenseStartTime + 5)) + .WillOnce(Return(kLicenseStartTime + 7)) .WillOnce(Return(kLicenseStartTime + 100)); EXPECT_CALL(crypto_session_, GetHdcpCapabilities(_, _))