diff options
author | ygorshenin@chromium.org <ygorshenin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-12 22:02:37 +0000 |
---|---|---|
committer | ygorshenin@chromium.org <ygorshenin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-12 22:02:37 +0000 |
commit | eedae13b54e9ca6b415ac59cca7e90026301ea40 (patch) | |
tree | aa802ca73d20f0ed3c0d364ba10dba1adc8ee3d7 /device | |
parent | 4be80faf23d58cf41ee808e8578b0233cdbb90e7 (diff) | |
download | chromium_src-eedae13b54e9ca6b415ac59cca7e90026301ea40.zip chromium_src-eedae13b54e9ca6b415ac59cca7e90026301ea40.tar.gz chromium_src-eedae13b54e9ca6b415ac59cca7e90026301ea40.tar.bz2 |
Added InputServiceLinux, which monitors all devices from hid or input subsystems.
Also, fixed bugs with DeviceMonitorLinux, such as:
* it's now deleted when message loop on which it was created is destroyed
* it's now checks that all methods are called on the valid thread
BUG=357050
TEST=device_unittests:InputServiceLinux*
Review URL: https://codereview.chromium.org/225053006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@263546 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device')
-rw-r--r-- | device/device_tests.gyp | 1 | ||||
-rw-r--r-- | device/hid/device_monitor_linux.cc | 27 | ||||
-rw-r--r-- | device/hid/device_monitor_linux.h | 19 | ||||
-rw-r--r-- | device/hid/hid.gyp | 2 | ||||
-rw-r--r-- | device/hid/hid_service_linux.h | 2 | ||||
-rw-r--r-- | device/hid/input_service_linux.cc | 170 | ||||
-rw-r--r-- | device/hid/input_service_linux.h | 93 | ||||
-rw-r--r-- | device/hid/input_service_linux_unittest.cc | 24 |
8 files changed, 329 insertions, 9 deletions
diff --git a/device/device_tests.gyp b/device/device_tests.gyp index db86a48..eff49d6 100644 --- a/device/device_tests.gyp +++ b/device/device_tests.gyp @@ -38,6 +38,7 @@ 'usb/usb_ids_unittest.cc', 'hid/hid_connection_unittest.cc', 'hid/hid_service_unittest.cc', + 'hid/input_service_linux_unittest.cc', ], 'conditions': [ ['chromeos==1', { diff --git a/device/hid/device_monitor_linux.cc b/device/hid/device_monitor_linux.cc index 37c4588..8d112c1 100644 --- a/device/hid/device_monitor_linux.cc +++ b/device/hid/device_monitor_linux.cc @@ -8,7 +8,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/message_loop/message_loop.h" +#include "base/threading/thread_restrictions.h" namespace device { @@ -25,6 +25,9 @@ base::LazyInstance<scoped_ptr<DeviceMonitorLinux> >::Leaky } // namespace DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) { + base::ThreadRestrictions::AssertIOAllowed(); + base::MessageLoop::current()->AddDestructionObserver(this); + udev_.reset(udev_new()); if (!udev_) { LOG(ERROR) << "Failed to create udev."; @@ -58,11 +61,6 @@ DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) { } } -DeviceMonitorLinux::~DeviceMonitorLinux() { - monitor_watcher_.StopWatchingFileDescriptor(); - close(monitor_fd_); -} - // static DeviceMonitorLinux* DeviceMonitorLinux::GetInstance() { if (!HasInstance()) @@ -76,23 +74,27 @@ bool DeviceMonitorLinux::HasInstance() { } void DeviceMonitorLinux::AddObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); if (observer) observers_.AddObserver(observer); } void DeviceMonitorLinux::RemoveObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); if (observer) observers_.RemoveObserver(observer); } ScopedUdevDevicePtr DeviceMonitorLinux::GetDeviceFromPath( const std::string& path) { + DCHECK(thread_checker_.CalledOnValidThread()); ScopedUdevDevicePtr device( udev_device_new_from_syspath(udev_.get(), path.c_str())); return device.Pass(); } void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); if (!enumerate) { @@ -116,7 +118,13 @@ void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) { } } +void DeviceMonitorLinux::WillDestroyCurrentMessageLoop() { + DCHECK(thread_checker_.CalledOnValidThread()); + g_device_monitor_linux_ptr.Get().reset(NULL); +} + void DeviceMonitorLinux::OnFileCanReadWithoutBlocking(int fd) { + DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(monitor_fd_, fd); ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get())); @@ -132,4 +140,11 @@ void DeviceMonitorLinux::OnFileCanReadWithoutBlocking(int fd) { void DeviceMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) {} +DeviceMonitorLinux::~DeviceMonitorLinux() { + DCHECK(thread_checker_.CalledOnValidThread()); + base::MessageLoop::current()->RemoveDestructionObserver(this); + monitor_watcher_.StopWatchingFileDescriptor(); + close(monitor_fd_); +} + } // namespace device diff --git a/device/hid/device_monitor_linux.h b/device/hid/device_monitor_linux.h index d198473..bc5ac63 100644 --- a/device/hid/device_monitor_linux.h +++ b/device/hid/device_monitor_linux.h @@ -9,15 +9,22 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" #include "base/message_loop/message_pump_libevent.h" #include "base/observer_list.h" +#include "base/threading/thread_checker.h" #include "device/hid/udev_common.h" struct udev_device; namespace device { -class DeviceMonitorLinux : public base::MessagePumpLibevent::Watcher { +// This class listends for notifications from libudev about +// connected/disconnected devices. This class is *NOT* thread-safe and +// all methods must be accessed from the FILE thread. +class DeviceMonitorLinux : public base::MessageLoop::DestructionObserver, + public base::MessagePumpLibevent::Watcher { public: typedef base::Callback<void(udev_device* device)> EnumerateCallback; @@ -29,7 +36,6 @@ class DeviceMonitorLinux : public base::MessagePumpLibevent::Watcher { }; DeviceMonitorLinux(); - virtual ~DeviceMonitorLinux(); static DeviceMonitorLinux* GetInstance(); static bool HasInstance(); @@ -40,11 +46,18 @@ class DeviceMonitorLinux : public base::MessagePumpLibevent::Watcher { ScopedUdevDevicePtr GetDeviceFromPath(const std::string& path); void Enumerate(const EnumerateCallback& callback); + // Implements base::MessageLoop::DestructionObserver + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + // Implements base::MessagePumpLibevent::Watcher virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; private: + friend struct base::DefaultDeleter<DeviceMonitorLinux>; + + virtual ~DeviceMonitorLinux(); + ScopedUdevPtr udev_; ScopedUdevMonitorPtr monitor_; int monitor_fd_; @@ -52,6 +65,8 @@ class DeviceMonitorLinux : public base::MessagePumpLibevent::Watcher { ObserverList<Observer> observers_; + base::ThreadChecker thread_checker_; + DISALLOW_COPY_AND_ASSIGN(DeviceMonitorLinux); }; diff --git a/device/hid/hid.gyp b/device/hid/hid.gyp index c438713..c286075 100644 --- a/device/hid/hid.gyp +++ b/device/hid/hid.gyp @@ -43,6 +43,8 @@ 'hid_service_win.h', 'hid_utils_mac.cc', 'hid_utils_mac.h', + 'input_service_linux.cc', + 'input_service_linux.h', 'udev_common.h' ], }, diff --git a/device/hid/hid_service_linux.h b/device/hid/hid_service_linux.h index 7bffd09..c69096d 100644 --- a/device/hid/hid_service_linux.h +++ b/device/hid/hid_service_linux.h @@ -25,7 +25,7 @@ class HidServiceLinux : public HidService, virtual scoped_refptr<HidConnection> Connect(const HidDeviceId& device_id) OVERRIDE; - // Implements base::DeviceMonitorLinux::Observer: + // Implements DeviceMonitorLinux::Observer: virtual void OnDeviceAdded(udev_device* device) OVERRIDE; virtual void OnDeviceRemoved(udev_device* device) OVERRIDE; diff --git a/device/hid/input_service_linux.cc b/device/hid/input_service_linux.cc new file mode 100644 index 0000000..7552824 --- /dev/null +++ b/device/hid/input_service_linux.cc @@ -0,0 +1,170 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <libudev.h> + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/threading/thread_restrictions.h" +#include "device/hid/input_service_linux.h" + +namespace device { + +namespace { + +const char kHidSubsystem[] = "hid"; +const char kInputSubsystem[] = "input"; +const char kIdInputAccelerometer[] = "ID_INPUT_ACCELEROMETER"; +const char kIdInputJoystick[] = "ID_INPUT_JOYSTICK"; +const char kIdInputKey[] = "ID_INPUT_KEY"; +const char kIdInputKeyboard[] = "ID_INPUT_KEYBOARD"; +const char kIdInputMouse[] = "ID_INPUT_MOUSE"; +const char kIdInputTablet[] = "ID_INPUT_TABLET"; +const char kIdInputTouchpad[] = "ID_INPUT_TOUCHPAD"; +const char kIdInputTouchscreen[] = "ID_INPUT_TOUCHSCREEN"; + +// The instance will be reset when message loop destroys. +base::LazyInstance<scoped_ptr<InputServiceLinux> >::Leaky + g_input_service_linux_ptr = LAZY_INSTANCE_INITIALIZER; + +bool GetBoolProperty(udev_device* device, const char* key) { + CHECK(device); + CHECK(key); + const char* property = udev_device_get_property_value(device, key); + if (!property) + return false; + int value; + if (!base::StringToInt(property, &value)) { + LOG(ERROR) << "Not an integer value for " << key << " property"; + return false; + } + return (value != 0); +} + +} // namespace + +InputServiceLinux::InputDeviceInfo::InputDeviceInfo() + : subsystem(SUBSYSTEM_UNKNOWN), + is_accelerometer(false), + is_joystick(false), + is_key(false), + is_keyboard(false), + is_mouse(false), + is_tablet(false), + is_touchpad(false), + is_touchscreen(false) {} + +InputServiceLinux::InputServiceLinux() { + base::ThreadRestrictions::AssertIOAllowed(); + base::MessageLoop::current()->AddDestructionObserver(this); + DeviceMonitorLinux::GetInstance()->AddObserver(this); + DeviceMonitorLinux::GetInstance()->Enumerate( + base::Bind(&InputServiceLinux::OnDeviceAdded, base::Unretained(this))); +} + +// static +InputServiceLinux* InputServiceLinux::GetInstance() { + if (!HasInstance()) + g_input_service_linux_ptr.Get().reset(new InputServiceLinux()); + return g_input_service_linux_ptr.Get().get(); +} + +// static +bool InputServiceLinux::HasInstance() { + return g_input_service_linux_ptr.Get().get(); +} + +void InputServiceLinux::AddObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (observer) + observers_.AddObserver(observer); +} + +void InputServiceLinux::RemoveObserver(Observer* observer) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (observer) + observers_.RemoveObserver(observer); +} + +void InputServiceLinux::GetDevices(std::vector<InputDeviceInfo>* devices) { + DCHECK(thread_checker_.CalledOnValidThread()); + for (DeviceMap::iterator it = devices_.begin(), ie = devices_.end(); it != ie; + ++it) + devices->push_back(it->second); +} + +bool InputServiceLinux::GetDeviceInfo(const std::string& id, + InputDeviceInfo* info) const { + DCHECK(thread_checker_.CalledOnValidThread()); + DeviceMap::const_iterator it = devices_.find(id); + if (it == devices_.end()) + return false; + *info = it->second; + return true; +} + +void InputServiceLinux::WillDestroyCurrentMessageLoop() { + DCHECK(thread_checker_.CalledOnValidThread()); + g_input_service_linux_ptr.Get().reset(NULL); +} + +void InputServiceLinux::OnDeviceAdded(udev_device* device) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!device) + return; + const char* path = udev_device_get_syspath(device); + if (!path) + return; + + InputDeviceInfo info; + info.id = path; + + const char* name = udev_device_get_property_value(device, "NAME"); + if (name) + info.name = name; + + const char* subsystem = udev_device_get_subsystem(device); + if (!subsystem) + return; + else if (strcmp(subsystem, kHidSubsystem) == 0) + info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID; + else if (strcmp(subsystem, kInputSubsystem) == 0) + info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT; + else + return; + + info.is_accelerometer = GetBoolProperty(device, kIdInputAccelerometer); + info.is_joystick = GetBoolProperty(device, kIdInputJoystick); + info.is_key = GetBoolProperty(device, kIdInputKey); + info.is_keyboard = GetBoolProperty(device, kIdInputKeyboard); + info.is_mouse = GetBoolProperty(device, kIdInputMouse); + info.is_tablet = GetBoolProperty(device, kIdInputTablet); + info.is_touchpad = GetBoolProperty(device, kIdInputTouchpad); + info.is_touchscreen = GetBoolProperty(device, kIdInputTouchscreen); + + devices_[info.id] = info; + FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info)); +} + +void InputServiceLinux::OnDeviceRemoved(udev_device* device) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!device) + return; + const char* path = udev_device_get_syspath(device); + if (!path) + return; + devices_.erase(path); + FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(path)); +} + +InputServiceLinux::~InputServiceLinux() { + DCHECK(thread_checker_.CalledOnValidThread()); + base::MessageLoop::current()->RemoveDestructionObserver(this); + if (DeviceMonitorLinux::HasInstance()) + DeviceMonitorLinux::GetInstance()->RemoveObserver(this); +} + +} // namespace device diff --git a/device/hid/input_service_linux.h b/device/hid/input_service_linux.h new file mode 100644 index 0000000..48584f4 --- /dev/null +++ b/device/hid/input_service_linux.h @@ -0,0 +1,93 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_HID_INPUT_SERVICE_LINUX_H_ +#define DEVICE_HID_INPUT_SERVICE_LINUX_H_ + +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "device/hid/device_monitor_linux.h" + +namespace device { + +// This class provides information and notifications about +// connected/disconnected input/HID devices. This class is *NOT* +// thread-safe and all methods must be called from the FILE thread. +class InputServiceLinux : public base::MessageLoop::DestructionObserver, + public DeviceMonitorLinux::Observer { + public: + struct InputDeviceInfo { + enum Subsystem { SUBSYSTEM_HID, SUBSYSTEM_INPUT, SUBSYSTEM_UNKNOWN }; + + InputDeviceInfo(); + + std::string id; + std::string name; + Subsystem subsystem; + + bool is_accelerometer : 1; + bool is_joystick : 1; + bool is_key : 1; + bool is_keyboard : 1; + bool is_mouse : 1; + bool is_tablet : 1; + bool is_touchpad : 1; + bool is_touchscreen : 1; + }; + + class Observer { + public: + virtual ~Observer() {} + virtual void OnInputDeviceAdded(const InputDeviceInfo& info) = 0; + virtual void OnInputDeviceRemoved(const std::string& id) = 0; + }; + + InputServiceLinux(); + + static InputServiceLinux* GetInstance(); + static bool HasInstance(); + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + // Returns list of all currently connected input/hid devices. + void GetDevices(std::vector<InputDeviceInfo>* devices); + + // Returns an info about input device identified by |id|. When there're + // no input or hid device with such id, returns false and doesn't + // modify |info|. + bool GetDeviceInfo(const std::string& id, InputDeviceInfo* info) const; + + // Implements base::MessageLoop::DestructionObserver + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + + // Implements DeviceMonitorLinux::Observer: + virtual void OnDeviceAdded(udev_device* device) OVERRIDE; + virtual void OnDeviceRemoved(udev_device* device) OVERRIDE; + + private: + friend struct base::DefaultDeleter<InputServiceLinux>; + + typedef base::hash_map<std::string, InputDeviceInfo> DeviceMap; + virtual ~InputServiceLinux(); + + DeviceMap devices_; + ObserverList<Observer> observers_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(InputServiceLinux); +}; + +} // namespace device + +#endif // DEVICE_HID_INPUT_SERVICE_LINUX_H_ diff --git a/device/hid/input_service_linux_unittest.cc b/device/hid/input_service_linux_unittest.cc new file mode 100644 index 0000000..9a8563a --- /dev/null +++ b/device/hid/input_service_linux_unittest.cc @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <vector> + +#include "base/message_loop/message_loop.h" +#include "device/hid/input_service_linux.cc" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +TEST(InputServiceLinux, Simple) { + base::MessageLoopForIO message_loop; + InputServiceLinux* service = InputServiceLinux::GetInstance(); + + ASSERT_TRUE(service); + std::vector<InputServiceLinux::InputDeviceInfo> devices; + service->GetDevices(&devices); + for (size_t i = 0; i < devices.size(); ++i) + ASSERT_TRUE(!devices[i].id.empty()); +} + +} // namespace device |