// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "wv_content_decryption_module.h" #include #include #include "cdm_client_property_set.h" #include "cdm_engine.h" #include "cdm_engine_factory.h" #include "initialization_data.h" #include "license.h" #include "log.h" #include "properties.h" #include "service_certificate.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" #include "wv_metrics.pb.h" namespace wvcdm { using wvutil::FileSystem; using wvutil::LoggingUidSetter; namespace { const int kCdmTimerDurationSeconds = 1; // In the interest of limiting memory usage, only a limited number of // the most recent metrics snapshots whould be stored. constexpr size_t kMaxSavedMetricsSnapshotCount = 50; // Value returned by time() if the operation fails. constexpr time_t kTimeError = static_cast(-1); // Removes certain field from the provided CdmIdentifier as to make // it better usable for tracking the same engine across different // calls into the DRM plugin service. CdmIdentifier ScrubIdentifierForMetrics(const CdmIdentifier& identifier) { return CdmIdentifier{identifier.spoid, identifier.origin, identifier.app_package_name, identifier.unique_id, /* user_id */ wvutil::UNKNOWN_UID}; } } // namespace WvMetricsSnapshot::WvMetricsSnapshot(const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics&& metrics, time_t ts) : cdm_id_(ScrubIdentifierForMetrics(identifier)), metrics_(std::move(metrics)), timestamp_(ts) {} WvMetricsSnapshot WvMetricsSnapshot::MakeSnapshot( const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics&& metrics) { const time_t now = time(nullptr); if (now == kTimeError) { LOGW("Failed to get current time"); return WvMetricsSnapshot(identifier, std::move(metrics), 0); } return WvMetricsSnapshot(identifier, std::move(metrics), now); } std::string WvMetricsSnapshot::GetFormattedTimestamp() const { if (timestamp_ == 0) return ""; struct tm time_info = {}; if (localtime_r(×tamp_, &time_info) == nullptr) { LOGE("Failed to convert to local time"); return ""; } char timestamp_buffer[64]; // asctime_r() requires at least 29 bytes. memset(timestamp_buffer, 0, sizeof(timestamp_buffer)); if (asctime_r(&time_info, timestamp_buffer) == nullptr) { LOGE("Failed to format localtime to ASC time"); return ""; } std::string result(timestamp_buffer); // The Linux manual illustrates asctime_r() as returning a value // with a new line character at the end. However, it does not // specify this in the description. This is to account for different // behavior on different systems. if (!result.empty() && result.back() == '\n') { result.pop_back(); } return result; } std::mutex WvContentDecryptionModule::session_sharing_id_generation_lock_; WvContentDecryptionModule::WvContentDecryptionModule() {} WvContentDecryptionModule::~WvContentDecryptionModule() { CryptoSession::DisableDelayedTermination(); CloseAllCdms(); CryptoSession::TryTerminate(); DisableTimerAndWaitForExit(); } bool WvContentDecryptionModule::IsSupported(const std::string& init_data_type) { return InitializationData(init_data_type).is_supported(); } bool WvContentDecryptionModule::IsCenc(const std::string& init_data_type) { return InitializationData(init_data_type).is_cenc(); } bool WvContentDecryptionModule::IsWebm(const std::string& init_data_type) { return InitializationData(init_data_type).is_webm(); } bool WvContentDecryptionModule::IsHls(const std::string& init_data_type) { return InitializationData(init_data_type).is_hls(); } bool WvContentDecryptionModule::IsAudio(const std::string& init_data_type) { return InitializationData(init_data_type).is_audio(); } CdmResponseType WvContentDecryptionModule::OpenSession( const CdmKeySystem& key_system, CdmClientPropertySet* property_set, const CdmIdentifier& identifier, WvCdmEventListener* event_listener, CdmSessionId* session_id) { if (property_set && property_set->is_session_sharing_enabled()) { std::unique_lock auto_lock(session_sharing_id_generation_lock_); if (property_set->session_sharing_id() == 0) property_set->set_session_sharing_id(GenerateSessionSharingId()); } CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); CdmResponseType sts = cdm_engine->OpenSession(key_system, property_set, event_listener, session_id); if (sts == NO_ERROR) { std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_[*session_id] = cdm_engine; } return sts; } CdmResponseType WvContentDecryptionModule::CloseSession( const CdmSessionId& session_id) { LOGV("Closing session ID: %s", session_id.c_str()); CdmEngine* cdm_engine = GetCdmForSessionId(session_id); // TODO(rfrias): Avoid reusing the error codes from CdmEngine. if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_1); const CdmResponseType sts = cdm_engine->CloseSession(session_id); if (sts == NO_ERROR) { std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(session_id); } return sts; } bool WvContentDecryptionModule::IsOpenSession(const CdmSessionId& session_id) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); return cdm_engine && cdm_engine->IsOpenSession(session_id); } CdmResponseType WvContentDecryptionModule::GenerateKeyRequest( const CdmSessionId& session_id, const CdmKeySetId& key_set_id, const std::string& init_data_type, const CdmInitData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, CdmClientPropertySet* property_set, const CdmIdentifier& identifier, CdmKeyRequest* key_request) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); CdmResponseType sts; if (license_type == kLicenseTypeRelease) { sts = cdm_engine->OpenKeySetSession(key_set_id, property_set, nullptr); if (sts != NO_ERROR) return sts; std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_[key_set_id] = cdm_engine; } const wvcdm::RequestedSecurityLevel requested_security_level = property_set && property_set->security_level().compare( wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3) == 0 ? wvcdm::kLevel3 : wvcdm::kLevelDefault; std::string oec_version; sts = cdm_engine->QueryStatus(requested_security_level, QUERY_KEY_OEMCRYPTO_API_VERSION, &oec_version); if (sts != NO_ERROR) { return sts; } InitializationData initialization_data(init_data_type, init_data, oec_version); sts = cdm_engine->GenerateKeyRequest(session_id, key_set_id, initialization_data, license_type, app_parameters, key_request); if (license_type == kLicenseTypeRelease && sts != KEY_MESSAGE) { cdm_engine->CloseKeySetSession(key_set_id); std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(key_set_id); } return sts; } CdmResponseType WvContentDecryptionModule::AddKey( const CdmSessionId& session_id, const CdmKeyResponse& key_data, CdmKeySetId* key_set_id) { CdmEngine* cdm_engine = session_id.empty() ? GetCdmForSessionId(*key_set_id) : GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_3); // Save key_set_id, as CDM will return an empty key_set_id on release CdmKeySetId release_key_set_id; if (session_id.empty() && key_set_id != nullptr) { release_key_set_id = *key_set_id; } CdmResponseType sts; CdmLicenseType license_type; sts = cdm_engine->AddKey(session_id, key_data, &license_type, key_set_id); // Empty session id indicates license type release. if (sts == KEY_ADDED && session_id.empty()) { cdm_engine->CloseKeySetSession(release_key_set_id); std::unique_lock auto_lock(cdms_lock_); cdm_by_session_id_.erase(release_key_set_id); } return sts; } CdmResponseType WvContentDecryptionModule::RestoreKey( const CdmSessionId& session_id, const CdmKeySetId& key_set_id) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_4); CdmResponseType sts; sts = cdm_engine->RestoreKey(session_id, key_set_id); return sts; } CdmResponseType WvContentDecryptionModule::RemoveKeys( const CdmSessionId& session_id) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_5); CdmResponseType sts = cdm_engine->RemoveKeys(session_id); return sts; } CdmResponseType WvContentDecryptionModule::QueryStatus( RequestedSecurityLevel security_level, const std::string& key, std::string* value) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier); return cdm_engine->QueryStatus(security_level, key, value); } CdmResponseType WvContentDecryptionModule::QuerySessionStatus( const CdmSessionId& session_id, CdmQueryMap* key_info) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_8); return cdm_engine->QuerySessionStatus(session_id, key_info); } CdmResponseType WvContentDecryptionModule::QueryKeyStatus( const CdmSessionId& session_id, CdmQueryMap* key_info) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_9); CdmResponseType sts; sts = cdm_engine->QueryKeyStatus(session_id, key_info); return sts; } CdmResponseType WvContentDecryptionModule::QueryOemCryptoSessionId( const CdmSessionId& session_id, CdmQueryMap* response) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_10); return cdm_engine->QueryOemCryptoSessionId(session_id, response); } bool WvContentDecryptionModule::IsSecurityLevelSupported( CdmSecurityLevel level) { return CdmEngine::IsSecurityLevelSupported(level); } CdmResponseType WvContentDecryptionModule::GetProvisioningRequest( CdmCertificateType cert_type, const std::string& cert_authority, const CdmIdentifier& identifier, const std::string& service_certificate, RequestedSecurityLevel requested_security_level, CdmProvisioningRequest* request, std::string* default_url) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->GetProvisioningRequest( cert_type, cert_authority, service_certificate, requested_security_level, request, default_url); } CdmResponseType WvContentDecryptionModule::HandleProvisioningResponse( const CdmIdentifier& identifier, const CdmProvisioningResponse& response, RequestedSecurityLevel requested_security_level, std::string* cert, std::string* wrapped_key) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->HandleProvisioningResponse( response, requested_security_level, cert, wrapped_key); } CdmResponseType WvContentDecryptionModule::Unprovision( CdmSecurityLevel level, const CdmIdentifier& identifier) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); // Persistent state is deleted on unprovisioning. The L3 OEMCrypto device // key may however remain in memory until |OEMCrypto_Terminate| is called. // It is not regenerated until |OEMCrypto_Initialize| is called. // Enable immediate OEMCrypto termination and re-initalization on // unprovisioning. CryptoSession::DisableDelayedTermination(); return cdm_engine->Unprovision(level); } bool WvContentDecryptionModule::IsProvisioned(CdmSecurityLevel security_level, const std::string& origin, const std::string& spoid, bool atsc_mode_enabled) { FileSystem file_system; file_system.set_origin(origin); file_system.set_identifier(spoid + origin); DeviceFiles device_files(&file_system); device_files.Init(security_level); return device_files.HasCertificate(atsc_mode_enabled); } CdmResponseType WvContentDecryptionModule::GetUsageInfo( const std::string& app_id, const CdmIdentifier& identifier, CdmUsageReportList* usage_reports) { if (usage_reports == nullptr) return CdmResponseType(PARAMETER_NULL); CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); int error_detail = NO_ERROR; 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, CdmUsageReport* usage_report) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); int error_detail = NO_ERROR; return cdm_engine->GetUsageInfo(app_id, ssid, &error_detail, usage_report); } CdmResponseType WvContentDecryptionModule::RemoveAllUsageInfo( const std::string& app_id, const CdmIdentifier& identifier) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->RemoveAllUsageInfo(app_id); } CdmResponseType WvContentDecryptionModule::RemoveUsageInfo( const std::string& app_id, const CdmIdentifier& identifier, const CdmSecureStopId& secure_stop_id) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->RemoveUsageInfo(app_id, secure_stop_id); } CdmResponseType WvContentDecryptionModule::ReleaseUsageInfo( const CdmKeyResponse& message, const CdmIdentifier& identifier) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->ReleaseUsageInfo(message); } CdmResponseType WvContentDecryptionModule::GetSecureStopIds( const std::string& app_id, const CdmIdentifier& identifier, std::vector* ssids) { if (ssids == nullptr) { LOGE("Secure stop IDs destination not provided"); return CdmResponseType(PARAMETER_NULL); } CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); const CdmResponseType sts = cdm_engine->ListUsageIds(app_id, kSecurityLevelL1, nullptr, ssids); 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; } CdmResponseType WvContentDecryptionModule::Decrypt( const CdmSessionId& session_id, bool validate_key_id, const CdmDecryptionParameters& parameters) { return DecryptV16(session_id, validate_key_id, CdmDecryptionParametersV16::from_v15(parameters)); } CdmResponseType WvContentDecryptionModule::DecryptV16( const CdmSessionId& session_id, bool validate_key_id, const CdmDecryptionParametersV16& parameters) { // First find the CdmEngine that has the given session_id. If we are using // key sharing, the shared session will still be in the same CdmEngine. CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) { LOGE("Decrypt session ID not found: %s", session_id.c_str()); return CdmResponseType(SESSION_NOT_FOUND_18); } CdmSessionId local_session_id = session_id; if (validate_key_id && Properties::GetSessionSharingId(session_id) != 0) { bool status = cdm_engine->FindSessionForKey(parameters.key_id, &local_session_id); if (!status) { bool is_any_protected = std::any_of( std::begin(parameters.samples), std::end(parameters.samples), [](const CdmDecryptionSample& sample) -> bool { return std::any_of( std::begin(sample.subsamples), std::end(sample.subsamples), [](const CdmDecryptionSubsample& subsample) -> bool { return subsample.protected_bytes > 0; }); }); // A key does not need to be loaded if // (a) the content consists of one or more samples and is entirely // clear data // (b) (legacy) the content consists of clear lead/frame in a single // subsample. In earlier releases content was presented for decryption // as individual subsamples. If the clear frame is broken up across // multiple subsamples decryption, requests should be rejected // (b/110251447) // TODO(b/149524614): Remove support for the legacy case if (is_any_protected || (parameters.observe_legacy_fields && parameters.samples.size() == 1 && parameters.samples[0].subsamples.size() == 1 && !(parameters.samples[0].subsamples[0].flags & OEMCrypto_FirstSubsample && parameters.samples[0].subsamples[0].flags & OEMCrypto_LastSubsample))) { return CdmResponseType(KEY_NOT_FOUND_IN_SESSION); } } } return cdm_engine->DecryptV16(local_session_id, parameters); } void WvContentDecryptionModule::NotifyResolution(const CdmSessionId& session_id, uint32_t width, uint32_t height) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return; cdm_engine->NotifyResolution(session_id, width, height); } bool WvContentDecryptionModule::IsValidServiceCertificate( const std::string& certificate) { ServiceCertificate cert; CdmResponseType status = cert.Init(certificate); if (status != NO_ERROR) return false; return cert.has_certificate(); } CdmResponseType WvContentDecryptionModule::GetCurrentMetrics( const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) { if (!metrics) { return CdmResponseType(PARAMETER_NULL); } std::unique_lock auto_lock(cdms_lock_); return GetCurrentMetricsInternal(identifier, metrics); } CdmResponseType WvContentDecryptionModule::GetAllCurrentMetricsSnapshots( std::vector* snapshots, bool* full_list_returned) { if (!snapshots || !full_list_returned) { return CdmResponseType(PARAMETER_NULL); } snapshots->clear(); std::unique_lock auto_lock(cdms_lock_); for (const auto& cdm_info : cdms_) { const CdmIdentifier& identifier = cdm_info.first; drm_metrics::WvCdmMetrics metric; const CdmResponseType status = GetCurrentMetricsInternal(identifier, &metric); if (status == NO_ERROR) { snapshots->push_back( WvMetricsSnapshot::MakeSnapshot(identifier, std::move(metric))); } else { LOGD("Failed to get metrics: cdm_identifier = %u, error = %s", identifier.unique_id, status.ToString().c_str()); } } // With no streaming activities, |cdms_| size would be zero, // treat it as a non full list in that case. *full_list_returned = !cdms_.empty() && snapshots->size() == cdms_.size(); // We only return error if no metrics is returned when cdms_ is not empty. // - metrics && cdms_ sizes can both be zero, returns NO_ERROR // - metrics size <= cdms_, returns NO_ERROR // - metrics is empty, but cdms_ is not, returns UNKNOWN_ERROR return (snapshots->empty() && !cdms_.empty()) ? CdmResponseType(UNKNOWN_ERROR) : CdmResponseType(NO_ERROR); } CdmResponseType WvContentDecryptionModule::GetAllSavedMetricsSnapshots( std::vector* snapshots) { if (snapshots == nullptr) { return CdmResponseType(PARAMETER_NULL); } std::unique_lock auto_lock(cdms_lock_); // This has the potential to be an expensive operation. However, // this function is only used during debug reporting. snapshots->assign(saved_metrics_snapshots_.cbegin(), saved_metrics_snapshots_.cend()); return CdmResponseType(NO_ERROR); } CdmResponseType WvContentDecryptionModule::GetCurrentMetricsInternal( const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics* metrics) { // Note: Caller should lock before calling. auto it = cdms_.find(identifier); if (it == cdms_.end()) { LOGE("Cdm Identifier not found"); // TODO(blueeyes): Add a better error. return CdmResponseType(UNKNOWN_ERROR); } return it->second.cdm_engine->GetMetricsSnapshot(metrics) ? CdmResponseType(NO_ERROR) : CdmResponseType(UNKNOWN_ERROR); } void WvContentDecryptionModule::SaveMetrics( const CdmIdentifier& identifier, drm_metrics::WvCdmMetrics&& metrics) { // Caller should have acquired |cdms_lock_|. saved_metrics_snapshots_.push_front( WvMetricsSnapshot::MakeSnapshot(identifier, std::move(metrics))); if (saved_metrics_snapshots_.size() > kMaxSavedMetricsSnapshotCount) { saved_metrics_snapshots_.resize(kMaxSavedMetricsSnapshotCount); } } WvContentDecryptionModule::CdmInfo::CdmInfo() : cdm_engine(CdmEngineFactory::CreateCdmEngine(&file_system)) {} CdmEngine* WvContentDecryptionModule::EnsureCdmForIdentifier( const CdmIdentifier& identifier) { CdmEngine* cdm_engine; bool enable_timer = false; { std::unique_lock auto_lock(cdms_lock_); if (cdms_.size() == 0) enable_timer = true; if (cdms_.find(identifier) == cdms_.end()) { // Accessing the map entry will create a new instance using the default // constructor. We then need to provide it with two pieces of info: The // origin provided by the app and an identifier that uniquely identifies // this CDM. We concatenate all pieces of the CdmIdentifier in order to // create an ID that is unique to that identifier. cdms_[identifier].file_system.set_origin(identifier.origin); cdms_[identifier].file_system.set_identifier(identifier.spoid + identifier.origin); cdms_[identifier].cdm_engine->SetAppPackageName( identifier.app_package_name); cdms_[identifier].cdm_engine->SetSpoid(identifier.spoid); cdms_[identifier].cdm_engine->SetUserId(identifier.user_id); } cdm_engine = cdms_[identifier].cdm_engine.get(); } // Do not enable timer while holding on to the |cdms_lock_| if (enable_timer) EnableTimer(); return cdm_engine; } CdmEngine* WvContentDecryptionModule::GetCdmForSessionId( const std::string& session_id) { std::unique_lock auto_lock(cdms_lock_); // Use find to avoid creating empty entries when not found. auto it = cdm_by_session_id_.find(session_id); if (it == cdm_by_session_id_.end()) return nullptr; return it->second; } void WvContentDecryptionModule::CloseAllCdms() { std::unique_lock auto_lock(cdms_lock_); for (const auto& cdm_info : cdms_) { const CdmIdentifier& identifier = cdm_info.first; drm_metrics::WvCdmMetrics metrics; const CdmResponseType status = GetCurrentMetricsInternal(identifier, &metrics); if (status == NO_ERROR) { SaveMetrics(identifier, std::move(metrics)); } else { LOGD("CloseAllCdms failed to save metrics"); } } cdm_by_session_id_.clear(); cdms_.clear(); } CdmResponseType WvContentDecryptionModule::CloseCdm( const CdmIdentifier& cdm_identifier) { std::unique_lock auto_lock(cdms_lock_); auto cdm_it = cdms_.find(cdm_identifier); if (cdm_it == cdms_.end()) { LOGE("Cdm Identifier not found"); // TODO(blueeyes): Create a better error. return CdmResponseType(UNKNOWN_ERROR); } CdmEngine* cdm_engine = cdm_it->second.cdm_engine.get(); // Save metrics snapshot. drm_metrics::WvCdmMetrics metrics; const bool success = cdm_engine->GetMetricsSnapshot(&metrics); if (success) { SaveMetrics(cdm_identifier, std::move(metrics)); } else { LOGW("Failed to get metrics snapshot: unique_id = %" PRIu32, cdm_identifier.unique_id); } // Remove any sessions that point to this engine. for (auto session_it = cdm_by_session_id_.begin(); session_it != cdm_by_session_id_.end();) { if (session_it->second == cdm_engine) { session_it = cdm_by_session_id_.erase(session_it); } else { ++session_it; } } cdms_.erase(cdm_it); return CdmResponseType(NO_ERROR); } CdmResponseType WvContentDecryptionModule::SetDebugIgnoreKeyboxCount( uint32_t count) { return CdmEngine::SetDebugIgnoreKeyboxCount(count); } CdmResponseType WvContentDecryptionModule::SetAllowTestKeybox(bool allow) { return CdmEngine::SetAllowTestKeybox(allow); } CdmResponseType WvContentDecryptionModule::SetDecryptHash( const std::string& hash_data, CdmSessionId* id) { if (id == nullptr) { LOGE("Cdm session ID not provided"); return CdmResponseType(PARAMETER_NULL); } uint32_t frame_number; std::string hash; CdmResponseType res = CdmEngine::ParseDecryptHashString(hash_data, id, &frame_number, &hash); if (res != NO_ERROR) return res; CdmEngine* cdm_engine = GetCdmForSessionId(*id); if (!cdm_engine) { LOGE("Unable to find CdmEngine"); return CdmResponseType(SESSION_NOT_FOUND_20); } res = cdm_engine->SetDecryptHash(*id, frame_number, hash); return res; } CdmResponseType WvContentDecryptionModule::GetDecryptHashError( const CdmSessionId& session_id, std::string* hash_error_string) { CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) { LOGE("Unable to find CdmEngine"); return CdmResponseType(SESSION_NOT_FOUND_20); } return cdm_engine->GetDecryptHashError(session_id, hash_error_string); } void WvContentDecryptionModule::EnableTimer() { std::unique_lock auto_lock(timer_lock_); if (!timer_.IsRunning()) timer_.Start(this, kCdmTimerDurationSeconds); } void WvContentDecryptionModule::DisableTimer() { std::unique_lock auto_lock(timer_lock_); if (timer_.IsRunning()) { timer_.Stop(false); } } void WvContentDecryptionModule::DisableTimerAndWaitForExit() { std::unique_lock auto_lock(timer_lock_); if (timer_.IsRunning()) { timer_.Stop(true); } } void WvContentDecryptionModule::OnTimerEvent() { bool disable_timer = false; { std::unique_lock auto_lock(cdms_lock_); for (auto it = cdms_.begin(); it != cdms_.end(); ++it) { LoggingUidSetter set_uid(it->first.user_id); it->second.cdm_engine->OnTimerEvent(); } if (cdms_.empty()) { // The following code cannot be attributed to any app uid. LoggingUidSetter set_uid(wvutil::UNKNOWN_UID); if (CryptoSession::TryTerminate()) { // If CryptoSession is in a state to be terminated, try acquiring the // |timer_lock_| before deciding whether to disable the timer. If the // lock cannot be acquired, there is no need to disable the timer. // The |timer_lock_| is either being held by // * EnableTimer - This does not appear possible, but if it did // happen |OnTimerEvent| will be called again. The timer can be // disabled during a future call. // * DisableTimer - If so, allow that call to disable the timer. No // need to call to disable it here. // * DisableTimerAndWaitForExit - If so, allow that call to disable // the timer. No need to call to disable it here. Note that // |DisableTimerAndWaitForExit| will try to stop the timer but // wait for it to exit. This might kick off the timer thread one // last time, which will call into OnTimerEvent. Calling // |DisableTimer| here will result in deadlock. if (timer_lock_.try_lock()) { disable_timer = true; timer_lock_.unlock(); } } } } // Release |cdms_lock_| before attempting to disable the timer if (disable_timer) { DisableTimer(); } } uint32_t WvContentDecryptionModule::GenerateSessionSharingId() { static int next_session_sharing_id = 0; return ++next_session_sharing_id; } CdmResponseType WvContentDecryptionModule::ListStoredLicenses( CdmSecurityLevel security_level, const CdmIdentifier& identifier, std::vector* key_set_ids) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->ListStoredLicenses(security_level, key_set_ids); } CdmResponseType WvContentDecryptionModule::GetOfflineLicenseState( const CdmKeySetId& key_set_id, CdmSecurityLevel security_level, const CdmIdentifier& identifier, CdmOfflineLicenseState* license_state) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->GetOfflineLicenseState(key_set_id, security_level, license_state); } CdmResponseType WvContentDecryptionModule::RemoveOfflineLicense( const CdmKeySetId& key_set_id, CdmSecurityLevel security_level, const CdmIdentifier& identifier) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->RemoveOfflineLicense(key_set_id, security_level); } CdmResponseType WvContentDecryptionModule::SetPlaybackId( const CdmSessionId& session_id, const std::string& playback_id) { LOGV("Setting session ID %s playback ID %s", session_id.c_str(), playback_id.c_str()); CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_23); return cdm_engine->SetPlaybackId(session_id, playback_id); } CdmResponseType WvContentDecryptionModule::GetSessionUserId( const CdmSessionId& session_id, uint32_t* user_id) { if (!user_id) return CdmResponseType(PARAMETER_NULL); CdmEngine* cdm_engine = GetCdmForSessionId(session_id); if (!cdm_engine) return CdmResponseType(SESSION_NOT_FOUND_23); *user_id = cdm_engine->GetUserId(); return CdmResponseType(NO_ERROR); } CdmResponseType WvContentDecryptionModule::StoreAtscLicense( const CdmIdentifier& identifier, RequestedSecurityLevel requested_security_level, const CdmKeySetId& key_set_id, const std::string& serialized_license_data) { CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier); return cdm_engine->StoreAtscLicense(requested_security_level, key_set_id, serialized_license_data); } bool WvContentDecryptionModule::SetDefaultOtaKeyboxFallbackDurationRules() { CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier); if (!cdm_engine) return false; cdm_engine->SetDefaultOtaKeyboxFallbackDurationRules(); return true; } bool WvContentDecryptionModule::SetFastOtaKeyboxFallbackDurationRules() { CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier); if (!cdm_engine) return false; cdm_engine->SetFastOtaKeyboxFallbackDurationRules(); return true; } } // namespace wvcdm