summaryrefslogtreecommitdiffstats
path: root/device/hid/hid_service_linux.cc
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2014-12-03 16:26:27 -0800
committerCommit bot <commit-bot@chromium.org>2014-12-04 00:27:20 +0000
commit56868deb02c83ade521fa482e2d03b9319162ae9 (patch)
tree7de3aa7d1acb084f9902bbcd943733fe7df1329a /device/hid/hid_service_linux.cc
parent255cf5511b0932fd3e500089ca3d53bd7e5c13c9 (diff)
downloadchromium_src-56868deb02c83ade521fa482e2d03b9319162ae9.zip
chromium_src-56868deb02c83ade521fa482e2d03b9319162ae9.tar.gz
chromium_src-56868deb02c83ade521fa482e2d03b9319162ae9.tar.bz2
Migrate HidServiceLinux and HidConnectionLinux to BrowserThread::UI.
This patch is a follow-up to https://crrev.com/e8fa00efd0965a7eb5816a that moves the HidService and HidConnection implementations on Linux from the browser's FILE thread to the UI thread. This is being done because the HID APIs on platforms other than Linux are more natural to use on the UI thread. The users of these objects are also usually on the UI thread. (For example, the extension API bindings.) BUG=422540 Review URL: https://codereview.chromium.org/771393002 Cr-Commit-Position: refs/heads/master@{#306729}
Diffstat (limited to 'device/hid/hid_service_linux.cc')
-rw-r--r--device/hid/hid_service_linux.cc379
1 files changed, 231 insertions, 148 deletions
diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc
index 45688f5..1319331 100644
--- a/device/hid/hid_service_linux.cc
+++ b/device/hid/hid_service_linux.cc
@@ -4,19 +4,21 @@
#include "device/hid/hid_service_linux.h"
+#include <fcntl.h>
#include <string>
#include "base/bind.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/location.h"
#include "base/logging.h"
-#include "base/stl_util.h"
+#include "base/scoped_observer.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
+#include "device/hid/device_monitor_linux.h"
#include "device/hid/hid_connection_linux.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_report_descriptor.h"
@@ -38,194 +40,275 @@ const char kHIDName[] = "HID_NAME";
const char kHIDUnique[] = "HID_UNIQ";
const char kSysfsReportDescriptorKey[] = "report_descriptor";
-#if defined(OS_CHROMEOS)
-void OnRequestAccessComplete(
- scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
- const base::Callback<void(bool success)>& callback,
- bool success) {
- reply_task_runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
+} // namespace
-void RequestAccess(
- const std::string& device_node,
- scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
- const base::Callback<void(bool success)>& callback) {
- bool success = false;
+struct HidServiceLinux::ConnectParams {
+ ConnectParams(const HidDeviceInfo& device_info,
+ const ConnectCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+ : device_info(device_info),
+ callback(callback),
+ task_runner(task_runner),
+ file_task_runner(file_task_runner) {}
+ ~ConnectParams() {}
- if (base::SysInfo::IsRunningOnChromeOS()) {
- chromeos::PermissionBrokerClient* client =
- chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
- DCHECK(client) << "Could not get permission broker client.";
- if (client) {
- client->RequestPathAccess(
- device_node,
- -1,
- base::Bind(OnRequestAccessComplete, reply_task_runner, callback));
- return;
- }
- } else {
- // Not really running on Chrome OS, declare success.
- success = true;
+ HidDeviceInfo device_info;
+ ConnectCallback callback;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner;
+ base::File device_file;
+};
+
+class HidServiceLinux::Helper : public DeviceMonitorLinux::Observer,
+ public base::MessageLoop::DestructionObserver {
+ public:
+ Helper(base::WeakPtr<HidServiceLinux> service,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : observer_(this), service_(service), task_runner_(task_runner) {
+ DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
+ observer_.Add(monitor);
+ monitor->Enumerate(
+ base::Bind(&Helper::OnDeviceAdded, base::Unretained(this)));
}
- reply_task_runner->PostTask(FROM_HERE, base::Bind(callback, success));
-}
-#endif
+ virtual ~Helper() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ }
-} // namespace
+ private:
+ // DeviceMonitorLinux::Observer:
+ void OnDeviceAdded(udev_device* device) override {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ const char* device_path = udev_device_get_syspath(device);
+ if (!device_path) {
+ return;
+ }
+ const char* subsystem = udev_device_get_subsystem(device);
+ if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0) {
+ return;
+ }
-HidServiceLinux::HidServiceLinux(
- scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
- : ui_task_runner_(ui_task_runner),
- weak_factory_(this) {
- base::ThreadRestrictions::AssertIOAllowed();
- task_runner_ = base::ThreadTaskRunnerHandle::Get();
- DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
- monitor->AddObserver(this);
- monitor->Enumerate(
- base::Bind(&HidServiceLinux::OnDeviceAdded, weak_factory_.GetWeakPtr()));
-}
+ HidDeviceInfo device_info;
+ device_info.device_id = device_path;
-void HidServiceLinux::Connect(const HidDeviceId& device_id,
- const ConnectCallback& callback) {
- DCHECK(thread_checker_.CalledOnValidThread());
+ const char* str_property = udev_device_get_devnode(device);
+ if (!str_property) {
+ return;
+ }
+ device_info.device_node = str_property;
- ScopedUdevDevicePtr device =
- DeviceMonitorLinux::GetInstance()->GetDeviceFromPath(
- device_id);
- if (!device) {
- task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
- return;
- }
+ udev_device* parent = udev_device_get_parent(device);
+ if (!parent) {
+ return;
+ }
- const char* device_node = udev_device_get_devnode(device.get());
- if (!device_node) {
- task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
- return;
- }
+ const char* hid_id = udev_device_get_property_value(parent, kHIDID);
+ if (!hid_id) {
+ return;
+ }
- base::Callback<void(bool success)> finish_connect =
- base::Bind(&HidServiceLinux::FinishConnect,
- weak_factory_.GetWeakPtr(),
- device_id,
- std::string(device_node),
- callback);
+ std::vector<std::string> parts;
+ base::SplitString(hid_id, ':', &parts);
+ if (parts.size() != 3) {
+ return;
+ }
-#if defined(OS_CHROMEOS)
- ui_task_runner_->PostTask(FROM_HERE,
- base::Bind(RequestAccess,
- std::string(device_node),
- task_runner_,
- finish_connect));
-#else
- // Use the task runner to preserve the asynchronous behavior of this call on
- // non-Chrome OS platforms.
- task_runner_->PostTask(FROM_HERE, base::Bind(finish_connect, true));
-#endif
-}
+ uint32_t int_property = 0;
+ if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
+ device_info.vendor_id = int_property;
+ }
-HidServiceLinux::~HidServiceLinux() {
- if (DeviceMonitorLinux::HasInstance())
- DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
-}
+ if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
+ device_info.product_id = int_property;
+ }
-void HidServiceLinux::OnDeviceAdded(udev_device* device) {
- if (!device)
- return;
+ str_property = udev_device_get_property_value(parent, kHIDUnique);
+ if (str_property != NULL) {
+ device_info.serial_number = str_property;
+ }
- const char* device_path = udev_device_get_syspath(device);
- if (!device_path)
- return;
- const char* subsystem = udev_device_get_subsystem(device);
- if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
- return;
+ str_property = udev_device_get_property_value(parent, kHIDName);
+ if (str_property != NULL) {
+ device_info.product_name = str_property;
+ }
- HidDeviceInfo device_info;
- device_info.device_id = device_path;
+ const char* parent_sysfs_path = udev_device_get_syspath(parent);
+ if (!parent_sysfs_path) {
+ return;
+ }
+ base::FilePath report_descriptor_path =
+ base::FilePath(parent_sysfs_path).Append(kSysfsReportDescriptorKey);
+ std::string report_descriptor_str;
+ if (!base::ReadFileToString(report_descriptor_path,
+ &report_descriptor_str)) {
+ return;
+ }
- uint32_t int_property = 0;
- const char* str_property = NULL;
+ HidReportDescriptor report_descriptor(
+ reinterpret_cast<uint8_t*>(&report_descriptor_str[0]),
+ report_descriptor_str.length());
+ report_descriptor.GetDetails(
+ &device_info.collections, &device_info.has_report_id,
+ &device_info.max_input_report_size, &device_info.max_output_report_size,
+ &device_info.max_feature_report_size);
- udev_device* parent = udev_device_get_parent(device);
- if (!parent) {
- return;
+ task_runner_->PostTask(FROM_HERE, base::Bind(&HidServiceLinux::AddDevice,
+ service_, device_info));
}
- const char* hid_id = udev_device_get_property_value(parent, kHIDID);
- if (!hid_id) {
- return;
+ void OnDeviceRemoved(udev_device* device) override {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ const char* device_path = udev_device_get_syspath(device);
+ if (device_path) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceLinux::RemoveDevice, service_, device_path));
+ }
}
- std::vector<std::string> parts;
- base::SplitString(hid_id, ':', &parts);
- if (parts.size() != 3) {
- return;
+ // base::MessageLoop::DestructionObserver:
+ void WillDestroyCurrentMessageLoop() override {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
}
- if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
- device_info.vendor_id = int_property;
- }
+ base::ThreadChecker thread_checker_;
+ ScopedObserver<DeviceMonitorLinux, DeviceMonitorLinux::Observer> observer_;
- if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
- device_info.product_id = int_property;
- }
+ // This weak pointer is only valid when checked on this task runner.
+ base::WeakPtr<HidServiceLinux> service_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
- str_property = udev_device_get_property_value(parent, kHIDUnique);
- if (str_property != NULL) {
- device_info.serial_number = str_property;
- }
+HidServiceLinux::HidServiceLinux(
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
+ : file_task_runner_(file_task_runner), weak_factory_(this) {
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ // The device watcher is passed a weak pointer back to this service so that it
+ // can be cleaned up after the service is destroyed however this weak pointer
+ // must be constructed on the this thread where it will be checked.
+ file_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&HidServiceLinux::StartHelper,
+ weak_factory_.GetWeakPtr(), task_runner_));
+}
- str_property = udev_device_get_property_value(parent, kHIDName);
- if (str_property != NULL) {
- device_info.product_name = str_property;
- }
+// static
+void HidServiceLinux::StartHelper(
+ base::WeakPtr<HidServiceLinux> weak_ptr,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ // Helper is a message loop destruction observer and will delete itself when
+ // this thread's message loop is destroyed.
+ new Helper(weak_ptr, task_runner);
+}
- const char* parent_sysfs_path = udev_device_get_syspath(parent);
- if (!parent_sysfs_path) {
+void HidServiceLinux::Connect(const HidDeviceId& device_id,
+ const ConnectCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ const auto& map_entry = devices().find(device_id);
+ if (map_entry == devices().end()) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
return;
}
- base::FilePath report_descriptor_path =
- base::FilePath(parent_sysfs_path).Append(kSysfsReportDescriptorKey);
- std::string report_descriptor_str;
- if (!base::ReadFileToString(report_descriptor_path, &report_descriptor_str)) {
+ const HidDeviceInfo& device_info = map_entry->second;
+
+ scoped_ptr<ConnectParams> params(new ConnectParams(
+ device_info, callback, task_runner_, file_task_runner_));
+
+#if defined(OS_CHROMEOS)
+ if (base::SysInfo::IsRunningOnChromeOS()) {
+ chromeos::PermissionBrokerClient* client =
+ chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
+ DCHECK(client) << "Could not get permission broker client.";
+ if (client) {
+ client->RequestPathAccess(
+ device_info.device_node, -1,
+ base::Bind(&HidServiceLinux::OnRequestPathAccessComplete,
+ base::Passed(&params)));
+ } else {
+ task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
+ }
return;
}
+#endif // defined(OS_CHROMEOS)
- HidReportDescriptor report_descriptor(
- reinterpret_cast<uint8_t*>(&report_descriptor_str[0]),
- report_descriptor_str.length());
- report_descriptor.GetDetails(&device_info.collections,
- &device_info.has_report_id,
- &device_info.max_input_report_size,
- &device_info.max_output_report_size,
- &device_info.max_feature_report_size);
+ file_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceLinux::OpenDevice, base::Passed(&params)));
+}
- AddDevice(device_info);
+HidServiceLinux::~HidServiceLinux() {
+ file_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
}
-void HidServiceLinux::OnDeviceRemoved(udev_device* device) {
- const char* device_path = udev_device_get_syspath(device);;
- if (device_path) {
- RemoveDevice(device_path);
+#if defined(OS_CHROMEOS)
+// static
+void HidServiceLinux::OnRequestPathAccessComplete(
+ scoped_ptr<ConnectParams> params,
+ bool success) {
+ if (success) {
+ scoped_refptr<base::SingleThreadTaskRunner> file_task_runner =
+ params->file_task_runner;
+ file_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(&HidServiceLinux::OpenDevice, base::Passed(&params)));
+ } else {
+ params->callback.Run(nullptr);
}
}
+#endif // defined(OS_CHROMEOS)
-void HidServiceLinux::FinishConnect(
- const HidDeviceId& device_id,
- const std::string device_node,
- const base::Callback<void(scoped_refptr<HidConnection>)>& callback,
- bool success) {
- DCHECK(thread_checker_.CalledOnValidThread());
- if (!success) {
- callback.Run(nullptr);
+// static
+void HidServiceLinux::OpenDevice(scoped_ptr<ConnectParams> params) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner = params->task_runner;
+ base::FilePath device_path(params->device_info.device_node);
+ base::File& device_file = params->device_file;
+ int flags =
+ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
+ device_file.Initialize(device_path, flags);
+ if (!device_file.IsValid()) {
+ base::File::Error file_error = device_file.error_details();
+
+ if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
+ VLOG(1) << "Access denied opening device read-write, trying read-only.";
+ flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
+ device_file.Initialize(device_path, flags);
+ }
+ }
+ if (!device_file.IsValid()) {
+ LOG(ERROR) << "Failed to open '" << params->device_info.device_node << "': "
+ << base::File::ErrorToString(device_file.error_details());
+ task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
+ return;
}
- const auto& map_entry = devices().find(device_id);
- if (map_entry == devices().end()) {
- callback.Run(nullptr);
+ int result = fcntl(device_file.GetPlatformFile(), F_GETFL);
+ if (result == -1) {
+ PLOG(ERROR) << "Failed to get flags from the device file descriptor";
+ task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
+ return;
+ }
+
+ result = fcntl(device_file.GetPlatformFile(), F_SETFL, result | O_NONBLOCK);
+ if (result == -1) {
+ PLOG(ERROR) << "Failed to set the non-blocking flag on the device fd";
+ task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
+ return;
}
- callback.Run(new HidConnectionLinux(map_entry->second, device_node));
+ task_runner->PostTask(FROM_HERE, base::Bind(&HidServiceLinux::ConnectImpl,
+ base::Passed(&params)));
+}
+
+// static
+void HidServiceLinux::ConnectImpl(scoped_ptr<ConnectParams> params) {
+ DCHECK(params->device_file.IsValid());
+ params->callback.Run(make_scoped_refptr(
+ new HidConnectionLinux(params->device_info, params->device_file.Pass(),
+ params->file_task_runner)));
}
} // namespace device