| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_DISPLAY_MANAGER_CONTENT_PROTECTION_MANAGER_H_ |
| #define UI_DISPLAY_MANAGER_CONTENT_PROTECTION_MANAGER_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/containers/queue.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/timer/timer.h" |
| #include "ui/display/manager/content_protection_key_manager.h" |
| #include "ui/display/manager/display_configurator.h" |
| #include "ui/display/manager/display_manager_export.h" |
| |
| namespace display { |
| |
| class DisplayLayoutManager; |
| class DisplaySnapshot; |
| class NativeDisplayDelegate; |
| |
| namespace test { |
| class ContentProtectionManagerTest; |
| } // namespace test |
| |
| // Fulfills client requests to query and apply per-display or all display |
| // content protection, and notifies observers of display security changes. |
| // Changes are detected by polling as required by the kernel API, since |
| // authentication latency depends on hardware topology, and the hardware may |
| // temporarily drop authentication, in which case the kernel automatically tries |
| // to re-establish protection. |
| class DISPLAY_MANAGER_EXPORT ContentProtectionManager |
| : public DisplayConfigurator::Observer { |
| public: |
| // |connection_mask| is a DisplayConnectionType bitmask, and |protection_mask| |
| // is a ContentProtectionMethod bitmask. |
| using QueryContentProtectionCallback = base::OnceCallback< |
| void(bool success, uint32_t connection_mask, uint32_t protection_mask)>; |
| using ApplyContentProtectionCallback = base::OnceCallback<void(bool success)>; |
| |
| using ContentProtections = |
| base::flat_map<int64_t /* display_id */, uint32_t /* protection_mask */>; |
| |
| // Though only run once, a task must outlive its asynchronous operations, so |
| // cannot be a OnceCallback. |
| struct Task { |
| enum class Status { KILLED, FAILURE, SUCCESS }; |
| |
| virtual ~Task() = default; |
| virtual void Run() = 0; |
| }; |
| |
| class Observer : public base::CheckedObserver { |
| public: |
| ~Observer() override = default; |
| |
| // Called after the secure state of a display has been changed. |
| virtual void OnDisplaySecurityMaybeChanged(int64_t display_id, |
| bool secure) = 0; |
| }; |
| |
| // Returns whether display configuration is disabled, in which case API calls |
| // are no-ops resulting in failure callbacks. |
| using ConfigurationDisabledCallback = base::RepeatingCallback<bool()>; |
| |
| ContentProtectionManager(DisplayLayoutManager*, |
| ConfigurationDisabledCallback); |
| |
| ContentProtectionManager(const ContentProtectionManager&) = delete; |
| ContentProtectionManager& operator=(const ContentProtectionManager&) = delete; |
| |
| ~ContentProtectionManager() override; |
| |
| void set_native_display_delegate(NativeDisplayDelegate* delegate) { |
| native_display_delegate_ = delegate; |
| hdcp_key_manager_.set_native_display_delegate(delegate); |
| } |
| |
| using ClientId = std::optional<uint64_t>; |
| |
| // On display reconfiguration, pending requests are cancelled, i.e. clients |
| // receive failure callbacks, and are responsible for renewing requests. If a |
| // client unregisters with pending requests, the callbacks are not run. |
| ClientId RegisterClient(); |
| void UnregisterClient(ClientId client_id); |
| |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| // Queries protection against the client's latest request on the same display, |
| // i.e. the result is CONTENT_PROTECTION_METHOD_NONE unless the client has |
| // previously applied protection on that display, such that requests from |
| // other clients are concealed. |
| void QueryContentProtection(ClientId client_id, |
| int64_t display_id, |
| QueryContentProtectionCallback callback); |
| |
| // |protection_mask| is a ContentProtectionMethod bitmask. Callback success |
| // does not mean that protection is active, but merely that the request went |
| // through. The client must periodically query protection status until it no |
| // longer requires protection and applies CONTENT_PROTECTION_METHOD_NONE. If |
| // protection becomes temporarily unavailable, the client is not required to |
| // renew the request, but should keep querying to detect if automatic retries |
| // to establish protection are successful. |
| void ApplyContentProtection(ClientId client_id, |
| int64_t display_id, |
| uint32_t protection_mask, |
| ApplyContentProtectionCallback callback); |
| |
| void SetProvisionedKeyRequest( |
| ContentProtectionKeyManager::ProvisionedKeyRequest request) { |
| hdcp_key_manager_.set_provisioned_key_request(request); |
| } |
| |
| private: |
| friend class test::ContentProtectionManagerTest; |
| |
| bool disabled() const { |
| return !native_display_delegate_ || config_disabled_callback_.Run(); |
| } |
| |
| const DisplaySnapshot* GetDisplay(int64_t display_id) const; |
| |
| // Returns cumulative content protections given all client requests. |
| ContentProtections AggregateContentProtections() const; |
| |
| // Returns content protections for |client_id|, or nullptr if invalid. |
| ContentProtections* GetContentProtections(ClientId client_id); |
| |
| void QueueTask(std::unique_ptr<Task> task); |
| void DequeueTask(); |
| void KillTasks(); |
| |
| // Called on task completion. Responsible for running the client callback, and |
| // dequeuing the next pending task. |
| void OnContentProtectionQueried(QueryContentProtectionCallback callback, |
| ClientId client_id, |
| int64_t display_id, |
| Task::Status status, |
| uint32_t connection_mask, |
| uint32_t protection_mask); |
| void OnContentProtectionApplied(ApplyContentProtectionCallback callback, |
| ClientId client_id, |
| Task::Status status); |
| |
| // DisplayConfigurator::Observer overrides: |
| void OnDisplayConfigurationChanged( |
| const DisplayConfigurator::DisplayStateList&) override; |
| void OnDisplayConfigurationChangeFailed( |
| const DisplayConfigurator::DisplayStateList&, |
| MultipleDisplayState) override; |
| |
| bool HasExternalDisplaysWithContentProtection() const; |
| |
| // Toggles timer for periodic security queries given latest client requests. |
| void ToggleDisplaySecurityPolling(); |
| |
| // Forces timer to fire if running, and returns whether it was running. |
| bool TriggerDisplaySecurityTimeoutForTesting(); |
| |
| // Queries protection status for all displays, and notifies observers whether |
| // each display is secure. Called periodically while protection is requested. |
| void QueueDisplaySecurityQueries(); |
| void OnDisplaySecurityQueried(int64_t display_id, |
| Task::Status status, |
| uint32_t connection_mask, |
| uint32_t protection_mask); |
| |
| void QueueContentProtectionTask(ApplyContentProtectionCallback callback, |
| ClientId client_id, |
| bool is_key_set); |
| |
| const raw_ptr<DisplayLayoutManager> layout_manager_; // Not owned. |
| raw_ptr<NativeDisplayDelegate> native_display_delegate_ = |
| nullptr; // Not owned. |
| |
| const ConfigurationDisabledCallback config_disabled_callback_; |
| |
| uint64_t next_client_id_ = 0; |
| |
| // Content protections requested by each client. |
| base::flat_map<uint64_t, ContentProtections> requests_; |
| |
| // Pending tasks to query or apply content protection. |
| base::queue<std::unique_ptr<Task>> tasks_; |
| |
| base::ObserverList<Observer> observers_; |
| |
| // Used for periodic queries to notify observers of display security changes. |
| base::RepeatingTimer security_timer_; |
| |
| ContentProtectionKeyManager hdcp_key_manager_; |
| |
| base::WeakPtrFactory<ContentProtectionManager> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace display |
| |
| #endif // UI_DISPLAY_MANAGER_CONTENT_PROTECTION_MANAGER_H_ |