// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/display/chromeos/apply_content_protection_task.h" #include "ui/display/chromeos/display_layout_manager.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/native_display_delegate.h" namespace ui { namespace { bool GetHDCPCapableDisplays( const DisplayLayoutManager& layout_manager, std::vector* hdcp_capable_displays) { for (DisplaySnapshot* display : layout_manager.GetDisplayStates()) { switch (display->type()) { case DISPLAY_CONNECTION_TYPE_UNKNOWN: return false; // DisplayPort, DVI, and HDMI all support HDCP. case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: case DISPLAY_CONNECTION_TYPE_DVI: case DISPLAY_CONNECTION_TYPE_HDMI: hdcp_capable_displays->push_back(display); break; case DISPLAY_CONNECTION_TYPE_INTERNAL: case DISPLAY_CONNECTION_TYPE_VGA: case DISPLAY_CONNECTION_TYPE_NETWORK: // No protections for these types. Do nothing. break; case DISPLAY_CONNECTION_TYPE_NONE: NOTREACHED(); break; } } return true; } } // namespace ApplyContentProtectionTask::ApplyContentProtectionTask( DisplayLayoutManager* layout_manager, NativeDisplayDelegate* native_display_delegate, const DisplayConfigurator::ContentProtections& requests, const ResponseCallback& callback) : layout_manager_(layout_manager), native_display_delegate_(native_display_delegate), requests_(requests), callback_(callback), query_status_(true), pending_requests_(0), weak_ptr_factory_(this) { } ApplyContentProtectionTask::~ApplyContentProtectionTask() { } void ApplyContentProtectionTask::Run() { std::vector hdcp_capable_displays; if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) { callback_.Run(false); return; } pending_requests_ = hdcp_capable_displays.size(); if (pending_requests_ == 0) { callback_.Run(true); return; } // Need to poll the driver for updates since other applications may have // updated the state. for (DisplaySnapshot* display : hdcp_capable_displays) { native_display_delegate_->GetHDCPState( *display, base::Bind(&ApplyContentProtectionTask::OnHDCPStateUpdate, weak_ptr_factory_.GetWeakPtr(), display->display_id())); } } void ApplyContentProtectionTask::OnHDCPStateUpdate(int64_t display_id, bool success, HDCPState state) { query_status_ &= success; display_hdcp_state_map_[display_id] = state; pending_requests_--; // Wait for all the requests before continuing. if (pending_requests_ != 0) return; if (!query_status_) { callback_.Run(false); return; } ApplyProtections(); } void ApplyContentProtectionTask::ApplyProtections() { std::vector hdcp_capable_displays; if (!GetHDCPCapableDisplays(*layout_manager_, &hdcp_capable_displays)) { callback_.Run(false); return; } std::vector> hdcp_requests; // Figure out which displays need to have their HDCP state changed. for (DisplaySnapshot* display : hdcp_capable_displays) { uint32_t desired_mask = GetDesiredProtectionMask(display->display_id()); auto it = display_hdcp_state_map_.find(display->display_id()); // If the display can't be found, the display configuration changed. if (it == display_hdcp_state_map_.end()) { callback_.Run(false); return; } bool hdcp_enabled = it->second != HDCP_STATE_UNDESIRED; bool hdcp_requested = desired_mask & CONTENT_PROTECTION_METHOD_HDCP; if (hdcp_enabled != hdcp_requested) { hdcp_requests.push_back(std::make_pair( display, hdcp_requested ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED)); } } pending_requests_ = hdcp_requests.size(); // All the requested changes are the same as the current HDCP state. Nothing // to do anymore, just ack the content protection change. if (pending_requests_ == 0) { callback_.Run(true); return; } for (const auto& pair : hdcp_requests) { native_display_delegate_->SetHDCPState( *pair.first, pair.second, base::Bind(&ApplyContentProtectionTask::OnHDCPStateApplied, weak_ptr_factory_.GetWeakPtr())); } } void ApplyContentProtectionTask::OnHDCPStateApplied(bool success) { query_status_ &= success; pending_requests_--; if (pending_requests_ == 0) callback_.Run(query_status_); } uint32_t ApplyContentProtectionTask::GetDesiredProtectionMask( int64_t display_id) const { uint32_t desired_mask = 0; // In mirror mode, protection request of all displays need to be fulfilled. // In non-mirror mode, only request of client's display needs to be // fulfilled. if (layout_manager_->IsMirroring()) { for (auto pair : requests_) desired_mask |= pair.second; } else { auto it = requests_.find(display_id); if (it != requests_.end()) desired_mask = it->second; } return desired_mask; } } // namespace ui