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