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 "services/device/hid/hid_service_linux.h"
6 
7 #include <fcntl.h>
8 #include <linux/input.h>
9 #include <stdint.h>
10 
11 #include <limits>
12 #include <memory>
13 #include <string>
14 #include <utility>
15 
16 #include "base/bind.h"
17 #include "base/callback_helpers.h"
18 #include "base/files/file.h"
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/scoped_file.h"
22 #include "base/location.h"
23 #include "base/macros.h"
24 #include "base/sequence_checker.h"
25 #include "base/sequenced_task_runner.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_split.h"
28 #include "base/strings/string_util.h"
29 #include "base/task/thread_pool.h"
30 #include "base/threading/scoped_blocking_call.h"
31 #include "base/threading/sequenced_task_runner_handle.h"
32 #include "build/build_config.h"
33 #include "build/chromeos_buildflags.h"
34 #include "components/device_event_log/device_event_log.h"
35 #include "device/udev_linux/scoped_udev.h"
36 #include "device/udev_linux/udev_watcher.h"
37 #include "services/device/hid/hid_connection_linux.h"
38 
39 #if BUILDFLAG(IS_ASH)
40 #include "base/system/sys_info.h"
41 #include "chromeos/dbus/permission_broker/permission_broker_client.h"
42 #endif  // BUILDFLAG(IS_ASH)
43 
44 namespace device {
45 
46 namespace {
47 
48 const char kDevtypeUsbDevice[] = "usb_device";
49 const char kSubsystemBluetooth[] = "bluetooth";
50 const char kSubsystemHid[] = "hid";
51 const char kSubsystemHidraw[] = "hidraw";
52 const char kSubsystemUsb[] = "usb";
53 const char kHIDID[] = "HID_ID";
54 const char kHIDName[] = "HID_NAME";
55 const char kHIDUnique[] = "HID_UNIQ";
56 const char kSysfsReportDescriptorKey[] = "report_descriptor";
57 const char kKernelHciPrefix[] = "hci";
58 
59 // Walks up the sysfs device tree starting at |device| and returns the first
60 // ancestor in the "hid" subsystem. Returns nullptr on failure.
FindFirstHidAncestor(udev_device * device)61 udev_device* FindFirstHidAncestor(udev_device* device) {
62   udev_device* ancestor = device;
63   do {
64     const char* subsystem = udev_device_get_subsystem(ancestor);
65     if (!subsystem)
66       return nullptr;
67     if (strcmp(subsystem, kSubsystemHid) == 0)
68       return ancestor;
69   } while ((ancestor = udev_device_get_parent(ancestor)));
70   return nullptr;
71 }
72 
73 // Walks up the sysfs device tree starting at |device| and returns the first
74 // ancestor not in the "hid" or "hidraw" subsystems. Returns nullptr on failure.
FindFirstNonHidAncestor(udev_device * device)75 udev_device* FindFirstNonHidAncestor(udev_device* device) {
76   udev_device* ancestor = device;
77   do {
78     const char* subsystem = udev_device_get_subsystem(ancestor);
79     if (!subsystem)
80       return nullptr;
81     if (strcmp(subsystem, kSubsystemHid) != 0 &&
82         strcmp(subsystem, kSubsystemHidraw) != 0) {
83       return ancestor;
84     }
85   } while ((ancestor = udev_device_get_parent(ancestor)));
86   return nullptr;
87 }
88 
89 // Returns the sysfs path for a USB device |usb_device|, or nullptr if the sysfs
90 // path could not be retrieved. |usb_device| must be a device in the "usb"
91 // subsystem.
92 //
93 // Some USB devices expose multiple interfaces. If |usb_device| refers to a
94 // single USB interface, walk up the device tree to find the ancestor that
95 // represents the physical device.
GetUsbDeviceSyspath(udev_device * usb_device)96 const char* GetUsbDeviceSyspath(udev_device* usb_device) {
97   do {
98     const char* subsystem = udev_device_get_subsystem(usb_device);
99     if (!subsystem || strcmp(subsystem, kSubsystemUsb) != 0)
100       return nullptr;
101 
102     const char* devtype = udev_device_get_devtype(usb_device);
103     if (!devtype)
104       return nullptr;
105 
106     // Use the syspath of the first ancestor with devtype "usb_device".
107     if (strcmp(devtype, kDevtypeUsbDevice) == 0)
108       return udev_device_get_syspath(usb_device);
109   } while ((usb_device = udev_device_get_parent(usb_device)));
110   return nullptr;
111 }
112 
113 // Returns the sysfs path for a Bluetooth Classic device |bt_device|, or nullptr
114 // if the sysfs path could not be retrieved. |bt_device| must be a device in the
115 // "bluetooth" subsystem.
GetBluetoothDeviceSyspath(udev_device * bt_device)116 const char* GetBluetoothDeviceSyspath(udev_device* bt_device) {
117   do {
118     const char* subsystem = udev_device_get_subsystem(bt_device);
119     if (!subsystem || strcmp(subsystem, kSubsystemBluetooth) != 0)
120       return nullptr;
121 
122     // Look for a sysname like "hci0:123".
123     const char* sysfs_name = udev_device_get_sysname(bt_device);
124     if (!sysfs_name)
125       return nullptr;
126 
127     std::vector<std::string> parts = base::SplitString(
128         sysfs_name, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
129     if (parts.size() == 2 && base::StartsWith(parts[0], kKernelHciPrefix,
130                                               base::CompareCase::SENSITIVE)) {
131       return udev_device_get_syspath(bt_device);
132     }
133   } while ((bt_device = udev_device_get_parent(bt_device)));
134   return nullptr;
135 }
136 
137 // Returns the physical device ID for a device |hidraw_device|. On Linux, the
138 // physical device ID is the sysfs path to the device node that represents the
139 // physical device if it is available. When the physical device node is not
140 // available, the sysfs path of the HID interface is returned instead. Returns
141 // nullptr on failure.
GetPhysicalDeviceId(udev_device * hidraw_device)142 const char* GetPhysicalDeviceId(udev_device* hidraw_device) {
143   const char* subsystem = udev_device_get_subsystem(hidraw_device);
144   if (!subsystem || strcmp(subsystem, kSubsystemHidraw) != 0)
145     return nullptr;
146 
147   udev_device* hid_ancestor = FindFirstHidAncestor(hidraw_device);
148   if (!hid_ancestor)
149     return nullptr;
150   const char* hid_sysfs_path = udev_device_get_syspath(hid_ancestor);
151 
152   udev_device* ancestor = FindFirstNonHidAncestor(hid_ancestor);
153   if (!ancestor)
154     return hid_sysfs_path;
155 
156   const char* ancestor_subsystem = udev_device_get_subsystem(ancestor);
157   if (!ancestor_subsystem)
158     return hid_sysfs_path;
159 
160   if (strcmp(ancestor_subsystem, kSubsystemUsb) == 0) {
161     const char* usb_sysfs_path = GetUsbDeviceSyspath(ancestor);
162     if (usb_sysfs_path)
163       return usb_sysfs_path;
164   }
165 
166   if (strcmp(ancestor_subsystem, kSubsystemBluetooth) == 0) {
167     const char* bt_sysfs_path = GetBluetoothDeviceSyspath(ancestor);
168     if (bt_sysfs_path)
169       return bt_sysfs_path;
170   }
171 
172   return hid_sysfs_path;
173 }
174 
175 }  // namespace
176 
177 struct HidServiceLinux::ConnectParams {
ConnectParamsdevice::HidServiceLinux::ConnectParams178   ConnectParams(scoped_refptr<HidDeviceInfo> device_info,
179                 ConnectCallback callback)
180       : device_info(std::move(device_info)),
181         callback(std::move(callback)),
182         task_runner(base::SequencedTaskRunnerHandle::Get()),
183         blocking_task_runner(
184             base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)) {}
~ConnectParamsdevice::HidServiceLinux::ConnectParams185   ~ConnectParams() {}
186 
187   scoped_refptr<HidDeviceInfo> device_info;
188   ConnectCallback callback;
189   scoped_refptr<base::SequencedTaskRunner> task_runner;
190   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
191   base::ScopedFD fd;
192 };
193 
194 class HidServiceLinux::BlockingTaskRunnerHelper : public UdevWatcher::Observer {
195  public:
BlockingTaskRunnerHelper(base::WeakPtr<HidServiceLinux> service)196   BlockingTaskRunnerHelper(base::WeakPtr<HidServiceLinux> service)
197       : service_(std::move(service)),
198         task_runner_(base::SequencedTaskRunnerHandle::Get()) {
199     DETACH_FROM_SEQUENCE(sequence_checker_);
200   }
201 
~BlockingTaskRunnerHelper()202   ~BlockingTaskRunnerHelper() override {
203     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
204   }
205 
Start()206   void Start() {
207     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
208 
209     watcher_ = UdevWatcher::StartWatching(this);
210     watcher_->EnumerateExistingDevices();
211     task_runner_->PostTask(
212         FROM_HERE,
213         base::BindOnce(&HidServiceLinux::FirstEnumerationComplete, service_));
214   }
215 
216  private:
217   // UdevWatcher::Observer
OnDeviceAdded(ScopedUdevDevicePtr device)218   void OnDeviceAdded(ScopedUdevDevicePtr device) override {
219     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
220     base::ScopedBlockingCall scoped_blocking_call(
221         FROM_HERE, base::BlockingType::MAY_BLOCK);
222 
223     const char* device_path = udev_device_get_syspath(device.get());
224     if (!device_path)
225       return;
226     HidPlatformDeviceId platform_device_id = device_path;
227 
228     const char* subsystem = udev_device_get_subsystem(device.get());
229     if (!subsystem || strcmp(subsystem, kSubsystemHidraw) != 0)
230       return;
231 
232     const char* str_property = udev_device_get_devnode(device.get());
233     if (!str_property)
234       return;
235     std::string device_node = str_property;
236 
237     udev_device* parent = udev_device_get_parent(device.get());
238     if (!parent)
239       return;
240 
241     const char* hid_id = udev_device_get_property_value(parent, kHIDID);
242     if (!hid_id)
243       return;
244 
245     std::vector<std::string> parts = base::SplitString(
246         hid_id, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
247     if (parts.size() != 3)
248       return;
249 
250     uint32_t int_property = 0;
251     if (!HexStringToUInt(base::StringPiece(parts[1]), &int_property) ||
252         int_property > std::numeric_limits<uint16_t>::max()) {
253       return;
254     }
255     uint16_t vendor_id = int_property;
256 
257     if (!HexStringToUInt(base::StringPiece(parts[2]), &int_property) ||
258         int_property > std::numeric_limits<uint16_t>::max()) {
259       return;
260     }
261     uint16_t product_id = int_property;
262 
263     std::string serial_number;
264     str_property = udev_device_get_property_value(parent, kHIDUnique);
265     if (str_property)
266       serial_number = str_property;
267 
268     std::string product_name;
269     str_property = udev_device_get_property_value(parent, kHIDName);
270     if (str_property)
271       product_name = str_property;
272 
273     const char* parent_sysfs_path = udev_device_get_syspath(parent);
274     if (!parent_sysfs_path)
275       return;
276     base::FilePath report_descriptor_path =
277         base::FilePath(parent_sysfs_path).Append(kSysfsReportDescriptorKey);
278     std::string report_descriptor_str;
279     if (!base::ReadFileToString(report_descriptor_path, &report_descriptor_str))
280       return;
281 
282     const char* physical_device_id = GetPhysicalDeviceId(device.get());
283     if (!physical_device_id) {
284       HID_LOG(EVENT) << "GetPhysicalDeviceId failed for '" << device_path
285                      << "'";
286       return;
287     }
288 
289     auto device_info = base::MakeRefCounted<HidDeviceInfo>(
290         platform_device_id, physical_device_id, vendor_id, product_id,
291         product_name, serial_number,
292         // TODO(reillyg): Detect Bluetooth. crbug.com/443335
293         mojom::HidBusType::kHIDBusTypeUSB,
294         std::vector<uint8_t>(report_descriptor_str.begin(),
295                              report_descriptor_str.end()),
296         device_node);
297 
298     task_runner_->PostTask(
299         FROM_HERE,
300         base::BindOnce(&HidServiceLinux::AddDevice, service_, device_info));
301   }
302 
OnDeviceRemoved(ScopedUdevDevicePtr device)303   void OnDeviceRemoved(ScopedUdevDevicePtr device) override {
304     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
305     base::ScopedBlockingCall scoped_blocking_call(
306         FROM_HERE, base::BlockingType::MAY_BLOCK);
307 
308     const char* device_path = udev_device_get_syspath(device.get());
309     if (device_path) {
310       task_runner_->PostTask(
311           FROM_HERE, base::BindOnce(&HidServiceLinux::RemoveDevice, service_,
312                                     std::string(device_path)));
313     }
314   }
315 
OnDeviceChanged(ScopedUdevDevicePtr)316   void OnDeviceChanged(ScopedUdevDevicePtr) override {}
317 
318   SEQUENCE_CHECKER(sequence_checker_);
319   std::unique_ptr<UdevWatcher> watcher_;
320 
321   // This weak pointer is only valid when checked on this task runner.
322   base::WeakPtr<HidServiceLinux> service_;
323   scoped_refptr<base::SequencedTaskRunner> task_runner_;
324 
325   DISALLOW_COPY_AND_ASSIGN(BlockingTaskRunnerHelper);
326 };
327 
HidServiceLinux()328 HidServiceLinux::HidServiceLinux()
329     : blocking_task_runner_(
330           base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)),
331       helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)) {
332   // We need to properly initialize |blocking_task_helper_| here because we need
333   // |weak_factory_| to be created first.
334   helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr()));
335   blocking_task_runner_->PostTask(
336       FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::Start,
337                                 base::Unretained(helper_.get())));
338 }
339 
340 HidServiceLinux::~HidServiceLinux() = default;
341 
GetWeakPtr()342 base::WeakPtr<HidService> HidServiceLinux::GetWeakPtr() {
343   return weak_factory_.GetWeakPtr();
344 }
345 
Connect(const std::string & device_guid,ConnectCallback callback)346 void HidServiceLinux::Connect(const std::string& device_guid,
347                               ConnectCallback callback) {
348   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
349 
350   const auto& map_entry = devices().find(device_guid);
351   if (map_entry == devices().end()) {
352     base::SequencedTaskRunnerHandle::Get()->PostTask(
353         FROM_HERE, base::BindOnce(std::move(callback), nullptr));
354     return;
355   }
356   scoped_refptr<HidDeviceInfo> device_info = map_entry->second;
357 
358 #if BUILDFLAG(IS_ASH)
359   // Adapt |callback| to a repeating callback because the implementation below
360   // requires separate callbacks for success and error. Only one will be called.
361   auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
362   chromeos::PermissionBrokerClient::Get()->OpenPath(
363       device_info->device_node(),
364       base::BindOnce(
365           &HidServiceLinux::OnPathOpenComplete,
366           std::make_unique<ConnectParams>(device_info, copyable_callback)),
367       base::BindOnce(&HidServiceLinux::OnPathOpenError,
368                      device_info->device_node(), copyable_callback));
369 #else
370   auto params =
371       std::make_unique<ConnectParams>(device_info, std::move(callback));
372   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
373       params->blocking_task_runner;
374   blocking_task_runner->PostTask(
375       FROM_HERE, base::BindOnce(&HidServiceLinux::OpenOnBlockingThread,
376                                 std::move(params)));
377 #endif  // BUILDFLAG(IS_ASH)
378 }
379 
380 #if BUILDFLAG(IS_ASH)
381 
382 // static
OnPathOpenComplete(std::unique_ptr<ConnectParams> params,base::ScopedFD fd)383 void HidServiceLinux::OnPathOpenComplete(std::unique_ptr<ConnectParams> params,
384                                          base::ScopedFD fd) {
385   params->fd = std::move(fd);
386   FinishOpen(std::move(params));
387 }
388 
389 // static
OnPathOpenError(const std::string & device_path,ConnectCallback callback,const std::string & error_name,const std::string & error_message)390 void HidServiceLinux::OnPathOpenError(const std::string& device_path,
391                                       ConnectCallback callback,
392                                       const std::string& error_name,
393                                       const std::string& error_message) {
394   HID_LOG(EVENT) << "Permission broker failed to open '" << device_path
395                  << "': " << error_name << ": " << error_message;
396   std::move(callback).Run(nullptr);
397 }
398 
399 #else
400 
401 // static
OpenOnBlockingThread(std::unique_ptr<ConnectParams> params)402 void HidServiceLinux::OpenOnBlockingThread(
403     std::unique_ptr<ConnectParams> params) {
404   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
405                                                 base::BlockingType::MAY_BLOCK);
406   scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner;
407 
408   base::FilePath device_path(params->device_info->device_node());
409   base::File device_file;
410   int flags =
411       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
412   device_file.Initialize(device_path, flags);
413   if (!device_file.IsValid()) {
414     base::File::Error file_error = device_file.error_details();
415 
416     if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
417       HID_LOG(EVENT)
418           << "Access denied opening device read-write, trying read-only.";
419       flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
420       device_file.Initialize(device_path, flags);
421     }
422   }
423   if (!device_file.IsValid()) {
424     HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node()
425                    << "': "
426                    << base::File::ErrorToString(device_file.error_details());
427     task_runner->PostTask(FROM_HERE,
428                           base::BindOnce(std::move(params->callback), nullptr));
429     return;
430   }
431   params->fd.reset(device_file.TakePlatformFile());
432 
433   task_runner->PostTask(FROM_HERE, base::BindOnce(&HidServiceLinux::FinishOpen,
434                                                   std::move(params)));
435 }
436 
437 #endif  // BUILDFLAG(IS_ASH)
438 
439 // static
FinishOpen(std::unique_ptr<ConnectParams> params)440 void HidServiceLinux::FinishOpen(std::unique_ptr<ConnectParams> params) {
441   DCHECK(params->fd.is_valid());
442 
443   if (!base::SetNonBlocking(params->fd.get())) {
444     HID_PLOG(ERROR) << "Failed to set the non-blocking flag on the device fd";
445     std::move(params->callback).Run(nullptr);
446     return;
447   }
448 
449   std::move(params->callback)
450       .Run(base::MakeRefCounted<HidConnectionLinux>(
451           std::move(params->device_info), std::move(params->fd),
452           std::move(params->blocking_task_runner)));
453 }
454 
455 }  // namespace device
456