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