Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: device/hid/hid_service_linux.cc

Issue 771393002: Migrate HidServiceLinux and HidConnectionLinux to BrowserThread::UI. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix HID receive buffer size. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/hid/hid_service_linux.h" 5 #include "device/hid/hid_service_linux.h"
6 6
7 #include <fcntl.h>
7 #include <string> 8 #include <string>
8 9
9 #include "base/bind.h" 10 #include "base/bind.h"
10 #include "base/files/file.h" 11 #include "base/files/file.h"
11 #include "base/files/file_path.h" 12 #include "base/files/file_path.h"
12 #include "base/files/file_util.h" 13 #include "base/files/file_util.h"
14 #include "base/location.h"
13 #include "base/logging.h" 15 #include "base/logging.h"
14 #include "base/stl_util.h" 16 #include "base/scoped_observer.h"
15 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/string_split.h" 18 #include "base/strings/string_split.h"
18 #include "base/thread_task_runner_handle.h" 19 #include "base/thread_task_runner_handle.h"
19 #include "base/threading/thread_restrictions.h" 20 #include "base/threading/thread_restrictions.h"
21 #include "device/hid/device_monitor_linux.h"
20 #include "device/hid/hid_connection_linux.h" 22 #include "device/hid/hid_connection_linux.h"
21 #include "device/hid/hid_device_info.h" 23 #include "device/hid/hid_device_info.h"
22 #include "device/hid/hid_report_descriptor.h" 24 #include "device/hid/hid_report_descriptor.h"
23 #include "device/udev_linux/scoped_udev.h" 25 #include "device/udev_linux/scoped_udev.h"
24 26
25 #if defined(OS_CHROMEOS) 27 #if defined(OS_CHROMEOS)
26 #include "base/sys_info.h" 28 #include "base/sys_info.h"
27 #include "chromeos/dbus/dbus_thread_manager.h" 29 #include "chromeos/dbus/dbus_thread_manager.h"
28 #include "chromeos/dbus/permission_broker_client.h" 30 #include "chromeos/dbus/permission_broker_client.h"
29 #endif // defined(OS_CHROMEOS) 31 #endif // defined(OS_CHROMEOS)
30 32
31 namespace device { 33 namespace device {
32 34
33 namespace { 35 namespace {
34 36
35 const char kHidrawSubsystem[] = "hidraw"; 37 const char kHidrawSubsystem[] = "hidraw";
36 const char kHIDID[] = "HID_ID"; 38 const char kHIDID[] = "HID_ID";
37 const char kHIDName[] = "HID_NAME"; 39 const char kHIDName[] = "HID_NAME";
38 const char kHIDUnique[] = "HID_UNIQ"; 40 const char kHIDUnique[] = "HID_UNIQ";
39 const char kSysfsReportDescriptorKey[] = "report_descriptor"; 41 const char kSysfsReportDescriptorKey[] = "report_descriptor";
40 42
43 } // namespace
44
45 struct HidServiceLinux::ConnectParams {
46 ConnectParams(const HidDeviceInfo& device_info,
47 const ConnectCallback& callback,
48 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
49 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
50 : device_info(device_info),
51 callback(callback),
52 task_runner(task_runner),
53 file_task_runner(file_task_runner) {}
54 ~ConnectParams() {}
55
56 HidDeviceInfo device_info;
57 ConnectCallback callback;
58 scoped_refptr<base::SingleThreadTaskRunner> task_runner;
59 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner;
60 base::File device_file;
61 };
62
63 class HidServiceLinux::Helper : public DeviceMonitorLinux::Observer,
64 public base::MessageLoop::DestructionObserver,
65 public base::NonThreadSafe {
66 public:
67 Helper(base::WeakPtr<HidServiceLinux> service,
68 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
69 : observer_(this), service_(service), task_runner_(task_runner) {
70 DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance();
71 observer_.Add(monitor);
72 monitor->Enumerate(
73 base::Bind(&Helper::OnDeviceAdded, base::Unretained(this)));
74 }
75
76 virtual ~Helper() {}
77
78 private:
79 // DeviceMonitorLinux::Observer:
80 void OnDeviceAdded(udev_device* device) override {
81 const char* device_path = udev_device_get_syspath(device);
82 if (!device_path) {
83 return;
84 }
85 const char* subsystem = udev_device_get_subsystem(device);
86 if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0) {
87 return;
88 }
89
90 HidDeviceInfo device_info;
91 device_info.device_id = device_path;
92
93 const char* str_property = udev_device_get_devnode(device);
94 if (!str_property) {
95 return;
96 }
97 device_info.device_node = str_property;
98
99 udev_device* parent = udev_device_get_parent(device);
100 if (!parent) {
101 return;
102 }
103
104 const char* hid_id = udev_device_get_property_value(parent, kHIDID);
105 if (!hid_id) {
106 return;
107 }
108
109 std::vector<std::string> parts;
110 base::SplitString(hid_id, ':', &parts);
111 if (parts.size() != 3) {
112 return;
113 }
114
115 uint32_t int_property = 0;
116 if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
117 device_info.vendor_id = int_property;
118 }
119
120 if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
121 device_info.product_id = int_property;
122 }
123
124 str_property = udev_device_get_property_value(parent, kHIDUnique);
125 if (str_property != NULL) {
Ken Rockot(use gerrit already) 2014/12/03 22:35:10 nit: could you nullptr these while you're here?
Reilly Grant (use Gerrit) 2014/12/03 23:10:28 Technically this is a C API and so NULL is correct
126 device_info.serial_number = str_property;
127 }
128
129 str_property = udev_device_get_property_value(parent, kHIDName);
130 if (str_property != NULL) {
131 device_info.product_name = str_property;
132 }
133
134 const char* parent_sysfs_path = udev_device_get_syspath(parent);
135 if (!parent_sysfs_path) {
136 return;
137 }
138 base::FilePath report_descriptor_path =
139 base::FilePath(parent_sysfs_path).Append(kSysfsReportDescriptorKey);
140 std::string report_descriptor_str;
141 if (!base::ReadFileToString(report_descriptor_path,
142 &report_descriptor_str)) {
143 return;
144 }
145
146 HidReportDescriptor report_descriptor(
147 reinterpret_cast<uint8_t*>(&report_descriptor_str[0]),
148 report_descriptor_str.length());
149 report_descriptor.GetDetails(
150 &device_info.collections, &device_info.has_report_id,
151 &device_info.max_input_report_size, &device_info.max_output_report_size,
152 &device_info.max_feature_report_size);
153
154 task_runner_->PostTask(FROM_HERE, base::Bind(&HidServiceLinux::AddDevice,
155 service_, device_info));
156 }
157
158 void OnDeviceRemoved(udev_device* device) override {
159 const char* device_path = udev_device_get_syspath(device);
160 if (device_path) {
161 task_runner_->PostTask(
162 FROM_HERE,
163 base::Bind(&HidServiceLinux::RemoveDevice, service_, device_path));
164 }
165 }
166
167 // base::MessageLoop::DestructionObserver:
168 void WillDestroyCurrentMessageLoop() override {
169 base::MessageLoop::current()->RemoveDestructionObserver(this);
170 delete this;
171 }
172
173 ScopedObserver<DeviceMonitorLinux, DeviceMonitorLinux::Observer> observer_;
174
175 // This weak pointer is only valid when checked on this task runner.
176 base::WeakPtr<HidServiceLinux> service_;
177 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
178 };
179
180 HidServiceLinux::HidServiceLinux(
181 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
182 : file_task_runner_(file_task_runner), weak_factory_(this) {
183 task_runner_ = base::ThreadTaskRunnerHandle::Get();
184 // The device watcher is passed a weak pointer back to this service so that it
185 // can be cleaned up after the service is destroyed however this weak pointer
186 // must be constructed on the this thread where it will be checked.
187 file_task_runner_->PostTask(
188 FROM_HERE, base::Bind(&HidServiceLinux::StartHelper,
189 weak_factory_.GetWeakPtr(), task_runner_));
190 }
191
192 // static
193 void HidServiceLinux::StartHelper(
194 base::WeakPtr<HidServiceLinux> weak_ptr,
195 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
196 // Helper is a message loop destruction observer and will delete itself when
197 // this thread's message loop is destroyed.
198 new Helper(weak_ptr, task_runner);
199 }
200
201 void HidServiceLinux::Connect(const HidDeviceId& device_id,
202 const ConnectCallback& callback) {
203 DCHECK(thread_checker_.CalledOnValidThread());
204
205 const auto& map_entry = devices().find(device_id);
206 if (map_entry == devices().end()) {
207 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
208 return;
209 }
210 const HidDeviceInfo& device_info = map_entry->second;
211
212 scoped_ptr<ConnectParams> params(new ConnectParams(
213 device_info, callback, task_runner_, file_task_runner_));
214
41 #if defined(OS_CHROMEOS) 215 #if defined(OS_CHROMEOS)
42 void OnRequestAccessComplete(
43 scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
44 const base::Callback<void(bool success)>& callback,
45 bool success) {
46 reply_task_runner->PostTask(FROM_HERE, base::Bind(callback, success));
47 }
48
49 void RequestAccess(
50 const std::string& device_node,
51 scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
52 const base::Callback<void(bool success)>& callback) {
53 bool success = false;
54
55 if (base::SysInfo::IsRunningOnChromeOS()) { 216 if (base::SysInfo::IsRunningOnChromeOS()) {
56 chromeos::PermissionBrokerClient* client = 217 chromeos::PermissionBrokerClient* client =
57 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); 218 chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient();
58 DCHECK(client) << "Could not get permission broker client."; 219 DCHECK(client) << "Could not get permission broker client.";
59 if (client) { 220 if (client) {
60 client->RequestPathAccess( 221 client->RequestPathAccess(
61 device_node, 222 device_info.device_node, -1,
62 -1, 223 base::Bind(&HidServiceLinux::OnRequestPathAccessComplete,
63 base::Bind(OnRequestAccessComplete, reply_task_runner, callback)); 224 base::Passed(&params)));
64 return; 225 } else {
65 } 226 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr));
227 }
228 return;
229 }
230 #endif // defined(OS_CHROMEOS)
231
232 file_task_runner_->PostTask(
233 FROM_HERE,
234 base::Bind(&HidServiceLinux::OpenDevice, base::Passed(&params)));
235 }
236
237 HidServiceLinux::~HidServiceLinux() {
238 file_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
239 }
240
241 #if defined(OS_CHROMEOS)
242 // static
243 void HidServiceLinux::OnRequestPathAccessComplete(
244 scoped_ptr<ConnectParams> params,
245 bool success) {
246 if (success) {
247 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner =
248 params->file_task_runner;
249 file_task_runner->PostTask(
250 FROM_HERE,
251 base::Bind(&HidServiceLinux::OpenDevice, base::Passed(&params)));
66 } else { 252 } else {
67 // Not really running on Chrome OS, declare success. 253 params->callback.Run(nullptr);
68 success = true; 254 }
69 } 255 }
70 256 #endif // defined(OS_CHROMEOS)
71 reply_task_runner->PostTask(FROM_HERE, base::Bind(callback, success)); 257
72 } 258 // static
73 #endif 259 void HidServiceLinux::OpenDevice(scoped_ptr<ConnectParams> params) {
74
75 } // namespace
76
77 HidServiceLinux::HidServiceLinux(
78 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
79 : ui_task_runner_(ui_task_runner),
80 weak_factory_(this) {
81 base::ThreadRestrictions::AssertIOAllowed(); 260 base::ThreadRestrictions::AssertIOAllowed();
82 task_runner_ = base::ThreadTaskRunnerHandle::Get(); 261 scoped_refptr<base::SingleThreadTaskRunner> task_runner = params->task_runner;
83 DeviceMonitorLinux* monitor = DeviceMonitorLinux::GetInstance(); 262 base::FilePath device_path(params->device_info.device_node);
84 monitor->AddObserver(this); 263 base::File& device_file = params->device_file;
85 monitor->Enumerate( 264 int flags =
86 base::Bind(&HidServiceLinux::OnDeviceAdded, weak_factory_.GetWeakPtr())); 265 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
87 } 266 device_file.Initialize(device_path, flags);
88 267 if (!device_file.IsValid()) {
89 void HidServiceLinux::Connect(const HidDeviceId& device_id, 268 base::File::Error file_error = device_file.error_details();
90 const ConnectCallback& callback) { 269
91 DCHECK(thread_checker_.CalledOnValidThread()); 270 if (file_error == base::File::FILE_ERROR_ACCESS_DENIED) {
92 271 VLOG(1) << "Access denied opening device read-write, trying read-only.";
93 ScopedUdevDevicePtr device = 272 flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
94 DeviceMonitorLinux::GetInstance()->GetDeviceFromPath( 273 device_file.Initialize(device_path, flags);
95 device_id); 274 }
96 if (!device) { 275 }
97 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); 276 if (!device_file.IsValid()) {
98 return; 277 LOG(ERROR) << "Failed to open '" << params->device_info.device_node << "': "
99 } 278 << base::File::ErrorToString(device_file.error_details());
100 279 task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
101 const char* device_node = udev_device_get_devnode(device.get()); 280 return;
102 if (!device_node) { 281 }
103 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); 282
104 return; 283 int result = fcntl(device_file.GetPlatformFile(), F_GETFL);
105 } 284 if (result == -1) {
106 285 PLOG(ERROR) << "Failed to get flags from the device file descriptor";
107 base::Callback<void(bool success)> finish_connect = 286 task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
108 base::Bind(&HidServiceLinux::FinishConnect, 287 return;
109 weak_factory_.GetWeakPtr(), 288 }
110 device_id, 289
111 std::string(device_node), 290 result = fcntl(device_file.GetPlatformFile(), F_SETFL, result | O_NONBLOCK);
112 callback); 291 if (result == -1) {
113 292 PLOG(ERROR) << "Failed to set the non-blocking flag on the device fd";
114 #if defined(OS_CHROMEOS) 293 task_runner->PostTask(FROM_HERE, base::Bind(params->callback, nullptr));
115 ui_task_runner_->PostTask(FROM_HERE, 294 return;
116 base::Bind(RequestAccess, 295 }
117 std::string(device_node), 296
118 task_runner_, 297 task_runner->PostTask(FROM_HERE, base::Bind(&HidServiceLinux::ConnectImpl,
119 finish_connect)); 298 base::Passed(&params)));
120 #else 299 }
121 // Use the task runner to preserve the asynchronous behavior of this call on 300
122 // non-Chrome OS platforms. 301 // static
123 task_runner_->PostTask(FROM_HERE, base::Bind(finish_connect, true)); 302 void HidServiceLinux::ConnectImpl(scoped_ptr<ConnectParams> params) {
124 #endif 303 DCHECK(params->device_file.IsValid());
125 } 304 params->callback.Run(make_scoped_refptr(
126 305 new HidConnectionLinux(params->device_info, params->device_file.Pass(),
127 HidServiceLinux::~HidServiceLinux() { 306 params->file_task_runner)));
128 if (DeviceMonitorLinux::HasInstance())
129 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
130 }
131
132 void HidServiceLinux::OnDeviceAdded(udev_device* device) {
133 if (!device)
134 return;
135
136 const char* device_path = udev_device_get_syspath(device);
137 if (!device_path)
138 return;
139 const char* subsystem = udev_device_get_subsystem(device);
140 if (!subsystem || strcmp(subsystem, kHidrawSubsystem) != 0)
141 return;
142
143 HidDeviceInfo device_info;
144 device_info.device_id = device_path;
145
146 uint32_t int_property = 0;
147 const char* str_property = NULL;
148
149 udev_device* parent = udev_device_get_parent(device);
150 if (!parent) {
151 return;
152 }
153
154 const char* hid_id = udev_device_get_property_value(parent, kHIDID);
155 if (!hid_id) {
156 return;
157 }
158
159 std::vector<std::string> parts;
160 base::SplitString(hid_id, ':', &parts);
161 if (parts.size() != 3) {
162 return;
163 }
164
165 if (HexStringToUInt(base::StringPiece(parts[1]), &int_property)) {
166 device_info.vendor_id = int_property;
167 }
168
169 if (HexStringToUInt(base::StringPiece(parts[2]), &int_property)) {
170 device_info.product_id = int_property;
171 }
172
173 str_property = udev_device_get_property_value(parent, kHIDUnique);
174 if (str_property != NULL) {
175 device_info.serial_number = str_property;
176 }
177
178 str_property = udev_device_get_property_value(parent, kHIDName);
179 if (str_property != NULL) {
180 device_info.product_name = str_property;
181 }
182
183 const char* parent_sysfs_path = udev_device_get_syspath(parent);
184 if (!parent_sysfs_path) {
185 return;
186 }
187 base::FilePath report_descriptor_path =
188 base::FilePath(parent_sysfs_path).Append(kSysfsReportDescriptorKey);
189 std::string report_descriptor_str;
190 if (!base::ReadFileToString(report_descriptor_path, &report_descriptor_str)) {
191 return;
192 }
193
194 HidReportDescriptor report_descriptor(
195 reinterpret_cast<uint8_t*>(&report_descriptor_str[0]),
196 report_descriptor_str.length());
197 report_descriptor.GetDetails(&device_info.collections,
198 &device_info.has_report_id,
199 &device_info.max_input_report_size,
200 &device_info.max_output_report_size,
201 &device_info.max_feature_report_size);
202
203 AddDevice(device_info);
204 }
205
206 void HidServiceLinux::OnDeviceRemoved(udev_device* device) {
207 const char* device_path = udev_device_get_syspath(device);;
208 if (device_path) {
209 RemoveDevice(device_path);
210 }
211 }
212
213 void HidServiceLinux::FinishConnect(
214 const HidDeviceId& device_id,
215 const std::string device_node,
216 const base::Callback<void(scoped_refptr<HidConnection>)>& callback,
217 bool success) {
218 DCHECK(thread_checker_.CalledOnValidThread());
219 if (!success) {
220 callback.Run(nullptr);
221 }
222
223 const auto& map_entry = devices().find(device_id);
224 if (map_entry == devices().end()) {
225 callback.Run(nullptr);
226 }
227
228 callback.Run(new HidConnectionLinux(map_entry->second, device_node));
229 } 307 }
230 308
231 } // namespace device 309 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698