Merge "Allow license renewals after expiry" into klp-dev
This commit is contained in:
@@ -53,6 +53,9 @@ class PolicyEngine {
|
||||
return license_id_;
|
||||
}
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
|
||||
private:
|
||||
typedef enum {
|
||||
kLicenseStateInitial,
|
||||
@@ -65,8 +68,6 @@ class PolicyEngine {
|
||||
|
||||
void Init(Clock* clock);
|
||||
|
||||
bool IsLicenseDurationExpired(int64_t current_time);
|
||||
bool IsPlaybackDurationExpired(int64_t current_time);
|
||||
bool IsRenewalDelayExpired(int64_t current_time);
|
||||
bool IsRenewalRecoveryDurationExpired(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())
|
||||
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
|
||||
|
||||
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,
|
||||
&buffer_descriptor, params.subsample_flags);
|
||||
|
||||
if (OEMCrypto_ERROR_INSUFFICIENT_RESOURCES == sts) {
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
} else if (OEMCrypto_SUCCESS != sts) {
|
||||
return UNKNOWN_ERROR;
|
||||
switch (sts) {
|
||||
case OEMCrypto_SUCCESS:
|
||||
break;
|
||||
case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES:
|
||||
return INSUFFICIENT_CRYPTO_RESOURCES;
|
||||
case OEMCrypto_ERROR_KEY_EXPIRED:
|
||||
return NEED_KEY;
|
||||
default:
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
@@ -106,95 +106,82 @@ void PolicyEngine::SetLicense(
|
||||
|
||||
void PolicyEngine::UpdateLicense(
|
||||
const video_widevine_server::sdk::License& license) {
|
||||
if (!license.has_policy() || kLicenseStateExpired == license_state_)
|
||||
if (!license.has_policy())
|
||||
return;
|
||||
|
||||
policy_.MergeFrom(license.policy());
|
||||
|
||||
switch (license_state_) {
|
||||
case kLicenseStateExpired:
|
||||
// Ignore policy updates.
|
||||
if (!policy_.can_play()) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
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;
|
||||
}
|
||||
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:
|
||||
case kLicenseStateInitialPendingUsage:
|
||||
case kLicenseStateCanPlay:
|
||||
case kLicenseStateNeedRenewal:
|
||||
case kLicenseStateWaitingLicenseUpdate:
|
||||
if (!policy_.can_play()) {
|
||||
license_state_ = kLicenseStateExpired;
|
||||
return;
|
||||
}
|
||||
// if renewal, discard license if version has not been updated
|
||||
if (license.id().version() > license_id_.version())
|
||||
license_id_.CopyFrom(license.id());
|
||||
else
|
||||
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;
|
||||
}
|
||||
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.
|
||||
// Update time information
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
// 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();
|
||||
|
||||
// if renewal, discard license if version has not been updated
|
||||
if (license.id().version() > license_id_.version())
|
||||
license_id_.CopyFrom(license.id());
|
||||
else
|
||||
return;
|
||||
}
|
||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||
// will be set to the minimum of the following policies :
|
||||
// rental_duration_seconds and license_duration_seconds.
|
||||
// The value is used to determine when the license expires.
|
||||
policy_max_duration_seconds_ = 0;
|
||||
|
||||
// Update time information
|
||||
int64_t current_time = clock_->GetCurrentTime();
|
||||
// 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();
|
||||
if (policy_.has_rental_duration_seconds())
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
|
||||
// Calculate policy_max_duration_seconds_. policy_max_duration_seconds_
|
||||
// will be set to the minimum of the following policies :
|
||||
// rental_duration_seconds and license_duration_seconds.
|
||||
// The value is used to determine when the license expires.
|
||||
policy_max_duration_seconds_ = 0;
|
||||
if ((policy_.license_duration_seconds() > 0) &&
|
||||
((policy_.license_duration_seconds() <
|
||||
policy_max_duration_seconds_) ||
|
||||
policy_max_duration_seconds_ == 0)) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (policy_.has_rental_duration_seconds())
|
||||
policy_max_duration_seconds_ = policy_.rental_duration_seconds();
|
||||
if (Properties::begin_license_usage_when_received())
|
||||
playback_start_time_ = current_time;
|
||||
|
||||
if ((policy_.license_duration_seconds() > 0) &&
|
||||
((policy_.license_duration_seconds() <
|
||||
policy_max_duration_seconds_) ||
|
||||
policy_max_duration_seconds_ == 0)) {
|
||||
policy_max_duration_seconds_ = policy_.license_duration_seconds();
|
||||
}
|
||||
|
||||
if (Properties::begin_license_usage_when_received())
|
||||
playback_start_time_ = current_time;
|
||||
|
||||
// Update state
|
||||
if (Properties::begin_license_usage_when_received()) {
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
license_state_ = kLicenseStateInitialPendingUsage;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
// Update state
|
||||
if (Properties::begin_license_usage_when_received()) {
|
||||
if (policy_.renew_with_usage()) {
|
||||
license_state_ = kLicenseStateNeedRenewal;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (license_state_ == kLicenseStateInitial) {
|
||||
license_state_ = kLicenseStateInitialPendingUsage;
|
||||
}
|
||||
else {
|
||||
license_state_ = kLicenseStateCanPlay;
|
||||
can_decrypt_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
|
||||
EXPECT_FALSE(policy_engine_->can_decrypt());
|
||||
}
|
||||
|
||||
TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
||||
TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
|
||||
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
@@ -496,7 +496,83 @@ TEST_F(PolicyEngineTest, PlaybackOk_RepeatedRenewFailures) {
|
||||
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())
|
||||
.WillOnce(Return(license_start_time_ + 1))
|
||||
.WillOnce(Return(license_start_time_ + license_duration_ -
|
||||
|
||||
Reference in New Issue
Block a user