diff --git a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h index 853c7658..ab995359 100644 --- a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h +++ b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h @@ -19,6 +19,7 @@ class CdmClientPropertySet { 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; + virtual const std::string& app_id() const = 0; }; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index da61b878..226140f1 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -93,7 +93,16 @@ class CdmEngine { virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); // Usage related methods for streaming licenses - virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); + // Retrieve a random usage info from the list of all usage infos for this app + // id. + virtual CdmResponseType GetUsageInfo(const std::string& app_id, + CdmUsageInfo* usage_info); + // Retrieve the usage info for the specified pst. + // Returns UNKNOWN_ERROR if no usage info was found. + virtual CdmResponseType GetUsageInfo(const std::string& app_id, + const CdmSecureStopId& ssid, + CdmUsageInfo* usage_info); + virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id); virtual CdmResponseType ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message); @@ -120,7 +129,8 @@ class CdmEngine { private: // private methods bool ValidateKeySystem(const CdmKeySystem& key_system); - CdmResponseType GetUsageInfo(SecurityLevel requested_security_level, + CdmResponseType GetUsageInfo(const std::string& app_id, + SecurityLevel requested_security_level, CdmUsageInfo* usage_info); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 2fb203f1..f6b47340 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -83,9 +83,11 @@ class CdmSession { virtual void OnTimerEvent(bool update_usage); virtual void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); + virtual void GetApplicationId(std::string* app_id); virtual SecurityLevel GetRequestedSecurityLevel(); virtual CdmSecurityLevel GetSecurityLevel(); + virtual CdmResponseType DeleteUsageInformation(const std::string& app_id); virtual CdmResponseType UpdateUsageInformation(); virtual bool is_initial_usage_update() { return is_initial_usage_update_; } @@ -120,6 +122,7 @@ class CdmSession { bool is_offline_; bool is_release_; CdmSecurityLevel security_level_; + std::string app_id_; // decryption and usage flags bool is_initial_decryption_; diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index 8e53fee5..ac225651 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -62,11 +62,22 @@ class DeviceFiles { virtual bool StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, - const CdmKeyResponse& key_response); - virtual bool DeleteUsageInfo(const std::string& provider_session_token); - virtual bool DeleteUsageInfo(); + const CdmKeyResponse& key_response, + const std::string& app_id); + virtual bool DeleteUsageInfo(const std::string& app_id, + const std::string& provider_session_token); + virtual bool DeleteAllUsageInfoForApp(const std::string& app_id); + // Retrieve one usage info from the file. Subsequent calls will retrieve + // subsequent entries in the table for this app_id. virtual bool RetrieveUsageInfo( + const std::string& app_id, std::vector >* usage_info); + // Retrieve the usage info entry specified by |provider_session_token|. + // Returns false if the entry could not be found. + virtual bool RetrieveUsageInfo(const std::string& app_id, + const std::string& provider_session_token, + CdmKeyMessage* license_request, + CdmKeyResponse* license_response); private: bool StoreFile(const char* name, const std::string& serialized_file); @@ -80,7 +91,7 @@ class DeviceFiles { // For testing only: static std::string GetCertificateFileName(); static std::string GetLicenseFileNameExtension(); - static std::string GetUsageInfoFileName(); + static std::string GetUsageInfoFileName(const std::string& app_id); void SetTestFile(File* file); #if defined(UNIT_TEST) FRIEND_TEST(DeviceFilesSecurityLevelTest, SecurityLevel); diff --git a/libwvdrmengine/cdm/core/include/properties.h b/libwvdrmengine/cdm/core/include/properties.h index cb0709cb..a34dcde2 100644 --- a/libwvdrmengine/cdm/core/include/properties.h +++ b/libwvdrmengine/cdm/core/include/properties.h @@ -53,6 +53,8 @@ class Properties { static bool GetFactoryKeyboxPath(std::string* keybox); static bool GetOEMCryptoPath(std::string* library_name); static bool GetSecurityLevelDirectories(std::vector* dirs); + static bool GetApplicationId(const CdmSessionId& session_id, + std::string* app_id); static bool GetSecurityLevel(const CdmSessionId& session_id, std::string* security_level); static bool GetServiceCertificate(const CdmSessionId& session_id, diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index fea94185..daf97151 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -15,6 +15,7 @@ typedef std::string CdmInitData; typedef std::string CdmKeyMessage; typedef std::string CdmKeyResponse; typedef std::string KeyId; +typedef std::string CdmSecureStopId; typedef std::string CdmSessionId; typedef std::string CdmKeySetId; typedef std::string RequestId; diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index c3a866e3..d133610a 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -43,7 +43,11 @@ class UsagePropertySet : public CdmClientPropertySet { virtual void set_session_sharing_id(uint32_t id) { id; // noop to suppress warning } + virtual const std::string& app_id() const { return app_id_; } + void set_app_id(const std::string& appId) { app_id_ = appId; } + private: + std::string app_id_; std::string security_level_; const std::string empty_; }; @@ -582,29 +586,95 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { return status; } -CdmResponseType CdmEngine::GetUsageInfo(CdmUsageInfo* usage_info) { +CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, + const CdmSecureStopId& ssid, + CdmUsageInfo* usage_info) { + if (NULL == usage_property_set_.get()) { + usage_property_set_.reset(new UsagePropertySet()); + } + usage_property_set_->set_security_level(kLevelDefault); + usage_property_set_->set_app_id(app_id); + usage_session_.reset(new CdmSession(usage_property_set_.get())); + CdmResponseType status = usage_session_->Init(); + if (NO_ERROR != status) { + LOGE("CdmEngine::GetUsageInfo: session init error"); + return status; + } + DeviceFiles handle; + if (!handle.Init(usage_session_->GetSecurityLevel())) { + LOGE("CdmEngine::GetUsageInfo: device file init error"); + return UNKNOWN_ERROR; + } + + CdmKeyMessage license_request; + CdmKeyResponse license_response; + if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request, + &license_response)) { + usage_property_set_->set_security_level(kLevel3); + usage_property_set_->set_app_id(app_id); + usage_session_.reset(new CdmSession(usage_property_set_.get())); + status = usage_session_->Init(); + if (NO_ERROR != status) { + LOGE("CdmEngine::GetUsageInfo: session init error"); + return status; + } + if (!handle.Reset(usage_session_->GetSecurityLevel())) { + LOGE("CdmEngine::GetUsageInfo: device file init error"); + return UNKNOWN_ERROR; + } + if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request, + &license_response)) { + // No entry found for that ssid. + return UNKNOWN_ERROR; + } + } + + std::string server_url; + usage_info->resize(1); + status = + usage_session_->RestoreUsageSession(license_request, license_response); + if (KEY_ADDED != status) { + LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status); + usage_info->clear(); + return status; + } + + status = + usage_session_->GenerateReleaseRequest(&(*usage_info)[0], &server_url); + + if (KEY_MESSAGE != status) { + LOGE("CdmEngine::GetUsageInfo: generate release request error: %d", status); + usage_info->clear(); + return status; + } + return KEY_MESSAGE; +} + +CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, + CdmUsageInfo* usage_info) { // Return a random usage report from a random security level SecurityLevel security_level = ((rand() % 2) == 0) ? kLevelDefault : kLevel3; - CdmResponseType status = GetUsageInfo(security_level, usage_info); + CdmResponseType status = GetUsageInfo(app_id, security_level, usage_info); if (KEY_MESSAGE == status && !usage_info->empty()) return status; security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3; - status = GetUsageInfo(security_level, usage_info); + status = GetUsageInfo(app_id, security_level, usage_info); if (NEED_PROVISIONING == status) return NO_ERROR; // Valid scenario that one of the security // levels has not been provisioned return status; } -CdmResponseType CdmEngine::GetUsageInfo( - SecurityLevel requested_security_level, - CdmUsageInfo* usage_info) { +CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, + SecurityLevel requested_security_level, + CdmUsageInfo* usage_info) { if (NULL == usage_property_set_.get()) { usage_property_set_.reset(new UsagePropertySet()); } usage_property_set_->set_security_level(requested_security_level); + usage_property_set_->set_app_id(app_id); usage_session_.reset(new CdmSession(usage_property_set_.get())); @@ -617,11 +687,11 @@ CdmResponseType CdmEngine::GetUsageInfo( DeviceFiles handle; if (!handle.Init(usage_session_->GetSecurityLevel())) { LOGE("CdmEngine::GetUsageInfo: unable to initialize device files"); - return status; + return UNKNOWN_ERROR; } std::vector > license_info; - if (!handle.RetrieveUsageInfo(&license_info)) { + if (!handle.RetrieveUsageInfo(app_id, &license_info)) { LOGE("CdmEngine::GetUsageInfo: unable to read usage information"); return UNKNOWN_ERROR; } @@ -655,6 +725,23 @@ CdmResponseType CdmEngine::GetUsageInfo( return KEY_MESSAGE; } +CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { + CdmResponseType status = NO_ERROR; + DeviceFiles handle[kSecurityLevelUnknown - kSecurityLevelL1]; + for (int i = 0, j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++i, ++j) { + if (handle[i].Init(static_cast(j))) { + if (!handle[i].DeleteAllUsageInfoForApp(app_id)) { + LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure stops", j); + status = UNKNOWN_ERROR; + } + } else { + LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device files", j); + status = UNKNOWN_ERROR; + } + } + return status; +} + CdmResponseType CdmEngine::ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message) { if (NULL == usage_session_.get()) { diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 95c432cf..de845f35 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -81,6 +81,7 @@ void CdmSession::Create( } security_level_ = GetRequestedSecurityLevel() == kLevel3 ? kSecurityLevelL3 : GetSecurityLevel(); + app_id_.clear(); } CdmSession::~CdmSession() { Properties::RemoveSessionPropertySet(session_id_); } @@ -503,8 +504,10 @@ CdmResponseType CdmSession::StoreLicense() { return UNKNOWN_ERROR; } + std::string app_id; + GetApplicationId(&app_id); if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_, - key_response_)) { + key_response_, app_id)) { LOGE("CdmSession::StoreLicense: Unable to store usage info"); return UNKNOWN_ERROR; } @@ -532,11 +535,14 @@ bool CdmSession::DeleteLicense() { return false; } - if (is_offline_) + if (is_offline_) { return file_handle_->DeleteLicense(key_set_id_); - else + } else { + std::string app_id; + GetApplicationId(&app_id); return file_handle_->DeleteUsageInfo( - license_parser_->provider_session_token()); + app_id, license_parser_->provider_session_token()); + } } bool CdmSession::AttachEventListener(WvCdmEventListener* listener) { @@ -579,6 +585,12 @@ void CdmSession::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { } } +void CdmSession::GetApplicationId(std::string* app_id) { + if (app_id && !Properties::GetApplicationId(session_id_, app_id)) { + *app_id = ""; + } +} + SecurityLevel CdmSession::GetRequestedSecurityLevel() { std::string security_level; if (Properties::GetSecurityLevel(session_id_, &security_level) && @@ -596,6 +608,20 @@ CdmSecurityLevel CdmSession::GetSecurityLevel() { return crypto_session_.get()->GetSecurityLevel(); } +CdmResponseType CdmSession::DeleteUsageInformation(const std::string& app_id) { + if (!file_handle_->Reset(security_level_)) { + LOGE("CdmSession::StoreLicense: Unable to initialize device files"); + return UNKNOWN_ERROR; + } + + if (file_handle_->DeleteAllUsageInfoForApp(app_id)) { + return NO_ERROR; + } else { + LOGE("CdmSession::DeleteUsageInformation: failed"); + return UNKNOWN_ERROR; + } +} + CdmResponseType CdmSession::UpdateUsageInformation() { return crypto_session_->UpdateUsageInformation(); } diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index 19fd57b6..92869026 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -8,6 +8,7 @@ # define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH #else # include +#include #endif #include @@ -31,7 +32,8 @@ using video_widevine_client::sdk::UsageInfo_ProviderSession; namespace { const char kCertificateFileName[] = "cert.bin"; -const char kUsageInfoFileName[] = "usage.bin"; +const char kUsageInfoFileNamePrefix[] = "usage"; +const char kUsageInfoFileNameExt[] = ".bin"; const char kLicenseFileNameExt[] = ".lic"; const char kWildcard[] = "*"; const char kDirectoryDelimiter = '/'; @@ -329,7 +331,8 @@ bool DeviceFiles::LicenseExists(const std::string& key_set_id) { bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, - const CdmKeyResponse& key_response) { + const CdmKeyResponse& key_response, + const std::string& app_id) { if (!initialized_) { LOGW("DeviceFiles::StoreUsageInfo: not initialized"); return false; @@ -337,7 +340,8 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, std::string serialized_file; video_widevine_client::sdk::File file; - if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) { + std::string file_name = GetUsageInfoFileName(app_id); + if (!RetrieveFile(file_name.c_str(), &serialized_file)) { file.set_type(video_widevine_client::sdk::File::USAGE_INFO); file.set_version(video_widevine_client::sdk::File::VERSION_1); } else { @@ -358,17 +362,19 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, key_response.size()); file.SerializeToString(&serialized_file); - return StoreFile(kUsageInfoFileName, serialized_file); + return StoreFile(file_name.c_str(), serialized_file); } -bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) { +bool DeviceFiles::DeleteUsageInfo(const std::string& app_id, + const std::string& provider_session_token) { if (!initialized_) { LOGW("DeviceFiles::DeleteUsageInfo: not initialized"); return false; } std::string serialized_file; - if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) return false; + std::string file_name = GetUsageInfoFileName(app_id); + if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false; video_widevine_client::sdk::File file; if (!file.ParseFromString(serialized_file)) { @@ -400,27 +406,29 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& provider_session_token) { sessions->RemoveLast(); file.SerializeToString(&serialized_file); - return StoreFile(kUsageInfoFileName, serialized_file); + return StoreFile(file_name.c_str(), serialized_file); } -bool DeviceFiles::DeleteUsageInfo() { +bool DeviceFiles::DeleteAllUsageInfoForApp(const std::string& app_id) { if (!initialized_) { - LOGW("DeviceFiles::DeleteUsageInfo: not initialized"); + LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized"); return false; } std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW("DeviceFiles::DeleteUsageInfo: Unable to get base path"); + LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to get base path"); return false; } - path.append(kUsageInfoFileName); + std::string file_name = GetUsageInfoFileName(app_id); + path.append(file_name); return file_->Remove(path); } -bool DeviceFiles::RetrieveUsageInfo(std::vector< - std::pair >* usage_info) { +bool DeviceFiles::RetrieveUsageInfo( + const std::string &app_id, + std::vector >* usage_info) { if (!initialized_) { LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); return false; @@ -433,13 +441,14 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector< } std::string serialized_file; - if (!RetrieveFile(kUsageInfoFileName, &serialized_file)) { + std::string file_name = GetUsageInfoFileName(app_id); + if (!RetrieveFile(file_name.c_str(), &serialized_file)) { std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { return false; } - path += kUsageInfoFileName; + path += file_name; if (!file_->Exists(path) || 0 == file_->FileSize(path)) { usage_info->resize(0); @@ -465,6 +474,42 @@ bool DeviceFiles::RetrieveUsageInfo(std::vector< return true; } +bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id, + const std::string& provider_session_token, + CdmKeyMessage* license_request, + CdmKeyResponse* license_response) { + if (!initialized_) { + LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); + return false; + } + std::string serialized_file; + std::string file_name = GetUsageInfoFileName(app_id); + if (!RetrieveFile(file_name.c_str(), &serialized_file)) return false; + + video_widevine_client::sdk::File file; + if (!file.ParseFromString(serialized_file)) { + LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file"); + return false; + } + int index = 0; + bool found = false; + for (; index < file.usage_info().sessions_size(); ++index) { + if (file.usage_info().sessions(index).token().compare( + provider_session_token) == 0) { + found = true; + break; + } + } + + if (!found) { + return false; + } + + *license_request = file.usage_info().sessions(index).license_request(); + *license_response = file.usage_info().sessions(index).license(); + return true; +} + bool DeviceFiles::StoreFile(const char* name, const std::string& serialized_file) { if (!file_.get()) { @@ -671,8 +716,15 @@ std::string DeviceFiles::GetLicenseFileNameExtension() { return kLicenseFileNameExt; } -std::string DeviceFiles::GetUsageInfoFileName() { - return kUsageInfoFileName; +std::string DeviceFiles::GetUsageInfoFileName(const std::string& app_id) { + if (app_id == "") return std::string(kUsageInfoFileNamePrefix) + + std::string(kUsageInfoFileNameExt); + std::vector hash(MD5_DIGEST_LENGTH); + const unsigned char* input = + reinterpret_cast(app_id.data()); + MD5(input, app_id.size(), &hash[0]); + return std::string(kUsageInfoFileNamePrefix) + wvcdm::Base64SafeEncode(hash) + + std::string(kUsageInfoFileNameExt); } void DeviceFiles::SetTestFile(File* file) { diff --git a/libwvdrmengine/cdm/core/src/properties.cpp b/libwvdrmengine/cdm/core/src/properties.cpp index 451cdc9d..3269423c 100644 --- a/libwvdrmengine/cdm/core/src/properties.cpp +++ b/libwvdrmengine/cdm/core/src/properties.cpp @@ -60,6 +60,17 @@ const CdmClientPropertySet* Properties::GetCdmClientPropertySet( return NULL; } +bool Properties::GetApplicationId(const CdmSessionId& session_id, + std::string* app_id) { + const CdmClientPropertySet* property_set = + GetCdmClientPropertySet(session_id); + if (NULL == property_set) { + return false; + } + *app_id = property_set->app_id(); + return true; +} + bool Properties::GetSecurityLevel(const CdmSessionId& session_id, std::string* security_level) { const CdmClientPropertySet* property_set = diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 61cb328b..4bbb1968 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -1747,7 +1747,8 @@ TEST_F(DeviceFilesTest, DeleteLicense) { TEST_P(DeviceFilesUsageInfoTest, Read) { MockFile file; - std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(); + std::string app_id; // TODO(fredgc): expand tests. + std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); std::string data; @@ -1777,7 +1778,7 @@ TEST_P(DeviceFilesUsageInfoTest, Read) { device_files.SetTestFile(&file); std::vector > license_info; - ASSERT_TRUE(device_files.RetrieveUsageInfo(&license_info)); + ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info)); if (index >= 0) { EXPECT_EQ(index, license_info.size()); for (size_t i = 0; i < license_info.size(); ++i) { @@ -1799,10 +1800,11 @@ TEST_P(DeviceFilesUsageInfoTest, Read) { TEST_P(DeviceFilesUsageInfoTest, Store) { MockFile file; + std::string app_id; // TODO(fredgc): expand tests. std::string pst(GenerateRandomData(kProviderSessionTokenLen)); std::string license_request(GenerateRandomData(kLicenseRequestLen)); std::string license(GenerateRandomData(kLicenseLen)); - std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(); + std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); std::string data; @@ -1836,12 +1838,13 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); device_files.SetTestFile(&file); - ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license)); + ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license, app_id)); } TEST_P(DeviceFilesUsageInfoTest, Delete) { MockFile file; - std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(); + std::string app_id; // TODO(fredgc): expand tests. + std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); if (index < 0) return; @@ -1886,9 +1889,9 @@ TEST_P(DeviceFilesUsageInfoTest, Delete) { device_files.SetTestFile(&file); if (index >= 1) { - ASSERT_TRUE(device_files.DeleteUsageInfo(pst)); + ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst)); } else { - ASSERT_FALSE(device_files.DeleteUsageInfo(pst)); + ASSERT_FALSE(device_files.DeleteUsageInfo(app_id, pst)); } } diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index c64d156a..0b246c8e 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -85,7 +85,12 @@ class WvContentDecryptionModule : public TimerHandler { virtual CdmResponseType Unprovision(CdmSecurityLevel level); // Secure stop related methods - virtual CdmResponseType GetUsageInfo(CdmUsageInfo* usage_info); + virtual CdmResponseType GetUsageInfo(const std::string& app_id, + CdmUsageInfo* usage_info); + virtual CdmResponseType GetUsageInfo(const std::string& app_id, + const CdmSecureStopId& ssid, + CdmUsageInfo* usage_info); + virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id); virtual CdmResponseType ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message); diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 2c07020c..2c2e4a2f 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -159,8 +159,20 @@ CdmResponseType WvContentDecryptionModule::Unprovision( } CdmResponseType WvContentDecryptionModule::GetUsageInfo( + const std::string& app_id, CdmUsageInfo* usage_info) { + return cdm_engine_->GetUsageInfo(app_id, usage_info); +} + +CdmResponseType WvContentDecryptionModule::GetUsageInfo( + const std::string& app_id, + const CdmSecureStopId& ssid, CdmUsageInfo* usage_info) { - return cdm_engine_->GetUsageInfo(usage_info); + return cdm_engine_->GetUsageInfo(app_id, ssid, usage_info); +} + +CdmResponseType WvContentDecryptionModule::ReleaseAllUsageInfo( + const std::string& app_id) { + return cdm_engine_->ReleaseAllUsageInfo(app_id); } CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo( diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index b95f10ae..20fc230f 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -1022,7 +1022,8 @@ TEST_P(WvCdmStreamingUsageReportTest, UsageTest) { uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; - CdmResponseType status = decryptor_.GetUsageInfo(&usage_info); + std::string app_id; + CdmResponseType status = decryptor_.GetUsageInfo(app_id, &usage_info); EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); while (usage_info.size() > 0) { for (size_t i = 0; i < usage_info.size(); ++i) { @@ -1034,7 +1035,7 @@ TEST_P(WvCdmStreamingUsageReportTest, UsageTest) { GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]); EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); } - status = decryptor_.GetUsageInfo(&usage_info); + status = decryptor_.GetUsageInfo(app_id, &usage_info); switch (status) { case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 8a90dd9f..6322b99b 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -393,6 +393,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { session_sharing_id_(0) {} virtual ~TestWvCdmClientPropertySet() {} + virtual const std::string& app_id() const { return app_id_; } virtual const std::string& security_level() const { return security_level_; } virtual const std::string& service_certificate() const { return service_certificate_; @@ -403,6 +404,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { } virtual uint32_t session_sharing_id() const { return session_sharing_id_; } + void set_app_id(const std::string& app_id) { app_id_ = app_id; } void set_security_level(const std::string& security_level) { if (!security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L1) || !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)) { @@ -421,6 +423,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } private: + std::string app_id_; std::string security_level_; std::string service_certificate_; bool use_privacy_mode_; @@ -830,6 +833,14 @@ TEST_F(WvCdmRequestLicenseTest, PropertySetTest) { EXPECT_TRUE(security_level.empty() || !security_level.compare(QUERY_VALUE_SECURITY_LEVEL_L3)); + std::string app_id = "not empty"; + EXPECT_TRUE(Properties::GetApplicationId(session_id_Ln, &app_id)); + EXPECT_STREQ("", app_id.c_str()); + + property_set_Ln.set_app_id("com.unittest.mock.app.id"); + EXPECT_TRUE(Properties::GetApplicationId(session_id_Ln, &app_id)); + EXPECT_STREQ("com.unittest.mock.app.id", app_id.c_str()); + decryptor_.CloseSession(session_id_L1); decryptor_.CloseSession(session_id_L3); decryptor_.CloseSession(session_id_Ln); @@ -1202,7 +1213,7 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { EXPECT_TRUE(handle.Init(security_level)); File file; handle.SetTestFile(&file); - EXPECT_TRUE(handle.DeleteUsageInfo()); + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp("")); // default app_id. for (size_t i = 0; i < usage_info_data->usage_info; ++i) { SubSampleInfo* data = usage_info_data->sub_sample + i; @@ -1236,7 +1247,8 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { uint32_t num_usage_info = 0; CdmUsageInfo usage_info; CdmUsageInfoReleaseMessage release_msg; - CdmResponseType status = decryptor_.GetUsageInfo(&usage_info); + CdmResponseType status = + decryptor_.GetUsageInfo(property_set->app_id(), &usage_info); EXPECT_EQ(usage_info.empty() ? NO_ERROR : KEY_MESSAGE, status); while (usage_info.size() > 0) { for (size_t i = 0; i < usage_info.size(); ++i) { @@ -1244,7 +1256,7 @@ TEST_P(WvCdmUsageInfoTest, UsageInfo) { GetUsageInfoResponse(g_license_server, g_client_auth, usage_info[i]); EXPECT_EQ(NO_ERROR, decryptor_.ReleaseUsageInfo(release_msg)); } - status = decryptor_.GetUsageInfo(&usage_info); + status = decryptor_.GetUsageInfo(property_set->app_id(), &usage_info); switch (status) { case KEY_MESSAGE: EXPECT_FALSE(usage_info.empty()); break; case NO_ERROR: EXPECT_TRUE(usage_info.empty()); break; diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 3817d308..488eb34d 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -85,8 +85,13 @@ class WVDrmPlugin : public android::DrmPlugin, virtual status_t unprovisionDevice(); + virtual status_t getSecureStop(const Vector& ssid, + Vector& secureStop); + virtual status_t getSecureStops(List >& secureStops); + virtual status_t releaseAllSecureStops(); + virtual status_t releaseSecureStops(const Vector& ssRelease); virtual status_t getPropertyString(const String8& name, String8& value) const; @@ -219,6 +224,14 @@ class WVDrmPlugin : public android::DrmPlugin, mSessionSharingId = id; } + virtual const std::string& app_id() const { + return mAppId; + } + + void set_app_id(const std::string& appId) { + mAppId = appId; + } + private: DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet); @@ -227,6 +240,7 @@ class WVDrmPlugin : public android::DrmPlugin, std::string mServiceCertificate; bool mShareKeys; uint32_t mSessionSharingId; + std::string mAppId; } mPropertySet; WvContentDecryptionModule* mCDM; diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index a28a00ff..fd7aefa3 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -23,7 +23,6 @@ namespace { static const char* const kResetSecurityLevel = ""; static const char* const kEnable = "enable"; static const char* const kDisable = "disable"; - static const std::string kPsshTag = "pssh"; } @@ -394,9 +393,30 @@ status_t WVDrmPlugin::unprovisionDevice() { } } +status_t WVDrmPlugin::getSecureStop(const Vector& ssid, + Vector& secureStop) { + CdmUsageInfo cdmUsageInfo; + CdmSecureStopId cdmSsid(ssid.begin(), ssid.end()); + CdmResponseType res = mCDM->GetUsageInfo( + mPropertySet.app_id(), cdmSsid, &cdmUsageInfo); + if (isCdmResponseTypeSuccess(res)) { + secureStop.clear(); + for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin(); + iter != cdmUsageInfo.end(); + ++iter) { + const string& cdmStop = *iter; + + secureStop.appendArray(reinterpret_cast(cdmStop.data()), + cdmStop.size()); + } + } + return mapCdmResponseType(res); +} + status_t WVDrmPlugin::getSecureStops(List >& secureStops) { CdmUsageInfo cdmUsageInfo; - CdmResponseType res = mCDM->GetUsageInfo(&cdmUsageInfo); + CdmResponseType res = + mCDM->GetUsageInfo(mPropertySet.app_id(), &cdmUsageInfo); if (isCdmResponseTypeSuccess(res)) { secureStops.clear(); for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin(); @@ -414,6 +434,11 @@ status_t WVDrmPlugin::getSecureStops(List >& secureStops) { return mapCdmResponseType(res); } +status_t WVDrmPlugin::releaseAllSecureStops() { + CdmResponseType res = mCDM->ReleaseAllUsageInfo(mPropertySet.app_id()); + return mapCdmResponseType(res); +} + status_t WVDrmPlugin::releaseSecureStops(const Vector& ssRelease) { CdmUsageInfoReleaseMessage cdmMessage(ssRelease.begin(), ssRelease.end()); CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage); @@ -628,6 +653,18 @@ status_t WVDrmPlugin::setPropertyString(const String8& name, ALOGE("App tried to change key sharing while sessions are open."); return kErrorSessionIsOpen; } + } else if (name == "appId") { + size_t sessionCount = 0; + { + Mutex::Autolock lock(mCryptoSessionsMutex); + sessionCount = mCryptoSessions.size(); + } + if (sessionCount == 0) { + mPropertySet.set_app_id(value.string()); + } else { + ALOGE("App tried to set the application id while sessions are opened."); + return kErrorSessionIsOpen; + } } else { ALOGE("App set unknown string property %s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; diff --git a/libwvdrmengine/mediadrm/test/Android.mk b/libwvdrmengine/mediadrm/test/Android.mk index 32585a68..0c860b16 100644 --- a/libwvdrmengine/mediadrm/test/Android.mk +++ b/libwvdrmengine/mediadrm/test/Android.mk @@ -12,6 +12,7 @@ LOCAL_C_INCLUDES := \ frameworks/native/include \ vendor/widevine/libwvdrmengine/cdm/core/include \ vendor/widevine/libwvdrmengine/cdm/include \ + vendor/widevine/libwvdrmengine/include \ vendor/widevine/libwvdrmengine/mediadrm/include \ vendor/widevine/libwvdrmengine/oemcrypto/include \ vendor/widevine/libwvdrmengine/test/gmock/include \ diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index b53b5624..e640401d 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -16,6 +16,7 @@ #include "wv_cdm_types.h" #include "wv_content_decryption_module.h" #include "WVDrmPlugin.h" +#include "WVErrors.h" using namespace android; using namespace std; @@ -65,10 +66,17 @@ class MockCDM : public WvContentDecryptionModule { MOCK_METHOD3(HandleProvisioningResponse, CdmResponseType(CdmProvisioningResponse&, std::string*, std::string*)); - MOCK_METHOD1(GetUsageInfo, CdmResponseType(CdmUsageInfo*)); + MOCK_METHOD2(GetUsageInfo, CdmResponseType(const std::string&, + CdmUsageInfo*)); + + MOCK_METHOD3(GetUsageInfo, CdmResponseType(const std::string&, + const CdmSecureStopId&, + CdmUsageInfo*)); MOCK_METHOD1(Unprovision, CdmResponseType(CdmSecurityLevel)); + MOCK_METHOD1(ReleaseAllUsageInfo, CdmResponseType(const std::string&)); + MOCK_METHOD1(ReleaseUsageInfo, CdmResponseType(const CdmUsageInfoReleaseMessage&)); @@ -599,6 +607,8 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) { StrictMock cdm; StrictMock crypto; WVDrmPlugin plugin(&cdm, &crypto); + const char* app_id = "my_app_id"; + plugin.setPropertyString(String8("appId"), String8(app_id)); static const uint32_t kStopSize = 53; static const uint32_t kStopCount = 7; @@ -614,8 +624,8 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) { cdmStops.push_back(string(stopsRaw[i], stopsRaw[i] + kStopSize)); } - EXPECT_CALL(cdm, GetUsageInfo(_)) - .WillOnce(DoAll(SetArgPointee<0>(cdmStops), + EXPECT_CALL(cdm, GetUsageInfo(StrEq(app_id), _)) + .WillOnce(DoAll(SetArgPointee<1>(cdmStops), Return(wvcdm::NO_ERROR))); List > stops; @@ -637,6 +647,21 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) { EXPECT_EQ(stops.end(), iter); } +TEST_F(WVDrmPluginTest, ReleasesAllSecureStops) { + StrictMock cdm; + StrictMock crypto; + WVDrmPlugin plugin(&cdm, &crypto); + + status_t res = plugin.setPropertyString(String8("appId"), String8("")); + ASSERT_EQ(OK, res); + + EXPECT_CALL(cdm, ReleaseAllUsageInfo(StrEq(""))) + .Times(1); + + res = plugin.releaseAllSecureStops(); + ASSERT_EQ(OK, res); +} + TEST_F(WVDrmPluginTest, ReleasesSecureStops) { StrictMock cdm; StrictMock crypto; @@ -1309,6 +1334,59 @@ TEST_F(WVDrmPluginTest, ProvidesExpectedDefaultPropertiesToCdm) { EXPECT_EQ(0u, propertySet->service_certificate().size()); EXPECT_FALSE(propertySet->is_session_sharing_enabled()); EXPECT_EQ(0u, propertySet->session_sharing_id()); + EXPECT_STREQ("", propertySet->app_id().c_str()); +} +TEST_F(WVDrmPluginTest, CanSetAppId) { + 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 setting an empty string + res = plugin.setPropertyString(String8("appId"), String8("")); + ASSERT_EQ(OK, res); + + // Test setting an application id before a session is opened. + const String8 kAppId("com.unittest.mock.app.id"); + res = plugin.setPropertyString(String8("appId"), kAppId); + ASSERT_EQ(OK, res); + + plugin.openSession(sessionId); + ASSERT_THAT(propertySet, NotNull()); + + // Verify application id is set correctly. + EXPECT_STREQ(kAppId, propertySet->app_id().c_str()); + + // Test setting application id while session is opened, this should fail. + res = plugin.setPropertyString(String8("appId"), kAppId); + ASSERT_EQ(kErrorSessionIsOpen, res); } TEST_F(WVDrmPluginTest, CanSetSecurityLevel) {