1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos/components/cdm_factory_daemon/output_protection_impl.h"
6 
7 #include <utility>
8 
9 #include "ash/shell.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "content/public/browser/browser_task_traits.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
15 #include "ui/display/manager/display_configurator.h"
16 #include "ui/display/manager/display_manager.h"
17 #include "ui/display/screen.h"
18 #include "ui/display/types/display_constants.h"
19 
20 namespace chromeos {
21 namespace {
22 // Make sure the mapping between the Mojo enums and the Chrome enums do not
23 // fall out of sync.
24 #define VALIDATE_ENUM(mojo_type, chrome_type, name)                           \
25   static_assert(                                                              \
26       static_cast<uint32_t>(cdm::mojom::OutputProtection::mojo_type::name) == \
27           display::chrome_type##_##name,                                      \
28       #chrome_type "_" #name "value doesn't match")
29 
30 VALIDATE_ENUM(ProtectionType, CONTENT_PROTECTION_METHOD, NONE);
31 VALIDATE_ENUM(ProtectionType, CONTENT_PROTECTION_METHOD, HDCP_TYPE_0);
32 VALIDATE_ENUM(ProtectionType, CONTENT_PROTECTION_METHOD, HDCP_TYPE_1);
33 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, NONE);
34 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, UNKNOWN);
35 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, INTERNAL);
36 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, VGA);
37 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, HDMI);
38 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, DVI);
39 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, DISPLAYPORT);
40 VALIDATE_ENUM(LinkType, DISPLAY_CONNECTION_TYPE, NETWORK);
41 
42 static_assert(display::DISPLAY_CONNECTION_TYPE_LAST ==
43                   display::DISPLAY_CONNECTION_TYPE_NETWORK,
44               "DISPLAY_CONNECTION_TYPE_LAST value doesn't match");
45 
46 constexpr uint32_t kUnprotectableConnectionTypes =
47     display::DISPLAY_CONNECTION_TYPE_UNKNOWN |
48     display::DISPLAY_CONNECTION_TYPE_VGA |
49     display::DISPLAY_CONNECTION_TYPE_NETWORK;
50 
51 constexpr uint32_t kProtectableConnectionTypes =
52     display::DISPLAY_CONNECTION_TYPE_HDMI |
53     display::DISPLAY_CONNECTION_TYPE_DVI |
54     display::DISPLAY_CONNECTION_TYPE_DISPLAYPORT;
55 
GetDisplayIdsFromSnapshots(const std::vector<display::DisplaySnapshot * > & snapshots)56 std::vector<int64_t> GetDisplayIdsFromSnapshots(
57     const std::vector<display::DisplaySnapshot*>& snapshots) {
58   std::vector<int64_t> display_ids;
59   for (display::DisplaySnapshot* ds : snapshots) {
60     display_ids.push_back(ds->display_id());
61   }
62   return display_ids;
63 }
64 
ConvertProtection(uint32_t protection_mask)65 cdm::mojom::OutputProtection::ProtectionType ConvertProtection(
66     uint32_t protection_mask) {
67   // Only return Type 1 if that is the only type active since we want to reflect
68   // the overall output security.
69   if ((protection_mask & display::kContentProtectionMethodHdcpAll) ==
70       display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1) {
71     return cdm::mojom::OutputProtection::ProtectionType::HDCP_TYPE_1;
72   } else if (protection_mask & display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0) {
73     return cdm::mojom::OutputProtection::ProtectionType::HDCP_TYPE_0;
74   } else {
75     return cdm::mojom::OutputProtection::ProtectionType::NONE;
76   }
77 }
78 
79 class DisplaySystemDelegateImpl
80     : public OutputProtectionImpl::DisplaySystemDelegate {
81  public:
DisplaySystemDelegateImpl()82   DisplaySystemDelegateImpl() {
83     display_configurator_ =
84         ash::Shell::Get()->display_manager()->configurator();
85     DCHECK(display_configurator_);
86     content_protection_manager_ =
87         display_configurator_->content_protection_manager();
88     DCHECK(content_protection_manager_);
89   }
90   ~DisplaySystemDelegateImpl() override = default;
91 
ApplyContentProtection(display::ContentProtectionManager::ClientId client_id,int64_t display_id,uint32_t protection_mask,display::ContentProtectionManager::ApplyContentProtectionCallback callback)92   void ApplyContentProtection(
93       display::ContentProtectionManager::ClientId client_id,
94       int64_t display_id,
95       uint32_t protection_mask,
96       display::ContentProtectionManager::ApplyContentProtectionCallback
97           callback) override {
98     content_protection_manager_->ApplyContentProtection(
99         client_id, display_id, protection_mask, std::move(callback));
100   }
QueryContentProtection(display::ContentProtectionManager::ClientId client_id,int64_t display_id,display::ContentProtectionManager::QueryContentProtectionCallback callback)101   void QueryContentProtection(
102       display::ContentProtectionManager::ClientId client_id,
103       int64_t display_id,
104       display::ContentProtectionManager::QueryContentProtectionCallback
105           callback) override {
106     content_protection_manager_->QueryContentProtection(client_id, display_id,
107                                                         std::move(callback));
108   }
RegisterClient()109   display::ContentProtectionManager::ClientId RegisterClient() override {
110     return content_protection_manager_->RegisterClient();
111   }
UnregisterClient(display::ContentProtectionManager::ClientId client_id)112   void UnregisterClient(
113       display::ContentProtectionManager::ClientId client_id) override {
114     content_protection_manager_->UnregisterClient(client_id);
115   }
AddObserver(display::DisplayObserver * observer)116   void AddObserver(display::DisplayObserver* observer) override {
117     display::Screen::GetScreen()->AddObserver(observer);
118   }
RemoveObserver(display::DisplayObserver * observer)119   void RemoveObserver(display::DisplayObserver* observer) override {
120     display::Screen::GetScreen()->RemoveObserver(observer);
121   }
cached_displays() const122   const std::vector<display::DisplaySnapshot*>& cached_displays()
123       const override {
124     return display_configurator_->cached_displays();
125   }
126 
127  private:
128   display::ContentProtectionManager* content_protection_manager_;  // Not owned.
129   display::DisplayConfigurator* display_configurator_;             // Not owned.
130 };
131 
132 }  // namespace
133 
134 // static
Create(mojo::PendingReceiver<cdm::mojom::OutputProtection> receiver,std::unique_ptr<DisplaySystemDelegate> delegate)135 void OutputProtectionImpl::Create(
136     mojo::PendingReceiver<cdm::mojom::OutputProtection> receiver,
137     std::unique_ptr<DisplaySystemDelegate> delegate) {
138   // This needs to run on the UI thread for its interactions with the display
139   // system.
140   if (!content::GetUIThreadTaskRunner({})->RunsTasksInCurrentSequence()) {
141     content::GetUIThreadTaskRunner({})->PostTask(
142         FROM_HERE, base::BindOnce(&OutputProtectionImpl::Create,
143                                   std::move(receiver), std::move(delegate)));
144     return;
145   }
146   if (!delegate)
147     delegate = std::make_unique<DisplaySystemDelegateImpl>();
148   // This object should destruct when the mojo connection is lost.
149   mojo::MakeSelfOwnedReceiver(
150       std::make_unique<OutputProtectionImpl>(std::move(delegate)),
151       std::move(receiver));
152 }
153 
OutputProtectionImpl(std::unique_ptr<DisplaySystemDelegate> delegate)154 OutputProtectionImpl::OutputProtectionImpl(
155     std::unique_ptr<DisplaySystemDelegate> delegate)
156     : delegate_(std::move(delegate)) {
157   DCHECK(delegate_);
158 }
159 
~OutputProtectionImpl()160 OutputProtectionImpl::~OutputProtectionImpl() {
161   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
162   if (client_id_) {
163     delegate_->RemoveObserver(this);
164     delegate_->UnregisterClient(client_id_);
165   }
166 }
167 
QueryStatus(QueryStatusCallback callback)168 void OutputProtectionImpl::QueryStatus(QueryStatusCallback callback) {
169   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
170   if (!client_id_)
171     Initialize();
172   if (display_id_list_.empty()) {
173     std::move(callback).Run(true, display::DISPLAY_CONNECTION_TYPE_NONE,
174                             ProtectionType::NONE);
175     return;
176   }
177 
178   // We want to copy this since we will manipulate it.
179   std::vector<int64_t> remaining_displays = display_id_list_;
180   int64_t curr_display_id = remaining_displays.back();
181   remaining_displays.pop_back();
182   delegate_->QueryContentProtection(
183       client_id_, curr_display_id,
184       base::BindOnce(&OutputProtectionImpl::QueryStatusCallbackAggregator,
185                      weak_factory_.GetWeakPtr(), std::move(remaining_displays),
186                      std::move(callback), true,
187                      display::DISPLAY_CONNECTION_TYPE_NONE,
188                      display::CONTENT_PROTECTION_METHOD_NONE,
189                      display::CONTENT_PROTECTION_METHOD_NONE));
190 }
191 
EnableProtection(ProtectionType desired_protection,EnableProtectionCallback callback)192 void OutputProtectionImpl::EnableProtection(ProtectionType desired_protection,
193                                             EnableProtectionCallback callback) {
194   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
195   if (!client_id_)
196     Initialize();
197 
198   if (display_id_list_.empty()) {
199     std::move(callback).Run(true);
200     return;
201   }
202 
203   // We just pass through what the client requests.
204   switch (desired_protection) {
205     case ProtectionType::HDCP_TYPE_0:
206       desired_protection_mask_ = display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_0;
207       break;
208     case ProtectionType::HDCP_TYPE_1:
209       desired_protection_mask_ = display::CONTENT_PROTECTION_METHOD_HDCP_TYPE_1;
210       break;
211     case ProtectionType::NONE:
212       desired_protection_mask_ = display::CONTENT_PROTECTION_METHOD_NONE;
213       break;
214   }
215 
216   // We want to copy this since we will manipulate it.
217   std::vector<int64_t> remaining_displays = display_id_list_;
218   int64_t curr_display_id = remaining_displays.back();
219   remaining_displays.pop_back();
220   delegate_->ApplyContentProtection(
221       client_id_, curr_display_id, desired_protection_mask_,
222       base::BindOnce(&OutputProtectionImpl::EnableProtectionCallbackAggregator,
223                      weak_factory_.GetWeakPtr(), std::move(remaining_displays),
224                      std::move(callback), true));
225 }
226 
Initialize()227 void OutputProtectionImpl::Initialize() {
228   DCHECK(!client_id_);
229   // This needs to be setup on the browser thread, so wait to do it until we
230   // are on that thread (i.e. don't do it in the constructor).
231   client_id_ = delegate_->RegisterClient();
232   DCHECK(client_id_);
233   delegate_->AddObserver(this);
234   display_id_list_ = GetDisplayIdsFromSnapshots(delegate_->cached_displays());
235 }
236 
EnableProtectionCallbackAggregator(std::vector<int64_t> remaining_displays,EnableProtectionCallback callback,bool aggregate_success,bool success)237 void OutputProtectionImpl::EnableProtectionCallbackAggregator(
238     std::vector<int64_t> remaining_displays,
239     EnableProtectionCallback callback,
240     bool aggregate_success,
241     bool success) {
242   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
243   aggregate_success &= success;
244   if (remaining_displays.empty()) {
245     std::move(callback).Run(aggregate_success);
246     return;
247   }
248   int64_t curr_display_id = remaining_displays.back();
249   remaining_displays.pop_back();
250   delegate_->ApplyContentProtection(
251       client_id_, curr_display_id, desired_protection_mask_,
252       base::BindOnce(&OutputProtectionImpl::EnableProtectionCallbackAggregator,
253                      weak_factory_.GetWeakPtr(), std::move(remaining_displays),
254                      std::move(callback), aggregate_success));
255 }
256 
QueryStatusCallbackAggregator(std::vector<int64_t> remaining_displays,QueryStatusCallback callback,bool aggregate_success,uint32_t aggregate_link_mask,uint32_t aggregate_protection_mask,uint32_t aggregate_no_protection_mask,bool success,uint32_t link_mask,uint32_t protection_mask)257 void OutputProtectionImpl::QueryStatusCallbackAggregator(
258     std::vector<int64_t> remaining_displays,
259     QueryStatusCallback callback,
260     bool aggregate_success,
261     uint32_t aggregate_link_mask,
262     uint32_t aggregate_protection_mask,
263     uint32_t aggregate_no_protection_mask,
264     bool success,
265     uint32_t link_mask,
266     uint32_t protection_mask) {
267   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
268   aggregate_success &= success;
269   aggregate_link_mask |= link_mask;
270   if (link_mask & kUnprotectableConnectionTypes) {
271     aggregate_no_protection_mask |= display::kContentProtectionMethodHdcpAll;
272   }
273   if (link_mask & kProtectableConnectionTypes) {
274     aggregate_protection_mask |= protection_mask;
275   }
276   if (!remaining_displays.empty()) {
277     int64_t curr_display_id = remaining_displays.back();
278     remaining_displays.pop_back();
279     delegate_->QueryContentProtection(
280         client_id_, curr_display_id,
281         base::BindOnce(
282             &OutputProtectionImpl::QueryStatusCallbackAggregator,
283             weak_factory_.GetWeakPtr(), std::move(remaining_displays),
284             std::move(callback), aggregate_success, aggregate_link_mask,
285             aggregate_protection_mask, aggregate_no_protection_mask));
286     return;
287   }
288 
289   aggregate_protection_mask &= ~aggregate_no_protection_mask;
290   std::move(callback).Run(aggregate_success, aggregate_link_mask,
291                           ConvertProtection(aggregate_protection_mask));
292 }
293 
HandleDisplayChange()294 void OutputProtectionImpl::HandleDisplayChange() {
295   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
296   display_id_list_ = GetDisplayIdsFromSnapshots(delegate_->cached_displays());
297   if (desired_protection_mask_) {
298     // We always reapply content protection on display changes since we affect
299     // all displays.
300     EnableProtection(ConvertProtection(desired_protection_mask_),
301                      base::DoNothing());
302   }
303 }
304 
OnDisplayAdded(const display::Display & display)305 void OutputProtectionImpl::OnDisplayAdded(const display::Display& display) {
306   HandleDisplayChange();
307 }
308 
OnDisplayMetricsChanged(const display::Display & display,uint32_t changed_metrics)309 void OutputProtectionImpl::OnDisplayMetricsChanged(
310     const display::Display& display,
311     uint32_t changed_metrics) {
312   HandleDisplayChange();
313 }
314 
OnDisplayRemoved(const display::Display & display)315 void OutputProtectionImpl::OnDisplayRemoved(const display::Display& display) {
316   HandleDisplayChange();
317 }
318 
319 }  // namespace chromeos
320