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:
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user