1 // Copyright 2014 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 "extensions/browser/api/hid/hid_device_manager.h"
6
7 #include <stdint.h>
8
9 #include <functional>
10 #include <limits>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/lazy_instance.h"
17 #include "base/location.h"
18 #include "base/no_destructor.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/threading/thread_task_runner_handle.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/device_service.h"
23 #include "extensions/browser/api/device_permissions_manager.h"
24 #include "extensions/common/permissions/permissions_data.h"
25 #include "extensions/common/permissions/usb_device_permission.h"
26 #include "mojo/public/cpp/bindings/callback_helpers.h"
27 #include "mojo/public/cpp/bindings/pending_remote.h"
28 #include "services/device/public/cpp/hid/hid_device_filter.h"
29 #include "services/device/public/cpp/hid/hid_usage_and_page.h"
30 #include "services/device/public/mojom/hid.mojom.h"
31
32 namespace hid = extensions::api::hid;
33
34 using device::HidDeviceFilter;
35
36 namespace extensions {
37
38 namespace {
39
PopulateHidDeviceInfo(hid::HidDeviceInfo * output,const device::mojom::HidDeviceInfo & input)40 void PopulateHidDeviceInfo(hid::HidDeviceInfo* output,
41 const device::mojom::HidDeviceInfo& input) {
42 output->vendor_id = input.vendor_id;
43 output->product_id = input.product_id;
44 output->product_name = input.product_name;
45 output->serial_number = input.serial_number;
46 output->max_input_report_size = input.max_input_report_size;
47 output->max_output_report_size = input.max_output_report_size;
48 output->max_feature_report_size = input.max_feature_report_size;
49
50 for (const auto& collection : input.collections) {
51 // Don't expose sensitive data.
52 if (device::IsProtected(*collection->usage)) {
53 continue;
54 }
55
56 hid::HidCollectionInfo api_collection;
57 api_collection.usage_page = collection->usage->usage_page;
58 api_collection.usage = collection->usage->usage;
59
60 api_collection.report_ids.insert(api_collection.report_ids.begin(),
61 collection->report_ids.begin(),
62 collection->report_ids.end());
63
64 output->collections.push_back(std::move(api_collection));
65 }
66
67 const std::vector<uint8_t>& report_descriptor = input.report_descriptor;
68 if (report_descriptor.size() > 0) {
69 output->report_descriptor.assign(report_descriptor.begin(),
70 report_descriptor.end());
71 }
72 }
73
WillDispatchDeviceEvent(base::WeakPtr<HidDeviceManager> device_manager,const device::mojom::HidDeviceInfo & device_info,content::BrowserContext * browser_context,Feature::Context target_context,const Extension * extension,Event * event,const base::DictionaryValue * listener_filter)74 bool WillDispatchDeviceEvent(base::WeakPtr<HidDeviceManager> device_manager,
75 const device::mojom::HidDeviceInfo& device_info,
76 content::BrowserContext* browser_context,
77 Feature::Context target_context,
78 const Extension* extension,
79 Event* event,
80 const base::DictionaryValue* listener_filter) {
81 if (device_manager && extension) {
82 return device_manager->HasPermission(extension, device_info, false);
83 }
84 return false;
85 }
86
GetHidManagerBinderOverride()87 HidDeviceManager::HidManagerBinder& GetHidManagerBinderOverride() {
88 static base::NoDestructor<HidDeviceManager::HidManagerBinder> binder;
89 return *binder;
90 }
91
92 } // namespace
93
94 struct HidDeviceManager::GetApiDevicesParams {
95 public:
GetApiDevicesParamsextensions::HidDeviceManager::GetApiDevicesParams96 GetApiDevicesParams(const Extension* extension,
97 const std::vector<HidDeviceFilter>& filters,
98 const GetApiDevicesCallback& callback)
99 : extension(extension), filters(filters), callback(callback) {}
~GetApiDevicesParamsextensions::HidDeviceManager::GetApiDevicesParams100 ~GetApiDevicesParams() {}
101
102 const Extension* extension;
103 std::vector<HidDeviceFilter> filters;
104 GetApiDevicesCallback callback;
105 };
106
HidDeviceManager(content::BrowserContext * context)107 HidDeviceManager::HidDeviceManager(content::BrowserContext* context)
108 : browser_context_(context) {
109 event_router_ = EventRouter::Get(context);
110 if (event_router_) {
111 event_router_->RegisterObserver(this, hid::OnDeviceAdded::kEventName);
112 event_router_->RegisterObserver(this, hid::OnDeviceRemoved::kEventName);
113 }
114 }
115
~HidDeviceManager()116 HidDeviceManager::~HidDeviceManager() {
117 DCHECK(thread_checker_.CalledOnValidThread());
118 }
119
120 // static
121 BrowserContextKeyedAPIFactory<HidDeviceManager>*
GetFactoryInstance()122 HidDeviceManager::GetFactoryInstance() {
123 static base::LazyInstance<BrowserContextKeyedAPIFactory<HidDeviceManager>>::
124 DestructorAtExit factory = LAZY_INSTANCE_INITIALIZER;
125 return &factory.Get();
126 }
127
GetApiDevices(const Extension * extension,const std::vector<HidDeviceFilter> & filters,const GetApiDevicesCallback & callback)128 void HidDeviceManager::GetApiDevices(
129 const Extension* extension,
130 const std::vector<HidDeviceFilter>& filters,
131 const GetApiDevicesCallback& callback) {
132 DCHECK(thread_checker_.CalledOnValidThread());
133 LazyInitialize();
134
135 if (enumeration_ready_) {
136 std::unique_ptr<base::ListValue> devices =
137 CreateApiDeviceList(extension, filters);
138 base::ThreadTaskRunnerHandle::Get()->PostTask(
139 FROM_HERE, base::BindOnce(callback, std::move(devices)));
140 } else {
141 pending_enumerations_.push_back(
142 std::make_unique<GetApiDevicesParams>(extension, filters, callback));
143 }
144 }
145
GetApiDevicesFromList(std::vector<device::mojom::HidDeviceInfoPtr> devices)146 std::unique_ptr<base::ListValue> HidDeviceManager::GetApiDevicesFromList(
147 std::vector<device::mojom::HidDeviceInfoPtr> devices) {
148 DCHECK(thread_checker_.CalledOnValidThread());
149 std::unique_ptr<base::ListValue> device_list(new base::ListValue());
150 for (const auto& device : devices) {
151 const auto device_entry = resource_ids_.find(device->guid);
152 DCHECK(device_entry != resource_ids_.end());
153
154 hid::HidDeviceInfo device_info;
155 device_info.device_id = device_entry->second;
156 PopulateHidDeviceInfo(&device_info, *device);
157 device_list->Append(device_info.ToValue());
158 }
159 return device_list;
160 }
161
GetDeviceInfo(int resource_id)162 const device::mojom::HidDeviceInfo* HidDeviceManager::GetDeviceInfo(
163 int resource_id) {
164 DCHECK(thread_checker_.CalledOnValidThread());
165
166 ResourceIdToDeviceInfoMap::const_iterator device_iter =
167 devices_.find(resource_id);
168 if (device_iter == devices_.end()) {
169 return nullptr;
170 }
171
172 return device_iter->second.get();
173 }
174
Connect(const std::string & device_guid,ConnectCallback callback)175 void HidDeviceManager::Connect(const std::string& device_guid,
176 ConnectCallback callback) {
177 DCHECK(initialized_);
178
179 hid_manager_->Connect(device_guid, /*connection_client=*/mojo::NullRemote(),
180 /*watcher=*/mojo::NullRemote(),
181 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
182 std::move(callback), mojo::NullRemote()));
183 }
184
HasPermission(const Extension * extension,const device::mojom::HidDeviceInfo & device_info,bool update_last_used)185 bool HidDeviceManager::HasPermission(
186 const Extension* extension,
187 const device::mojom::HidDeviceInfo& device_info,
188 bool update_last_used) {
189 DevicePermissionsManager* permissions_manager =
190 DevicePermissionsManager::Get(browser_context_);
191 CHECK(permissions_manager);
192 DevicePermissions* device_permissions =
193 permissions_manager->GetForExtension(extension->id());
194 DCHECK(device_permissions);
195 scoped_refptr<DevicePermissionEntry> permission_entry =
196 device_permissions->FindHidDeviceEntry(device_info);
197 if (permission_entry) {
198 if (update_last_used) {
199 permissions_manager->UpdateLastUsed(extension->id(), permission_entry);
200 }
201 return true;
202 }
203
204 std::unique_ptr<UsbDevicePermission::CheckParam> usb_param =
205 UsbDevicePermission::CheckParam::ForHidDevice(
206 extension, device_info.vendor_id, device_info.product_id);
207 if (extension->permissions_data()->CheckAPIPermissionWithParam(
208 APIPermission::kUsbDevice, usb_param.get())) {
209 return true;
210 }
211
212 if (extension->permissions_data()->HasAPIPermission(
213 APIPermission::kU2fDevices)) {
214 HidDeviceFilter u2f_filter;
215 u2f_filter.SetUsagePage(0xF1D0);
216 if (u2f_filter.Matches(device_info)) {
217 return true;
218 }
219 }
220
221 return false;
222 }
223
Shutdown()224 void HidDeviceManager::Shutdown() {
225 if (event_router_) {
226 event_router_->UnregisterObserver(this);
227 }
228 }
229
OnListenerAdded(const EventListenerInfo & details)230 void HidDeviceManager::OnListenerAdded(const EventListenerInfo& details) {
231 LazyInitialize();
232 }
DeviceAdded(device::mojom::HidDeviceInfoPtr device)233 void HidDeviceManager::DeviceAdded(device::mojom::HidDeviceInfoPtr device) {
234 DCHECK(thread_checker_.CalledOnValidThread());
235 DCHECK_LT(next_resource_id_, std::numeric_limits<int>::max());
236 int new_id = next_resource_id_++;
237 DCHECK(!base::Contains(resource_ids_, device->guid));
238 resource_ids_[device->guid] = new_id;
239 devices_[new_id] = std::move(device);
240
241 // Don't generate events during the initial enumeration.
242 if (enumeration_ready_ && event_router_) {
243 api::hid::HidDeviceInfo api_device_info;
244 api_device_info.device_id = new_id;
245
246 PopulateHidDeviceInfo(&api_device_info, *devices_[new_id]);
247
248 if (api_device_info.collections.size() > 0) {
249 std::unique_ptr<base::ListValue> args(
250 hid::OnDeviceAdded::Create(api_device_info));
251 DispatchEvent(events::HID_ON_DEVICE_ADDED, hid::OnDeviceAdded::kEventName,
252 std::move(args), *devices_[new_id]);
253 }
254 }
255 }
256
DeviceRemoved(device::mojom::HidDeviceInfoPtr device)257 void HidDeviceManager::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) {
258 DCHECK(thread_checker_.CalledOnValidThread());
259 const auto& resource_entry = resource_ids_.find(device->guid);
260 DCHECK(resource_entry != resource_ids_.end());
261 int resource_id = resource_entry->second;
262 const auto& device_entry = devices_.find(resource_id);
263 DCHECK(device_entry != devices_.end());
264 resource_ids_.erase(resource_entry);
265 devices_.erase(device_entry);
266
267 if (event_router_) {
268 DCHECK(enumeration_ready_);
269 std::unique_ptr<base::ListValue> args(
270 hid::OnDeviceRemoved::Create(resource_id));
271 DispatchEvent(events::HID_ON_DEVICE_REMOVED,
272 hid::OnDeviceRemoved::kEventName, std::move(args), *device);
273 }
274
275 // Remove permission entry for ephemeral hid device.
276 DevicePermissionsManager* permissions_manager =
277 DevicePermissionsManager::Get(browser_context_);
278 DCHECK(permissions_manager);
279 permissions_manager->RemoveEntryByDeviceGUID(DevicePermissionEntry::Type::HID,
280 device->guid);
281 }
282
LazyInitialize()283 void HidDeviceManager::LazyInitialize() {
284 DCHECK(thread_checker_.CalledOnValidThread());
285
286 if (initialized_) {
287 return;
288 }
289 // |hid_manager_| may already be initialized in tests.
290 if (!hid_manager_) {
291 // |hid_manager_| is initialized and safe to use whether or not the
292 // connection is successful.
293
294 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
295 auto receiver = hid_manager_.BindNewPipeAndPassReceiver();
296 const auto& binder = GetHidManagerBinderOverride();
297 if (binder)
298 binder.Run(std::move(receiver));
299 else
300 content::GetDeviceService().BindHidManager(std::move(receiver));
301 }
302 // Enumerate HID devices and set client.
303 std::vector<device::mojom::HidDeviceInfoPtr> empty_devices;
304 hid_manager_->GetDevicesAndSetClient(
305 receiver_.BindNewEndpointAndPassRemote(),
306 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
307 base::BindOnce(&HidDeviceManager::OnEnumerationComplete,
308 weak_factory_.GetWeakPtr()),
309 std::move(empty_devices)));
310
311 initialized_ = true;
312 }
313
314 // static
OverrideHidManagerBinderForTesting(HidManagerBinder binder)315 void HidDeviceManager::OverrideHidManagerBinderForTesting(
316 HidManagerBinder binder) {
317 GetHidManagerBinderOverride() = std::move(binder);
318 }
319
CreateApiDeviceList(const Extension * extension,const std::vector<HidDeviceFilter> & filters)320 std::unique_ptr<base::ListValue> HidDeviceManager::CreateApiDeviceList(
321 const Extension* extension,
322 const std::vector<HidDeviceFilter>& filters) {
323 std::unique_ptr<base::ListValue> api_devices(new base::ListValue());
324 for (const ResourceIdToDeviceInfoMap::value_type& map_entry : devices_) {
325 int resource_id = map_entry.first;
326 auto& device_info = map_entry.second;
327
328 if (!filters.empty() &&
329 !HidDeviceFilter::MatchesAny(*device_info, filters)) {
330 continue;
331 }
332
333 if (!HasPermission(extension, *device_info, false)) {
334 continue;
335 }
336
337 hid::HidDeviceInfo api_device_info;
338 api_device_info.device_id = resource_id;
339 PopulateHidDeviceInfo(&api_device_info, *device_info);
340
341 // Expose devices with which user can communicate.
342 if (api_device_info.collections.size() > 0) {
343 api_devices->Append(api_device_info.ToValue());
344 }
345 }
346
347 return api_devices;
348 }
349
OnEnumerationComplete(std::vector<device::mojom::HidDeviceInfoPtr> devices)350 void HidDeviceManager::OnEnumerationComplete(
351 std::vector<device::mojom::HidDeviceInfoPtr> devices) {
352 DCHECK(resource_ids_.empty());
353 DCHECK(devices_.empty());
354
355 for (auto& device_info : devices) {
356 DeviceAdded(std::move(device_info));
357 }
358 enumeration_ready_ = true;
359
360 for (const auto& params : pending_enumerations_) {
361 std::unique_ptr<base::ListValue> devices =
362 CreateApiDeviceList(params->extension, params->filters);
363 params->callback.Run(std::move(devices));
364 }
365 pending_enumerations_.clear();
366 }
367
DispatchEvent(events::HistogramValue histogram_value,const std::string & event_name,std::unique_ptr<base::ListValue> event_args,const device::mojom::HidDeviceInfo & device_info)368 void HidDeviceManager::DispatchEvent(
369 events::HistogramValue histogram_value,
370 const std::string& event_name,
371 std::unique_ptr<base::ListValue> event_args,
372 const device::mojom::HidDeviceInfo& device_info) {
373 std::unique_ptr<Event> event(
374 new Event(histogram_value, event_name, std::move(event_args)));
375 // The |event->will_dispatch_callback| will be called synchronously, it is
376 // safe to pass |device_info| by reference.
377 event->will_dispatch_callback =
378 base::BindRepeating(&WillDispatchDeviceEvent, weak_factory_.GetWeakPtr(),
379 std::cref(device_info));
380 event_router_->BroadcastEvent(std::move(event));
381 }
382
383 } // namespace extensions
384