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(_, _))