diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 00b34478..3b77b098 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -146,6 +146,8 @@ class CdmEngine { virtual CdmResponseType ReleaseAllUsageInfo(const std::string& app_id); virtual CdmResponseType ReleaseUsageInfo( const CdmUsageInfoReleaseMessage& message); + virtual CdmResponseType LoadUsageSession(const CdmKeySetId& key_set_id, + CdmKeyMessage* release_message); // Decryption and key related methods // Accept encrypted buffer and return decrypted data. diff --git a/libwvdrmengine/cdm/core/include/device_files.h b/libwvdrmengine/cdm/core/include/device_files.h index ee029ccc..a3e5f09d 100644 --- a/libwvdrmengine/cdm/core/include/device_files.h +++ b/libwvdrmengine/cdm/core/include/device_files.h @@ -72,7 +72,8 @@ class DeviceFiles { virtual bool StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, const CdmKeyResponse& key_response, - const std::string& app_id); + const std::string& app_id, + const std::string& key_set_id); virtual bool DeleteUsageInfo(const std::string& app_id, const std::string& provider_session_token); // Delete usage information from the file system. Puts a list of all the @@ -91,6 +92,12 @@ class DeviceFiles { const std::string& provider_session_token, CdmKeyMessage* license_request, CdmKeyResponse* license_response); + // Retrieve the usage info entry specified by |key_set_id|. + // Returns false if the entry could not be found. + virtual bool RetrieveUsageInfoByKeySetId(const std::string& app_id, + const std::string& key_set_id, + CdmKeyMessage* license_request, + CdmKeyResponse* license_response); private: // Helpers that wrap the File interface and automatically handle hashing, as diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 2c1e1b2e..18bcdeb4 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -2,6 +2,7 @@ #include "cdm_engine.h" +#include #include #include @@ -934,6 +935,66 @@ CdmResponseType CdmEngine::ReleaseUsageInfo( return status; } +CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, + CdmKeyMessage* release_message) { + LOGI("CdmEngine::LoadUsageSession"); + // This method is currently only used by the CE CDM, in which all session IDs + // are key set IDs. + assert(Properties::AlwaysUseKeySetIds()); + + if (key_set_id.empty()) { + LOGE("CdmEngine::LoadUsageSession: invalid key set id"); + return EMPTY_KEYSET_ID_ENG_5; + } + + CdmSessionMap::iterator iter = sessions_.find(key_set_id); + if (iter == sessions_.end()) { + LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ", + key_set_id.c_str()); + return SESSION_NOT_FOUND_11; + } + + DeviceFiles handle; + if (!handle.Init(iter->second->GetSecurityLevel())) { + LOGE("CdmEngine::LoadUsageSession: unable to initialize device files"); + return LOAD_USAGE_INFO_FILE_ERROR; + } + + std::string app_id; + iter->second->GetApplicationId(&app_id); + + CdmKeyMessage key_message; + CdmKeyResponse key_response; + if (!handle.RetrieveUsageInfoByKeySetId(app_id, key_set_id, &key_message, + &key_response)) { + LOGE("CdmEngine::LoadUsageSession: unable to find usage information"); + return LOAD_USAGE_INFO_MISSING; + } + + CdmResponseType status = + iter->second->RestoreUsageSession(key_message, key_response); + if (KEY_ADDED != status) { + LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status); + return status; + } + + std::string server_url; + status = iter->second->GenerateReleaseRequest(release_message, &server_url); + + switch (status) { + case KEY_MESSAGE: + break; + case KEY_CANCELED: // usage information not present in + iter->second->DeleteLicense(); // OEMCrypto, delete and try again + break; + default: + LOGE("CdmEngine::LoadUsageSession: generate release request error: %d", + status); + break; + } + return status; +} + CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, const CdmDecryptionParameters& parameters) { if (parameters.key_id == NULL) { diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 9b386afe..46c0767b 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -541,7 +541,7 @@ CdmResponseType CdmSession::StoreLicense() { std::string app_id; GetApplicationId(&app_id); if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_, - key_response_, app_id)) { + key_response_, app_id, key_set_id_)) { LOGE("CdmSession::StoreLicense: Unable to store usage info"); return STORE_USAGE_INFO_ERROR; } diff --git a/libwvdrmengine/cdm/core/src/device_files.cpp b/libwvdrmengine/cdm/core/src/device_files.cpp index aaaef2b0..a17b948e 100644 --- a/libwvdrmengine/cdm/core/src/device_files.cpp +++ b/libwvdrmengine/cdm/core/src/device_files.cpp @@ -355,7 +355,8 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) { bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, const CdmKeyResponse& key_response, - const std::string& app_id) { + const std::string& app_id, + const std::string& key_set_id) { if (!initialized_) { LOGW("DeviceFiles::StoreUsageInfo: not initialized"); return false; @@ -381,6 +382,7 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, provider_session_token.size()); provider_session->set_license_request(key_request.data(), key_request.size()); provider_session->set_license(key_response.data(), key_response.size()); + provider_session->set_key_set_id(key_set_id.data(), key_set_id.size()); file.SerializeToString(&serialized_file); return StoreFileWithHash(file_name, serialized_file); @@ -522,22 +524,48 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id, 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() == provider_session_token) { - found = true; - break; + *license_request = file.usage_info().sessions(index).license_request(); + *license_response = file.usage_info().sessions(index).license(); + return true; } } - if (!found) { + return false; +} + +bool DeviceFiles::RetrieveUsageInfoByKeySetId( + const std::string& app_id, + const std::string& key_set_id, + CdmKeyMessage* license_request, + CdmKeyResponse* license_response) { + if (!initialized_) { + LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized"); + return false; + } + std::string serialized_file; + std::string file_name = GetUsageInfoFileName(app_id); + if (!RetrieveHashedFile(file_name, &serialized_file)) return false; + + video_widevine_client::sdk::File file; + if (!file.ParseFromString(serialized_file)) { + LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: Unable to parse file"); return false; } - *license_request = file.usage_info().sessions(index).license_request(); - *license_response = file.usage_info().sessions(index).license(); - return true; + int index = 0; + for (; index < file.usage_info().sessions_size(); ++index) { + if (file.usage_info().sessions(index).key_set_id() == key_set_id) { + *license_request = file.usage_info().sessions(index).license_request(); + *license_response = file.usage_info().sessions(index).license(); + return true; + } + } + + return false; } bool DeviceFiles::StoreFileWithHash(const std::string& name, diff --git a/libwvdrmengine/cdm/core/src/device_files.proto b/libwvdrmengine/cdm/core/src/device_files.proto index 468b4252..e9b793fd 100644 --- a/libwvdrmengine/cdm/core/src/device_files.proto +++ b/libwvdrmengine/cdm/core/src/device_files.proto @@ -46,6 +46,7 @@ message UsageInfo { optional bytes token = 1; optional bytes license_request = 2; optional bytes license = 3; + optional bytes key_set_id = 4; } repeated ProviderSession sessions = 1; diff --git a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp index 416e6274..340ed059 100644 --- a/libwvdrmengine/cdm/core/test/device_files_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/device_files_unittest.cpp @@ -21,6 +21,7 @@ const uint32_t kProtobufEstimatedOverhead = 75; const uint32_t kLicenseRequestLen = 300; const uint32_t kLicenseLen = 500; const uint32_t kProviderSessionTokenLen = 128; +const uint32_t kKeySetIdLen = 20; // Structurally valid test certificate. // The data elements in this module are used to test the storage and @@ -1536,6 +1537,16 @@ MATCHER_P4(Contains, str1, str2, str3, size, "") { data.find(str2) != std::string::npos && data.find(str3) != std::string::npos); } +MATCHER_P5(Contains, str1, str2, str3, str4, size, "") { + // Estimating the length of data. We can have gmock provide length + // as well as pointer to data but that will introduce a dependency on tr1 + std::string data(arg, size + str1.size() + str2.size() + str3.size() + + str4.size() + kProtobufEstimatedOverhead); + return (data.find(str1) != std::string::npos && + data.find(str2) != std::string::npos && + data.find(str3) != std::string::npos && + data.find(str4) != std::string::npos); +} MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 @@ -1549,7 +1560,6 @@ MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") { data.find(str5) != std::string::npos && data.find(str6) != std::string::npos); } - MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 @@ -2207,6 +2217,7 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { std::string pst(GenerateRandomData(kProviderSessionTokenLen)); std::string license_request(GenerateRandomData(kLicenseRequestLen)); std::string license(GenerateRandomData(kLicenseLen)); + std::string key_set_id(GenerateRandomData(kKeySetIdLen)); std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); @@ -2236,8 +2247,10 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { } EXPECT_CALL(file, - Write(Contains(pst, license_request, license, data.size()), - Gt(pst.size() + license_request.size() + license.size()))) + Write(Contains(pst, license_request, license, key_set_id, + data.size()), + Gt(pst.size() + license_request.size() + license.size() + + key_set_id.size()))) .WillOnce(ReturnArg<1>()); DeviceFiles device_files; @@ -2245,7 +2258,8 @@ TEST_P(DeviceFilesUsageInfoTest, Store) { device_files.SetTestFile(&file); ASSERT_TRUE( - device_files.StoreUsageInfo(pst, license_request, license, app_id)); + device_files.StoreUsageInfo(pst, license_request, license, app_id, + key_set_id)); } TEST_P(DeviceFilesUsageInfoTest, Delete) { diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index 9e2ca991..61c512c1 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -179,7 +179,11 @@ enum { kLicenseRenewalProhibited = ERROR_DRM_VENDOR_MIN + 165, kOfflineLicenseProhibited = ERROR_DRM_VENDOR_MIN + 166, kStorageProhibited = ERROR_DRM_VENDOR_MIN + 167, - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 167, + kEmptyKeySetIdEng5 = ERROR_DRM_VENDOR_MIN + 168, + kSessionNotFound11 = ERROR_DRM_VENDOR_MIN + 169, + kLoadUsageInfoFileError = ERROR_DRM_VENDOR_MIN + 170, + kLoadUsageInfoMissing = ERROR_DRM_VENDOR_MIN + 171, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 171, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index 64711323..4d6cee0f 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -347,6 +347,14 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kOfflineLicenseProhibited; case wvcdm::STORAGE_PROHIBITED: return kStorageProhibited; + case wvcdm::EMPTY_KEYSET_ID_ENG_5: + return kEmptyKeySetIdEng5; + case wvcdm::SESSION_NOT_FOUND_11: + return kSessionNotFound11; + case wvcdm::LOAD_USAGE_INFO_FILE_ERROR: + return kLoadUsageInfoFileError; + case wvcdm::LOAD_USAGE_INFO_MISSING: + return kLoadUsageInfoMissing; case wvcdm::UNKNOWN_ERROR: return android::ERROR_DRM_UNKNOWN; case wvcdm::SECURE_BUFFER_REQUIRED: