input_service_linux.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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/input_service_linux.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/logging.h"
  8. #include "base/scoped_observation.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_util.h"
  11. #include "base/threading/scoped_blocking_call.h"
  12. #include "device/base/device_monitor_linux.h"
  13. #include "device/udev_linux/udev.h"
  14. namespace device {
  15. namespace {
  16. const char kSubsystemHid[] = "hid";
  17. const char kSubsystemInput[] = "input";
  18. const char kSubsystemMisc[] = "misc";
  19. const char kTypeBluetooth[] = "bluetooth";
  20. const char kTypeUsb[] = "usb";
  21. const char kTypeSerio[] = "serio";
  22. const char kIdInputAccelerometer[] = "ID_INPUT_ACCELEROMETER";
  23. const char kIdInputJoystick[] = "ID_INPUT_JOYSTICK";
  24. const char kIdInputKey[] = "ID_INPUT_KEY";
  25. const char kIdInputKeyboard[] = "ID_INPUT_KEYBOARD";
  26. const char kIdInputMouse[] = "ID_INPUT_MOUSE";
  27. const char kIdInputTablet[] = "ID_INPUT_TABLET";
  28. const char kIdInputTouchpad[] = "ID_INPUT_TOUCHPAD";
  29. const char kIdInputTouchscreen[] = "ID_INPUT_TOUCHSCREEN";
  30. InputServiceLinux* g_input_service_linux = nullptr;
  31. bool GetBoolProperty(udev_device* device, const char* key) {
  32. CHECK(device);
  33. CHECK(key);
  34. const char* property = udev_device_get_property_value(device, key);
  35. if (!property)
  36. return false;
  37. int value;
  38. if (!base::StringToInt(property, &value)) {
  39. LOG(ERROR) << "Not an integer value for " << key << " property";
  40. return false;
  41. }
  42. return (value != 0);
  43. }
  44. mojom::InputDeviceType GetDeviceType(udev_device* device) {
  45. // Bluetooth classic hid devices are registered under bluetooth subsystem.
  46. // Bluetooth LE hid devices are registered under virtual misc/hid subsystems.
  47. if (udev_device_get_parent_with_subsystem_devtype(device, kTypeBluetooth,
  48. NULL) ||
  49. (udev_device_get_parent_with_subsystem_devtype(device, kSubsystemHid,
  50. NULL) &&
  51. udev_device_get_parent_with_subsystem_devtype(device, kSubsystemMisc,
  52. NULL))) {
  53. return mojom::InputDeviceType::TYPE_BLUETOOTH;
  54. }
  55. if (udev_device_get_parent_with_subsystem_devtype(device, kTypeUsb, NULL))
  56. return mojom::InputDeviceType::TYPE_USB;
  57. if (udev_device_get_parent_with_subsystem_devtype(device, kTypeSerio, NULL))
  58. return mojom::InputDeviceType::TYPE_SERIO;
  59. return mojom::InputDeviceType::TYPE_UNKNOWN;
  60. }
  61. std::string GetParentDeviceName(udev_device* device, const char* subsystem) {
  62. udev_device* parent =
  63. udev_device_get_parent_with_subsystem_devtype(device, subsystem, NULL);
  64. if (!parent)
  65. return std::string();
  66. const char* name = udev_device_get_property_value(parent, "NAME");
  67. if (!name)
  68. return std::string();
  69. std::string result;
  70. base::TrimString(name, "\"", &result);
  71. return result;
  72. }
  73. class InputServiceLinuxImpl : public InputServiceLinux,
  74. public DeviceMonitorLinux::Observer {
  75. public:
  76. InputServiceLinuxImpl(const InputServiceLinuxImpl&) = delete;
  77. InputServiceLinuxImpl& operator=(const InputServiceLinuxImpl&) = delete;
  78. // Implements DeviceMonitorLinux::Observer:
  79. void OnDeviceAdded(udev_device* device) override;
  80. void OnDeviceRemoved(udev_device* device) override;
  81. private:
  82. friend class InputServiceLinux;
  83. InputServiceLinuxImpl();
  84. ~InputServiceLinuxImpl() override;
  85. base::ScopedObservation<DeviceMonitorLinux, DeviceMonitorLinux::Observer>
  86. observation_{this};
  87. };
  88. InputServiceLinuxImpl::InputServiceLinuxImpl() {
  89. DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
  90. observation_.Observe(monitor);
  91. monitor->Enumerate(base::BindRepeating(&InputServiceLinuxImpl::OnDeviceAdded,
  92. base::Unretained(this)));
  93. }
  94. InputServiceLinuxImpl::~InputServiceLinuxImpl() {
  95. // Never destroyed.
  96. NOTREACHED();
  97. }
  98. void InputServiceLinuxImpl::OnDeviceAdded(udev_device* device) {
  99. DCHECK(CalledOnValidThread());
  100. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  101. base::BlockingType::MAY_BLOCK);
  102. if (!device)
  103. return;
  104. const char* devnode = udev_device_get_devnode(device);
  105. if (!devnode)
  106. return;
  107. auto info = mojom::InputDeviceInfo::New();
  108. info->id = devnode;
  109. const char* subsystem = udev_device_get_subsystem(device);
  110. if (!subsystem)
  111. return;
  112. if (strcmp(subsystem, kSubsystemHid) == 0) {
  113. info->subsystem = mojom::InputDeviceSubsystem::SUBSYSTEM_HID;
  114. info->name = GetParentDeviceName(device, kSubsystemHid);
  115. } else if (strcmp(subsystem, kSubsystemInput) == 0) {
  116. info->subsystem = mojom::InputDeviceSubsystem::SUBSYSTEM_INPUT;
  117. info->name = GetParentDeviceName(device, kSubsystemInput);
  118. } else {
  119. return;
  120. }
  121. info->type = GetDeviceType(device);
  122. info->is_accelerometer = GetBoolProperty(device, kIdInputAccelerometer);
  123. info->is_joystick = GetBoolProperty(device, kIdInputJoystick);
  124. info->is_key = GetBoolProperty(device, kIdInputKey);
  125. info->is_keyboard = GetBoolProperty(device, kIdInputKeyboard);
  126. info->is_mouse = GetBoolProperty(device, kIdInputMouse);
  127. info->is_tablet = GetBoolProperty(device, kIdInputTablet);
  128. info->is_touchpad = GetBoolProperty(device, kIdInputTouchpad);
  129. info->is_touchscreen = GetBoolProperty(device, kIdInputTouchscreen);
  130. AddDevice(std::move(info));
  131. }
  132. void InputServiceLinuxImpl::OnDeviceRemoved(udev_device* device) {
  133. DCHECK(CalledOnValidThread());
  134. if (!device)
  135. return;
  136. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
  137. base::BlockingType::MAY_BLOCK);
  138. const char* devnode = udev_device_get_devnode(device);
  139. if (devnode)
  140. RemoveDevice(devnode);
  141. }
  142. } // namespace
  143. InputServiceLinux::InputServiceLinux() {}
  144. InputServiceLinux::~InputServiceLinux() {
  145. DCHECK(CalledOnValidThread());
  146. }
  147. // static
  148. void InputServiceLinux::BindReceiver(
  149. mojo::PendingReceiver<mojom::InputDeviceManager> receiver) {
  150. GetInstance()->AddReceiver(std::move(receiver));
  151. }
  152. // static
  153. InputServiceLinux* InputServiceLinux::GetInstance() {
  154. if (!HasInstance())
  155. g_input_service_linux = new InputServiceLinuxImpl();
  156. return g_input_service_linux;
  157. }
  158. // static
  159. bool InputServiceLinux::HasInstance() {
  160. return !!g_input_service_linux;
  161. }
  162. // static
  163. void InputServiceLinux::SetForTesting(
  164. std::unique_ptr<InputServiceLinux> service) {
  165. DCHECK(!HasInstance());
  166. DCHECK(service);
  167. // |service| will never be destroyed.
  168. g_input_service_linux = service.release();
  169. }
  170. void InputServiceLinux::AddReceiver(
  171. mojo::PendingReceiver<mojom::InputDeviceManager> receiver) {
  172. receivers_.Add(this, std::move(receiver));
  173. }
  174. void InputServiceLinux::GetDevicesAndSetClient(
  175. mojo::PendingAssociatedRemote<mojom::InputDeviceManagerClient> client,
  176. GetDevicesCallback callback) {
  177. GetDevices(std::move(callback));
  178. if (!client.is_valid())
  179. return;
  180. clients_.Add(std::move(client));
  181. }
  182. void InputServiceLinux::GetDevices(GetDevicesCallback callback) {
  183. DCHECK(CalledOnValidThread());
  184. std::vector<mojom::InputDeviceInfoPtr> devices;
  185. for (auto& device : devices_)
  186. devices.push_back(device.second->Clone());
  187. std::move(callback).Run(std::move(devices));
  188. }
  189. void InputServiceLinux::AddDevice(mojom::InputDeviceInfoPtr info) {
  190. auto* device_info = info.get();
  191. for (auto& client : clients_)
  192. client->InputDeviceAdded(device_info->Clone());
  193. devices_[info->id] = std::move(info);
  194. }
  195. void InputServiceLinux::RemoveDevice(const std::string& id) {
  196. devices_.erase(id);
  197. for (auto& client : clients_)
  198. client->InputDeviceRemoved(id);
  199. }
  200. bool InputServiceLinux::CalledOnValidThread() const {
  201. return thread_checker_.CalledOnValidThread();
  202. }
  203. } // namespace device