summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorygorshenin@chromium.org <ygorshenin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-12 22:02:37 +0000
committerygorshenin@chromium.org <ygorshenin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-12 22:02:37 +0000
commiteedae13b54e9ca6b415ac59cca7e90026301ea40 (patch)
treeaa802ca73d20f0ed3c0d364ba10dba1adc8ee3d7 /device
parent4be80faf23d58cf41ee808e8578b0233cdbb90e7 (diff)
downloadchromium_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.gyp1
-rw-r--r--device/hid/device_monitor_linux.cc27
-rw-r--r--device/hid/device_monitor_linux.h19
-rw-r--r--device/hid/hid.gyp2
-rw-r--r--device/hid/hid_service_linux.h2
-rw-r--r--device/hid/input_service_linux.cc170
-rw-r--r--device/hid/input_service_linux.h93
-rw-r--r--device/hid/input_service_linux_unittest.cc24
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