hid_service_linux.cc 17 KB

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