Home | History | Annotate | Download | only in hid
      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 <libudev.h>
      6 
      7 #include "base/bind.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/logging.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "device/hid/input_service_linux.h"
     14 
     15 namespace device {
     16 
     17 namespace {
     18 
     19 const char kSubsystemHid[] = "hid";
     20 const char kSubsystemInput[] = "input";
     21 const char kTypeBluetooth[] = "bluetooth";
     22 const char kTypeUsb[] = "usb";
     23 const char kTypeSerio[] = "serio";
     24 const char kIdInputAccelerometer[] = "ID_INPUT_ACCELEROMETER";
     25 const char kIdInputJoystick[] = "ID_INPUT_JOYSTICK";
     26 const char kIdInputKey[] = "ID_INPUT_KEY";
     27 const char kIdInputKeyboard[] = "ID_INPUT_KEYBOARD";
     28 const char kIdInputMouse[] = "ID_INPUT_MOUSE";
     29 const char kIdInputTablet[] = "ID_INPUT_TABLET";
     30 const char kIdInputTouchpad[] = "ID_INPUT_TOUCHPAD";
     31 const char kIdInputTouchscreen[] = "ID_INPUT_TOUCHSCREEN";
     32 
     33 // The instance will be reset when message loop destroys.
     34 base::LazyInstance<scoped_ptr<InputServiceLinux> >::Leaky
     35     g_input_service_linux_ptr = LAZY_INSTANCE_INITIALIZER;
     36 
     37 bool GetBoolProperty(udev_device* device, const char* key) {
     38   CHECK(device);
     39   CHECK(key);
     40   const char* property = udev_device_get_property_value(device, key);
     41   if (!property)
     42     return false;
     43   int value;
     44   if (!base::StringToInt(property, &value)) {
     45     LOG(ERROR) << "Not an integer value for " << key << " property";
     46     return false;
     47   }
     48   return (value != 0);
     49 }
     50 
     51 InputServiceLinux::InputDeviceInfo::Type GetDeviceType(udev_device* device) {
     52   if (udev_device_get_parent_with_subsystem_devtype(
     53           device, kTypeBluetooth, NULL)) {
     54     return InputServiceLinux::InputDeviceInfo::TYPE_BLUETOOTH;
     55   }
     56   if (udev_device_get_parent_with_subsystem_devtype(device, kTypeUsb, NULL))
     57     return InputServiceLinux::InputDeviceInfo::TYPE_USB;
     58   if (udev_device_get_parent_with_subsystem_devtype(device, kTypeSerio, NULL))
     59     return InputServiceLinux::InputDeviceInfo::TYPE_SERIO;
     60   return InputServiceLinux::InputDeviceInfo::TYPE_UNKNOWN;
     61 }
     62 
     63 std::string GetParentDeviceName(udev_device* device, const char* subsystem) {
     64   udev_device* parent =
     65       udev_device_get_parent_with_subsystem_devtype(device, subsystem, NULL);
     66   if (!parent)
     67     return std::string();
     68   const char* name = udev_device_get_property_value(parent, "NAME");
     69   if (!name)
     70     return std::string();
     71   std::string result;
     72   base::TrimString(name, "\"", &result);
     73   return result;
     74 }
     75 
     76 class InputServiceLinuxImpl : public InputServiceLinux,
     77                               public DeviceMonitorLinux::Observer {
     78  public:
     79   // Implements DeviceMonitorLinux::Observer:
     80   virtual void OnDeviceAdded(udev_device* device) OVERRIDE;
     81   virtual void OnDeviceRemoved(udev_device* device) OVERRIDE;
     82 
     83  private:
     84   friend class InputServiceLinux;
     85 
     86   InputServiceLinuxImpl();
     87   virtual ~InputServiceLinuxImpl();
     88 
     89   DISALLOW_COPY_AND_ASSIGN(InputServiceLinuxImpl);
     90 };
     91 
     92 InputServiceLinuxImpl::InputServiceLinuxImpl() {
     93   DeviceMonitorLinux::GetInstance()->AddObserver(this);
     94   DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
     95       &InputServiceLinuxImpl::OnDeviceAdded, base::Unretained(this)));
     96 }
     97 
     98 InputServiceLinuxImpl::~InputServiceLinuxImpl() {
     99   if (DeviceMonitorLinux::HasInstance())
    100     DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
    101 }
    102 
    103 void InputServiceLinuxImpl::OnDeviceAdded(udev_device* device) {
    104   DCHECK(CalledOnValidThread());
    105   if (!device)
    106     return;
    107   const char* devnode = udev_device_get_devnode(device);
    108   if (!devnode)
    109     return;
    110 
    111   InputDeviceInfo info;
    112   info.id = devnode;
    113 
    114   const char* subsystem = udev_device_get_subsystem(device);
    115   if (!subsystem)
    116     return;
    117   if (strcmp(subsystem, kSubsystemHid) == 0) {
    118     info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID;
    119     info.name = GetParentDeviceName(device, kSubsystemHid);
    120   } else if (strcmp(subsystem, kSubsystemInput) == 0) {
    121     info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT;
    122     info.name = GetParentDeviceName(device, kSubsystemInput);
    123   } else {
    124     return;
    125   }
    126 
    127   info.type = GetDeviceType(device);
    128 
    129   info.is_accelerometer = GetBoolProperty(device, kIdInputAccelerometer);
    130   info.is_joystick = GetBoolProperty(device, kIdInputJoystick);
    131   info.is_key = GetBoolProperty(device, kIdInputKey);
    132   info.is_keyboard = GetBoolProperty(device, kIdInputKeyboard);
    133   info.is_mouse = GetBoolProperty(device, kIdInputMouse);
    134   info.is_tablet = GetBoolProperty(device, kIdInputTablet);
    135   info.is_touchpad = GetBoolProperty(device, kIdInputTouchpad);
    136   info.is_touchscreen = GetBoolProperty(device, kIdInputTouchscreen);
    137 
    138   AddDevice(info);
    139 }
    140 
    141 void InputServiceLinuxImpl::OnDeviceRemoved(udev_device* device) {
    142   DCHECK(CalledOnValidThread());
    143   if (!device)
    144     return;
    145   const char* devnode = udev_device_get_devnode(device);
    146   if (devnode)
    147     RemoveDevice(devnode);
    148 }
    149 
    150 }  // namespace
    151 
    152 InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
    153     : subsystem(SUBSYSTEM_UNKNOWN),
    154       type(TYPE_UNKNOWN),
    155       is_accelerometer(false),
    156       is_joystick(false),
    157       is_key(false),
    158       is_keyboard(false),
    159       is_mouse(false),
    160       is_tablet(false),
    161       is_touchpad(false),
    162       is_touchscreen(false) {}
    163 
    164 InputServiceLinux::InputServiceLinux() {
    165   base::ThreadRestrictions::AssertIOAllowed();
    166   base::MessageLoop::current()->AddDestructionObserver(this);
    167 }
    168 
    169 InputServiceLinux::~InputServiceLinux() {
    170   DCHECK(CalledOnValidThread());
    171   base::MessageLoop::current()->RemoveDestructionObserver(this);
    172 }
    173 
    174 // static
    175 InputServiceLinux* InputServiceLinux::GetInstance() {
    176   if (!HasInstance())
    177     g_input_service_linux_ptr.Get().reset(new InputServiceLinuxImpl());
    178   return g_input_service_linux_ptr.Get().get();
    179 }
    180 
    181 // static
    182 bool InputServiceLinux::HasInstance() {
    183   return g_input_service_linux_ptr.Get().get();
    184 }
    185 
    186 // static
    187 void InputServiceLinux::SetForTesting(InputServiceLinux* service) {
    188   g_input_service_linux_ptr.Get().reset(service);
    189 }
    190 
    191 void InputServiceLinux::AddObserver(Observer* observer) {
    192   DCHECK(CalledOnValidThread());
    193   if (observer)
    194     observers_.AddObserver(observer);
    195 }
    196 
    197 void InputServiceLinux::RemoveObserver(Observer* observer) {
    198   DCHECK(CalledOnValidThread());
    199   if (observer)
    200     observers_.RemoveObserver(observer);
    201 }
    202 
    203 void InputServiceLinux::GetDevices(std::vector<InputDeviceInfo>* devices) {
    204   DCHECK(CalledOnValidThread());
    205   for (DeviceMap::iterator it = devices_.begin(), ie = devices_.end(); it != ie;
    206        ++it) {
    207     devices->push_back(it->second);
    208   }
    209 }
    210 
    211 bool InputServiceLinux::GetDeviceInfo(const std::string& id,
    212                                       InputDeviceInfo* info) const {
    213   DCHECK(CalledOnValidThread());
    214   DeviceMap::const_iterator it = devices_.find(id);
    215   if (it == devices_.end())
    216     return false;
    217   *info = it->second;
    218   return true;
    219 }
    220 
    221 void InputServiceLinux::WillDestroyCurrentMessageLoop() {
    222   DCHECK(CalledOnValidThread());
    223   g_input_service_linux_ptr.Get().reset(NULL);
    224 }
    225 
    226 void InputServiceLinux::AddDevice(const InputDeviceInfo& info) {
    227   devices_[info.id] = info;
    228   FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info));
    229 }
    230 
    231 void InputServiceLinux::RemoveDevice(const std::string& id) {
    232   devices_.erase(id);
    233   FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id));
    234 }
    235 
    236 bool InputServiceLinux::CalledOnValidThread() const {
    237   return thread_checker_.CalledOnValidThread();
    238 }
    239 
    240 }  // namespace device
    241