From e928670c85b36cba5022a6c785ee893011b43481 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Thu, 23 Mar 2023 22:03:21 -0700 Subject: [PATCH] Android CDM: Restored secure stop tests. [ Semi-revert of http://ag/20183443 ] [ Merge of http://go/wvgerrit/168898 ] These tests were removed from Android last quarter; however, they now need to be restored. These tests will be removed in Android V. To help with ambiguity around where the CDM is operating on a single or set of usage info messages, the variables have been renamed to propery indicate plurality. Bug: 263319220 Test: cdm_extended_duration_test Test: request_license_test Test: libwvdrmdrmplugin_hal_test Change-Id: I38b16dd5811069fafaeab5ffc19d0f8a8095f0cf --- libwvdrmengine/cdm/core/include/cdm_engine.h | 23 +- .../include/cdm_engine_metrics_decorator.h | 31 +- .../cdm/core/include/wv_cdm_types.h | 6 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 66 +- .../cdm_engine_metrics_decorator_unittest.cpp | 20 +- .../include/wv_content_decryption_module.h | 9 +- .../cdm/src/wv_content_decryption_module.cpp | 29 +- .../cdm/test/cdm_extended_duration_test.cpp | 320 +++- .../cdm/test/request_license_test.cpp | 1383 +++++++++++++++-- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 81 +- .../mediadrm/test/WVDrmPlugin_hal_test.cpp | 217 ++- 11 files changed, 1842 insertions(+), 343 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index 18381a79..154d2dd8 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -214,17 +214,17 @@ class CdmEngine { // Return the list of IDs associated with usage records for the // current (origin-specific) file system. At least one parameter - // |ksids| or |provider_session_tokens| needs to be supplied. - virtual CdmResponseType ListUsageIds( - const std::string& app_id, CdmSecurityLevel security_level, - std::vector* ksids, - std::vector* provider_session_tokens); + // |ksids| or |psts| needs to be supplied. + virtual CdmResponseType ListUsageIds(const std::string& app_id, + CdmSecurityLevel security_level, + std::vector* ksids, + std::vector* psts); // Delete the usage record for the given key_set_id. This removes the // usage record in the file system and the OEMCrypto usage record. virtual CdmResponseType DeleteUsageRecord(const std::string& app_id, CdmSecurityLevel security_level, - const std::string& key_set_id); + const CdmKeySetId& key_set_id); // Get offline license status: active, release or unknown virtual CdmResponseType GetOfflineLicenseState( @@ -245,7 +245,7 @@ class CdmEngine { // in the event of an error. virtual CdmResponseType GetUsageInfo(const std::string& app_id, int* error_detail, - CdmUsageInfo* usage_info); + CdmUsageReport* usage_report); // Retrieve usage info whose PST is specified by |ssid| // If |error_detail| is not null, an additional error code may be provided @@ -253,7 +253,7 @@ class CdmEngine { virtual CdmResponseType GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, int* error_detail, - CdmUsageInfo* usage_info); + CdmUsageReport* usage_report); // Retrieve usage info for a given security level and whose // PST is specified by |ssid|. @@ -263,7 +263,7 @@ class CdmEngine { const CdmSecureStopId& ssid, RequestedSecurityLevel security_level, int* error_detail, - CdmUsageInfo* usage_info); + CdmUsageReport* usage_report); // Remove all usage records for the current origin. virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id, @@ -276,8 +276,7 @@ class CdmEngine { virtual CdmResponseType RemoveUsageInfo( const std::string& app_id, const CdmSecureStopId& secure_stop_id); - virtual CdmResponseType ReleaseUsageInfo( - const CdmUsageInfoReleaseMessage& message); + virtual CdmResponseType ReleaseUsageInfo(const CdmKeyResponse& message); virtual CdmResponseType LoadUsageSession(const CdmKeySetId& key_set_id, CdmKeyMessage* release_message); @@ -412,7 +411,7 @@ class CdmEngine { bool ValidateKeySystem(const CdmKeySystem& key_system); CdmResponseType GetUsageInfo(const std::string& app_id, RequestedSecurityLevel requested_security_level, - int* error_detail, CdmUsageInfo* usage_info); + int* error_detail, CdmUsageReport* usage_report); void OnKeyReleaseEvent(const CdmKeySetId& key_set_id); diff --git a/libwvdrmengine/cdm/core/include/cdm_engine_metrics_decorator.h b/libwvdrmengine/cdm/core/include/cdm_engine_metrics_decorator.h index 5d065d14..e1719254 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine_metrics_decorator.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine_metrics_decorator.h @@ -184,10 +184,10 @@ class CdmEngineMetricsImpl : public T { } CdmResponseType GetUsageInfo(const std::string& app_id, int* error_detail, - CdmUsageInfo* usage_info) override { + CdmUsageReport* usage_report) override { CdmResponseType sts; int error_detail_alt; - M_TIME(sts = T::GetUsageInfo(app_id, &error_detail_alt, usage_info), + M_TIME(sts = T::GetUsageInfo(app_id, &error_detail_alt, usage_report), metrics_, cdm_engine_get_usage_info_, sts, error_detail_alt); if (error_detail != nullptr) { *error_detail = error_detail_alt; @@ -197,10 +197,10 @@ class CdmEngineMetricsImpl : public T { CdmResponseType GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, int* error_detail, - CdmUsageInfo* usage_info) override { + CdmUsageReport* usage_report) override { CdmResponseType sts; int error_detail_alt; - M_TIME(sts = T::GetUsageInfo(app_id, ssid, &error_detail_alt, usage_info), + M_TIME(sts = T::GetUsageInfo(app_id, ssid, &error_detail_alt, usage_report), metrics_, cdm_engine_get_usage_info_, sts, error_detail_alt); if (error_detail != nullptr) { *error_detail = error_detail_alt; @@ -209,14 +209,14 @@ class CdmEngineMetricsImpl : public T { } CdmResponseType RemoveAllUsageInfo(const std::string& app_id) override { - CdmResponseType sts = T::RemoveAllUsageInfo(app_id); + const CdmResponseType sts = T::RemoveAllUsageInfo(app_id); metrics_->cdm_engine_remove_all_usage_info_.Increment(sts); return sts; } CdmResponseType RemoveAllUsageInfo(const std::string& app_id, CdmSecurityLevel security_level) override { - CdmResponseType sts = T::RemoveAllUsageInfo(app_id, security_level); + const CdmResponseType sts = T::RemoveAllUsageInfo(app_id, security_level); metrics_->cdm_engine_remove_all_usage_info_.Increment(sts); return sts; } @@ -224,24 +224,23 @@ class CdmEngineMetricsImpl : public T { CdmResponseType RemoveUsageInfo( const std::string& app_id, const CdmSecureStopId& secure_stop_id) override { - CdmResponseType sts = T::RemoveUsageInfo(app_id, secure_stop_id); + const CdmResponseType sts = T::RemoveUsageInfo(app_id, secure_stop_id); metrics_->cdm_engine_remove_usage_info_.Increment(sts); return sts; } - CdmResponseType ReleaseUsageInfo( - const CdmUsageInfoReleaseMessage& message) override { - CdmResponseType sts = T::ReleaseUsageInfo(message); + CdmResponseType ReleaseUsageInfo(const CdmKeyResponse& message) override { + const CdmResponseType sts = T::ReleaseUsageInfo(message); metrics_->cdm_engine_release_usage_info_.Increment(sts); return sts; } - CdmResponseType ListUsageIds( - const std::string& app_id, CdmSecurityLevel security_level, - std::vector* ksids, - std::vector* provider_session_tokens) override { - CdmResponseType sts = - T::ListUsageIds(app_id, security_level, ksids, provider_session_tokens); + CdmResponseType ListUsageIds(const std::string& app_id, + CdmSecurityLevel security_level, + std::vector* ksids, + std::vector* ssids) override { + const CdmResponseType sts = + T::ListUsageIds(app_id, security_level, ksids, ssids); metrics_->cdm_engine_get_secure_stop_ids_.Increment(sts); return sts; } diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 41499651..0b667b1e 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -21,7 +21,6 @@ using CdmInitData = std::string; using CdmKeyMessage = std::string; using CdmKeyResponse = std::string; using KeyId = std::string; -using CdmSecureStopId = std::string; using CdmSessionId = std::string; using CdmKeySetId = std::string; using RequestId = std::string; @@ -39,6 +38,11 @@ using UsageTableHeader = std::string; using UsageEntry = std::string; using UsageEntryIndex = uint32_t; +// Secure stop related data types. +using CdmSecureStopId = std::string; +using CdmUsageReport = std::string; +using CdmUsageReportList = std::vector; + enum CdmKeyRequestType : uint32_t { kKeyRequestTypeUnknown, kKeyRequestTypeInitial, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 949f528f..069a7842 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -1302,12 +1302,12 @@ CdmResponseType CdmEngine::ListStoredLicenses( return CdmResponseType(NO_ERROR); } -CdmResponseType CdmEngine::ListUsageIds( - const std::string& app_id, CdmSecurityLevel security_level, - std::vector* ksids, - std::vector* provider_session_tokens) { - if (!ksids && !provider_session_tokens) { - LOGE("Outputs |ksids| and |provider_session_tokens| are null"); +CdmResponseType CdmEngine::ListUsageIds(const std::string& app_id, + CdmSecurityLevel security_level, + std::vector* ksids, + std::vector* psts) { + if (!ksids && !psts) { + LOGE("Outputs |ksids| and |psts| are null"); return CdmResponseType(INVALID_PARAMETERS_ENG_23); } if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) { @@ -1319,7 +1319,7 @@ CdmResponseType CdmEngine::ListUsageIds( LOGE("Unable to initialize device files"); return CdmResponseType(LIST_USAGE_ERROR_1); } - if (!handle.ListUsageIds(app_id, ksids, provider_session_tokens)) { + if (!handle.ListUsageIds(app_id, ksids, psts)) { LOGE("Failed: app_id = %s, security_level = %s", IdToString(app_id), CdmSecurityLevelToString(security_level)); return CdmResponseType(LIST_USAGE_ERROR_2); @@ -1329,7 +1329,7 @@ CdmResponseType CdmEngine::ListUsageIds( CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id, CdmSecurityLevel security_level, - const std::string& key_set_id) { + const CdmKeySetId& key_set_id) { LOGI("app_id = %s, key_set_id = %s", IdToString(app_id), IdToString(key_set_id)); if (security_level == kSecurityLevelL1 && OkpIsInFallbackMode()) { @@ -1465,18 +1465,18 @@ CdmResponseType CdmEngine::StoreAtscLicense( CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, int* error_detail, - CdmUsageInfo* usage_info) { + CdmUsageReport* usage_report) { // Try to find usage info at the default security level. If the // security level is unprovisioned or we are unable to find it, // try L3. CdmResponseType status = - GetUsageInfo(app_id, ssid, kLevelDefault, error_detail, usage_info); + GetUsageInfo(app_id, ssid, kLevelDefault, error_detail, usage_report); switch (status.code()) { case NEED_PROVISIONING: case GET_USAGE_INFO_ERROR_1: case GET_USAGE_INFO_ERROR_2: case USAGE_INFO_NOT_FOUND: - status = GetUsageInfo(app_id, ssid, kLevel3, error_detail, usage_info); + status = GetUsageInfo(app_id, ssid, kLevel3, error_detail, usage_report); return status; default: return status; @@ -1487,15 +1487,17 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, RequestedSecurityLevel security_level, int* error_detail, - CdmUsageInfo* usage_info) { + CdmUsageReport* usage_report) { LOGI("app_id = %s, ssid = %s", IdToString(app_id), IdToString(ssid)); if (!usage_property_set_) { usage_property_set_.reset(new UsagePropertySet()); } - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); + if (usage_report == nullptr) { + LOGE("Output |usage_report| is null"); return CdmResponseType(PARAMETER_NULL); } + usage_report->clear(); + usage_property_set_->set_security_level(security_level); usage_property_set_->set_app_id(app_id); usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession())); @@ -1536,32 +1538,28 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, if (KEY_ADDED != status) { LOGE("RestoreUsageSession failed: status = %d", static_cast(status)); - usage_info->clear(); return status; } CdmKeyRequest request; status = usage_session_->GenerateReleaseRequest(&request); - usage_info->clear(); - usage_info->push_back(request.message); - if (KEY_MESSAGE != status) { LOGE("GenerateReleaseRequest failed: status = %d", static_cast(status)); - usage_info->clear(); return status; } + *usage_report = std::move(request.message); return CdmResponseType(KEY_MESSAGE); } CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, int* error_detail, - CdmUsageInfo* usage_info) { + CdmUsageReport* usage_report) { LOGI("app_id = %s", IdToString(app_id)); - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); + if (usage_report == nullptr) { + LOGE("Output |usage_report| is null"); return CdmResponseType(PARAMETER_NULL); } // Return a random usage report from a random security level @@ -1569,15 +1567,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, wvutil::CdmRandom::RandomBool() ? kLevelDefault : kLevel3; CdmResponseType status(UNKNOWN_ERROR); do { - status = GetUsageInfo(app_id, security_level, error_detail, usage_info); - if (KEY_MESSAGE == status && !usage_info->empty()) { + status = GetUsageInfo(app_id, security_level, error_detail, usage_report); + if (KEY_MESSAGE == status && !usage_report->empty()) { return status; } } while (KEY_CANCELED == status); security_level = (kLevel3 == security_level) ? kLevelDefault : kLevel3; do { - status = GetUsageInfo(app_id, security_level, error_detail, usage_info); + status = GetUsageInfo(app_id, security_level, error_detail, usage_report); if (NEED_PROVISIONING == status) return CdmResponseType( NO_ERROR); // Valid scenario that one of the security @@ -1588,13 +1586,14 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, CdmResponseType CdmEngine::GetUsageInfo( const std::string& app_id, RequestedSecurityLevel requested_security_level, - int* error_detail, CdmUsageInfo* usage_info) { + int* error_detail, CdmUsageReport* usage_report) { LOGI("app_id = %s, security_level = %s", IdToString(app_id), RequestedSecurityLevelToString(requested_security_level)); - if (usage_info == nullptr) { - LOGE("Output |usage_info| is null"); + if (usage_report == nullptr) { + LOGE("Output |usage_report| is null"); return CdmResponseType(PARAMETER_NULL); } + usage_report->clear(); if (requested_security_level == kLevelDefault && OkpIsInFallbackMode()) { LOGD("OKP fallback to L3"); requested_security_level = kLevel3; @@ -1627,7 +1626,6 @@ CdmResponseType CdmEngine::GetUsageInfo( } if (usage_data.empty()) { - usage_info->clear(); return CdmResponseType(NO_ERROR); } @@ -1637,27 +1635,22 @@ CdmResponseType CdmEngine::GetUsageInfo( // TODO(b/141704872): Make multiple attempts. LOGE("RestoreUsageSession failed: index = %zu, status = %d", index, static_cast(status)); - usage_info->clear(); return status; } CdmKeyRequest request; status = usage_session_->GenerateReleaseRequest(&request); - usage_info->clear(); - usage_info->push_back(request.message); - switch (status.code()) { case KEY_MESSAGE: + *usage_report = std::move(request.message); break; case KEY_CANCELED: // usage information not present in usage_session_->DeleteLicenseFile(); // OEMCrypto, delete and try again - usage_info->clear(); break; default: LOGE("GenerateReleaseRequest failed: status = %d", static_cast(status)); - usage_info->clear(); break; } return status; @@ -1782,8 +1775,7 @@ CdmResponseType CdmEngine::RemoveUsageInfo( return CdmResponseType(REMOVE_USAGE_INFO_ERROR_3); } -CdmResponseType CdmEngine::ReleaseUsageInfo( - const CdmUsageInfoReleaseMessage& message) { +CdmResponseType CdmEngine::ReleaseUsageInfo(const CdmKeyResponse& message) { LOGI("message_size = %zu", message.size()); if (!usage_session_) { LOGE("Usage session not initialized"); diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp index 53e53103..b254e871 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp @@ -95,8 +95,8 @@ class MockCdmEngineImpl : public CdmEngine { (override)); MOCK_METHOD(CdmResponseType, Unprovision, (CdmSecurityLevel), (override)); MOCK_METHOD(CdmResponseType, ListUsageIds, - (const std::string&, CdmSecurityLevel, std::vector*, - std::vector*), + (const std::string&, CdmSecurityLevel, std::vector*, + std::vector*), (override)); MOCK_METHOD(CdmResponseType, RemoveAllUsageInfo, (const std::string&), (override)); @@ -104,8 +104,8 @@ class MockCdmEngineImpl : public CdmEngine { (const std::string&, CdmSecurityLevel), (override)); MOCK_METHOD(CdmResponseType, RemoveUsageInfo, (const std::string&, const CdmSecureStopId&), (override)); - MOCK_METHOD(CdmResponseType, ReleaseUsageInfo, - (const CdmUsageInfoReleaseMessage&), (override)); + MOCK_METHOD(CdmResponseType, ReleaseUsageInfo, (const CdmKeyResponse&), + (override)); MOCK_METHOD(CdmResponseType, DecryptV16, (const CdmSessionId&, const CdmDecryptionParametersV16&), (override)); @@ -464,17 +464,17 @@ TEST_F(WvCdmEngineMetricsImplTest, ReleaseUsageInfo) { } TEST_F(WvCdmEngineMetricsImplTest, ListUsageIds) { - std::vector ksids; - std::vector provider_session_tokens; + std::vector ksids; + std::vector ssids; EXPECT_CALL(*test_cdm_metrics_engine_, ListUsageIds(Eq("fake app id"), Eq(kSecurityLevelL2), Eq(&ksids), - Eq(&provider_session_tokens))) + Eq(&ssids))) .WillOnce(Return(CdmResponseType(UNKNOWN_ERROR))); - ASSERT_EQ(wvcdm::UNKNOWN_ERROR, test_cdm_metrics_engine_->ListUsageIds( - "fake app id", kSecurityLevelL2, &ksids, - &provider_session_tokens)); + ASSERT_EQ(wvcdm::UNKNOWN_ERROR, + test_cdm_metrics_engine_->ListUsageIds( + "fake app id", kSecurityLevelL2, &ksids, &ssids)); drm_metrics::WvCdmMetrics metrics_proto; test_cdm_metrics_engine_->GetMetricsSnapshot(&metrics_proto); diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 186b6d8f..70a5543e 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -105,19 +105,18 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler { // Secure stop related methods virtual CdmResponseType GetUsageInfo(const std::string& app_id, const CdmIdentifier& identifier, - CdmUsageInfo* usage_info); + CdmUsageReportList* usage_reports); virtual CdmResponseType GetUsageInfo(const std::string& app_id, const CdmSecureStopId& ssid, const CdmIdentifier& identifier, - CdmUsageInfo* usage_info); + CdmUsageReport* usage_report); virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id, const CdmIdentifier& identifier); virtual CdmResponseType RemoveUsageInfo( const std::string& app_id, const CdmIdentifier& identifier, const CdmSecureStopId& secure_stop_id); - virtual CdmResponseType ReleaseUsageInfo( - const CdmUsageInfoReleaseMessage& message, - const CdmIdentifier& identifier); + virtual CdmResponseType ReleaseUsageInfo(const CdmKeyResponse& message, + const CdmIdentifier& identifier); virtual CdmResponseType GetSecureStopIds(const std::string& app_id, const CdmIdentifier& identifier, diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 745671f7..c4895761 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -261,18 +261,28 @@ bool WvContentDecryptionModule::IsProvisioned(CdmSecurityLevel security_level, CdmResponseType WvContentDecryptionModule::GetUsageInfo( const std::string& app_id, const CdmIdentifier& identifier, - CdmUsageInfo* usage_info) { + CdmUsageReportList* usage_reports) { + if (usage_reports == nullptr) return CdmResponseType(PARAMETER_NULL); CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); int error_detail = NO_ERROR; - return cdm_engine->GetUsageInfo(app_id, &error_detail, usage_info); + CdmUsageReport usage_report; + // Doesn't actually retrieve all reports, just a single random one. + const CdmResponseType status = + cdm_engine->GetUsageInfo(app_id, &error_detail, &usage_report); + usage_reports->clear(); + // Possible that no report is available, still a successful call. + if ((status == NO_ERROR || status == KEY_MESSAGE) && !usage_report.empty()) { + usage_reports->push_back(std::move(usage_report)); + } + return status; } CdmResponseType WvContentDecryptionModule::GetUsageInfo( const std::string& app_id, const CdmSecureStopId& ssid, - const CdmIdentifier& identifier, CdmUsageInfo* usage_info) { + const CdmIdentifier& identifier, CdmUsageReport* usage_report) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); int error_detail = NO_ERROR; - return cdm_engine->GetUsageInfo(app_id, ssid, &error_detail, usage_info); + return cdm_engine->GetUsageInfo(app_id, ssid, &error_detail, usage_report); } CdmResponseType WvContentDecryptionModule::RemoveAllUsageInfo( @@ -289,8 +299,7 @@ CdmResponseType WvContentDecryptionModule::RemoveUsageInfo( } CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo( - const CdmUsageInfoReleaseMessage& message, - const CdmIdentifier& identifier) { + const CdmKeyResponse& message, const CdmIdentifier& identifier) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->ReleaseUsageInfo(message); } @@ -306,10 +315,10 @@ CdmResponseType WvContentDecryptionModule::GetSecureStopIds( CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); const CdmResponseType sts = cdm_engine->ListUsageIds(app_id, kSecurityLevelL1, nullptr, ssids); - std::vector secure_stop_ids; - const CdmResponseType sts_l3 = cdm_engine->ListUsageIds( - app_id, kSecurityLevelL3, nullptr, &secure_stop_ids); - ssids->insert(ssids->end(), secure_stop_ids.begin(), secure_stop_ids.end()); + std::vector ssids_l3; + const CdmResponseType sts_l3 = + cdm_engine->ListUsageIds(app_id, kSecurityLevelL3, nullptr, &ssids_l3); + ssids->insert(ssids->end(), ssids_l3.begin(), ssids_l3.end()); return sts_l3 != NO_ERROR ? sts_l3 : sts; } diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index 38a55f9d..259de488 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -50,7 +50,7 @@ const int kHttpOk = 200; const uint32_t kMinute = 60; const uint32_t kClockTolerance = 10; -const uint32_t kMaxUsageTableSize = 50; +const uint32_t kMaxUsageTableSize = 300; const std::string kEmptyServiceCertificate; constexpr int64_t kUnlimitedDurationValue = LLONG_MAX; @@ -71,7 +71,7 @@ struct SubSampleInfo { }; // clang-format off -SubSampleInfo kEncryptedStreamingNoPstSubSample = { +const SubSampleInfo kEncryptedStreamingNoPstSubSample = { // key SD, encrypted, 256b true, 1, true, true, false, wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), @@ -95,7 +95,7 @@ SubSampleInfo kEncryptedStreamingNoPstSubSample = { "08a899cd760ff0899a1170c2f224b9c52997a0785b7fe170805fd3e8b1127659"), wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, 3}; -SubSampleInfo kEncryptedStreamingClip8SubSample = { +const SubSampleInfo kEncryptedStreamingClip8SubSample = { true, 1, true, true, false, wvutil::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"), wvutil::a2b_hex( @@ -118,7 +118,7 @@ SubSampleInfo kEncryptedStreamingClip8SubSample = { "37326df26fa509343faa98dff667629f557873f1284903202e451227ef465a62"), wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; -SubSampleInfo kEncryptedStreamingClip5SubSample = { +const SubSampleInfo kEncryptedStreamingClip5SubSample = { true, 1, true, true, false, wvutil::a2bs_hex("3AE243D83B93B3311A1D777FF5FBE01A"), wvutil::a2b_hex( @@ -141,7 +141,7 @@ SubSampleInfo kEncryptedStreamingClip5SubSample = { "ed0716cd8bceb80cf59166a217006bd147c51b04dfb183088ce3f51e9b9f759e"), wvutil::a2b_hex("b358ab21ac90455bbf60490daad457e3"), 0, 3}; -SubSampleInfo kEncryptedOfflineClip2SubSample = { +const SubSampleInfo kEncryptedOfflineClip2SubSample = { true, 1, true, true, false, wvutil::a2bs_hex("3260F39E12CCF653529990168A3583FF"), wvutil::a2b_hex( @@ -164,13 +164,13 @@ SubSampleInfo kEncryptedOfflineClip2SubSample = { "944B5080B998BB0FE6E566AAFAE2328B37FD189F1920A964434ECF18E11AC81E"), wvutil::a2b_hex("7362b5140c4ce0cd5f863858668d3f1a"), 0, 3}; -std::string kStreamingClip8PstInitData = wvutil::a2bs_hex( +const std::string kStreamingClip8PstInitData = wvutil::a2bs_hex( "000000427073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id "08011a0d7769646576696e655f74657374220f73" // pssh data "747265616d696e675f636c697038"); -std::string kOfflineClip2PstInitData = wvutil::a2bs_hex( +const std::string kOfflineClip2PstInitData = wvutil::a2bs_hex( "000000407073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id "08011a0d7769646576696e655f74657374220d6f" // pssh data @@ -399,30 +399,30 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase { // Post a request and extract the signed provisioning message from // the HTTP response. - std::string GetUsageInfoResponse(const std::string& server_url, - const std::string& client_auth, - const std::string& usage_info_request) { + std::string GetSecureStopResponse(const std::string& server_url, + const std::string& client_auth, + const std::string& secure_stop_request) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth); - EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " - << server_url << client_auth; - url_request.PostRequest(usage_info_request); + EXPECT_TRUE(url_request.is_connected()) + << "Fail to connect to " << server_url << client_auth; + url_request.PostRequest(secure_stop_request); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); - int http_status_code = url_request.GetStatusCode(message); + const int http_status_code = url_request.GetStatusCode(message); if (kHttpOk != http_status_code) { LogResponseError(message, http_status_code); } EXPECT_EQ(kHttpOk, http_status_code); - std::string usage_info; + std::string secure_stop_response; if (kHttpOk == http_status_code) { LicenseRequest license; - license.GetDrmMessage(message, usage_info); - LOGV("HTTP response body: (%zu bytes)", usage_info.size()); + license.GetDrmMessage(message, secure_stop_response); + LOGV("HTTP response body: (%zu bytes)", secure_stop_response.size()); } - return usage_info; + return secure_stop_response; } void VerifyKeyRequestResponse(const std::string& server_url, @@ -554,7 +554,6 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase { license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); - EXPECT_TRUE(license_renewal.has_key_control_nonce()); } void ValidateReleaseRequest(std::string& usage_msg, bool license_used, @@ -635,7 +634,6 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase { license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); - EXPECT_TRUE(license_renewal.has_key_control_nonce()); } void ValidateHasUpdateUsageEntry( @@ -909,7 +907,6 @@ TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) { license_renewal.type()); EXPECT_LT(0, license_renewal.request_time()); EXPECT_EQ(video_widevine::VERSION_2_1, license_renewal.protocol_version()); - EXPECT_TRUE(license_renewal.has_key_control_nonce()); decryptor_->CloseSession(session_id_); } @@ -955,7 +952,7 @@ TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) { (delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) { result = nanosleep(&delay_remaining, &delay_remaining); } - SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; + const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( @@ -986,6 +983,67 @@ TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) { decryptor_->CloseSession(session_id); } +TEST_F(WvCdmExtendedDurationTest, DISABLED_UsageOverflowTest) { + Provision(); + TestWvCdmClientPropertySet client_property_set; + TestWvCdmClientPropertySet* property_set = nullptr; + + CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector provider_session_tokens; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &provider_session_tokens)); + + const std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c697035"); + + for (size_t i = 0; i < kMaxUsageTableSize + 100; ++i) { + decryptor_->OpenSession(config_.key_system(), property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(key_id, kLicenseTypeStreaming); + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + decryptor_->CloseSession(session_id_); + } + + CdmUsageReportList usage_reports; + CdmResponseType status = + decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status); + int error_count = 0; + while (!usage_reports.empty()) { + // Release each of the listed secure stops. + for (size_t i = 0; i < usage_reports.size(); ++i) { + const CdmUsageReport& usage_report = usage_reports[i]; + const CdmKeyMessage release_msg = GetSecureStopResponse( + config_.license_server(), config_.client_auth(), usage_report); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)) + << i << "/" << usage_reports.size() << " (err " << (error_count++) << ")" + << release_msg; + } + ASSERT_LE(error_count, 100); // Give up after 100 failures. + // Get an updated list. The CDM might not list all entries at once. + status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports); + switch (status.code()) { + case KEY_MESSAGE: + EXPECT_FALSE(usage_reports.empty()); + break; + case NO_ERROR: + EXPECT_TRUE(usage_reports.empty()); + break; + default: + FAIL() << "GetUsageInfo failed with error " << static_cast(status); + break; + } + } +} + // This test verifies that sessions allocated internally during key release // message generation are deallocated after their time to live period expires // by timer events (if other sessions are open). @@ -1218,7 +1276,7 @@ TEST_P(WvCdmStreamingNoPstTest, UsageTest) { for (size_t i = 0; i < GetParam(); ++i) { // Decrypt data - SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample; + const SubSampleInfo* data = &kEncryptedStreamingNoPstSubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( @@ -1282,6 +1340,218 @@ TEST_P(WvCdmStreamingNoPstTest, UsageTest) { INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingNoPstTest, ::testing::Values(0, 1, 2)); +class WvCdmStreamingPstTest : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +// TODO(b275651559): Re-enable test once the issue with "license remaining +// duration" is addressed. +TEST_P(WvCdmStreamingPstTest, DISABLED_UsageTest) { + Unprovision(); + Provision(); + + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_); + GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming); + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + + ValidateResponse(video_widevine::STREAMING, true); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(true, false, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + const size_t minutes = GetParam(); + for (size_t m = 0; m < minutes; ++m) { + // Decrypt data + const SubSampleInfo& data = kEncryptedStreamingClip8SubSample; + + 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.data()); + decryption_parameters.is_encrypted = data.is_encrypted; + decryption_parameters.is_secure = data.is_secure; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_EQ(data.decrypt_data, decrypt_buffer); + + sleep(kMinute); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(true, false, &license_duration_remaining, + &playback_duration_remaining); + + if (initial_license_duration_remaining == kUnlimitedDurationValue) { + EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue); + } else { + EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, + expected_seconds_since_license_received, kClockTolerance) + << "initial_license_duration_remaining = " + << initial_license_duration_remaining + << ", license_duration_remaining = " << license_duration_remaining; + } + + if (initial_playback_duration_remaining == kUnlimitedDurationValue) { + EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue); + } else { + EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, + expected_seconds_since_initial_playback, kClockTolerance) + << "initial_playback_duration_remaining = " + << initial_playback_duration_remaining + << ", playback_duration_remaining = " << playback_duration_remaining; + } + + decryptor_->CloseSession(session_id_); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingPstTest, ::testing::Values(0, 1, 2)); + +class WvCdmStreamingUsageReportTest + : public WvCdmExtendedDurationTest, + public ::testing::WithParamInterface {}; + +// TODO(b275651559): Re-enable test once the issue with "license remaining +// duration" is addressed. +TEST_P(WvCdmStreamingUsageReportTest, DISABLED_UsageTest) { + Unprovision(); + Provision(); + + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_); + GenerateKeyRequest(kStreamingClip8PstInitData, kLicenseTypeStreaming); + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + + ValidateResponse(video_widevine::STREAMING, true); + + int64_t initial_license_duration_remaining = 0; + int64_t initial_playback_duration_remaining = 0; + QueryKeyStatus(true, false, &initial_license_duration_remaining, + &initial_playback_duration_remaining); + + sleep(kMinute); + int64_t expected_seconds_since_license_received = kMinute; + int64_t expected_seconds_since_initial_playback = 0; + int64_t expected_seconds_since_last_playback = 0; + + const size_t minutes = GetParam(); + for (size_t m = 0; m < minutes; ++m) { + // Decrypt data + const SubSampleInfo& data = kEncryptedStreamingClip8SubSample; + + 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.data()); + decryption_parameters.is_encrypted = data.is_encrypted; + decryption_parameters.is_secure = data.is_secure; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, + decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_EQ(data.decrypt_data, decrypt_buffer); + + sleep(kMinute); + expected_seconds_since_license_received += kMinute; + expected_seconds_since_initial_playback += kMinute; + expected_seconds_since_last_playback = kMinute; + } + + // Query and validate usage information + int64_t license_duration_remaining = 0; + int64_t playback_duration_remaining = 0; + QueryKeyStatus(true, false, &license_duration_remaining, + &playback_duration_remaining); + + // For unlimited "rental durations", the "license duration" will + // effectively be unlimited. Remaining license duration in this + // case is represented by |kUnlimitedDurationValue| and will not + // change over time. + if (initial_license_duration_remaining == kUnlimitedDurationValue) { + EXPECT_EQ(license_duration_remaining, kUnlimitedDurationValue); + } else { + EXPECT_NEAR(initial_license_duration_remaining - license_duration_remaining, + expected_seconds_since_license_received, kClockTolerance) + << "initial_license_duration_remaining = " + << initial_license_duration_remaining + << ", license_duration_remaining = " << license_duration_remaining; + } + + if (initial_playback_duration_remaining == kUnlimitedDurationValue) { + EXPECT_EQ(playback_duration_remaining, kUnlimitedDurationValue); + } else { + EXPECT_NEAR(initial_playback_duration_remaining - playback_duration_remaining, + expected_seconds_since_initial_playback, kClockTolerance) + << "initial_playback_duration_remaining = " + << initial_playback_duration_remaining + << ", playback_duration_remaining = " << playback_duration_remaining; + } + + decryptor_->CloseSession(session_id_); + + // Create usage report and validate + CdmUsageReportList usage_reports; + CdmResponseType status = + decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status); + int error_count = 0; + while (!usage_reports.empty()) { + for (size_t i = 0; i < usage_reports.size(); ++i) { + ValidateReleaseRequest(usage_reports[i], + expected_seconds_since_initial_playback != 0, + expected_seconds_since_license_received, + expected_seconds_since_initial_playback, + expected_seconds_since_last_playback); + const CdmKeyResponse release_msg = GetSecureStopResponse(config_.license_server(), + config_.client_auth(), usage_reports[i]); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)) + << i << "/" << usage_reports.size() << " (err " << (error_count++) << ")" + << release_msg; + } + ASSERT_LE(error_count, 100); // Give up after 100 failures. + status = decryptor_->GetUsageInfo("", kDefaultCdmIdentifier, &usage_reports); + switch (status.code()) { + case KEY_MESSAGE: + EXPECT_FALSE(usage_reports.empty()); + break; + case NO_ERROR: + EXPECT_TRUE(usage_reports.empty()); + break; + default: + FAIL() << "GetUsageInfo failed with error " << static_cast(status); + break; + } + } + + // Validate that update usage table entry is exercised. + drm_metrics::WvCdmMetrics metrics; + ASSERT_EQ(NO_ERROR, decryptor_->GetMetrics(kDefaultCdmIdentifier, &metrics)); + ValidateHasUpdateUsageEntry(metrics); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmStreamingUsageReportTest, + ::testing::Values(0, 1, 2)); + class WvCdmOfflineUsageReportTest : public WvCdmExtendedDurationTest, public ::testing::WithParamInterface {}; @@ -1350,7 +1620,7 @@ TEST_P(WvCdmOfflineUsageReportTest, UsageTest) { } // Decrypt data - SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; + const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; for (size_t i = 0; i < data->num_of_subsamples; i++) { std::vector decrypt_buffer((data + i)->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( @@ -1464,7 +1734,7 @@ TEST_F(WvCdmExtendedDurationTest, MaxUsageEntryOfflineRecoveryTest) { ++number_of_valid_offline_sessions; // Decrypt data - SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; + const SubSampleInfo* data = &kEncryptedOfflineClip2SubSample; std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &data->key_id, &data->encrypt_data.front(), diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 729b527c..22eb3bc5 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -109,7 +109,7 @@ struct SubSampleInfo { uint8_t subsample_flags; }; -const SubSampleInfo clear_sub_sample = { +const SubSampleInfo kClearSubSample = { true, 1, true, @@ -138,7 +138,7 @@ const SubSampleInfo clear_sub_sample = { 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}; -const SubSampleInfo clear_sub_samples[2] = { +const SubSampleInfo kClearSubSamples[2] = { // block 0, key SD, encrypted, 128b {true, 1, true, false, false, wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), @@ -170,7 +170,7 @@ const SubSampleInfo clear_sub_samples[2] = { wvutil::a2b_hex("f6f4b1e600a5b67813ed2bded913ba9f"), 0, OEMCrypto_LastSubsample}}; -const SubSampleInfo clear_sub_sample_no_key = { +const SubSampleInfo kClearSubSampleNoKey = { false, 1, false, @@ -199,7 +199,7 @@ const SubSampleInfo clear_sub_sample_no_key = { 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}; -const SubSampleInfo single_encrypted_sub_sample = { +const SubSampleInfo kSingleEncryptedSubSample = { // key SD, encrypted, 256b true, 1, @@ -229,7 +229,7 @@ const SubSampleInfo single_encrypted_sub_sample = { 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}; -const SubSampleInfo single_encrypted_offline_sub_sample = { +const SubSampleInfo kSingleEncryptedOfflineSubSample = { // key SD, encrypted, 256b true, 1, @@ -259,7 +259,7 @@ const SubSampleInfo single_encrypted_offline_sub_sample = { 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}; -const SubSampleInfo switch_key_encrypted_sub_samples[2] = { +const SubSampleInfo kSwitchKeyEncryptedSubSamples[2] = { // block 0, key SD, encrypted, 256b {true, 2, true, true, false, wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), @@ -307,7 +307,7 @@ const SubSampleInfo switch_key_encrypted_sub_samples[2] = { wvutil::a2b_hex("f56ab022666de858920e532f19bb32f6"), 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}}; -const SubSampleInfo partial_encrypted_sub_samples[3] = { +const SubSampleInfo kPartialEncryptedSubSamples[3] = { // block 1, key SD, encrypted, 1-125b, offset 0 {true, 3, true, true, false, wvutil::a2bs_hex("371EA35E1A985D75D198A7F41020DC23"), @@ -349,7 +349,7 @@ const SubSampleInfo partial_encrypted_sub_samples[3] = { wvutil::a2b_hex("6ba18dd40f49da7f64c368e4db43fc93"), 11, OEMCrypto_LastSubsample}}; -const SubSampleInfo single_encrypted_sub_sample_short_expiry = { +const SubSampleInfo kSingleEncryptedSubSampleShortExpiry = { // key 1, encrypted, 256b true, 1, @@ -379,7 +379,7 @@ const SubSampleInfo single_encrypted_sub_sample_short_expiry = { 0, OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample}; -const SubSampleInfo usage_info_sub_samples_icp[] = { +const SubSampleInfo kSecureStopSubSamplesIcp[] = { {true, 1, true, true, false, wvutil::a2bs_hex("E82DDD1D07CBB31CDD31EBAAE0894609"), wvutil::a2b_hex( @@ -593,16 +593,43 @@ struct SessionSharingSubSampleInfo { wvcdm::CdmIdentifier cdm_identifier; }; -const SessionSharingSubSampleInfo session_sharing_sub_samples[] = { - {&clear_sub_sample, false, wvcdm::kDefaultCdmIdentifier}, - {&clear_sub_sample, true, wvcdm::kDefaultCdmIdentifier}, - {&clear_sub_sample_no_key, false, wvcdm::kDefaultCdmIdentifier}, - {&clear_sub_sample_no_key, true, wvcdm::kDefaultCdmIdentifier}, - {&single_encrypted_sub_sample, false, wvcdm::kDefaultCdmIdentifier}, - {&single_encrypted_sub_sample, true, wvcdm::kDefaultCdmIdentifier}, +const SessionSharingSubSampleInfo kSessionSharingSubSamples[] = { + {&kClearSubSample, false, wvcdm::kDefaultCdmIdentifier}, + {&kClearSubSample, true, wvcdm::kDefaultCdmIdentifier}, + {&kClearSubSampleNoKey, false, wvcdm::kDefaultCdmIdentifier}, + {&kClearSubSampleNoKey, true, wvcdm::kDefaultCdmIdentifier}, + {&kSingleEncryptedSubSample, false, wvcdm::kDefaultCdmIdentifier}, + {&kSingleEncryptedSubSample, true, wvcdm::kDefaultCdmIdentifier}, // The last entry simulates session sharing using the non default // identifier. - {&single_encrypted_sub_sample, true, kExampleIdentifier}}; + {&kSingleEncryptedSubSample, true, kExampleIdentifier}}; + +struct SecureStopSubSampleInfo { + const SubSampleInfo* sub_sample; + size_t sub_sample_count; + wvcdm::RequestedSecurityLevel security_level; + std::string app_id; +}; + +void PrintTo(const SecureStopSubSampleInfo* param, std::ostream* os) { + if (param) { + *os << "sub_sample_count=" << param->sub_sample_count << ", " + << "level=" << (param->security_level == wvcdm::kLevel3 ? "L3" : "L1") + << " app_id=" << param->app_id; + } else { + *os << ""; + } +} + +const SecureStopSubSampleInfo kSecureStopSubSampleInfo[] = { + {&kSecureStopSubSamplesIcp[0], 1, wvcdm::kLevelDefault, ""}, + {&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevelDefault, ""}, + {&kSecureStopSubSamplesIcp[0], 5, wvcdm::kLevelDefault, ""}, + {&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevelDefault, "other app id"}, + {&kSecureStopSubSamplesIcp[0], 1, wvcdm::kLevel3, ""}, + {&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevel3, ""}, + {&kSecureStopSubSamplesIcp[0], 5, wvcdm::kLevel3, ""}, + {&kSecureStopSubSamplesIcp[0], 3, wvcdm::kLevel3, "other app id"}}; struct UsageLicenseAndSubSampleInfo { std::string pssh; @@ -784,15 +811,15 @@ const std::string kDualPsshEntitlementWithKeyRotation[] = { "8622100d9cfe972bc17003b49ecd5f45" "f3bb28")}; -std::string kProviderSessionTokenStreamingClip3 = +const std::string kProviderSessionTokenStreamingClip3 = wvutil::a2bs_hex("4851305A4A4156485A554936444E4931"); -std::string kProviderSessionTokenStreamingClip4 = +const std::string kProviderSessionTokenStreamingClip4 = wvutil::a2bs_hex("4942524F4355544E5557553145463243"); -std::string kProviderSessionTokenStreamingClip7 = +const std::string kProviderSessionTokenStreamingClip7 = wvutil::a2bs_hex("44434C53524F4E30394C4E5535544B4C"); -std::string kProviderSessionTokenStreamingClip20 = +const std::string kProviderSessionTokenStreamingClip20 = wvutil::a2bs_hex("4851305A4A4156485A554936444E4931"); -std::string kProviderSessionTokenStreamingClip21 = +const std::string kProviderSessionTokenStreamingClip21 = wvutil::a2bs_hex("4851305A4A4156485A554936444E4931"); // playback duration is 10 seconds+uncertainty window @@ -800,15 +827,15 @@ const std::chrono::milliseconds kExpirationStreamingClip21PlaybackDurationTimeMs = std::chrono::milliseconds(12 * 1000); -UsageLicenseAndSubSampleInfo kUsageLicenseTestVector1[] = { - {kPsshStreamingClip3, &usage_info_sub_samples_icp[0], +const UsageLicenseAndSubSampleInfo kUsageLicenseTestVector1[] = { + {kPsshStreamingClip3, &kSecureStopSubSamplesIcp[0], kProviderSessionTokenStreamingClip3}, - {kPsshStreamingClip4, &usage_info_sub_samples_icp[1], + {kPsshStreamingClip4, &kSecureStopSubSamplesIcp[1], kProviderSessionTokenStreamingClip4}, }; -UsageLicenseAndSubSampleInfo kUsageLicenseTestVector2[] = { - {kPsshStreamingClip7, &usage_info_sub_samples_icp[4], +const UsageLicenseAndSubSampleInfo kUsageLicenseTestVector2[] = { + {kPsshStreamingClip7, &kSecureStopSubSamplesIcp[4], kProviderSessionTokenStreamingClip7}, // TODO(rfrias): Add another streaming usage license. Streaming // clip 5 has includes a randomly generated PST, while @@ -823,7 +850,8 @@ struct RenewWithClientIdTestConfiguration { std::string test_description; }; -void PrintTo(RenewWithClientIdTestConfiguration* param, std::ostream* os) { +void PrintTo(const RenewWithClientIdTestConfiguration* param, + std::ostream* os) { if (param) { *os << param->test_description; } else { @@ -831,8 +859,8 @@ void PrintTo(RenewWithClientIdTestConfiguration* param, std::ostream* os) { } } -RenewWithClientIdTestConfiguration - streaming_renew_client_id_test_configuration[] = { +const RenewWithClientIdTestConfiguration + kStreamingRenewClientIdTestConfiguration[] = { {false, false, false, false, "Test: Streaming renewal without client Id"}, {true, false, false, false, "Test: Streaming renewal with client Id"}, @@ -845,8 +873,8 @@ RenewWithClientIdTestConfiguration // Note: Offline renewal/release with encrypted client Ids and where a service // certificate needs to be fetched is not supported. -RenewWithClientIdTestConfiguration - offline_release_client_id_test_configuration[] = { +const RenewWithClientIdTestConfiguration + kOfflineReleaseClientIdTestConfiguration[] = { {false, false, false, false, "Test: Offline renewal/release without client Id"}, {true, false, false, false, @@ -856,7 +884,7 @@ RenewWithClientIdTestConfiguration {true, false, true, true, "Test: Offline renewal/release, service cert provided"}}; -RenewWithClientIdTestConfiguration usage_client_id_test_configuration[] = { +const RenewWithClientIdTestConfiguration kUsageClientIdTestConfiguration[] = { {false, false, false, false, "Test: Usage reporting without client Id"}, {true, false, false, false, "Test: Usage reporting with client Id"}}; @@ -867,7 +895,7 @@ struct EntitlementTestConfiguration { const SubSampleInfo* sub_sample_with_rotated_keys; }; -EntitlementTestConfiguration kEntitlementTestConfiguration[] = { +const EntitlementTestConfiguration kEntitlementTestConfiguration[] = { {// Single Widevine PSSH containing PSSH data of type ENTITLED_KEY kSinglePsshEntitlementWithKeyRotation[0], kSinglePsshEntitlementWithKeyRotation[1], @@ -956,7 +984,7 @@ struct HlsSegmentInfo { uint8_t subsample_flags; }; -HlsSegmentInfo kAes128SingleSegmentInfo[] = { +const HlsSegmentInfo kAes128SingleSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -982,7 +1010,7 @@ HlsSegmentInfo kAes128SingleSegmentInfo[] = { }, }; -HlsSegmentInfo kAes128PartialSegmentInfo[] = { +const HlsSegmentInfo kAes128PartialSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1031,7 +1059,7 @@ HlsSegmentInfo kAes128PartialSegmentInfo[] = { }, }; -HlsSegmentInfo kAes128MultiSegmentInfo[] = { +const HlsSegmentInfo kAes128MultiSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1080,7 +1108,7 @@ HlsSegmentInfo kAes128MultiSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes10ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes10ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1090,7 +1118,7 @@ HlsSegmentInfo kSampleAes10ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes16ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes16ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1100,7 +1128,7 @@ HlsSegmentInfo kSampleAes16ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes18ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes18ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1110,7 +1138,7 @@ HlsSegmentInfo kSampleAes18ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes160ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes160ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1132,7 +1160,7 @@ HlsSegmentInfo kSampleAes160ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes175ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes175ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1155,7 +1183,7 @@ HlsSegmentInfo kSampleAes175ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes176ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes176ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1178,7 +1206,7 @@ HlsSegmentInfo kSampleAes176ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes192ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes192ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1200,7 +1228,7 @@ HlsSegmentInfo kSampleAes192ByteSegmentInfo[] = { }, }; -HlsSegmentInfo kSampleAes338ByteSegmentInfo[] = { +const HlsSegmentInfo kSampleAes338ByteSegmentInfo[] = { { true, wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1235,11 +1263,11 @@ HlsSegmentInfo kSampleAes338ByteSegmentInfo[] = { struct HlsDecryptionInfo { bool sample_aes; size_t number_of_segments; - HlsSegmentInfo* segment_info; + const HlsSegmentInfo* segment_info; const std::string& attribute_list; }; -HlsDecryptionInfo kHlsDecryptionTestVectors[] = { +const HlsDecryptionInfo kHlsDecryptionTestVectors[] = { {false, 1, &kAes128SingleSegmentInfo[0], kAttributeListAes128}, {false, 2, &kAes128PartialSegmentInfo[0], kAttributeListAes128}, {false, 2, &kAes128MultiSegmentInfo[0], kAttributeListAes128}, @@ -1300,7 +1328,7 @@ const std::string kHlsPsshSampleAesFourCC = wvutil::a2bs_hex( "6165735F73747265616D696E6748F3C6" "899B06"); -HlsDecryptionInfo kHlsFourCCBackwardCompatibilityTestVectors[] = { +const HlsDecryptionInfo kHlsFourCCBackwardCompatibilityTestVectors[] = { {false, 2, &kAes128MultiSegmentInfo[0], kHlsPsshAes128LittleEndianFourCC}, {true, 1, &kSampleAes160ByteSegmentInfo[0], kHlsPsshSampleAesLittleEndianFourCC}, @@ -1339,7 +1367,7 @@ struct Cenc30SampleInfo { wvcdm::CdmCipherMode cipher_mode; }; -Cenc30SampleInfo kCenc30CencKey33Sample = { +const Cenc30SampleInfo kCenc30CencKey33Sample = { true, wvutil::a2bs_hex("30303030303030303030303030303033"), wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1365,7 +1393,7 @@ Cenc30SampleInfo kCenc30CencKey33Sample = { wvcdm::kCipherModeCtr, }; -Cenc30SampleInfo kCenc30CencKey32Sample = { +const Cenc30SampleInfo kCenc30CencKey32Sample = { true, wvutil::a2bs_hex("30303030303030303030303030303032"), wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1391,7 +1419,7 @@ Cenc30SampleInfo kCenc30CencKey32Sample = { wvcdm::kCipherModeCtr, }; -Cenc30SampleInfo kCenc30CbcsKey32Sample = { +const Cenc30SampleInfo kCenc30CbcsKey32Sample = { true, wvutil::a2bs_hex("30303030303030303030303030303032"), wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1417,7 +1445,7 @@ Cenc30SampleInfo kCenc30CbcsKey32Sample = { wvcdm::kCipherModeCbc, }; -Cenc30SampleInfo kCenc30CbcsKey33Sample = { +const Cenc30SampleInfo kCenc30CbcsKey33Sample = { true, wvutil::a2bs_hex("30303030303030303030303030303033"), wvutil::a2bs_hex("9FBE45DD47DA7EBA09A3E24CBA95C9AF"), @@ -1448,7 +1476,7 @@ struct SingleSampleDecryptionInfo { Cenc30SampleInfo sample_info; }; -SingleSampleDecryptionInfo kCenc30DecryptionData[2] = { +const SingleSampleDecryptionInfo kCenc30DecryptionData[2] = { {kFullCencPssh, kCenc30CencKey33Sample}, {kFullCbcsPssh, kCenc30CbcsKey33Sample}, }; @@ -1458,7 +1486,7 @@ struct FourSampleDecryptionInfo { Cenc30SampleInfo sample_info[4]; }; -FourSampleDecryptionInfo kCenc30SwitchCipherData[8] = { +const FourSampleDecryptionInfo kCenc30SwitchCipherData[8] = { // Switch between cipher modes {kFullCencPssh, { @@ -1996,14 +2024,14 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { // Post a request and extract the signed provisioning message from // the HTTP response. - std::string GetUsageInfoResponse(const std::string& server_url, - const std::string& client_auth, - const std::string& usage_info_request) { + std::string GetSecureStopResponse(const std::string& server_url, + const std::string& client_auth, + const std::string& secure_stop_request) { // Use secure connection and chunk transfer coding. UrlRequest url_request(server_url + client_auth); EXPECT_TRUE(url_request.is_connected()) << "Fail to connect to " << server_url << client_auth; - url_request.PostRequest(usage_info_request); + url_request.PostRequest(secure_stop_request); std::string message; EXPECT_TRUE(url_request.GetResponse(&message)); @@ -2013,13 +2041,13 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { } EXPECT_EQ(kHttpOk, http_status_code); - std::string usage_info; + std::string secure_stop_response; if (kHttpOk == http_status_code) { LicenseRequest license; - license.GetDrmMessage(message, usage_info); - LOGV("HTTP response body: (%zu bytes)", usage_info.size()); + license.GetDrmMessage(message, secure_stop_response); + LOGV("HTTP response body: (%zu bytes)", secure_stop_response.size()); } - return usage_info; + return secure_stop_response; } void VerifyKeyRequestResponse(const std::string& server_url, @@ -2045,7 +2073,7 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase { client_auth, is_usage, response); } - void VerifyKeyRequestResponse(CdmResponseType expected_response, + void VerifyKeyRequestResponse(const CdmResponseType& expected_response, const std::string& server_url, const std::string& client_auth, bool is_usage, std::string* response) { @@ -3506,7 +3534,7 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewalAndRelease) { EXPECT_FALSE(key_set_id_.empty()); // Verify that we can decrypt a subsample - const SubSampleInfo* data = &single_encrypted_offline_sub_sample; + const SubSampleInfo* data = &kSingleEncryptedOfflineSubSample; std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(), @@ -3565,12 +3593,12 @@ TEST_F(WvCdmRequestLicenseTest, OfflineLicenseRenewalAndRelease) { VerifyKeyRequestResponse(config_.license_server(), client_auth); } -class WvCdmEntitlementTest - : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; +class WvCdmEntitlementTest : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface< + const EntitlementTestConfiguration*> {}; TEST_P(WvCdmEntitlementTest, EntitlementWithKeyRotation) { - EntitlementTestConfiguration* config = GetParam(); + const EntitlementTestConfiguration* config = GetParam(); decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, nullptr, &session_id_); @@ -3604,7 +3632,7 @@ TEST_F(WvCdmRequestLicenseTest, RemoveKeys) { GenerateKeyRequest(binary_key_id(), kLicenseTypeStreaming); VerifyKeyRequestResponse(config_.license_server(), config_.client_auth()); - const SubSampleInfo* data = &single_encrypted_sub_sample; + const SubSampleInfo* data = &kSingleEncryptedSubSample; std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( &data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(), @@ -3631,10 +3659,10 @@ TEST_F(WvCdmRequestLicenseTest, RemoveKeys) { class WvCdmStreamingLicenseRenewalTest : public WvCdmRequestLicenseTest, public ::testing::WithParamInterface< - RenewWithClientIdTestConfiguration*> {}; + const RenewWithClientIdTestConfiguration*> {}; TEST_P(WvCdmStreamingLicenseRenewalTest, WithClientId) { - RenewWithClientIdTestConfiguration* config = GetParam(); + const RenewWithClientIdTestConfiguration* config = GetParam(); std::string key_id; if (config->always_include_client_id) { key_id = wvutil::a2bs_hex( @@ -3751,19 +3779,19 @@ TEST_P(WvCdmStreamingLicenseRenewalTest, WithClientId) { INSTANTIATE_TEST_CASE_P( Cdm, WvCdmStreamingLicenseRenewalTest, - ::testing::Range(&streaming_renew_client_id_test_configuration[0], - &streaming_renew_client_id_test_configuration[5])); + ::testing::Range(&kStreamingRenewClientIdTestConfiguration[0], + &kStreamingRenewClientIdTestConfiguration[5])); class WvCdmOfflineLicenseReleaseTest : public WvCdmRequestLicenseTest, public ::testing::WithParamInterface< - RenewWithClientIdTestConfiguration*> {}; + const RenewWithClientIdTestConfiguration*> {}; TEST_P(WvCdmOfflineLicenseReleaseTest, WithClientId) { Unprovision(); Provision(); - RenewWithClientIdTestConfiguration* config = GetParam(); + const RenewWithClientIdTestConfiguration* config = GetParam(); std::string key_id; std::string client_auth; GetOfflineConfiguration(&key_id, &client_auth); @@ -3897,8 +3925,1144 @@ TEST_P(WvCdmOfflineLicenseReleaseTest, WithClientId) { INSTANTIATE_TEST_CASE_P( Cdm, WvCdmOfflineLicenseReleaseTest, - ::testing::Range(&offline_release_client_id_test_configuration[0], - &offline_release_client_id_test_configuration[4])); + ::testing::Range(&kOfflineReleaseClientIdTestConfiguration[0], + &kOfflineReleaseClientIdTestConfiguration[4])); + +class WvCdmUsageTest : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface< + const RenewWithClientIdTestConfiguration*> {}; + +TEST_P(WvCdmUsageTest, WithClientId) { + Unprovision(); + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + const std::string app_id = ""; + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), &psts)); + + const RenewWithClientIdTestConfiguration* config = GetParam(); + + std::string key_id; + if (config->always_include_client_id) { + key_id = wvutil::a2bs_hex( // streaming_clip20 + "000000437073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000023" // Widevine system id + "08011a0d7769646576696e655f746573" // pssh data + "74221073747265616d696e675f636c69703230"); + } else { + key_id = wvutil::a2bs_hex( // streaming_clip3 + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f746573" // pssh data + "74220f73747265616d696e675f636c697033"); + } + wvcdm::CdmAppParameterMap app_parameters; + TestWvCdmClientPropertySet property_set; + + const SubSampleInfo* data = &kSecureStopSubSamplesIcp[0]; + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + + GenerateKeyRequest(key_id, app_parameters, kLicenseTypeStreaming, + &property_set); + + std::string key_response; + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + true, &key_response); + + // Validate signed license + SignedMessage signed_message; + EXPECT_TRUE(signed_message.ParseFromString(key_response)) + << config->test_description; + EXPECT_EQ(SignedMessage::LICENSE, signed_message.type()) + << config->test_description; + EXPECT_TRUE(signed_message.has_signature()) << config->test_description; + EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description; + + // Verify license request + video_widevine::License license; + EXPECT_TRUE(license.ParseFromString(signed_message.msg())) + << config->test_description; + + // Verify always_include_client_id + EXPECT_EQ(config->always_include_client_id, + license.policy().has_always_include_client_id()); + + EXPECT_FALSE(license.id().provider_session_token().empty()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + + CdmUsageReportList usage_reports; + const CdmResponseType status = + decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports); + + EXPECT_EQ(KEY_MESSAGE, status); + ASSERT_FALSE(usage_reports.empty()); + + // Validate signed renewal request + EXPECT_TRUE(signed_message.ParseFromString(usage_reports[0])) + << config->test_description; + EXPECT_EQ(SignedMessage::LICENSE_REQUEST, signed_message.type()) + << config->test_description; + EXPECT_TRUE(signed_message.has_signature()) << config->test_description; + EXPECT_TRUE(!signed_message.msg().empty()) << config->test_description; + + // Verify license request + video_widevine::LicenseRequest license_renewal; + EXPECT_TRUE(license_renewal.ParseFromString(signed_message.msg())) + << config->test_description; + + // Verify ClientId + EXPECT_EQ(config->always_include_client_id && !config->enable_privacy_mode, + license_renewal.has_client_id()) + << config->test_description; + + if (config->enable_privacy_mode) { + EXPECT_EQ(config->always_include_client_id, + license_renewal.has_encrypted_client_id()) + << config->test_description; + EXPECT_NE( + 0u, license_renewal.encrypted_client_id().encrypted_client_id().size()); + } + + const CdmKeyResponse release_msg = GetSecureStopResponse( + config_.license_server(), config_.client_auth(), usage_reports[0]); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)); +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmUsageTest, + ::testing::Range(&kUsageClientIdTestConfiguration[0], + &kUsageClientIdTestConfiguration[2])); + +TEST_F(WvCdmRequestLicenseTest, SecureStopRetryTest) { + Unprovision(); + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + const std::string app_id = ""; + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), &psts)); + + const SubSampleInfo* data = &kSecureStopSubSamplesIcp[0]; + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_); + const std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c697033"); + + GenerateKeyRequest(key_id, kLicenseTypeStreaming, nullptr); + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + + CdmUsageReportList usage_reports; + CdmResponseType status = + decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status.code()); + + // Discard and retry to verify usage reports can be generated multiple times + // before release. + status = + decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status.code()); + int error_count = 0; + while (usage_reports.size() > 0) { + for (size_t i = 0; i < usage_reports.size(); ++i) { + const CdmKeyResponse release_msg = GetSecureStopResponse( + config_.license_server(), config_.client_auth(), usage_reports[i]); + EXPECT_EQ(NO_ERROR, decryptor_->ReleaseUsageInfo(release_msg, + kDefaultCdmIdentifier)) + << i << "/" << usage_reports.size() << " (err " << (error_count++) + << ")" << release_msg; + } + ASSERT_LE(error_count, 100); // Give up after 100 failures. + status = + decryptor_->GetUsageInfo(app_id, kDefaultCdmIdentifier, &usage_reports); + switch (status.code()) { + case KEY_MESSAGE: + EXPECT_FALSE(usage_reports.empty()); + break; + case NO_ERROR: + EXPECT_TRUE(usage_reports.empty()); + break; + default: + FAIL() << "GetUsageInfo failed with error " << static_cast(status); + break; + } + } +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_ReleaseThreeRecords) { + Unprovision(); + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + const std::string app_id = ""; + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), &psts)); + + std::string session_id_clip3, session_id_clip4, session_id_clip7; + + // Open session for streaming_clip3 and verify decryption is successful + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_clip3); + + session_id_ = session_id_clip3; + GenerateKeyRequest(kPsshStreamingClip3, kLicenseTypeStreaming, nullptr); + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + EXPECT_TRUE(VerifyDecryption(session_id_clip3, kSecureStopSubSamplesIcp[0])); + + // Open session for streaming_clip4 and verify decryption is successful + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_clip4); + + session_id_ = session_id_clip4; + GenerateKeyRequest(kPsshStreamingClip4, kLicenseTypeStreaming, nullptr); + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + EXPECT_TRUE(VerifyDecryption(session_id_clip4, kSecureStopSubSamplesIcp[1])); + + // Open session for streaming_clip7 and verify decryption is successful + decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, + nullptr, &session_id_clip7); + + session_id_ = session_id_clip7; + GenerateKeyRequest(kPsshStreamingClip7, kLicenseTypeStreaming, nullptr); + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + EXPECT_TRUE(VerifyDecryption(session_id_clip7, kSecureStopSubSamplesIcp[4])); + + // Close session for streaming_clip4 and release usage information + decryptor_->CloseSession(session_id_clip4); + + CdmUsageReport usage_report; + CdmResponseType status = + decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip4, + kDefaultCdmIdentifier, &usage_report); + EXPECT_EQ(KEY_MESSAGE, status); + ASSERT_FALSE(usage_report.empty()); + CdmKeyMessage release_msg = GetSecureStopResponse( + config_.license_server(), config_.client_auth(), usage_report); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)); + + decryptor_->CloseSession(session_id_clip7); + + status = decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip7, + kDefaultCdmIdentifier, &usage_report); + EXPECT_EQ(KEY_MESSAGE, status); + ASSERT_FALSE(usage_report.empty()); + release_msg = GetSecureStopResponse(config_.license_server(), + config_.client_auth(), usage_report); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)); + + decryptor_->CloseSession(session_id_clip3); + + status = decryptor_->GetUsageInfo(app_id, kProviderSessionTokenStreamingClip3, + kDefaultCdmIdentifier, &usage_report); + EXPECT_EQ(KEY_MESSAGE, status); + ASSERT_FALSE(usage_report.empty()); + release_msg = GetSecureStopResponse(config_.license_server(), + config_.client_auth(), usage_report); + EXPECT_EQ(NO_ERROR, + decryptor_->ReleaseUsageInfo(release_msg, kDefaultCdmIdentifier)); +} + +class WvCdmSecureStopTest + : public WvCdmRequestLicenseTest, + public ::testing::WithParamInterface {}; + +TEST_P(WvCdmSecureStopTest, SecureStop) { + Unprovision(); + + const SecureStopSubSampleInfo* secure_stop_data = GetParam(); + TestWvCdmClientPropertySet client_property_set; + TestWvCdmClientPropertySet* property_set = nullptr; + if (kLevel3 == secure_stop_data->security_level) { + client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); + property_set = &client_property_set; + Provision(kDefaultCdmIdentifier, kLevel3); + Provision(); + } else { + Provision(); + } + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(secure_stop_data->app_id), &psts)); + + for (size_t i = 0; i < secure_stop_data->sub_sample_count; ++i) { + const SubSampleInfo& data = secure_stop_data->sub_sample[i]; + decryptor_->OpenSession(config_.key_system(), property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c6970"); + + const char ch = '3' + i; + key_id.append(1, ch); + + GenerateKeyRequest(key_id, kLicenseTypeStreaming, property_set); + + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + else + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + CdmUsageReportList usage_reports; + CdmResponseType status = decryptor_->GetUsageInfo( + secure_stop_data->app_id, kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status); + int error_count = 0; + while (!usage_reports.empty()) { + for (size_t i = 0; i < usage_reports.size(); ++i) { + const CdmKeyMessage release_msg = GetSecureStopResponse( + config_.license_server(), config_.client_auth(), usage_reports[i]); + EXPECT_EQ(NO_ERROR, decryptor_->ReleaseUsageInfo(release_msg, + kDefaultCdmIdentifier)) + << i << "/" << usage_reports.size() << " (err " << (error_count++) + << ")" << release_msg; + } + ASSERT_LE(error_count, 100); // Give up after 100 failures. + status = decryptor_->GetUsageInfo(secure_stop_data->app_id, + kDefaultCdmIdentifier, &usage_reports); + EXPECT_EQ(usage_reports.empty() ? NO_ERROR : KEY_MESSAGE, status); + } +} + +INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSecureStopTest, + ::testing::Values(&kSecureStopSubSampleInfo[0], + &kSecureStopSubSampleInfo[1], + &kSecureStopSubSampleInfo[2], + &kSecureStopSubSampleInfo[3], + &kSecureStopSubSampleInfo[4], + &kSecureStopSubSampleInfo[5], + &kSecureStopSubSampleInfo[6], + &kSecureStopSubSampleInfo[7])); + +TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveAllTest) { + Unprovision(); + + const std::string app_id_empty = ""; + const std::string app_id_not_empty = "not empty"; + + TestWvCdmClientPropertySet property_set; + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) { + const SubSampleInfo& data = kSecureStopSubSamplesIcp[i]; + property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c6970"); + + const char ch = '3' + i; + key_id.append(1, ch); + + GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set); + + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + else + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data.is_encrypted; + decryption_parameters.is_secure = data.is_secure; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + CdmUsageReportList usage_reports; + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo( + app_id_empty, kDefaultCdmIdentifier, &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveCorruptedTest) { + Unprovision(); + + const std::string app_id_empty = ""; + const std::string app_id_not_empty = "not empty"; + + TestWvCdmClientPropertySet property_set; + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + ASSERT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) { + const SubSampleInfo& data = kSecureStopSubSamplesIcp[i]; + property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c6970"); + + const char ch = '3' + i; + key_id.append(1, ch); + + GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set); + + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + else + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data.is_encrypted; + decryption_parameters.is_secure = data.is_secure; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + CdmUsageReportList usage_reports; + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + + // Read in usage info file + std::string path; + EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path)); + const std::string secure_stop_not_empty_app_id_file_name = + path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty); + + ssize_t file_size = + file_system.FileSize(secure_stop_not_empty_app_id_file_name); + EXPECT_LT(4, file_size); + std::unique_ptr file = file_system.Open( + secure_stop_not_empty_app_id_file_name, FileSystem::kReadOnly); + ASSERT_NE(file, nullptr); + std::string file_data; + file_data.resize(file_size); + ssize_t bytes = file->Read(&file_data[0], file_data.size()); + EXPECT_EQ(file_size, bytes); + + // Corrupt the hash of the usage info file with non-empty app id and write + // it back out + memset(&file_data[0] + bytes - 4, 0, 4); + file = file_system.Open(secure_stop_not_empty_app_id_file_name, + FileSystem::kCreate | FileSystem::kTruncate); + ASSERT_NE(file, nullptr); + bytes = file->Write(file_data.data(), file_data.size()); + EXPECT_EQ(file_size, bytes); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.size() > 0); + + // Read in usage info file + const std::string secure_stop_empty_app_id_file_name = + path + DeviceFiles::GetUsageInfoFileName(app_id_empty); + + file_size = file_system.FileSize(secure_stop_empty_app_id_file_name); + EXPECT_LT(4, file_size); + file = file_system.Open(secure_stop_empty_app_id_file_name, + FileSystem::kReadOnly); + ASSERT_NE(file, nullptr); + file_data.resize(file_size); + bytes = file->Read(&file_data[0], file_data.size()); + EXPECT_EQ(file_size, bytes); + + // Corrupt the hash of the usage info file with empty app id and write it + // back out + memset(&file_data[0] + bytes - 4, 0, 4); + file = file_system.Open(secure_stop_empty_app_id_file_name, + FileSystem::kCreate | FileSystem::kTruncate); + ASSERT_NE(file, nullptr); + bytes = file->Write(file_data.data(), file_data.size()); + EXPECT_EQ(file_size, bytes); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo( + app_id_empty, kDefaultCdmIdentifier, &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveCorruptedTest2) { + Unprovision(); + + const std::string app_id_empty = ""; + const std::string app_id_not_empty = "not empty"; + + TestWvCdmClientPropertySet property_set; + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + for (size_t i = 0; i < ArraySize(kSecureStopSubSamplesIcp); ++i) { + const SubSampleInfo& data = kSecureStopSubSamplesIcp[i]; + property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + std::string key_id = wvutil::a2bs_hex( + "000000427073736800000000" // blob size and pssh + "EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id + "08011a0d7769646576696e655f74657374220f73" // pssh data + "747265616d696e675f636c6970"); + + const char ch = '3' + i; + key_id.append(1, ch); + + GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set); + + // TODO(rfrias): streaming_clip6 is a streaming license without a pst + if (ch == '6') + VerifyKeyRequestResponse(config_.license_server(), config_.client_auth(), + false); + else + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data.is_encrypted; + decryption_parameters.is_secure = data.is_secure; + decryption_parameters.subsample_flags = data.subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data.validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data.decrypt_data.begin(), data.decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + CdmUsageReportList usage_reports; + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + + // Read in usage info file + std::string path; + EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path)); + const std::string secure_stop_not_empty_app_id_file_name = + path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty); + + ssize_t file_size = + file_system.FileSize(secure_stop_not_empty_app_id_file_name); + EXPECT_LT(4, file_size); + std::unique_ptr file = file_system.Open( + secure_stop_not_empty_app_id_file_name, FileSystem::kReadOnly); + ASSERT_NE(file, nullptr); + std::string file_data; + file_data.resize(file_size); + ssize_t bytes = file->Read(&file_data[0], file_data.size()); + EXPECT_EQ(file_size, bytes); + + video_widevine_client::sdk::HashedFile hash_file; + + EXPECT_TRUE(hash_file.ParseFromString(file_data)); + size_t pos = file_data.find(hash_file.hash()); + EXPECT_NE(std::string::npos, pos); + EXPECT_NE(0u, pos); + + // Corrupt the protobuf key field of the hash of the usage info file + // with non-empty app id and write it back out + file_data[pos - 1] = 0xff; + + file = file_system.Open(secure_stop_not_empty_app_id_file_name, + FileSystem::kCreate | FileSystem::kTruncate); + ASSERT_NE(file, nullptr); + bytes = file->Write(file_data.data(), file_data.size()); + EXPECT_EQ(file_size, bytes); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_not_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(KEY_MESSAGE, + decryptor_->GetUsageInfo(app_id_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_FALSE(usage_reports.empty()); + + // Read in usage info file + const std::string secure_stop_empty_app_id_file_name = + path + DeviceFiles::GetUsageInfoFileName(app_id_empty); + + file_size = file_system.FileSize(secure_stop_empty_app_id_file_name); + EXPECT_LT(4, file_size); + file = file_system.Open(secure_stop_empty_app_id_file_name, + FileSystem::kReadOnly); + ASSERT_NE(file, nullptr); + file_data.resize(file_size); + bytes = file->Read(&file_data[0], file_data.size()); + EXPECT_EQ(file_size, bytes); + + EXPECT_TRUE(hash_file.ParseFromString(file_data)); + pos = file_data.find(hash_file.hash()); + EXPECT_NE(std::string::npos, pos); + EXPECT_NE(0u, pos); + + // Corrupt the protobuf key field of the hash of the usage info file + // with empty app id and write it back out + file_data[pos - 1] = 0xff; + + file = file_system.Open(secure_stop_empty_app_id_file_name, + FileSystem::kCreate | FileSystem::kTruncate); + ASSERT_NE(file, nullptr); + bytes = file->Write(file_data.data(), file_data.size()); + EXPECT_EQ(file_size, bytes); + + EXPECT_EQ(NO_ERROR, decryptor_->RemoveAllUsageInfo(app_id_empty, + kDefaultCdmIdentifier)); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier, + &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); + EXPECT_EQ(NO_ERROR, decryptor_->GetUsageInfo( + app_id_empty, kDefaultCdmIdentifier, &usage_reports)); + EXPECT_TRUE(usage_reports.empty()); +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_GetSecureStopIdsTest) { + Unprovision(); + + const std::string app_id_empty = ""; + + TestWvCdmClientPropertySet property_set; + Provision(); + + CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + std::vector retrieved_secure_stop_ids; + EXPECT_EQ(NO_ERROR, + decryptor_->GetSecureStopIds(app_id_empty, kDefaultCdmIdentifier, + &retrieved_secure_stop_ids)); + EXPECT_TRUE(retrieved_secure_stop_ids.empty()); + + // First fetch licenses for the default app + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) { + const SubSampleInfo* data = kUsageLicenseTestVector1[i].sub_sample; + + property_set.set_app_id(app_id_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(kUsageLicenseTestVector1[i].pssh, kLicenseTypeStreaming, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + // Provision the other identifier + Provision(kExampleIdentifier, kLevelDefault); + + // Verify that there are usage records for the default identifier but + // none yet for the non-default one + std::vector expected_provider_session_tokens; + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) { + expected_provider_session_tokens.push_back( + kUsageLicenseTestVector1[i].provider_session_token); + } + + EXPECT_EQ(NO_ERROR, + decryptor_->GetSecureStopIds(app_id_empty, kDefaultCdmIdentifier, + &retrieved_secure_stop_ids)); + EXPECT_EQ(ArraySize(kUsageLicenseTestVector1), + retrieved_secure_stop_ids.size()); + EXPECT_THAT(retrieved_secure_stop_ids, + UnorderedElementsAreArray(expected_provider_session_tokens)); + + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kExampleIdentifier.app_package_name, + kDefaultCdmIdentifier, &retrieved_secure_stop_ids)); + EXPECT_TRUE(retrieved_secure_stop_ids.empty()); + + // Now fetch licenses for the other identifier + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) { + const SubSampleInfo* data = kUsageLicenseTestVector2[i].sub_sample; + + property_set.set_app_id(kExampleIdentifier.app_package_name); + EXPECT_EQ(NO_ERROR, decryptor_->OpenSession( + config_.key_system(), &property_set, + kExampleIdentifier, nullptr, &session_id_)); + const std::string init_data_type = "video/mp4"; + CdmAppParameterMap app_parameters; + GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), init_data_type, + kUsageLicenseTestVector2[i].pssh, app_parameters, + kLicenseTypeStreaming, kExampleIdentifier, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + // Verify that there are usage records for both the default and + // non-default identifier. + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kDefaultCdmIdentifier.app_package_name, + kDefaultCdmIdentifier, &retrieved_secure_stop_ids)); + EXPECT_EQ(ArraySize(kUsageLicenseTestVector1), + retrieved_secure_stop_ids.size()); + EXPECT_THAT(retrieved_secure_stop_ids, + UnorderedElementsAreArray(expected_provider_session_tokens)); + + retrieved_secure_stop_ids.clear(); + expected_provider_session_tokens.clear(); + + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) { + expected_provider_session_tokens.push_back( + kUsageLicenseTestVector2[i].provider_session_token); + } + + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kExampleIdentifier.app_package_name, + kExampleIdentifier, &retrieved_secure_stop_ids)); + EXPECT_EQ(ArraySize(kUsageLicenseTestVector2), + retrieved_secure_stop_ids.size()); + EXPECT_THAT(retrieved_secure_stop_ids, + UnorderedElementsAreArray(expected_provider_session_tokens)); + + EXPECT_EQ(NO_ERROR, + decryptor_->RemoveAllUsageInfo( + kDefaultCdmIdentifier.app_package_name, kDefaultCdmIdentifier)); + EXPECT_EQ(NO_ERROR, + decryptor_->RemoveAllUsageInfo(kExampleIdentifier.app_package_name, + kExampleIdentifier)); + + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kDefaultCdmIdentifier.app_package_name, + kDefaultCdmIdentifier, &retrieved_secure_stop_ids)); + EXPECT_TRUE(retrieved_secure_stop_ids.empty()); + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kExampleIdentifier.app_package_name, + kExampleIdentifier, &retrieved_secure_stop_ids)); + EXPECT_TRUE(retrieved_secure_stop_ids.empty()); +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_RecoveryTest) { + Unprovision(); + + const std::string app_id_empty = ""; + + TestWvCdmClientPropertySet property_set; + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + // Fetch a usage license + const SubSampleInfo* data = kUsageLicenseTestVector1[0].sub_sample; + + property_set.set_app_id(app_id_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(kUsageLicenseTestVector1[0].pssh, kLicenseTypeStreaming, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + + std::string path; + EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path)); + const std::string secure_stop_file_name = + path + DeviceFiles::GetUsageInfoFileName(app_id_empty); + + // Read in usage info file + const ssize_t file_size = file_system.FileSize(secure_stop_file_name); + EXPECT_LT(4, file_size); + std::unique_ptr file = + file_system.Open(secure_stop_file_name, FileSystem::kReadOnly); + ASSERT_NE(file, nullptr); + std::string file_data; + file_data.resize(file_size); + ssize_t bytes = file->Read(&file_data[0], file_data.size()); + EXPECT_EQ(file_size, bytes); + + // Corrupt the hash of the usage info file and write it back out + memset(&file_data[0] + bytes - 4, 0, 4); + file = file_system.Open(secure_stop_file_name, + FileSystem::kCreate | FileSystem::kTruncate); + ASSERT_NE(file, nullptr); + bytes = file->Write(file_data.data(), file_data.size()); + EXPECT_EQ(file_size, bytes); + + // Fetch a second usage license, this should fail as the usage table is + // corrupt + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming, + &property_set); + + std::string response; + VerifyKeyRequestResponse(CdmResponseType(wvcdm::STORE_USAGE_INFO_ERROR), + config_.license_server(), config_.client_auth(), + true, &response); + + decryptor_->CloseSession(session_id_); + + // Fetch the second usage license and verify that it is usable + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + data = kUsageLicenseTestVector1[1].sub_sample; + + decrypt_buffer.resize(data->encrypt_data.size()); + decryption_parameters.key_id = &data->key_id; + decryption_parameters.encrypt_buffer = &data->encrypt_data.front(); + decryption_parameters.encrypt_length = data->encrypt_data.size(); + decryption_parameters.iv = &data->iv; + decryption_parameters.block_offset = data->block_offset; + decryption_parameters.decrypt_buffer = decrypt_buffer.data(); + decryption_parameters.decrypt_buffer_length = data->encrypt_data.size(); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); +} + +TEST_F(WvCdmRequestLicenseTest, SecureStop_RemoveTest) { + Unprovision(); + + const std::string app_id_empty = ""; + + TestWvCdmClientPropertySet property_set; + Provision(); + + const CdmSecurityLevel security_level = GetDefaultSecurityLevel(); + FileSystem file_system; + DeviceFiles handle(&file_system); + EXPECT_TRUE(handle.Init(security_level)); + std::vector psts; + EXPECT_TRUE(handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(""), &psts)); + + // First fetch licenses for the default app + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector1); ++i) { + const SubSampleInfo* data = kUsageLicenseTestVector1[i].sub_sample; + + property_set.set_app_id(app_id_empty); + decryptor_->OpenSession(config_.key_system(), &property_set, + kDefaultCdmIdentifier, nullptr, &session_id_); + GenerateKeyRequest(kUsageLicenseTestVector1[i].pssh, kLicenseTypeStreaming, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + // Provision and fetch licenses for the other identifier + Provision(kExampleIdentifier, kLevelDefault); + + for (size_t i = 0; i < ArraySize(kUsageLicenseTestVector2); ++i) { + const SubSampleInfo* data = kUsageLicenseTestVector2[i].sub_sample; + + property_set.set_app_id(kExampleIdentifier.app_package_name); + EXPECT_EQ(NO_ERROR, decryptor_->OpenSession( + config_.key_system(), &property_set, + kExampleIdentifier, nullptr, &session_id_)); + const std::string init_data_type = "video/mp4"; + CdmAppParameterMap app_parameters; + GenerateKeyRequest(CdmResponseType(wvcdm::KEY_MESSAGE), init_data_type, + kUsageLicenseTestVector2[i].pssh, app_parameters, + kLicenseTypeStreaming, kExampleIdentifier, + &property_set); + + VerifyUsageKeyRequestResponse(config_.license_server(), + config_.client_auth()); + + 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.data()); + decryption_parameters.is_encrypted = data->is_encrypted; + decryption_parameters.is_secure = data->is_secure; + decryption_parameters.subsample_flags = data->subsample_flags; + EXPECT_EQ(NO_ERROR, decryptor_->Decrypt(session_id_, data->validate_key_id, + decryption_parameters)); + + EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(), + decrypt_buffer.begin())); + decryptor_->CloseSession(session_id_); + } + + // Release usage records for both the default and non-default identifier. + std::vector secure_stop_ids; + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kDefaultCdmIdentifier.app_package_name, + kDefaultCdmIdentifier, &secure_stop_ids)); + EXPECT_EQ(ArraySize(kUsageLicenseTestVector1), secure_stop_ids.size()); + + for (size_t i = 0; i < secure_stop_ids.size(); ++i) { + EXPECT_EQ(NO_ERROR, decryptor_->RemoveUsageInfo( + kDefaultCdmIdentifier.app_package_name, + kDefaultCdmIdentifier, secure_stop_ids[i])); + } + + EXPECT_EQ(NO_ERROR, decryptor_->GetSecureStopIds( + kDefaultCdmIdentifier.app_package_name, + kDefaultCdmIdentifier, &secure_stop_ids)); + EXPECT_TRUE(secure_stop_ids.empty()); + + EXPECT_EQ(NO_ERROR, + decryptor_->GetSecureStopIds(kExampleIdentifier.app_package_name, + kExampleIdentifier, &secure_stop_ids)); + EXPECT_EQ(ArraySize(kUsageLicenseTestVector2), secure_stop_ids.size()); + + for (size_t i = 0; i < secure_stop_ids.size(); ++i) { + EXPECT_EQ(NO_ERROR, decryptor_->RemoveUsageInfo( + kExampleIdentifier.app_package_name, + kExampleIdentifier, secure_stop_ids[i])); + } + + EXPECT_EQ(NO_ERROR, + decryptor_->GetSecureStopIds(kExampleIdentifier.app_package_name, + kExampleIdentifier, &secure_stop_ids)); + EXPECT_TRUE(secure_stop_ids.empty()); +} // TODO(rfrias): Enable when b/123370099 has been addressed TEST_F(WvCdmRequestLicenseTest, VerifyProviderClientToken) { @@ -4719,8 +5883,8 @@ TEST_P(WvCdmSessionSharingTest, SessionSharingTest) { } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingTest, - ::testing::Range(&session_sharing_sub_samples[0], - &session_sharing_sub_samples[7])); + ::testing::Range(&kSessionSharingSubSamples[0], + &kSessionSharingSubSamples[7])); TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) { TestWvCdmClientPropertySet property_set; @@ -4752,7 +5916,7 @@ TEST_F(WvCdmRequestLicenseTest, SessionSharingTest) { GenerateKeyRequest(init_data2, kLicenseTypeStreaming); VerifyKeyRequestResponse(config_.license_server(), gp_client_auth2); - const SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry; + const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry; std::vector decrypt_buffer(data->encrypt_data.size()); CdmDecryptionParameters decryption_parameters( @@ -4795,7 +5959,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) { "000000347073736800000000" // blob size and pssh "EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id "0801121030313233343536373839616263646566"); // pssh data - const SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry; + const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry; decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, nullptr, &session_id_); if (data->retrieve_key) { @@ -4823,7 +5987,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionKeyExpiredTest) { TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry) { StrictMock listener; DecryptCallbackTester decrypt_callback(decryptor_, - &usage_info_sub_samples_icp[0]); + &kSecureStopSubSamplesIcp[0]); decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, &listener, &session_id_); @@ -4850,13 +6014,13 @@ TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry) { TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry_DecryptBeforeLicense) { StrictMock listener; DecryptCallbackTester decrypt_callback(decryptor_, - &usage_info_sub_samples_icp[0]); + &kSecureStopSubSamplesIcp[0]); decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, &listener, &session_id_); // Decrypt before license is received is expected to fail but should // not start the playback timer - EXPECT_FALSE(VerifyDecryption(session_id_, usage_info_sub_samples_icp[0], + EXPECT_FALSE(VerifyDecryption(session_id_, kSecureStopSubSamplesIcp[0], CdmResponseType(NEED_KEY))); std::this_thread::sleep_for(kExpirationStreamingClip21PlaybackDurationTimeMs); @@ -4882,8 +6046,8 @@ TEST_F(WvCdmRequestLicenseTest, PlaybackExpiry_DecryptBeforeLicense) { TEST_F(WvCdmRequestLicenseTest, SessionKeyChangeNotificationTest) { StrictMock listener; - DecryptCallbackTester decrypt_callback( - decryptor_, &single_encrypted_sub_sample_short_expiry); + DecryptCallbackTester decrypt_callback(decryptor_, + &kSingleEncryptedSubSampleShortExpiry); decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier, &listener, &session_id_); EXPECT_CALL(listener, @@ -4951,11 +6115,11 @@ TEST_P(WvCdmDecryptionTest, DecryptionTest) { } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmDecryptionTest, - ::testing::Values(&clear_sub_sample, - &clear_sub_sample_no_key, - &single_encrypted_sub_sample, - &switch_key_encrypted_sub_samples[0], - &partial_encrypted_sub_samples[0])); + ::testing::Values(&kClearSubSample, + &kClearSubSampleNoKey, + &kSingleEncryptedSubSample, + &kSwitchKeyEncryptedSubSamples[0], + &kPartialEncryptedSubSamples[0])); class WvCdmSessionSharingNoKeyTest : public WvCdmRequestLicenseTest, @@ -5009,11 +6173,11 @@ TEST_P(WvCdmSessionSharingNoKeyTest, DecryptionTest) { } INSTANTIATE_TEST_CASE_P(Cdm, WvCdmSessionSharingNoKeyTest, - ::testing::Values(&clear_sub_sample, - &clear_sub_samples[0], - &clear_sub_samples[1], - &clear_sub_sample_no_key, - &single_encrypted_sub_sample)); + ::testing::Values(&kClearSubSample, + &kClearSubSamples[0], + &kClearSubSamples[1], + &kClearSubSampleNoKey, + &kSingleEncryptedSubSample)); TEST(VersionNumberTest, VersionNumberChangeCanary) { std::string release_number = @@ -5061,13 +6225,13 @@ INSTANTIATE_TEST_CASE_P( class WvHlsDecryptionTest : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; + public ::testing::WithParamInterface {}; TEST_P(WvHlsDecryptionTest, HlsDecryptionTest) { Provision(kDefaultCdmIdentifier, kLevel3); TestWvCdmClientPropertySet client_property_set; client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); - HlsDecryptionInfo* info = GetParam(); + const HlsDecryptionInfo* info = GetParam(); TestWvCdmHlsEventListener listener; decryptor_->OpenSession(config_.key_system(), &client_property_set, @@ -5085,7 +6249,7 @@ TEST_P(WvHlsDecryptionTest, HlsDecryptionTest) { EXPECT_EQ(KEY_ID_SIZE, key_id.size()); for (size_t i = 0; i < info->number_of_segments; ++i) { - HlsSegmentInfo* data = info->segment_info + i; + const HlsSegmentInfo* data = info->segment_info + i; std::vector output_buffer(data->encrypted_data.size(), 0); std::vector iv(data->iv.begin(), data->iv.end()); CdmDecryptionParameters decryption_parameters( @@ -5115,13 +6279,13 @@ INSTANTIATE_TEST_CASE_P(Cdm, WvHlsDecryptionTest, class WvHlsFourCCBackwardCompatibilityTest : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; + public ::testing::WithParamInterface {}; TEST_P(WvHlsFourCCBackwardCompatibilityTest, HlsDecryptionTest) { Provision(kDefaultCdmIdentifier, kLevel3); TestWvCdmClientPropertySet client_property_set; client_property_set.set_security_level(QUERY_VALUE_SECURITY_LEVEL_L3); - HlsDecryptionInfo* info = GetParam(); + const HlsDecryptionInfo* info = GetParam(); TestWvCdmHlsEventListener listener; decryptor_->OpenSession(config_.key_system(), &client_property_set, @@ -5139,7 +6303,7 @@ TEST_P(WvHlsFourCCBackwardCompatibilityTest, HlsDecryptionTest) { EXPECT_EQ(KEY_ID_SIZE, key_id.size()); for (size_t i = 0; i < info->number_of_segments; ++i) { - HlsSegmentInfo* data = info->segment_info + i; + const HlsSegmentInfo* data = info->segment_info + i; std::vector output_buffer(data->encrypted_data.size(), 0); std::vector iv(data->iv.begin(), data->iv.end()); CdmDecryptionParameters decryption_parameters( @@ -5170,7 +6334,8 @@ INSTANTIATE_TEST_CASE_P( class WvCenc30Test : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; + public ::testing::WithParamInterface { +}; TEST_P(WvCenc30Test, DecryptionTest) { Provision(kDefaultCdmIdentifier, kLevel3); @@ -5191,7 +6356,7 @@ TEST_P(WvCenc30Test, DecryptionTest) { // was introduced in OEMCrypto v14 if (api_version < 14u) return; - SingleSampleDecryptionInfo* info = GetParam(); + const SingleSampleDecryptionInfo* info = GetParam(); TestWvCdmHlsEventListener listener; decryptor_->OpenSession(config_.key_system(), &client_property_set, @@ -5203,7 +6368,7 @@ TEST_P(WvCenc30Test, DecryptionTest) { CdmKeyStatusMap key_status_map = listener.GetKeyStatusMap(); EXPECT_EQ(8u, key_status_map.size()); - Cenc30SampleInfo* data = &info->sample_info; + const Cenc30SampleInfo* data = &info->sample_info; std::vector output_buffer(data->encrypted_data.size(), 0); std::vector iv(data->iv.begin(), data->iv.end()); CdmDecryptionParameters decryption_parameters( @@ -5229,7 +6394,7 @@ INSTANTIATE_TEST_CASE_P(Cdm, WvCenc30Test, class WvCenc30SwitchCipherModeTest : public WvCdmRequestLicenseTest, - public ::testing::WithParamInterface {}; + public ::testing::WithParamInterface {}; TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { Provision(kDefaultCdmIdentifier, kLevel3); @@ -5250,7 +6415,7 @@ TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { // was introduced in OEMCrypto v14 if (api_version < 14) return; - FourSampleDecryptionInfo* info = GetParam(); + const FourSampleDecryptionInfo* info = GetParam(); TestWvCdmHlsEventListener listener; decryptor_->OpenSession(config_.key_system(), &client_property_set, @@ -5263,7 +6428,7 @@ TEST_P(WvCenc30SwitchCipherModeTest, DecryptionTest) { EXPECT_EQ(8u, key_status_map.size()); for (size_t i = 0; i < ArraySize(info->sample_info); ++i) { - Cenc30SampleInfo* data = &info->sample_info[i]; + const Cenc30SampleInfo* data = &info->sample_info[i]; std::vector output_buffer(data->encrypted_data.size(), 0); std::vector iv(data->iv.begin(), data->iv.end()); CdmDecryptionParameters decryption_parameters( @@ -5474,7 +6639,7 @@ class WvCdmRequestLicenseRollbackTest public ::testing::WithParamInterface { public: WvCdmRequestLicenseRollbackTest() { - const SubSampleInfo* data = &single_encrypted_sub_sample_short_expiry; + const SubSampleInfo* data = &kSingleEncryptedSubSampleShortExpiry; decrypt_buffer_.resize(data->encrypt_data.size()); decryption_parameters_ = CdmDecryptionParameters( &data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(), diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index 26887da3..1a16f6cb 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -84,8 +84,8 @@ using wvcdm::CdmProvisioningResponse; using wvcdm::CdmQueryMap; using wvcdm::CdmSecureStopId; using wvcdm::CdmSecurityLevel; -using wvcdm::CdmUsageInfo; -using wvcdm::CdmUsageInfoReleaseMessage; +using wvcdm::CdmUsageReport; +using wvcdm::CdmUsageReportList; using wvcdm::kDefaultCdmIdentifier; using wvcdm::KeyId; using wvcdm::RequestedSecurityLevel; @@ -672,73 +672,55 @@ Status WVDrmPlugin::unprovisionDevice() { ::ndk::ScopedAStatus WVDrmPlugin::getSecureStop( const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, ::aidl::android::hardware::drm::SecureStop* _aidl_return) { - if (!in_secureStopId.secureStopId.size()) { - *_aidl_return = SecureStop(); + *_aidl_return = SecureStop(); + if (in_secureStopId.secureStopId.empty()) { return toNdkScopedAStatus(Status::BAD_VALUE); } - vector cdmStopVec; - SecureStop secureStop; - CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { - *_aidl_return = SecureStop(); return toNdkScopedAStatus(status); } - CdmUsageInfo cdmUsageInfo; - CdmSecureStopId cdmSsId(in_secureStopId.secureStopId.begin(), - in_secureStopId.secureStopId.end()); + const CdmSecureStopId cdmSsId(in_secureStopId.secureStopId.begin(), + in_secureStopId.secureStopId.end()); + CdmUsageReport cdmUsageReport; CdmResponseType res = mCDM->GetUsageInfo(mPropertySet.app_id(), cdmSsId, - identifier, &cdmUsageInfo); + identifier, &cdmUsageReport); + SecureStop secureStop; if (isCdmResponseTypeSuccess(res)) { - for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin(); - iter != cdmUsageInfo.end(); ++iter) { - const std::string& cdmStop = *iter; - cdmStopVec = StrToVector(cdmStop); - } - secureStop.opaqueData = cdmStopVec; + secureStop.opaqueData = StrToVector(cdmUsageReport); + *_aidl_return = std::move(secureStop); } - *_aidl_return = secureStop; return toNdkScopedAStatus(mapCdmResponseType(res)); } ::ndk::ScopedAStatus WVDrmPlugin::getSecureStops( vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) { - std::list> secureStops; - vector secureStopsVec; - + _aidl_return->clear(); CdmIdentifier identifier; auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); if (status != Status::OK) { - *_aidl_return = secureStopsVec; return toNdkScopedAStatus(status); } - CdmUsageInfo cdmUsageInfo; - CdmResponseType res = - mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageInfo); + CdmUsageReportList cdmUsageReports; + const CdmResponseType res = + mCDM->GetUsageInfo(mPropertySet.app_id(), identifier, &cdmUsageReports); + vector secureStops; if (isCdmResponseTypeSuccess(res)) { - secureStops.clear(); - for (CdmUsageInfo::const_iterator iter = cdmUsageInfo.begin(); - iter != cdmUsageInfo.end(); ++iter) { - const std::string& cdmStop = *iter; - secureStops.push_back(StrToVector(cdmStop)); + for (const CdmUsageReport& cdmUsageReport : cdmUsageReports) { + SecureStop secureStop; + secureStop.opaqueData = StrToVector(cdmUsageReport); + secureStops.push_back(std::move(secureStop)); } } + *_aidl_return = std::move(secureStops); - std::list>::iterator iter = secureStops.begin(); - while (iter != secureStops.end()) { - SecureStop secureStop; - secureStop.opaqueData = *iter++; - secureStopsVec.push_back(secureStop); - } - - *_aidl_return = secureStopsVec; return toNdkScopedAStatus(mapCdmResponseType(res)); } @@ -763,8 +745,8 @@ Status WVDrmPlugin::unprovisionDevice() { return toNdkScopedAStatus(status); } - CdmUsageInfoReleaseMessage cdmMessage(in_secureStopId.secureStopId.begin(), - in_secureStopId.secureStopId.end()); + const CdmKeyResponse cdmMessage(in_secureStopId.secureStopId.begin(), + in_secureStopId.secureStopId.end()); CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } @@ -832,9 +814,10 @@ Status WVDrmPlugin::unprovisionDevice() { return toNdkScopedAStatus(status); } - const vector data = in_ssRelease.opaqueData; - CdmUsageInfoReleaseMessage cdmMessage(data.begin(), data.end()); - CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier); + const vector& data = in_ssRelease.opaqueData; + const CdmKeyResponse cdmMessage(data.begin(), data.end()); + // Only releases a single secure stop. + const CdmResponseType res = mCDM->ReleaseUsageInfo(cdmMessage, identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } @@ -850,10 +833,10 @@ Status WVDrmPlugin::unprovisionDevice() { return toNdkScopedAStatus(status); } - CdmSecureStopId id(in_secureStopId.secureStopId.begin(), - in_secureStopId.secureStopId.end()); - CdmResponseType res = - mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, id); + const CdmSecureStopId ssid(in_secureStopId.secureStopId.begin(), + in_secureStopId.secureStopId.end()); + const CdmResponseType res = + mCDM->RemoveUsageInfo(mPropertySet.app_id(), identifier, ssid); return toNdkScopedAStatus(mapCdmResponseType(res)); } @@ -864,7 +847,7 @@ Status WVDrmPlugin::unprovisionDevice() { return toNdkScopedAStatus(status); } - CdmResponseType res = + const CdmResponseType res = mCDM->RemoveAllUsageInfo(mPropertySet.app_id(), identifier); return toNdkScopedAStatus(mapCdmResponseType(res)); } diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_hal_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_hal_test.cpp index 527e4833..a4d3b7f0 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_hal_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_hal_test.cpp @@ -5,8 +5,6 @@ // // #define LOG_NDEBUG 0 #define LOG_TAG "WVDrmPluginHalTest" -#include "WVDrmPlugin.h" - #include #include @@ -15,16 +13,18 @@ #include #include +#include "WVDrmPlugin.h" #include "WVErrors.h" #include "cdm_client_property_set.h" +#include "cdm_random.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "media/stagefright/MediaErrors.h" #include "media/stagefright/foundation/ABase.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_types.h" #include "wv_content_decryption_module.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" namespace { typedef std::vector<::aidl::android::hardware::drm::KeyValue> KeyedVector; @@ -47,6 +47,8 @@ using ::aidl::android::hardware::drm::KeyValue; using ::aidl::android::hardware::drm::OfflineLicenseState; using ::aidl::android::hardware::drm::ProvideProvisionResponseResult; using ::aidl::android::hardware::drm::ProvisionRequest; +using ::aidl::android::hardware::drm::SecureStop; +using ::aidl::android::hardware::drm::SecureStopId; using ::aidl::android::hardware::drm::SecurityLevel; using ::aidl::android::hardware::drm::Status; @@ -54,6 +56,7 @@ using ::testing::_; using ::testing::AllOf; using ::testing::Args; using ::testing::AtLeast; +using ::testing::Contains; using ::testing::DefaultValue; using ::testing::DoAll; using ::testing::ElementsAreArray; @@ -89,7 +92,10 @@ using wvcdm::CdmOfflineLicenseState; using wvcdm::CdmProvisioningRequest; using wvcdm::CdmProvisioningResponse; using wvcdm::CdmQueryMap; +using wvcdm::CdmSecureStopId; using wvcdm::CdmSecurityLevel; +using wvcdm::CdmUsageReport; +using wvcdm::CdmUsageReportList; using wvcdm::EMPTY_ORIGIN; using wvcdm::kCertificateWidevine; using wvcdm::KEY_ID_SIZE; @@ -133,6 +139,7 @@ using wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3; using wvcdm::SESSION_ID_PREFIX; using wvcdm::WvCdmEventListener; using wvutil::Base64Encode; +using wvutil::CdmRandom; namespace { const std::string kEmptyString; @@ -157,93 +164,107 @@ const char kSerializedMetricsHex[] = "0"; #define N_ELEM(a) (sizeof(a) / sizeof(a[0])) -} // anonymous namespace class MockCDM : public WvContentDecryptionModule { -public: - MOCK_METHOD(CdmResponseType, OpenSession, - (const CdmKeySystem &, CdmClientPropertySet *, - const CdmIdentifier &, WvCdmEventListener *, CdmSessionId *), - (override)); + public: + virtual ~MockCDM() {} - MOCK_METHOD(CdmResponseType, CloseSession, (const CdmSessionId &), - (override)); + MOCK_METHOD(CdmResponseType, OpenSession, + (const CdmKeySystem &, CdmClientPropertySet *, + const CdmIdentifier &, WvCdmEventListener *, CdmSessionId *), + (override)); - MOCK_METHOD(CdmResponseType, GenerateKeyRequest, - (const CdmSessionId &, const CdmKeySetId &, const std::string &, - const CdmInitData &, const CdmLicenseType, CdmAppParameterMap &, - CdmClientPropertySet *, const CdmIdentifier &, CdmKeyRequest *), - (override)); + MOCK_METHOD(CdmResponseType, CloseSession, (const CdmSessionId &), (override)); - MOCK_METHOD(CdmResponseType, AddKey, - (const CdmSessionId &, const CdmKeyResponse &, CdmKeySetId *), - (override)); + MOCK_METHOD(CdmResponseType, GenerateKeyRequest, + (const CdmSessionId &, const CdmKeySetId &, const std::string &, + const CdmInitData &, const CdmLicenseType, CdmAppParameterMap &, + CdmClientPropertySet *, const CdmIdentifier &, CdmKeyRequest *), + (override)); - MOCK_METHOD(CdmResponseType, RemoveKeys, (const CdmSessionId &), (override)); + MOCK_METHOD(CdmResponseType, AddKey, + (const CdmSessionId &, const CdmKeyResponse &, CdmKeySetId *), + (override)); - MOCK_METHOD(CdmResponseType, RestoreKey, - (const CdmSessionId &, const CdmKeySetId &), (override)); + MOCK_METHOD(CdmResponseType, RemoveKeys, (const CdmSessionId &), (override)); - MOCK_METHOD(CdmResponseType, QueryStatus, - (wvcdm::RequestedSecurityLevel, const std::string &, - std::string *), - (override)); + MOCK_METHOD(CdmResponseType, RestoreKey, + (const CdmSessionId &, const CdmKeySetId &), (override)); - MOCK_METHOD(CdmResponseType, QuerySessionStatus, - (const CdmSessionId &, CdmQueryMap *), (override)); + MOCK_METHOD(CdmResponseType, QueryStatus, + (wvcdm::RequestedSecurityLevel, const std::string &, + std::string *), + (override)); - MOCK_METHOD(CdmResponseType, QueryKeyStatus, - (const CdmSessionId &, CdmQueryMap *), (override)); + MOCK_METHOD(CdmResponseType, QuerySessionStatus, + (const CdmSessionId &, CdmQueryMap *), (override)); - MOCK_METHOD(CdmResponseType, QueryOemCryptoSessionId, - (const CdmSessionId &, CdmQueryMap *), (override)); + MOCK_METHOD(CdmResponseType, QueryKeyStatus, + (const CdmSessionId &, CdmQueryMap *), (override)); - MOCK_METHOD(CdmResponseType, GetProvisioningRequest, - (CdmCertificateType, const std::string &, const CdmIdentifier &, - const std::string &, wvcdm::RequestedSecurityLevel, - CdmProvisioningRequest *, std::string *), - (override)); + MOCK_METHOD(CdmResponseType, QueryOemCryptoSessionId, + (const CdmSessionId &, CdmQueryMap *), (override)); - MOCK_METHOD(CdmResponseType, HandleProvisioningResponse, - (const CdmIdentifier &, const CdmProvisioningResponse &, - wvcdm::RequestedSecurityLevel, std::string *, std::string *), - (override)); + MOCK_METHOD(CdmResponseType, GetProvisioningRequest, + (CdmCertificateType, const std::string &, const CdmIdentifier &, + const std::string &, wvcdm::RequestedSecurityLevel, + CdmProvisioningRequest *, std::string *), + (override)); - MOCK_METHOD(CdmResponseType, Unprovision, - (CdmSecurityLevel, const CdmIdentifier &), (override)); + MOCK_METHOD(CdmResponseType, HandleProvisioningResponse, + (const CdmIdentifier &, const CdmProvisioningResponse &, + wvcdm::RequestedSecurityLevel, std::string *, std::string *), + (override)); - MOCK_METHOD(bool, IsProvisioned, - (CdmSecurityLevel, const std::string &, const std::string &, - bool), - (override)); + MOCK_METHOD(CdmResponseType, Unprovision, + (CdmSecurityLevel, const CdmIdentifier &), (override)); - MOCK_METHOD(bool, IsValidServiceCertificate, (const std::string &), - (override)); + MOCK_METHOD(bool, IsProvisioned, + (CdmSecurityLevel, const std::string &, const std::string &, bool), + (override)); - MOCK_METHOD(CdmResponseType, GetMetrics, - (const CdmIdentifier &, drm_metrics::WvCdmMetrics *), (override)); + MOCK_METHOD(CdmResponseType, GetUsageInfo, + (const std::string &, const CdmIdentifier &, CdmUsageReportList *), + (override)); - MOCK_METHOD(CdmResponseType, GetDecryptHashError, - (const CdmSessionId &, std::string *), (override)); + MOCK_METHOD(CdmResponseType, GetUsageInfo, + (const std::string &, const CdmSecureStopId &, + const CdmIdentifier &, CdmUsageReport *), + (override)); - MOCK_METHOD(CdmResponseType, ListStoredLicenses, - (CdmSecurityLevel, const CdmIdentifier &, - std::vector *), - (override)); + MOCK_METHOD(CdmResponseType, RemoveAllUsageInfo, + (const std::string &, const CdmIdentifier &), (override)); - MOCK_METHOD(CdmResponseType, GetOfflineLicenseState, - (const std::string &, CdmSecurityLevel, const CdmIdentifier &, - CdmOfflineLicenseState *), - (override)); + MOCK_METHOD(CdmResponseType, ReleaseUsageInfo, + (const CdmKeyResponse &, const CdmIdentifier &), (override)); - MOCK_METHOD(CdmResponseType, RemoveOfflineLicense, - (const std::string &, CdmSecurityLevel, const CdmIdentifier &), - (override)); + MOCK_METHOD(bool, IsValidServiceCertificate, (const std::string &), + (override)); - MOCK_METHOD(CdmResponseType, StoreAtscLicense, - (const CdmIdentifier &, wvcdm::RequestedSecurityLevel, - const std::string &, const std::string &), - (override)); + MOCK_METHOD(CdmResponseType, GetMetrics, + (const CdmIdentifier &, drm_metrics::WvCdmMetrics *), (override)); + + MOCK_METHOD(CdmResponseType, GetDecryptHashError, + (const CdmSessionId &, std::string *), (override)); + + MOCK_METHOD(CdmResponseType, ListStoredLicenses, + (CdmSecurityLevel, const CdmIdentifier &, + std::vector *), + (override)); + + MOCK_METHOD(CdmResponseType, GetOfflineLicenseState, + (const std::string &, CdmSecurityLevel, const CdmIdentifier &, + CdmOfflineLicenseState *), + (override)); + + MOCK_METHOD(CdmResponseType, RemoveOfflineLicense, + (const std::string &, CdmSecurityLevel, const CdmIdentifier &), + (override)); + + MOCK_METHOD(CdmResponseType, StoreAtscLicense, + (const CdmIdentifier &, wvcdm::RequestedSecurityLevel, + const std::string &, const std::string &), + (override)); }; class MockCrypto : public WVGenericCryptoInterface { @@ -280,6 +301,8 @@ public: (override)); }; +} // anonymous namespace + template CdmResponseType setSessionIdOnMap(testing::Unused, CdmQueryMap *map) { static const char oecId[] = {DIGIT + '0', '\0'}; @@ -968,6 +991,62 @@ TEST_F(WVDrmPluginHalTest, RejectsAtscUnprovisionDeviceRequests) { ASSERT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status); } +TEST_F(WVDrmPluginHalTest, GetsSecureStops) { + static constexpr uint32_t kStopSize = 53; + static constexpr uint32_t kStopCount = 7; + + CdmUsageReportList fakeSecureStops; + for (uint32_t i = 0; i < kStopCount; ++i) { + fakeSecureStops.push_back(CdmRandom::RandomData(kStopSize)); + } + + const std::string app_id = "my_app_id"; + EXPECT_CALL(*mCdm, GetUsageInfo(app_id, _, _)) + .WillOnce(DoAll(SetArgPointee<2>(fakeSecureStops), + testing::Return(CdmResponseType(wvcdm::NO_ERROR)))); + + auto ret = mPlugin->setPropertyString("appId", app_id); + EXPECT_TRUE(ret.isOk()); + + std::vector secureStops; + ret = mPlugin->getSecureStops(&secureStops); + EXPECT_TRUE(ret.isOk()); + + CdmUsageReportList stops; + for (const auto &stop : secureStops) { + stops.emplace_back(stop.opaqueData.begin(), stop.opaqueData.end()); + } + + EXPECT_EQ(kStopCount, stops.size()); + + for (const CdmUsageReport &expectedSecureStop : fakeSecureStops) { + EXPECT_THAT(stops, Contains(expectedSecureStop)); + } +} + +TEST_F(WVDrmPluginHalTest, ReleasesAllSecureStops) { + const std::string app_id = ""; + EXPECT_CALL(*mCdm, RemoveAllUsageInfo(app_id, _)).Times(1); + + auto ret = mPlugin->setPropertyString("appId", app_id); + EXPECT_TRUE(ret.isOk()); + + ret = mPlugin->releaseAllSecureStops(); + EXPECT_TRUE(ret.isOk()); +} + +TEST_F(WVDrmPluginHalTest, ReleasesSecureStop) { + static constexpr uint32_t kMessageSize = 128; + const CdmKeyResponse releaseMessage = CdmRandom::RandomData(kMessageSize); + + EXPECT_CALL(*mCdm, ReleaseUsageInfo(releaseMessage, _)).Times(1); + + SecureStopId stopId; + stopId.secureStopId.assign(releaseMessage.begin(), releaseMessage.end()); + auto ret = mPlugin->releaseSecureStop(stopId); + EXPECT_TRUE(ret.isOk()); +} + TEST_F(WVDrmPluginHalTest, ReturnsExpectedPropertyValues) { CdmQueryMap l1Map; l1Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1;