| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/display/manager/content_protection_key_manager.h" |
| |
| #include "base/memory/raw_ptr.h" |
| #include "ui/display/display_features.h" |
| #include "ui/display/manager/util/display_manager_util.h" |
| |
| namespace display { |
| |
| namespace { |
| |
| // Length of the key as expected to come from the server. |
| // This is the same length that the kernel also expects. |
| constexpr size_t kHdcpKeySize = 285; |
| |
| display::DisplaySnapshot* GetDisplayWithIdIfHdcpCapableAndKeyNeeded( |
| const std::vector<raw_ptr<display::DisplaySnapshot, VectorExperimental>>& |
| displays_states, |
| int64_t display_id) { |
| for (display::DisplaySnapshot* display : displays_states) { |
| if (display->display_id() == display_id) { |
| uint32_t protection_mask; |
| bool is_hdcp_capable = |
| GetContentProtectionMethods(display->type(), &protection_mask) && |
| protection_mask & display::kContentProtectionMethodHdcpAll; |
| if (is_hdcp_capable && display->has_content_protection_key()) { |
| return display; |
| } |
| break; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| } // namespace |
| |
| ContentProtectionKeyManager::ContentProtectionKeyManager() = default; |
| ContentProtectionKeyManager::~ContentProtectionKeyManager() = default; |
| |
| void ContentProtectionKeyManager::SetKeyIfRequired( |
| const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>& |
| displays_states, |
| int64_t display_id, |
| KeySetCallback on_key_set) { |
| DCHECK(!on_key_set.is_null()); |
| |
| // TODO(markyacoub): Remove this flag once the feature is fully launched. |
| if (!features::IsHdcpKeyProvisioningRequired()) { |
| std::move(on_key_set).Run(false); |
| return; |
| } |
| |
| if (!GetDisplayWithIdIfHdcpCapableAndKeyNeeded(displays_states, display_id)) { |
| std::move(on_key_set).Run(false); |
| return; |
| } |
| |
| pending_display_callbacks_[display_id] = std::move(on_key_set); |
| |
| // If we already learnt that we need a key and already fetched the key, go |
| // ahead and inject it into the kernel. |
| if (!cached_provisioned_key_.empty()) { |
| InjectKeyToKernel(display_id); |
| return; |
| } |
| |
| // If there are no pending displays nor we have the key, it means we haven't |
| // fetched the key from the server yet. |
| if (displays_pending_set_key_.empty()) { |
| displays_pending_set_key_.insert(display_id); |
| FetchKeyFromServer(); |
| // If the list isn't empty, it means we're in the process of fetching the |
| // key from the server already. Just add the pending display to the list and |
| // we'll set it later when the key is fetched. |
| } else { |
| displays_pending_set_key_.insert(display_id); |
| } |
| } |
| |
| void ContentProtectionKeyManager::FetchKeyFromServer() { |
| DCHECK(!provisioned_key_request_.is_null()); |
| provisioned_key_request_.Run( |
| base::BindOnce(&ContentProtectionKeyManager::OnKeyFetchedFromServer, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ContentProtectionKeyManager::OnKeyFetchedFromServer( |
| const std::string& key) { |
| if (key.size()) { |
| // This is the size of the key that we expect from the server as of now. |
| DCHECK_EQ(key.size(), kHdcpKeySize); |
| cached_provisioned_key_ = key; |
| for (int64_t display_id : displays_pending_set_key_) { |
| InjectKeyToKernel(display_id); |
| } |
| } else { |
| LOG(ERROR) << "Fetched an empty HDCP key from widevine server"; |
| for (int64_t display_id : displays_pending_set_key_) { |
| TriggerPendingCallbacks(display_id, false); |
| } |
| } |
| displays_pending_set_key_.clear(); |
| } |
| |
| void ContentProtectionKeyManager::InjectKeyToKernel(int64_t display_id) { |
| DCHECK(native_display_delegate_); |
| native_display_delegate_->SetHdcpKeyProp( |
| display_id, cached_provisioned_key_, |
| base::BindOnce(&ContentProtectionKeyManager::OnKeyInjectedToKernel, |
| weak_ptr_factory_.GetWeakPtr(), display_id)); |
| } |
| |
| void ContentProtectionKeyManager::OnKeyInjectedToKernel(int64_t display_id, |
| bool success) { |
| LOG_IF(ERROR, !success) << "Failed to Inject the HDCP Key to Display #" |
| << display_id; |
| TriggerPendingCallbacks(display_id, success); |
| } |
| |
| void ContentProtectionKeyManager::TriggerPendingCallbacks(int64_t display_id, |
| bool is_key_set) { |
| CHECK(pending_display_callbacks_.contains(display_id)); |
| KeySetCallback callback = std::move(pending_display_callbacks_[display_id]); |
| DCHECK(!callback.is_null()); |
| std::move(callback).Run(is_key_set); |
| |
| pending_display_callbacks_.erase(display_id); |
| } |
| |
| } // namespace display |