* commit '48742bba5bd9b4c39fb41359a62e15a6503f1a5e': Allow license renewals after expiry
This commit is contained in:
@@ -53,6 +53,9 @@ class PolicyEngine {
|
|||||||
return license_id_;
|
return license_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsLicenseDurationExpired(int64_t current_time);
|
||||||
|
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kLicenseStateInitial,
|
kLicenseStateInitial,
|
||||||
@@ -65,8 +68,6 @@ class PolicyEngine {
|
|||||||
|
|
||||||
void Init(Clock* clock);
|
void Init(Clock* clock);
|
||||||
|
|
||||||
bool IsLicenseDurationExpired(int64_t current_time);
|
|
||||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
|
||||||
bool IsRenewalDelayExpired(int64_t current_time);
|
bool IsRenewalDelayExpired(int64_t current_time);
|
||||||
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
bool IsRenewalRecoveryDurationExpired(int64_t current_time);
|
||||||
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
bool IsRenewalRetryIntervalExpired(int64_t current_time);
|
||||||
|
|||||||
@@ -303,7 +303,17 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
|||||||
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
if (crypto_session_.get() == NULL || !crypto_session_->IsOpen())
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
return crypto_session_->Decrypt(params);
|
CdmResponseType status = crypto_session_->Decrypt(params);
|
||||||
|
// TODO(rfrias): Remove after support for OEMCrypto_ERROR_KEY_EXPIRED is in
|
||||||
|
if (UNKNOWN_ERROR == status) {
|
||||||
|
Clock clock;
|
||||||
|
int64_t current_time = clock.GetCurrentTime();
|
||||||
|
if (policy_engine_.IsLicenseDurationExpired(current_time) ||
|
||||||
|
policy_engine_.IsPlaybackDurationExpired(current_time)) {
|
||||||
|
return NEED_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// License renewal
|
// License renewal
|
||||||
|
|||||||
13
libwvdrmengine/cdm/core/src/crypto_session.cpp
Executable file → Normal file
13
libwvdrmengine/cdm/core/src/crypto_session.cpp
Executable file → Normal file
@@ -588,10 +588,15 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) {
|
|||||||
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
params.is_encrypted, &(*params.iv).front(), params.block_offset,
|
||||||
&buffer_descriptor, params.subsample_flags);
|
&buffer_descriptor, params.subsample_flags);
|
||||||
|
|
||||||
if (OEMCrypto_ERROR_INSUFFICIENT_RESOURCES == sts) {
|
switch (sts) {
|
||||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
case OEMCrypto_SUCCESS:
|
||||||
} else if (OEMCrypto_SUCCESS != sts) {
|
break;
|
||||||
return UNKNOWN_ERROR;
|
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
||||||
|
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||||
|
case OEMCrypto_ERROR_KEY_EXPIRED:
|
||||||
|
return NEED_KEY;
|
||||||
|
default:
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,95 +106,82 @@ void PolicyEngine::SetLicense(
|
|||||||
|
|
||||||
void PolicyEngine::UpdateLicense(
|
void PolicyEngine::UpdateLicense(
|
||||||
const video_widevine_server::sdk::License& license) {
|
const video_widevine_server::sdk::License& license) {
|
||||||
if (!license.has_policy() || kLicenseStateExpired == license_state_)
|
if (!license.has_policy())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
policy_.MergeFrom(license.policy());
|
policy_.MergeFrom(license.policy());
|
||||||
|
|
||||||
switch (license_state_) {
|
if (!policy_.can_play()) {
|
||||||
case kLicenseStateExpired:
|
license_state_ = kLicenseStateExpired;
|
||||||
// Ignore policy updates.
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// some basic license validation
|
||||||
|
if (license_state_ == kLicenseStateInitial) {
|
||||||
|
// license start time needs to be present in the initial response
|
||||||
|
if (!license.has_license_start_time())
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
||||||
|
// we need to enforce that all duration windows are absent if
|
||||||
|
// license_start_time is not present. This is a TBD.
|
||||||
|
|
||||||
case kLicenseStateInitial:
|
// if renewal, discard license if version has not been updated
|
||||||
case kLicenseStateInitialPendingUsage:
|
if (license.id().version() > license_id_.version())
|
||||||
case kLicenseStateCanPlay:
|
license_id_.CopyFrom(license.id());
|
||||||
case kLicenseStateNeedRenewal:
|
else
|
||||||
case kLicenseStateWaitingLicenseUpdate:
|
return;
|
||||||
if (!policy_.can_play()) {
|
}
|
||||||
license_state_ = kLicenseStateExpired;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// some basic license validation
|
// Update time information
|
||||||
if (license_state_ == kLicenseStateInitial) {
|
int64_t current_time = clock_->GetCurrentTime();
|
||||||
// license start time needs to be present in the initial response
|
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
||||||
if (!license.has_license_start_time())
|
// we need to enforce that all duration windows are absent if
|
||||||
return;
|
// license_start_time is not present. This is a TBD.
|
||||||
}
|
if (license.has_license_start_time())
|
||||||
else {
|
license_start_time_ = license.license_start_time();
|
||||||
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
license_received_time_ = current_time;
|
||||||
// we need to enforce that all duration windows are absent if
|
next_renewal_time_ = current_time +
|
||||||
// license_start_time is not present. This is a TBD.
|
policy_.renewal_delay_seconds();
|
||||||
|
|
||||||
// if renewal, discard license if version has not been updated
|
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||||
if (license.id().version() > license_id_.version())
|
// will be set to the minimum of the following policies :
|
||||||
license_id_.CopyFrom(license.id());
|
// rental_duration_seconds and license_duration_seconds.
|
||||||
else
|
// The value is used to determine when the license expires.
|
||||||
return;
|
policy_max_duration_seconds_ = 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Update time information
|
if (policy_.has_rental_duration_seconds())
|
||||||
int64_t current_time = clock_->GetCurrentTime();
|
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||||
// TODO(edwingwong, rfrias): Check back with Thomas and see if
|
|
||||||
// we need to enforce that all duration windows are absent if
|
|
||||||
// license_start_time is not present. This is a TBD.
|
|
||||||
if (license.has_license_start_time())
|
|
||||||
license_start_time_ = license.license_start_time();
|
|
||||||
license_received_time_ = current_time;
|
|
||||||
next_renewal_time_ = current_time +
|
|
||||||
policy_.renewal_delay_seconds();
|
|
||||||
|
|
||||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
if ((policy_.license_duration_seconds() > 0) &&
|
||||||
// will be set to the minimum of the following policies :
|
((policy_.license_duration_seconds() <
|
||||||
// rental_duration_seconds and license_duration_seconds.
|
policy_max_duration_seconds_) ||
|
||||||
// The value is used to determine when the license expires.
|
policy_max_duration_seconds_ == 0)) {
|
||||||
policy_max_duration_seconds_ = 0;
|
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||||
|
}
|
||||||
|
|
||||||
if (policy_.has_rental_duration_seconds())
|
if (Properties::begin_license_usage_when_received())
|
||||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
playback_start_time_ = current_time;
|
||||||
|
|
||||||
if ((policy_.license_duration_seconds() > 0) &&
|
// Update state
|
||||||
((policy_.license_duration_seconds() <
|
if (Properties::begin_license_usage_when_received()) {
|
||||||
policy_max_duration_seconds_) ||
|
if (policy_.renew_with_usage()) {
|
||||||
policy_max_duration_seconds_ == 0)) {
|
license_state_ = kLicenseStateNeedRenewal;
|
||||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
}
|
||||||
}
|
else {
|
||||||
|
license_state_ = kLicenseStateCanPlay;
|
||||||
if (Properties::begin_license_usage_when_received())
|
can_decrypt_ = true;
|
||||||
playback_start_time_ = current_time;
|
}
|
||||||
|
}
|
||||||
// Update state
|
else {
|
||||||
if (Properties::begin_license_usage_when_received()) {
|
if (license_state_ == kLicenseStateInitial) {
|
||||||
if (policy_.renew_with_usage()) {
|
license_state_ = kLicenseStateInitialPendingUsage;
|
||||||
license_state_ = kLicenseStateNeedRenewal;
|
}
|
||||||
}
|
else {
|
||||||
else {
|
license_state_ = kLicenseStateCanPlay;
|
||||||
license_state_ = kLicenseStateCanPlay;
|
can_decrypt_ = true;
|
||||||
can_decrypt_ = true;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (license_state_ == kLicenseStateInitial) {
|
|
||||||
license_state_ = kLicenseStateInitialPendingUsage;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
license_state_ = kLicenseStateCanPlay;
|
|
||||||
can_decrypt_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -438,7 +438,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
|
|||||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
|
||||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
.WillOnce(Return(license_start_time_ + 1))
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
@@ -496,7 +496,83 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
|||||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PolicyEngineTest, PlaybackOk_RenewedSuccessAfterExpiry) {
|
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
|
||||||
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
|
playback_duration_ + 1))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 10))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 30))
|
||||||
|
.WillOnce(Return(license_start_time_ + license_duration_ + 40));
|
||||||
|
|
||||||
|
policy_engine_->SetLicense(license_);
|
||||||
|
|
||||||
|
policy_engine_->BeginDecryption();
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
bool event_occurred;
|
||||||
|
CdmEventType event;
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_TRUE(event_occurred);
|
||||||
|
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
|
||||||
|
|
||||||
|
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||||
|
|
||||||
|
license_.set_license_start_time(license_start_time_ +
|
||||||
|
license_duration_ + 20);
|
||||||
|
LicenseIdentification* id = license_.mutable_id();
|
||||||
|
id->set_version(2);
|
||||||
|
License_Policy* policy = license_.mutable_policy();
|
||||||
|
policy = license_.mutable_policy();
|
||||||
|
policy->set_playback_duration_seconds(playback_duration_ + 100);
|
||||||
|
policy->set_license_duration_seconds(license_duration_ + 100);
|
||||||
|
|
||||||
|
policy_engine_->UpdateLicense(license_);
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent(event_occurred, event);
|
||||||
|
EXPECT_FALSE(event_occurred);
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->can_decrypt());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
|
||||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
.WillOnce(Return(license_start_time_ + 1))
|
.WillOnce(Return(license_start_time_ + 1))
|
||||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||||
|
|||||||
Reference in New Issue
Block a user