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