Report key status change

Also modified PolicyEngine::CanDecrypt to use the new Keys_status_.

Bug: 19771431

Merged from Widevine CDM repo:
https://widevine-internal-review.googlesource.com/#/c/13904/

Change-Id: If7fb7fa45ecf9ff4e65278cda00ad7230b57f50b
This commit is contained in:
Kongqun Yang
2015-03-30 19:10:56 -07:00
parent bdb82e04f8
commit 4621028434
10 changed files with 315 additions and 26 deletions

View File

@@ -128,7 +128,6 @@ class CdmSession {
// instance variables
bool initialized_;
CdmSessionId session_id_;
WvCdmEventListener* event_listener_;
scoped_ptr<CdmLicense> license_parser_;
scoped_ptr<CryptoSession> crypto_session_;
scoped_ptr<PolicyEngine> policy_engine_;

View File

@@ -3,6 +3,7 @@
#ifndef WVCDM_CORE_POLICY_ENGINE_H_
#define WVCDM_CORE_POLICY_ENGINE_H_
#include <map>
#include <string>
#include "license_protocol.pb.h"
@@ -59,6 +60,8 @@ class PolicyEngine {
// Used for notifying the Policy Engine of resolution changes
virtual void NotifyResolution(uint32_t width, uint32_t height);
virtual void NotifySessionExpiration();
virtual CdmResponseType Query(CdmQueryMap* key_info);
virtual const LicenseIdentification& license_id() { return license_id_; }
@@ -105,15 +108,19 @@ class PolicyEngine {
void UpdateRenewalRequest(int64_t current_time);
// Notifies updates in keys information and fire OnKeysChange event if
// key changes.
void NotifyKeysChange(CdmKeyStatus new_status);
// Notifies updates in expiry time and fire OnExpirationUpdate event if
// expiry time changes.
void NotifyExpirationUpdate();
// These setters are for testing only. Takes ownership of the pointers.
void set_clock(Clock* clock);
void set_max_res_engine(MaxResEngine* max_res_engine);
LicenseState license_state_;
bool can_decrypt_;
// This is the current policy information for this license. This gets updated
// as license renewals occur.
@@ -142,7 +149,10 @@ class PolicyEngine {
CdmSessionId session_id_;
WvCdmEventListener* event_listener_;
MaxResEngine max_res_engine_;
scoped_ptr<MaxResEngine> max_res_engine_;
std::map<KeyId, CdmKeyStatus> keys_status_;
scoped_ptr<Clock> clock_;
CORE_DISALLOW_COPY_AND_ASSIGN(PolicyEngine);

View File

@@ -15,6 +15,10 @@ class WvCdmEventListener {
virtual void OnSessionRenewalNeeded(const CdmSessionId& session_id) = 0;
virtual void OnSessionExpiration(const CdmSessionId& session_id) = 0;
virtual void OnSessionKeysChange(
const CdmSessionId& session_id,
const std::vector<CdmKeyInformation>& cdm_keys_info,
bool has_new_usable_key) = 0;
virtual void OnExpirationUpdate(const CdmSessionId& session_id,
int64_t new_expiry_time) = 0;

View File

@@ -49,6 +49,15 @@ enum CdmResponseType {
INSUFFICIENT_CRYPTO_RESOURCES,
};
enum CdmKeyStatus {
kKeyStatusUsable,
kKeyStatusInternalError,
kKeyStatusExpired,
kKeyStatusOutputNotAllowed,
kKeyStatusOutputDownscaled,
kKeyStatusPending,
};
#define CORE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
@@ -116,6 +125,14 @@ struct CdmDecryptionParameters {
is_video(true) {}
};
struct CdmKeyInformation {
CdmKeyInformation(const KeyId& id, CdmKeyStatus status)
: key_id(id), key_status(status) {}
KeyId key_id;
CdmKeyStatus key_status;
};
// forward class references
class KeyMessage;
class Request;

View File

@@ -27,11 +27,10 @@ CdmSession::CdmSession(const CdmClientPropertySet* cdm_client_property_set,
WvCdmEventListener* event_listener)
: initialized_(false),
session_id_(GenerateSessionId()),
event_listener_(event_listener),
license_parser_(new CdmLicense),
crypto_session_(new CryptoSession),
policy_engine_(new PolicyEngine(session_id_, event_listener_,
crypto_session_.get())),
policy_engine_(
new PolicyEngine(session_id_, event_listener, crypto_session_.get())),
file_handle_(new DeviceFiles),
license_received_(false),
is_offline_(false),
@@ -532,7 +531,7 @@ void CdmSession::OnTimerEvent(bool update_usage) {
void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) {
if (key_set_id_ == key_set_id) {
if (event_listener_) event_listener_->OnSessionExpiration(session_id_);
policy_engine_->NotifySessionExpiration();
}
}

View File

@@ -15,13 +15,14 @@
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
using video_widevine_server::sdk::License;
namespace wvcdm {
PolicyEngine::PolicyEngine(CdmSessionId session_id,
WvCdmEventListener* event_listener,
CryptoSession* crypto_session)
: license_state_(kLicenseStateInitial),
can_decrypt_(false),
license_start_time_(0),
playback_start_time_(0),
last_playback_time_(0),
@@ -30,13 +31,18 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
policy_max_duration_seconds_(0),
session_id_(session_id),
event_listener_(event_listener),
max_res_engine_(crypto_session),
max_res_engine_(new MaxResEngine(crypto_session)),
clock_(new Clock) {}
PolicyEngine::~PolicyEngine() {}
bool PolicyEngine::CanDecrypt(const KeyId& key_id) {
return can_decrypt_ && max_res_engine_.CanDecrypt(key_id);
if (keys_status_.find(key_id) == keys_status_.end()) {
LOGE("PolicyEngine::CanDecrypt Key '%s' not in license.",
b2a_hex(key_id).c_str());
return false;
}
return keys_status_[key_id] == kKeyStatusUsable;
}
void PolicyEngine::OnTimerEvent() {
@@ -47,17 +53,21 @@ void PolicyEngine::OnTimerEvent() {
IsPlaybackDurationExpired(current_time)) &&
license_state_ != kLicenseStateExpired) {
license_state_ = kLicenseStateExpired;
can_decrypt_ = false;
NotifyKeysChange(kKeyStatusExpired);
if (event_listener_) event_listener_->OnSessionExpiration(session_id_);
return;
}
max_res_engine_->OnTimerEvent();
bool renewal_needed = false;
// Test to determine if renewal should be attempted.
switch (license_state_) {
case kLicenseStateCanPlay: {
if (IsRenewalDelayExpired(current_time)) renewal_needed = true;
// HDCP may change, so force a check.
NotifyKeysChange(kKeyStatusUsable);
break;
}
@@ -74,7 +84,7 @@ void PolicyEngine::OnTimerEvent() {
case kLicenseStatePending: {
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
NotifyKeysChange(kKeyStatusUsable);
}
break;
}
@@ -86,7 +96,7 @@ void PolicyEngine::OnTimerEvent() {
default: {
license_state_ = kLicenseStateExpired;
can_decrypt_ = false;
NotifyKeysChange(kKeyStatusInternalError);
break;
}
}
@@ -95,21 +105,26 @@ void PolicyEngine::OnTimerEvent() {
UpdateRenewalRequest(current_time);
if (event_listener_) event_listener_->OnSessionRenewalNeeded(session_id_);
}
max_res_engine_.OnTimerEvent();
}
void PolicyEngine::SetLicense(
const video_widevine_server::sdk::License& license) {
void PolicyEngine::SetLicense(const License& license) {
license_id_.Clear();
license_id_.CopyFrom(license.id());
policy_.Clear();
// Extract content key ids.
keys_status_.clear();
for (int key_index = 0; key_index < license.key_size(); ++key_index) {
const License::KeyContainer& key = license.key(key_index);
if (key.type() == License::KeyContainer::CONTENT && key.has_id())
keys_status_[key.id()] = kKeyStatusInternalError;
}
UpdateLicense(license);
max_res_engine_.SetLicense(license);
max_res_engine_->SetLicense(license);
}
void PolicyEngine::UpdateLicense(
const video_widevine_server::sdk::License& license) {
void PolicyEngine::UpdateLicense(const License& license) {
if (!license.has_policy()) return;
if (kLicenseStateExpired == license_state_) {
@@ -153,16 +168,17 @@ void PolicyEngine::UpdateLicense(
if (!policy_.can_play() || IsLicenseDurationExpired(current_time) ||
IsPlaybackDurationExpired(current_time)) {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
return;
}
// Update state
if (current_time >= license_start_time_) {
license_state_ = kLicenseStateCanPlay;
can_decrypt_ = true;
NotifyKeysChange(kKeyStatusUsable);
} else {
license_state_ = kLicenseStatePending;
can_decrypt_ = false;
NotifyKeysChange(kKeyStatusPending);
}
NotifyExpirationUpdate();
}
@@ -195,7 +211,13 @@ void PolicyEngine::DecryptionEvent() {
}
void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) {
max_res_engine_.SetResolution(width, height);
max_res_engine_->SetResolution(width, height);
}
void PolicyEngine::NotifySessionExpiration() {
license_state_ = kLicenseStateExpired;
NotifyKeysChange(kKeyStatusExpired);
if (event_listener_) event_listener_->OnSessionExpiration(session_id_);
}
CdmResponseType PolicyEngine::Query(CdmQueryMap* key_info) {
@@ -303,6 +325,35 @@ bool PolicyEngine::IsRenewalRetryIntervalExpired(int64_t current_time) {
next_renewal_time_ <= current_time;
}
void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) {
bool keys_changed = false;
bool has_new_usable_key = false;
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
it != keys_status_.end(); ++it) {
const KeyId key_id = it->first;
CdmKeyStatus& key_status = it->second;
CdmKeyStatus updated_status = new_status;
if (updated_status == kKeyStatusUsable) {
if (!max_res_engine_->CanDecrypt(key_id))
updated_status = kKeyStatusOutputNotAllowed;
}
if (key_status != updated_status) {
key_status = updated_status;
if (updated_status == kKeyStatusUsable) has_new_usable_key = true;
keys_changed = true;
}
}
if (keys_changed && event_listener_) {
std::vector<CdmKeyInformation> keys_info;
for (std::map<KeyId, CdmKeyStatus>::iterator it = keys_status_.begin();
it != keys_status_.end(); ++it) {
keys_info.push_back(CdmKeyInformation(it->first, it->second));
}
event_listener_->OnSessionKeysChange(session_id_, keys_info,
has_new_usable_key);
}
}
void PolicyEngine::NotifyExpirationUpdate() {
int64_t expiry_time =
std::min(GetLicenseExpiryTime(), GetPlaybackExpiryTime());
@@ -315,4 +366,8 @@ void PolicyEngine::NotifyExpirationUpdate() {
void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); }
void PolicyEngine::set_max_res_engine(MaxResEngine* max_res_engine) {
max_res_engine_.reset(max_res_engine);
}
} // wvcdm

View File

@@ -38,6 +38,8 @@ const int64_t kHighDuration =
const char* kRenewalServerUrl =
"https://test.google.com/license/GetCencLicense";
const wvcdm::KeyId kKeyId = "357adc89f1673433c36c621f1b5c41ee";
const wvcdm::KeyId kAnotherKeyId = "another_key_id";
const wvcdm::KeyId kSomeRandomKeyId = "some_random_key_id";
const wvcdm::CdmSessionId kSessionId = "mock_session_id";
int64_t GetLicenseRenewalDelay(int64_t license_duration) {
@@ -58,20 +60,34 @@ using video_widevine_server::sdk::OFFLINE;
// gmock methods
using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::Field;
using ::testing::InSequence;
using ::testing::MockFunction;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
class MockCdmEventListener : public WvCdmEventListener {
public:
MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id));
MOCK_METHOD1(OnSessionExpiration, void(const CdmSessionId& session_id));
MOCK_METHOD3(OnSessionKeysChange,
void(const CdmSessionId& session_id,
const std::vector<CdmKeyInformation>& cdm_keys_info,
bool has_new_usable_key));
MOCK_METHOD2(OnExpirationUpdate,
void(const CdmSessionId& session_id, int64_t new_expiry_time));
};
class MockMaxResEngine : public MaxResEngine {
public:
MockMaxResEngine() : MaxResEngine(NULL) {}
MOCK_METHOD1(CanDecrypt, bool(const KeyId& key_id));
};
class PolicyEngineTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -85,6 +101,10 @@ class PolicyEngineTest : public ::testing::Test {
id->set_version(1);
id->set_type(STREAMING);
License::KeyContainer* key = license_.add_key();
key->set_type(License::KeyContainer::CONTENT);
key->set_id(kKeyId);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_can_play(true);
@@ -120,8 +140,42 @@ class PolicyEngineTest : public ::testing::Test {
policy_engine_->set_clock(mock_clock_);
}
void InjectMockMaxResEngine() {
mock_max_res_engine_ = new MockMaxResEngine();
policy_engine_->set_max_res_engine(mock_max_res_engine_);
}
void ExpectSessionKeysChange(CdmKeyStatus expected_key_status,
bool expected_has_new_usable_key) {
EXPECT_CALL(mock_event_listener_,
OnSessionKeysChange(
kSessionId,
UnorderedElementsAre(
AllOf(Field(&CdmKeyInformation::key_id, kKeyId),
Field(&CdmKeyInformation::key_status,
expected_key_status))),
expected_has_new_usable_key));
}
void ExpectSessionKeysChange(CdmKeyStatus expected_key1_status,
CdmKeyStatus expected_key2_status,
bool expected_has_new_usable_key) {
EXPECT_CALL(mock_event_listener_,
OnSessionKeysChange(
kSessionId,
UnorderedElementsAre(
AllOf(Field(&CdmKeyInformation::key_id, kKeyId),
Field(&CdmKeyInformation::key_status,
expected_key1_status)),
AllOf(Field(&CdmKeyInformation::key_id, kAnotherKeyId),
Field(&CdmKeyInformation::key_status,
expected_key2_status))),
expected_has_new_usable_key));
}
StrictMock<MockCdmEventListener> mock_event_listener_;
MockClock* mock_clock_;
MockMaxResEngine* mock_max_res_engine_;
scoped_ptr<PolicyEngine> policy_engine_;
License license_;
MockFunction<void(int i)> check_;
@@ -137,6 +191,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) {
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + 10));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
@@ -146,6 +201,7 @@ TEST_F(PolicyEngineTest, PlaybackSuccess) {
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kSomeRandomKeyId));
}
TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
@@ -156,6 +212,8 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5));
ExpectSessionKeysChange(kKeyStatusExpired, false);
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
@@ -179,9 +237,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
.WillOnce(Return(kLicenseStartTime + min_duration));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(kSessionId));
EXPECT_CALL(check_, Call(2));
@@ -211,11 +271,13 @@ TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
.WillOnce(Return(playback_start_time + kPlaybackDuration + 2));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kHighDuration));
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, playback_start_time + kPlaybackDuration));
EXPECT_CALL(check_, Call(1));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(2));
@@ -244,9 +306,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
.WillOnce(Return(kLicenseStartTime + min_duration));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(2));
@@ -275,9 +339,11 @@ TEST_F(PolicyEngineTest, PlaybackFails_ExpiryBeforeRenewalDelay) {
.WillOnce(Return(kLicenseStartTime + min_duration));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(2));
@@ -309,6 +375,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -316,6 +383,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
EXPECT_CALL(check_, Call(2));
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(3));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(4));
@@ -348,6 +416,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
.WillOnce(Return(kLicenseStartTime + kHighDuration + 2));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kHighDuration));
EXPECT_CALL(check_, Call(1));
@@ -355,6 +424,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
EXPECT_CALL(check_, Call(2));
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(3));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(4));
@@ -385,9 +455,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
.WillOnce(Return(kLicenseStartTime + min_duration));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(2));
@@ -417,6 +489,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
.WillOnce(Return(kLicenseStartTime + kHighDuration - 1))
.WillOnce(Return(kLicenseStartTime + kHighDuration));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, kNoExpiration));
policy_engine_->SetLicense(license_);
@@ -437,8 +510,11 @@ TEST_F(PolicyEngineTest, PlaybackOk_LicenseWithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime))
.WillOnce(Return(kLicenseStartTime + 10));
InSequence s;
ExpectSessionKeysChange(kKeyStatusPending, false);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
ExpectSessionKeysChange(kKeyStatusUsable, true);
policy_engine_->SetLicense(license_);
@@ -466,10 +542,12 @@ TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
EXPECT_CALL(check_, Call(2));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(3));
@@ -502,6 +580,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
kLicenseRenewalRetryInterval + 10));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -550,14 +629,17 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess_WithFutureStartTime) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 60));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(2));
ExpectSessionKeysChange(kKeyStatusPending, false);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, new_license_start_time + kLowDuration));
EXPECT_CALL(check_, Call(3));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(check_, Call(4));
policy_engine_->SetLicense(license_);
@@ -598,6 +680,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 10));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -605,6 +688,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
EXPECT_CALL(check_, Call(2));
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(3));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(4));
@@ -652,6 +736,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 15));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -664,6 +749,7 @@ TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(6));
EXPECT_CALL(check_, Call(7));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(8));
@@ -702,6 +788,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
.WillOnce(Return(kLicenseStartTime + kStreamingLicenseDuration + 40));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -714,8 +801,10 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
EXPECT_CALL(mock_event_listener_, OnSessionRenewalNeeded(_));
EXPECT_CALL(check_, Call(6));
EXPECT_CALL(check_, Call(7));
ExpectSessionKeysChange(kKeyStatusExpired, false);
EXPECT_CALL(mock_event_listener_, OnSessionExpiration(_));
EXPECT_CALL(check_, Call(8));
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(
mock_event_listener_,
OnExpirationUpdate(_, new_license_start_time + new_license_duration));
@@ -767,6 +856,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
.WillOnce(Return(kLicenseStartTime + license_renewal_delay + 200));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -818,6 +908,7 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
.WillOnce(Return(kLicenseStartTime + 50));
InSequence s;
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kLicenseStartTime + kLowDuration));
EXPECT_CALL(check_, Call(1));
@@ -850,6 +941,87 @@ TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
}
TEST_F(PolicyEngineTest, MultipleKeysInLicense) {
const char kSigningKeyId[] = "signing_key";
license_.clear_key();
License::KeyContainer* content_key = license_.add_key();
content_key->set_type(License::KeyContainer::CONTENT);
content_key->set_id(kKeyId);
License::KeyContainer* non_content_key = license_.add_key();
non_content_key->set_type(License::KeyContainer::SIGNING);
non_content_key->set_id(kSigningKeyId);
License::KeyContainer* content_key_without_id = license_.add_key();
content_key_without_id->set_type(License::KeyContainer::CONTENT);
License::KeyContainer* another_content_key = license_.add_key();
another_content_key->set_type(License::KeyContainer::CONTENT);
another_content_key->set_id(kAnotherKeyId);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
policy_engine_->SetLicense(license_);
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kSigningKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kSomeRandomKeyId));
}
TEST_F(PolicyEngineTest, KeysOutputNotAllowedByMaxResEngine) {
license_.clear_key();
License::KeyContainer* content_key = license_.add_key();
content_key->set_type(License::KeyContainer::CONTENT);
content_key->set_id(kKeyId);
License::KeyContainer* another_content_key = license_.add_key();
another_content_key->set_type(License::KeyContainer::CONTENT);
another_content_key->set_id(kAnotherKeyId);
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kLicenseStartTime + 5))
.WillOnce(Return(kLicenseStartTime + 8))
.WillOnce(Return(kLicenseStartTime + 10));
InjectMockMaxResEngine();
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kKeyId))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_CALL(*mock_max_res_engine_, CanDecrypt(kAnotherKeyId))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(true));
InSequence s;
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed,
kKeyStatusOutputNotAllowed, false);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, _));
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusOutputNotAllowed, true);
ExpectSessionKeysChange(kKeyStatusOutputNotAllowed, kKeyStatusUsable, true);
ExpectSessionKeysChange(kKeyStatusUsable, kKeyStatusUsable, true);
policy_engine_->SetLicense(license_);
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecrypt(kAnotherKeyId));
policy_engine_->OnTimerEvent();
EXPECT_FALSE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecrypt(kKeyId));
EXPECT_TRUE(policy_engine_->CanDecrypt(kAnotherKeyId));
}
class PolicyEngineQueryTest : public PolicyEngineTest {
protected:
virtual void SetUp() {

View File

@@ -23,6 +23,12 @@
#include "wv_cdm_event_listener.h"
#include "wv_content_decryption_module.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::Each;
using ::testing::Field;
using ::testing::StrictMock;
namespace {
const char kPathDelimiter = '/';
@@ -444,6 +450,10 @@ class TestWvCdmEventListener : public WvCdmEventListener {
MOCK_METHOD1(OnSessionRenewalNeeded, void(const CdmSessionId& session_id));
MOCK_METHOD1(OnSessionExpiration, void(const CdmSessionId& session_id));
MOCK_METHOD3(OnSessionKeysChange,
void(const CdmSessionId& session_id,
const std::vector<CdmKeyInformation>& cdm_keys_info,
bool has_new_usable_key));
MOCK_METHOD2(OnExpirationUpdate,
void(const CdmSessionId& session_id, int64_t new_expiry_time));
};
@@ -1156,10 +1166,15 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
session_id_.clear();
key_set_id_.clear();
::testing::StrictMock<TestWvCdmEventListener> listener;
StrictMock<TestWvCdmEventListener> listener;
decryptor_.OpenSession(g_key_system, NULL, &listener, &session_id_);
CdmSessionId restore_session_id = session_id_;
EXPECT_CALL(listener, OnExpirationUpdate(restore_session_id, ::testing::_));
EXPECT_CALL(listener,
OnSessionKeysChange(restore_session_id,
Each(Field(&CdmKeyInformation::key_status,
kKeyStatusUsable)),
true));
EXPECT_CALL(listener, OnExpirationUpdate(restore_session_id, _));
EXPECT_EQ(wvcdm::KEY_ADDED,
decryptor_.RestoreKey(restore_session_id, key_set_id));
@@ -1167,7 +1182,12 @@ TEST_F(WvCdmRequestLicenseTest, ExpiryOnReleaseOfflineKeyTest) {
key_set_id_.clear();
// Maybe called since VerifyKeyRequestResponse could take some time.
EXPECT_CALL(listener, OnSessionRenewalNeeded(restore_session_id))
.Times(::testing::AtLeast(0));
.Times(AtLeast(0));
EXPECT_CALL(listener,
OnSessionKeysChange(restore_session_id,
Each(Field(&CdmKeyInformation::key_status,
kKeyStatusExpired)),
false));
EXPECT_CALL(listener, OnSessionExpiration(restore_session_id));
GenerateKeyRelease(key_set_id);
key_set_id_ = key_set_id;

View File

@@ -32,6 +32,7 @@ using android::status_t;
using android::String8;
using android::Vector;
using std::map;
using wvcdm::CdmKeyInformation;
using wvcdm::CdmSessionId;
using wvcdm::CdmResponseType;
using wvcdm::WvContentDecryptionModule;
@@ -143,6 +144,11 @@ class WVDrmPlugin : public android::DrmPlugin,
virtual void OnSessionExpiration(const CdmSessionId& cdmSessionId);
virtual void OnSessionKeysChange(
const CdmSessionId& session_id,
const std::vector<CdmKeyInformation>& cdm_keys_info,
bool has_new_usable_key);
virtual void OnExpirationUpdate(const CdmSessionId& cdmSessionId,
int64_t new_expiry_time);

View File

@@ -954,6 +954,13 @@ void WVDrmPlugin::OnSessionExpiration(const CdmSessionId& cdmSessionId) {
sendEvent(kDrmPluginEventKeyExpired, 0, &sessionId, NULL);
}
void WVDrmPlugin::OnSessionKeysChange(
const CdmSessionId& session_id,
const std::vector<CdmKeyInformation>& cdm_keys_info,
bool has_new_usable_key) {
// TODO(kqyang): Glue with DrmPlugin API when it is ready.
}
void WVDrmPlugin::OnExpirationUpdate(const CdmSessionId& cdmSessionId,
int64_t new_expiry_time) {
// TODO(kqyang): Glue with DrmPlugin API when it is ready. Note that