Merge "Corrections for limited duration licenses"
This commit is contained in:
@@ -68,7 +68,7 @@ class PolicyEngine {
|
|||||||
virtual void SetLicenseForRelease(const video_widevine::License& license);
|
virtual void SetLicenseForRelease(const video_widevine::License& license);
|
||||||
|
|
||||||
// Call this on first decrypt to set the start of playback.
|
// Call this on first decrypt to set the start of playback.
|
||||||
virtual void BeginDecryption(void);
|
virtual bool BeginDecryption(void);
|
||||||
virtual void DecryptionEvent(void);
|
virtual void DecryptionEvent(void);
|
||||||
|
|
||||||
// UpdateLicense is used in handling a license response for a renewal request.
|
// UpdateLicense is used in handling a license response for a renewal request.
|
||||||
|
|||||||
@@ -640,8 +640,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
|
|||||||
|
|
||||||
if (status == NO_ERROR) {
|
if (status == NO_ERROR) {
|
||||||
if (is_initial_decryption_) {
|
if (is_initial_decryption_) {
|
||||||
policy_engine_->BeginDecryption();
|
is_initial_decryption_ = !policy_engine_->BeginDecryption();
|
||||||
is_initial_decryption_ = false;
|
|
||||||
}
|
}
|
||||||
has_decrypted_since_last_report_ = true;
|
has_decrypted_since_last_report_ = true;
|
||||||
if (!is_usage_update_needed_) {
|
if (!is_usage_update_needed_) {
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
|
|||||||
NotifyExpirationUpdate(current_time);
|
NotifyExpirationUpdate(current_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PolicyEngine::BeginDecryption() {
|
bool PolicyEngine::BeginDecryption() {
|
||||||
if (playback_start_time_ == 0) {
|
if (playback_start_time_ == 0) {
|
||||||
switch (license_state_) {
|
switch (license_state_) {
|
||||||
case kLicenseStateCanPlay:
|
case kLicenseStateCanPlay:
|
||||||
@@ -246,14 +246,17 @@ void PolicyEngine::BeginDecryption() {
|
|||||||
license_state_ = kLicenseStateNeedRenewal;
|
license_state_ = kLicenseStateNeedRenewal;
|
||||||
}
|
}
|
||||||
NotifyExpirationUpdate(playback_start_time_);
|
NotifyExpirationUpdate(playback_start_time_);
|
||||||
break;
|
return true;
|
||||||
case kLicenseStateInitial:
|
case kLicenseStateInitial:
|
||||||
case kLicenseStatePending:
|
case kLicenseStatePending:
|
||||||
case kLicenseStateExpired:
|
case kLicenseStateExpired:
|
||||||
default:
|
default:
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PolicyEngine::DecryptionEvent() { last_playback_time_ = GetCurrentTime(); }
|
void PolicyEngine::DecryptionEvent() { last_playback_time_ = GetCurrentTime(); }
|
||||||
|
|||||||
@@ -819,6 +819,40 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalAndLicense0_WithPlayback) {
|
|||||||
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PolicyEngineTest,
|
||||||
|
PlaybackOk_RentalAndLicense0_WithPlaybackBeforeLicense) {
|
||||||
|
License_Policy* policy = license_.mutable_policy();
|
||||||
|
policy->clear_license_duration_seconds();
|
||||||
|
policy->clear_rental_duration_seconds();
|
||||||
|
// Only |playback_duration_seconds| set.
|
||||||
|
|
||||||
|
policy_engine_->BeginDecryption();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock_clock_, GetCurrentTime())
|
||||||
|
.WillOnce(Return(kLicenseStartTime + 1))
|
||||||
|
.WillOnce(Return(kPlaybackStartTime))
|
||||||
|
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10))
|
||||||
|
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10));
|
||||||
|
|
||||||
|
ExpectSessionKeysChange(kKeyStatusExpired, false);
|
||||||
|
ExpectSessionKeysChange(kKeyStatusUsable, true);
|
||||||
|
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0));
|
||||||
|
EXPECT_CALL(mock_event_listener_,
|
||||||
|
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
|
||||||
|
|
||||||
|
policy_engine_->SetLicense(license_);
|
||||||
|
policy_engine_->BeginDecryption();
|
||||||
|
policy_engine_->OnTimerEvent();
|
||||||
|
|
||||||
|
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId));
|
||||||
|
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
||||||
|
|
||||||
|
policy_engine_->OnTimerEvent();
|
||||||
|
|
||||||
|
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
|
||||||
|
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
|
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
|
||||||
License_Policy* policy = license_.mutable_policy();
|
License_Policy* policy = license_.mutable_policy();
|
||||||
policy->set_rental_duration_seconds(kDurationUnlimited);
|
policy->set_rental_duration_seconds(kDurationUnlimited);
|
||||||
|
|||||||
@@ -520,6 +520,16 @@ std::string kPsshStreamingClip7 = wvcdm::a2bs_hex(
|
|||||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||||
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
||||||
"747265616d696e675f636c697037");
|
"747265616d696e675f636c697037");
|
||||||
|
std::string kPsshStreamingClip20 = wvcdm::a2bs_hex(
|
||||||
|
"000000437073736800000000" // blob size and pssh
|
||||||
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
||||||
|
"08011a0d7769646576696e655f746573" // pssh data
|
||||||
|
"74221073747265616d696e675f636c69703230");
|
||||||
|
std::string kPsshStreamingClip21 = wvcdm::a2bs_hex(
|
||||||
|
"000000437073736800000000" // blob size and pssh
|
||||||
|
"EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id
|
||||||
|
"08011a0d7769646576696e655f746573" // pssh data
|
||||||
|
"74221073747265616d696e675f636c69703231");
|
||||||
|
|
||||||
std::string kProviderSessionTokenStreamingClip3 = wvcdm::a2bs_hex(
|
std::string kProviderSessionTokenStreamingClip3 = wvcdm::a2bs_hex(
|
||||||
"4851305A4A4156485A554936444E4931");
|
"4851305A4A4156485A554936444E4931");
|
||||||
@@ -527,6 +537,15 @@ std::string kProviderSessionTokenStreamingClip4 = wvcdm::a2bs_hex(
|
|||||||
"4942524F4355544E5557553145463243");
|
"4942524F4355544E5557553145463243");
|
||||||
std::string kProviderSessionTokenStreamingClip7 = wvcdm::a2bs_hex(
|
std::string kProviderSessionTokenStreamingClip7 = wvcdm::a2bs_hex(
|
||||||
"44434C53524F4E30394C4E5535544B4C");
|
"44434C53524F4E30394C4E5535544B4C");
|
||||||
|
std::string kProviderSessionTokenStreamingClip20 = wvcdm::a2bs_hex(
|
||||||
|
"4851305A4A4156485A554936444E4931");
|
||||||
|
std::string kProviderSessionTokenStreamingClip21 = wvcdm::a2bs_hex(
|
||||||
|
"4851305A4A4156485A554936444E4931");
|
||||||
|
|
||||||
|
// playback duration is 10 seconds+uncertainty window
|
||||||
|
const std::chrono::milliseconds
|
||||||
|
kExpirationStreamingClip21PlaybackDurationTimeMs =
|
||||||
|
std::chrono::milliseconds(12*1000);
|
||||||
|
|
||||||
UsageLicenseAndSubSampleInfo kUsageLicenseTestVector1[] = {
|
UsageLicenseAndSubSampleInfo kUsageLicenseTestVector1[] = {
|
||||||
{ kPsshStreamingClip3, &usage_info_sub_samples_icp[0],
|
{ kPsshStreamingClip3, &usage_info_sub_samples_icp[0],
|
||||||
@@ -1634,6 +1653,12 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
|||||||
|
|
||||||
bool VerifyDecryption(const CdmSessionId& session_id,
|
bool VerifyDecryption(const CdmSessionId& session_id,
|
||||||
const SubSampleInfo& data) {
|
const SubSampleInfo& data) {
|
||||||
|
return VerifyDecryption(session_id, data, NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VerifyDecryption(const CdmSessionId& session_id,
|
||||||
|
const SubSampleInfo& data,
|
||||||
|
CdmResponseType expected_response) {
|
||||||
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
std::vector<uint8_t> decrypt_buffer(data.encrypt_data.size());
|
||||||
CdmDecryptionParameters decryption_parameters(
|
CdmDecryptionParameters decryption_parameters(
|
||||||
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
&data.key_id, &data.encrypt_data.front(), data.encrypt_data.size(),
|
||||||
@@ -1644,7 +1669,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
|||||||
CdmResponseType status = decryptor_.Decrypt(session_id,
|
CdmResponseType status = decryptor_.Decrypt(session_id,
|
||||||
data.validate_key_id,
|
data.validate_key_id,
|
||||||
decryption_parameters);
|
decryption_parameters);
|
||||||
EXPECT_EQ(NO_ERROR, status);
|
EXPECT_EQ(expected_response, status);
|
||||||
if (status != NO_ERROR)
|
if (status != NO_ERROR)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -4981,6 +5006,70 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) {
|
|||||||
decryptor_.CloseSession(session_id_);
|
decryptor_.CloseSession(session_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry) {
|
||||||
|
StrictMock<TestWvCdmEventListener> listener;
|
||||||
|
DecryptCallbackTester decrypt_callback(
|
||||||
|
&decryptor_,
|
||||||
|
&usage_info_sub_samples_icp[0]);
|
||||||
|
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
|
||||||
|
&listener, &session_id_);
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
listener,
|
||||||
|
OnSessionKeysChange(
|
||||||
|
session_id_,
|
||||||
|
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
|
||||||
|
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
|
||||||
|
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
|
||||||
|
|
||||||
|
GenerateKeyRequest(kPsshStreamingClip21, kLicenseTypeStreaming, NULL);
|
||||||
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
listener,
|
||||||
|
OnSessionKeysChange(
|
||||||
|
session_id_,
|
||||||
|
AllOf(Each(Pair(_, kKeyStatusExpired)), Not(IsEmpty())), false));
|
||||||
|
|
||||||
|
// Elapse time so that the key should now be considered expired.
|
||||||
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry_DecryptBeforeLicense) {
|
||||||
|
StrictMock<TestWvCdmEventListener> listener;
|
||||||
|
DecryptCallbackTester decrypt_callback(
|
||||||
|
&decryptor_,
|
||||||
|
&usage_info_sub_samples_icp[0]);
|
||||||
|
decryptor_.OpenSession(config_.key_system(), NULL, kDefaultCdmIdentifier,
|
||||||
|
&listener, &session_id_);
|
||||||
|
|
||||||
|
// Decrypt before license is received is expected to fail but should
|
||||||
|
// not start the playback timer
|
||||||
|
EXPECT_FALSE(VerifyDecryption(session_id_,
|
||||||
|
usage_info_sub_samples_icp[0], NEED_KEY));
|
||||||
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
listener,
|
||||||
|
OnSessionKeysChange(
|
||||||
|
session_id_,
|
||||||
|
AllOf(Each(Pair(_, kKeyStatusUsable)), Not(IsEmpty())), true))
|
||||||
|
.WillOnce(Invoke(&decrypt_callback, &DecryptCallbackTester::Decrypt));
|
||||||
|
EXPECT_CALL(listener, OnExpirationUpdate(session_id_, _));
|
||||||
|
|
||||||
|
GenerateKeyRequest(kPsshStreamingClip21, kLicenseTypeStreaming, NULL);
|
||||||
|
VerifyKeyRequestResponse(config_.license_server(), config_.client_auth());
|
||||||
|
|
||||||
|
EXPECT_CALL(
|
||||||
|
listener,
|
||||||
|
OnSessionKeysChange(
|
||||||
|
session_id_,
|
||||||
|
AllOf(Each(Pair(_, kKeyStatusExpired)), Not(IsEmpty())), false));
|
||||||
|
|
||||||
|
// Elapse time so that the key should now be considered expired.
|
||||||
|
std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) {
|
TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) {
|
||||||
StrictMock<TestWvCdmEventListener> listener;
|
StrictMock<TestWvCdmEventListener> listener;
|
||||||
DecryptCallbackTester decrypt_callback(
|
DecryptCallbackTester decrypt_callback(
|
||||||
|
|||||||
Reference in New Issue
Block a user