From 56bd5d5d82c300d08ab144eb40321633d8817aff Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Thu, 10 Oct 2013 22:56:57 -0700 Subject: [PATCH] Allows sharing of keys between sessions This change allows the app to specify that keys may be shared by sessions. The app enables this by setting the session sharing properties in DRM Plugin. Keys are shared only amoungst the specified group of sessions. Merged from widevine CDM repo * https://widevine-internal-review.googlesource.com/#/c/8019/ * https://widevine-internal-review.googlesource.com/#/c/8021/ Bug: 11013707 Change-Id: I52db41a53138b4fc563ebc6d38a623f23f7cdfb5 --- .../core/include/cdm_client_property_set.h | 3 + libwvdrmengine/cdm/core/include/cdm_engine.h | 1 + libwvdrmengine/cdm/core/include/license.h | 4 + libwvdrmengine/cdm/core/include/properties.h | 1 + libwvdrmengine/cdm/core/src/cdm_engine.cpp | 23 ++++ libwvdrmengine/cdm/core/src/cdm_session.cpp | 17 +-- libwvdrmengine/cdm/core/src/license.cpp | 23 +++- libwvdrmengine/cdm/core/src/properties.cpp | 11 ++ .../include/wv_content_decryption_module.h | 3 +- .../cdm/src/wv_content_decryption_module.cpp | 24 +++- .../cdm/test/request_license_test.cpp | 67 ++++++++++- libwvdrmengine/mediadrm/include/WVDrmPlugin.h | 36 ++++-- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 20 ++++ .../mediadrm/test/WVDrmPlugin_test.cpp | 109 +++++++++++++++++- 14 files changed, 308 insertions(+), 34 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h index 7945859f..40687f4a 100644 --- a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h +++ b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h @@ -16,6 +16,9 @@ class CdmClientPropertySet { virtual std::string security_level() const = 0; virtual bool use_privacy_mode() const = 0; virtual std::vector service_certificate() const = 0; + virtual bool is_session_sharing_enabled() const = 0; + virtual uint32_t session_sharing_id() const = 0; + virtual void set_session_sharing_id(uint32_t id) = 0; }; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index c7814751..ca5487b9 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -93,6 +93,7 @@ class CdmEngine : public TimerHandler { // Is the key known to any session? virtual bool IsKeyValid(const KeyId& key_id); + virtual bool FindSessionForKey(const KeyId& key_id, CdmSessionId* sessionId); // Event listener related methods virtual bool AttachEventListener(const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/core/include/license.h b/libwvdrmengine/cdm/core/include/license.h index d5990b6c..6e4534df 100644 --- a/libwvdrmengine/cdm/core/include/license.h +++ b/libwvdrmengine/cdm/core/include/license.h @@ -3,6 +3,8 @@ #ifndef CDM_BASE_LICENSE_H_ #define CDM_BASE_LICENSE_H_ +#include + #include "wv_cdm_types.h" namespace video_widevine_server { @@ -41,6 +43,7 @@ class CdmLicense { CdmKeyResponse& license_response, CdmKeyResponse& license_renewal_response); bool HasInitData() { return !init_data_.empty(); } + bool IsKeyLoaded(const KeyId& key_id); private: bool PrepareServiceCertificateRequest(CdmKeyMessage* signed_request, @@ -58,6 +61,7 @@ class CdmLicense { std::string service_certificate_; std::string init_data_; bool initialized_; + std::set loaded_keys_; // Used for certificate based licensing CdmKeyMessage key_request_; diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index 3a08055f..726491d6 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -66,6 +66,7 @@ class Properties { static const std::vector GetServiceCertificate( const CdmSessionId& session_id); static bool UsePrivacyMode(const CdmSessionId& session_id); + static uint32_t GetSessionSharingId(const CdmSessionId& session_id); static bool AddSessionPropertySet( const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 0089b969..935fa4ff 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -525,6 +525,29 @@ bool CdmEngine::IsKeyValid(const KeyId& key_id) { return false; } +bool CdmEngine::FindSessionForKey( + const KeyId& key_id, + CdmSessionId* session_id) { + if (NULL == session_id) { + LOGE("CdmEngine::FindSessionForKey: session id not provided"); + return false; + } + + uint32_t session_sharing_id = Properties::GetSessionSharingId(*session_id); + + for (CdmSessionMap::iterator iter = sessions_.begin(); + iter != sessions_.end(); ++iter) { + CdmSessionId id = iter->second->session_id(); + if (Properties::GetSessionSharingId(id) == session_sharing_id) { + if (iter->second->IsKeyValid(key_id)) { + *session_id = id; + return true; + } + } + } + return false; +} + bool CdmEngine::AttachEventListener( const CdmSessionId& session_id, WvCdmEventListener* listener) { diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 4ecce4a5..bb7acfc5 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -335,22 +335,7 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { } bool CdmSession::IsKeyValid(const KeyId& key_id) { - if (crypto_session_.get() == NULL || !crypto_session_->IsOpen()) - return false; - - if (key_id_ != key_id) { - // OEMCrypto does not provide a way to query the existence/validity of a - // key. SelectKey can be used to check whether a key is valid, but there - // is also a side effect - the key is selected for decryption, which might - // be undesirable and it posts restriction on the use of IsKeyValid API. - // TODO(kqyang, gmorgan): consider adding a function in OEMCrypto to check - // if a key is valid. - if (!crypto_session_->SelectKey(key_id)) { - return false; - } - key_id_ = key_id; - } - return true; + return license_parser_.IsKeyLoaded(key_id); } CdmSessionId CdmSession::GenerateSessionId() { diff --git a/libwvdrmengine/cdm/core/src/license.cpp b/libwvdrmengine/cdm/core/src/license.cpp index df1009fb..a0d99c75 100644 --- a/libwvdrmengine/cdm/core/src/license.cpp +++ b/libwvdrmengine/cdm/core/src/license.cpp @@ -539,9 +539,22 @@ CdmResponseType CdmLicense::HandleKeyResponse( // merge from Eureka) policy_engine_->SetLicense(license); - return session_->LoadKeys(signed_response.msg(), signed_response.signature(), - mac_key_iv, mac_key, key_array.size(), - &key_array[0]); + CdmResponseType resp = session_->LoadKeys(signed_response.msg(), + signed_response.signature(), + mac_key_iv, + mac_key, + key_array.size(), + &key_array[0]); + + if (KEY_ADDED == resp) { + loaded_keys_.clear(); + for (std::vector::iterator it = key_array.begin(); + it != key_array.end(); + ++it) { + loaded_keys_.insert(it->key_id()); + } + } + return resp; } CdmResponseType CdmLicense::HandleKeyUpdateResponse( @@ -753,4 +766,8 @@ CdmResponseType CdmLicense::HandleKeyErrorResponse( } } +bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { + return loaded_keys_.find(key_id) != loaded_keys_.end(); +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index 352c0951..14cfc8b2 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -100,6 +100,17 @@ bool Properties::UsePrivacyMode(const CdmSessionId& session_id) { return property_set->use_privacy_mode(); } +uint32_t Properties::GetSessionSharingId(const CdmSessionId& session_id) { + const CdmClientPropertySet* property_set = + GetCdmClientPropertySet(session_id); + if (NULL == property_set) { + LOGE("Properties::GetSessionSharingId: cannot find property set for %s", + session_id.c_str()); + return 0; + } + return property_set->session_sharing_id(); +} + bool Properties::GetSecurityLevelDirectories(std::vector* dirs) { dirs->resize(sizeof(kSecurityLevelDirs)/sizeof(const char*)); for (size_t i = 0; i < dirs->size(); ++i) { diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index d89c4c98..0613e507 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -21,7 +21,7 @@ class WvContentDecryptionModule { // Session related methods virtual CdmResponseType OpenSession( const CdmKeySystem& key_system, - const CdmClientPropertySet* property_set, + CdmClientPropertySet* property_set, CdmSessionId* session_id); virtual CdmResponseType CloseSession(const CdmSessionId& session_id); @@ -84,6 +84,7 @@ class WvContentDecryptionModule { WvCdmEventListener* listener); private: + uint32_t GenerateSessionSharingId(); // instance variables UniquePtr cdm_engine_; diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index ae8a1ada..258d75ac 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -4,8 +4,10 @@ #include +#include "cdm_client_property_set.h" #include "cdm_engine.h" #include "log.h" +#include "properties.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" @@ -18,8 +20,13 @@ WvContentDecryptionModule::~WvContentDecryptionModule() {} CdmResponseType WvContentDecryptionModule::OpenSession( const CdmKeySystem& key_system, - const CdmClientPropertySet* property_set, + CdmClientPropertySet* property_set, CdmSessionId* session_id) { + if (property_set && property_set->is_session_sharing_enabled()) { + if (property_set->session_sharing_id() == 0) + property_set->set_session_sharing_id(GenerateSessionSharingId()); + } + return cdm_engine_->OpenSession(key_system, property_set, session_id); } @@ -111,7 +118,15 @@ CdmResponseType WvContentDecryptionModule::ReleaseSecureStops( CdmResponseType WvContentDecryptionModule::Decrypt( const CdmSessionId& session_id, const CdmDecryptionParameters& parameters) { - return cdm_engine_->Decrypt(session_id, parameters); + CdmSessionId id = session_id; + if (Properties::GetSessionSharingId(session_id) != 0) { + bool status = cdm_engine_->FindSessionForKey(*parameters.key_id, &id); + if (!status) { + LOGE("WvContentDecryptionModule::Decrypt: unable to find session"); + return KEY_ERROR; + } + } + return cdm_engine_->Decrypt(id, parameters); } bool WvContentDecryptionModule::AttachEventListener( @@ -124,4 +139,9 @@ bool WvContentDecryptionModule::DetachEventListener( return cdm_engine_->DetachEventListener(session_id, listener); } +uint32_t WvContentDecryptionModule::GenerateSessionSharingId() { + static int next_session_sharing_id = 0; + return ++next_session_sharing_id; +} + } // namespace wvcdm diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 22025d60..e028249e 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -205,7 +205,10 @@ SubSampleInfo partial_offset_single_encrypted_sub_sample = { namespace wvcdm { class TestWvCdmClientPropertySet : public CdmClientPropertySet { public: - TestWvCdmClientPropertySet() : use_privacy_mode_(false) {} + TestWvCdmClientPropertySet() + : use_privacy_mode_(false), + is_session_sharing_enabled_(false), + session_sharing_id_(0) {} virtual ~TestWvCdmClientPropertySet() {} virtual std::string security_level() const { return security_level_; } @@ -213,6 +216,10 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { return service_certificate_; } virtual bool use_privacy_mode() const { return use_privacy_mode_; } + bool is_session_sharing_enabled() const { + return is_session_sharing_enabled_; + } + uint32_t session_sharing_id() const { return session_sharing_id_; } void set_security_level(const std::string& security_level) { if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || @@ -227,11 +234,17 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { void set_use_privacy_mode(bool use_privacy_mode) { use_privacy_mode_ = use_privacy_mode; } + void set_session_sharing_mode(bool enable) { + is_session_sharing_enabled_ = enable; + } + void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } private: std::string security_level_; std::vector service_certificate_; bool use_privacy_mode_; + bool is_session_sharing_enabled_; + uint32_t session_sharing_id_; }; class TestWvCdmEventListener : public WvCdmEventListener { @@ -377,6 +390,10 @@ class WvCdmDecryptionTest : public WvCdmRequestLicenseTest, public ::testing::WithParamInterface {}; +class WvCdmSessionSharingTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + TEST_F(WvCdmRequestLicenseTest, ProvisioningTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); std::string provisioning_server_url; @@ -523,6 +540,54 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_PrivacyModeWithServiceCertificateTest) decryptor_.CloseSession(session_id_); } +TEST_P(WvCdmSessionSharingTest, SessionSharingTest) { + bool enable_session_sharing = GetParam(); + + TestWvCdmClientPropertySet property_set; + property_set.set_session_sharing_mode(enable_session_sharing); + + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + CdmSessionId gp_session_id_1 = session_id_; + GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, g_client_auth, g_key_id, false); + + // TODO(rfrias): Move content information to ConfigTestEnv + std::string gp_client_auth2 = + "?source=YOUTUBE&video_id=z3S_NhwueaM&oauth=ya.gtsqawidevine"; + std::string gp_key_id2 = + wvcdm::a2bs_hex( + "000000347073736800000000" // blob size and pssh + "edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id + "08011210bdf1cb4fffc6506b8b7945b0bd2917fb"); // pssh data + + decryptor_.OpenSession(g_key_system, &property_set, &session_id_); + CdmSessionId gp_session_id_2 = session_id_; + GenerateKeyRequest(g_key_system, gp_key_id2, kLicenseTypeStreaming); + VerifyKeyRequestResponse(g_license_server, gp_client_auth2, gp_key_id2, false); + + SubSampleInfo* data = &single_encrypted_sub_sample; + std::vector decrypt_buffer(data->encrypt_data.size()); + CdmDecryptionParameters decryption_parameters(&data->key_id, + &data->encrypt_data.front(), + data->encrypt_data.size(), + &data->iv, + data->block_offset, + &decrypt_buffer[0]); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + EXPECT_EQ( + NO_ERROR == decryptor_.Decrypt(gp_session_id_2, decryption_parameters), + enable_session_sharing); + EXPECT_EQ(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin()), + enable_session_sharing); + + decryptor_.CloseSession(gp_session_id_1); + decryptor_.CloseSession(gp_session_id_2); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest, ::testing::Bool()); + TEST_F(WvCdmRequestLicenseTest, BaseMessageTest) { decryptor_.OpenSession(g_key_system, NULL, &session_id_); GenerateKeyRequest(g_key_system, g_key_id, kLicenseTypeStreaming); diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 292d6153..e33350fc 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -161,40 +161,58 @@ class WVDrmPlugin : public android::DrmPlugin, class WVClientPropertySet : public wvcdm::CdmClientPropertySet { public: WVClientPropertySet() - : mUsePrivacyMode(false) {} + : mUsePrivacyMode(false), mShareKeys(false), mSessionSharingId(0) {} virtual ~WVClientPropertySet() {} - void set_security_level(const std::string& securityLevel) { - mSecurityLevel = securityLevel; - } - virtual std::string security_level() const { return mSecurityLevel; } - void set_use_privacy_mode(bool usePrivacyMode) { - mUsePrivacyMode = usePrivacyMode; + void set_security_level(const std::string& securityLevel) { + mSecurityLevel = securityLevel; } virtual bool use_privacy_mode() const { return mUsePrivacyMode; } - void set_service_certificate(const std::vector& serviceCertificate) { - mServiceCertificate = serviceCertificate; + void set_use_privacy_mode(bool usePrivacyMode) { + mUsePrivacyMode = usePrivacyMode; } virtual std::vector service_certificate() const { return mServiceCertificate; } + void set_service_certificate(const std::vector& serviceCertificate) { + mServiceCertificate = serviceCertificate; + } + + virtual bool is_session_sharing_enabled() const { + return mShareKeys; + } + + void set_is_session_sharing_enabled(bool shareKeys) { + mShareKeys = shareKeys; + } + + virtual uint32_t session_sharing_id() const { + return mSessionSharingId; + } + + virtual void set_session_sharing_id(uint32_t id) { + mSessionSharingId = id; + } + private: DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet); std::string mSecurityLevel; bool mUsePrivacyMode; std::vector mServiceCertificate; + bool mShareKeys; + uint32_t mSessionSharingId; } mPropertySet; WvContentDecryptionModule* mCDM; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index bf09a930..83ea7855 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -392,6 +392,12 @@ status_t WVDrmPlugin::getPropertyString(const String8& name, } else { value = kDisable; } + } else if (name == "sessionSharing") { + if (mPropertySet.is_session_sharing_enabled()) { + value = kEnable; + } else { + value = kDisable; + } } else { ALOGE("App requested unknown string property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; @@ -475,6 +481,20 @@ status_t WVDrmPlugin::setPropertyString(const String8& name, ALOGE("App requested unknown privacy mode %s", value.string()); return android::BAD_VALUE; } + } else if (name == "sessionSharing") { + if (mCryptoSessions.size() == 0) { + if (value == kEnable) { + mPropertySet.set_is_session_sharing_enabled(true); + } else if (value == kDisable) { + mPropertySet.set_is_session_sharing_enabled(false); + } else { + ALOGE("App requested unknown sharing type %s", value.string()); + return android::BAD_VALUE; + } + } else { + ALOGE("App tried to change key sharing while sessions are open."); + return kErrorSessionIsOpen; + } } else { ALOGE("App set unknown string property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index e42a1ffb..ac2efb23 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -26,7 +26,7 @@ using namespace wvdrm; class MockCDM : public WvContentDecryptionModule { public: MOCK_METHOD3(OpenSession, CdmResponseType(const CdmKeySystem&, - const CdmClientPropertySet*, + CdmClientPropertySet*, CdmSessionId*)); MOCK_METHOD1(CloseSession, CdmResponseType(const CdmSessionId&)); @@ -1181,7 +1181,6 @@ TEST_F(WVDrmPluginTest, GeneratesProvisioningNeededEvent) { ASSERT_EQ(ERROR_DRM_NOT_PROVISIONED, res); } - TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) { StrictMock cdm; StrictMock crypto; @@ -1219,6 +1218,8 @@ TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) { EXPECT_STREQ("", propertySet->security_level().c_str()); EXPECT_FALSE(propertySet->use_privacy_mode()); EXPECT_EQ(0u, propertySet->service_certificate().size()); + EXPECT_FALSE(propertySet->is_session_sharing_enabled()); + EXPECT_EQ(0u, propertySet->session_sharing_id()); } TEST_F(WVDrmPluginTest, CanSetSecurityLevel) { @@ -1395,3 +1396,107 @@ TEST_F(WVDrmPluginTest, CanSetServiceCertificate) { ASSERT_EQ(OK, res); EXPECT_EQ(0u, propertySet->service_certificate().size()); } + +TEST_F(WVDrmPluginTest, CanSetSessionSharing) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + const CdmClientPropertySet* propertySet = NULL; + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + status_t res; + + // Test turning on session sharing + res = plugin.setPropertyString(String8("sessionSharing"), String8("enable")); + ASSERT_EQ(OK, res); + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + EXPECT_TRUE(propertySet->is_session_sharing_enabled()); + plugin.closeSession(sessionId); + + // Test turning off session sharing + res = plugin.setPropertyString(String8("sessionSharing"), String8("disable")); + ASSERT_EQ(OK, res); + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + EXPECT_FALSE(propertySet->is_session_sharing_enabled()); + plugin.closeSession(sessionId); + + // Test nonsense (Should Fail) + res = plugin.setPropertyString(String8("sessionSharing"), String8("nonsense")); + ASSERT_NE(OK, res); + + // Test changing sharing with a session open (Should Fail) + plugin.openSession(sessionId); + res = plugin.setPropertyString(String8("sessionSharing"), String8("enable")); + ASSERT_NE(OK, res); +} + +TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + CdmClientPropertySet* propertySet = NULL; + + uint32_t sharingId; + FILE* fp = fopen("/dev/urandom", "r"); + fread(&sharingId, sizeof(uint32_t), 1, fp); + fclose(fp); + + // Provide expected mock behavior + { + // Provide expected behavior in response to OpenSession and store the + // property set + EXPECT_CALL(cdm, OpenSession(_, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(cdmSessionId), + SaveArg<1>(&propertySet), + Return(wvcdm::NO_ERROR))); + + // Provide expected behavior when plugin requests session control info + EXPECT_CALL(cdm, QueryKeyControlInfo(cdmSessionId, _)) + .WillRepeatedly(Invoke(setSessionIdOnMap<4>)); + + // Let gMock know these calls will happen but we aren't interested in them. + EXPECT_CALL(cdm, AttachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, DetachEventListener(_, _)) + .Times(AtLeast(0)); + + EXPECT_CALL(cdm, CloseSession(_)) + .Times(AtLeast(0)); + } + + plugin.openSession(sessionId); + + ASSERT_THAT(propertySet, NotNull()); + propertySet->set_session_sharing_id(sharingId); + EXPECT_EQ(sharingId, propertySet->session_sharing_id()); +}