OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "device/bluetooth/bluetooth_socket_chromeos.h" | |
6 | |
7 #include <queue> | |
8 #include <string> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/bind.h" | |
12 #include "base/callback.h" | |
13 #include "base/logging.h" | |
14 #include "base/memory/linked_ptr.h" | |
15 #include "base/memory/ref_counted.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/message_loop/message_loop.h" | |
18 #include "base/sequenced_task_runner.h" | |
19 #include "base/strings/string_util.h" | |
20 #include "base/task_runner_util.h" | |
21 #include "base/threading/thread_restrictions.h" | |
22 #include "base/threading/worker_pool.h" | |
23 #include "dbus/bus.h" | |
24 #include "dbus/file_descriptor.h" | |
25 #include "dbus/object_path.h" | |
26 #include "device/bluetooth/bluetooth_adapter.h" | |
27 #include "device/bluetooth/bluetooth_adapter_chromeos.h" | |
28 #include "device/bluetooth/bluetooth_adapter_profile_chromeos.h" | |
29 #include "device/bluetooth/bluetooth_device.h" | |
30 #include "device/bluetooth/bluetooth_device_chromeos.h" | |
31 #include "device/bluetooth/bluetooth_socket.h" | |
32 #include "device/bluetooth/bluetooth_socket_net.h" | |
33 #include "device/bluetooth/bluetooth_socket_thread.h" | |
34 #include "device/bluetooth/dbus/bluetooth_device_client.h" | |
35 #include "device/bluetooth/dbus/bluetooth_profile_manager_client.h" | |
36 #include "device/bluetooth/dbus/bluetooth_profile_service_provider.h" | |
37 #include "device/bluetooth/dbus/bluez_dbus_manager.h" | |
38 #include "net/base/ip_endpoint.h" | |
39 #include "net/base/net_errors.h" | |
40 #include "third_party/cros_system_api/dbus/service_constants.h" | |
41 | |
42 using device::BluetoothAdapter; | |
43 using device::BluetoothDevice; | |
44 using device::BluetoothSocketThread; | |
45 using device::BluetoothUUID; | |
46 | |
47 namespace { | |
48 | |
49 const char kAcceptFailed[] = "Failed to accept connection."; | |
50 const char kInvalidUUID[] = "Invalid UUID"; | |
51 const char kSocketNotListening[] = "Socket is not listening."; | |
52 | |
53 } // namespace | |
54 | |
55 namespace chromeos { | |
56 | |
57 // static | |
58 scoped_refptr<BluetoothSocketChromeOS> | |
59 BluetoothSocketChromeOS::CreateBluetoothSocket( | |
60 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | |
61 scoped_refptr<BluetoothSocketThread> socket_thread) { | |
62 DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); | |
63 | |
64 return make_scoped_refptr( | |
65 new BluetoothSocketChromeOS(ui_task_runner, socket_thread)); | |
66 } | |
67 | |
68 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {} | |
69 | |
70 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {} | |
71 | |
72 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest() | |
73 : accepting(false), | |
74 cancelled(false) {} | |
75 | |
76 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {} | |
77 | |
78 BluetoothSocketChromeOS::BluetoothSocketChromeOS( | |
79 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | |
80 scoped_refptr<BluetoothSocketThread> socket_thread) | |
81 : BluetoothSocketNet(ui_task_runner, socket_thread), profile_(nullptr) { | |
82 } | |
83 | |
84 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() { | |
85 DCHECK(!profile_); | |
86 | |
87 if (adapter_.get()) { | |
88 adapter_->RemoveObserver(this); | |
89 adapter_ = nullptr; | |
90 } | |
91 } | |
92 | |
93 void BluetoothSocketChromeOS::Connect( | |
94 const BluetoothDeviceChromeOS* device, | |
95 const BluetoothUUID& uuid, | |
96 SecurityLevel security_level, | |
97 const base::Closure& success_callback, | |
98 const ErrorCompletionCallback& error_callback) { | |
99 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
100 DCHECK(!profile_); | |
101 | |
102 if (!uuid.IsValid()) { | |
103 error_callback.Run(kInvalidUUID); | |
104 return; | |
105 } | |
106 | |
107 device_address_ = device->GetAddress(); | |
108 device_path_ = device->object_path(); | |
109 uuid_ = uuid; | |
110 options_.reset(new bluez::BluetoothProfileManagerClient::Options()); | |
111 if (security_level == SECURITY_LEVEL_LOW) | |
112 options_->require_authentication.reset(new bool(false)); | |
113 | |
114 adapter_ = device->adapter(); | |
115 | |
116 RegisterProfile(device->adapter(), success_callback, error_callback); | |
117 } | |
118 | |
119 void BluetoothSocketChromeOS::Listen( | |
120 scoped_refptr<BluetoothAdapter> adapter, | |
121 SocketType socket_type, | |
122 const BluetoothUUID& uuid, | |
123 const BluetoothAdapter::ServiceOptions& service_options, | |
124 const base::Closure& success_callback, | |
125 const ErrorCompletionCallback& error_callback) { | |
126 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
127 DCHECK(!profile_); | |
128 | |
129 if (!uuid.IsValid()) { | |
130 error_callback.Run(kInvalidUUID); | |
131 return; | |
132 } | |
133 | |
134 adapter_ = adapter; | |
135 adapter_->AddObserver(this); | |
136 | |
137 uuid_ = uuid; | |
138 options_.reset(new bluez::BluetoothProfileManagerClient::Options()); | |
139 if (service_options.name) | |
140 options_->name.reset(new std::string(*service_options.name)); | |
141 | |
142 switch (socket_type) { | |
143 case kRfcomm: | |
144 options_->channel.reset( | |
145 new uint16(service_options.channel ? *service_options.channel : 0)); | |
146 break; | |
147 case kL2cap: | |
148 options_->psm.reset( | |
149 new uint16(service_options.psm ? *service_options.psm : 0)); | |
150 break; | |
151 default: | |
152 NOTREACHED(); | |
153 } | |
154 | |
155 RegisterProfile(static_cast<BluetoothAdapterChromeOS*>(adapter.get()), | |
156 success_callback, error_callback); | |
157 } | |
158 | |
159 void BluetoothSocketChromeOS::Close() { | |
160 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
161 | |
162 if (profile_) | |
163 UnregisterProfile(); | |
164 | |
165 // In the case below, where an asynchronous task gets posted on the socket | |
166 // thread in BluetoothSocketNet::Close, a reference will be held to this | |
167 // socket by the callback. This may cause the BluetoothAdapter to outlive | |
168 // BluezDBusManager during shutdown if that callback executes too late. | |
169 if (adapter_.get()) { | |
170 adapter_->RemoveObserver(this); | |
171 adapter_ = nullptr; | |
172 } | |
173 | |
174 if (!device_path_.value().empty()) { | |
175 BluetoothSocketNet::Close(); | |
176 } else { | |
177 DoCloseListening(); | |
178 } | |
179 } | |
180 | |
181 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) { | |
182 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
183 | |
184 if (profile_) | |
185 UnregisterProfile(); | |
186 | |
187 if (!device_path_.value().empty()) { | |
188 BluetoothSocketNet::Disconnect(callback); | |
189 } else { | |
190 DoCloseListening(); | |
191 callback.Run(); | |
192 } | |
193 } | |
194 | |
195 void BluetoothSocketChromeOS::Accept( | |
196 const AcceptCompletionCallback& success_callback, | |
197 const ErrorCompletionCallback& error_callback) { | |
198 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
199 | |
200 if (!device_path_.value().empty()) { | |
201 error_callback.Run(kSocketNotListening); | |
202 return; | |
203 } | |
204 | |
205 // Only one pending accept at a time | |
206 if (accept_request_.get()) { | |
207 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING)); | |
208 return; | |
209 } | |
210 | |
211 accept_request_.reset(new AcceptRequest); | |
212 accept_request_->success_callback = success_callback; | |
213 accept_request_->error_callback = error_callback; | |
214 | |
215 if (connection_request_queue_.size() >= 1) { | |
216 AcceptConnectionRequest(); | |
217 } | |
218 } | |
219 | |
220 void BluetoothSocketChromeOS::RegisterProfile( | |
221 BluetoothAdapterChromeOS* adapter, | |
222 const base::Closure& success_callback, | |
223 const ErrorCompletionCallback& error_callback) { | |
224 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
225 DCHECK(!profile_); | |
226 DCHECK(adapter); | |
227 | |
228 // If the adapter is not present, this is a listening socket and the | |
229 // adapter isn't running yet. Report success and carry on; | |
230 // the profile will be registered when the daemon becomes available. | |
231 if (!adapter->IsPresent()) { | |
232 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
233 << ": Delaying profile registration."; | |
234 base::MessageLoop::current()->PostTask(FROM_HERE, success_callback); | |
235 return; | |
236 } | |
237 | |
238 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
239 << ": Acquiring profile."; | |
240 | |
241 adapter->UseProfile( | |
242 uuid_, device_path_, *options_, this, | |
243 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile, this, | |
244 success_callback, error_callback), | |
245 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError, this, | |
246 error_callback)); | |
247 } | |
248 | |
249 void BluetoothSocketChromeOS::OnRegisterProfile( | |
250 const base::Closure& success_callback, | |
251 const ErrorCompletionCallback& error_callback, | |
252 BluetoothAdapterProfileChromeOS* profile) { | |
253 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
254 DCHECK(!profile_); | |
255 | |
256 profile_ = profile; | |
257 | |
258 if (device_path_.value().empty()) { | |
259 VLOG(1) << uuid_.canonical_value() << ": Profile registered."; | |
260 success_callback.Run(); | |
261 return; | |
262 } | |
263 | |
264 VLOG(1) << uuid_.canonical_value() << ": Got profile, connecting to " | |
265 << device_path_.value(); | |
266 | |
267 bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ConnectProfile( | |
268 device_path_, uuid_.canonical_value(), | |
269 base::Bind(&BluetoothSocketChromeOS::OnConnectProfile, this, | |
270 success_callback), | |
271 base::Bind(&BluetoothSocketChromeOS::OnConnectProfileError, this, | |
272 error_callback)); | |
273 } | |
274 | |
275 void BluetoothSocketChromeOS::OnRegisterProfileError( | |
276 const ErrorCompletionCallback& error_callback, | |
277 const std::string& error_message) { | |
278 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
279 | |
280 LOG(WARNING) << uuid_.canonical_value() | |
281 << ": Failed to register profile: " << error_message; | |
282 error_callback.Run(error_message); | |
283 } | |
284 | |
285 void BluetoothSocketChromeOS::OnConnectProfile( | |
286 const base::Closure& success_callback) { | |
287 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
288 DCHECK(profile_); | |
289 | |
290 VLOG(1) << profile_->object_path().value() << ": Profile connected."; | |
291 UnregisterProfile(); | |
292 success_callback.Run(); | |
293 } | |
294 | |
295 void BluetoothSocketChromeOS::OnConnectProfileError( | |
296 const ErrorCompletionCallback& error_callback, | |
297 const std::string& error_name, | |
298 const std::string& error_message) { | |
299 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
300 DCHECK(profile_); | |
301 | |
302 LOG(WARNING) << profile_->object_path().value() | |
303 << ": Failed to connect profile: " << error_name << ": " | |
304 << error_message; | |
305 UnregisterProfile(); | |
306 error_callback.Run(error_message); | |
307 } | |
308 | |
309 void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter, | |
310 bool present) { | |
311 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
312 | |
313 if (!present) { | |
314 // Adapter removed, we can't use the profile anymore. | |
315 UnregisterProfile(); | |
316 return; | |
317 } | |
318 | |
319 DCHECK(!profile_); | |
320 | |
321 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
322 << ": Acquiring profile."; | |
323 | |
324 static_cast<BluetoothAdapterChromeOS*>(adapter)->UseProfile( | |
325 uuid_, device_path_, *options_, this, | |
326 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile, this), | |
327 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError, | |
328 this)); | |
329 } | |
330 | |
331 void BluetoothSocketChromeOS::OnInternalRegisterProfile( | |
332 BluetoothAdapterProfileChromeOS* profile) { | |
333 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
334 DCHECK(!profile_); | |
335 | |
336 profile_ = profile; | |
337 | |
338 VLOG(1) << uuid_.canonical_value() << ": Profile re-registered"; | |
339 } | |
340 | |
341 void BluetoothSocketChromeOS::OnInternalRegisterProfileError( | |
342 const std::string& error_message) { | |
343 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
344 | |
345 LOG(WARNING) << "Failed to re-register profile: " << error_message; | |
346 } | |
347 | |
348 void BluetoothSocketChromeOS::Released() { | |
349 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
350 DCHECK(profile_); | |
351 | |
352 VLOG(1) << profile_->object_path().value() << ": Release"; | |
353 } | |
354 | |
355 void BluetoothSocketChromeOS::NewConnection( | |
356 const dbus::ObjectPath& device_path, | |
357 scoped_ptr<dbus::FileDescriptor> fd, | |
358 const bluez::BluetoothProfileServiceProvider::Delegate::Options& options, | |
359 const ConfirmationCallback& callback) { | |
360 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
361 | |
362 VLOG(1) << uuid_.canonical_value() | |
363 << ": New connection from device: " << device_path.value(); | |
364 | |
365 if (!device_path_.value().empty()) { | |
366 DCHECK(device_path_ == device_path); | |
367 | |
368 socket_thread()->task_runner()->PostTask( | |
369 FROM_HERE, | |
370 base::Bind( | |
371 &BluetoothSocketChromeOS::DoNewConnection, | |
372 this, | |
373 device_path_, | |
374 base::Passed(&fd), | |
375 options, | |
376 callback)); | |
377 } else { | |
378 linked_ptr<ConnectionRequest> request(new ConnectionRequest()); | |
379 request->device_path = device_path; | |
380 request->fd = fd.Pass(); | |
381 request->options = options; | |
382 request->callback = callback; | |
383 | |
384 connection_request_queue_.push(request); | |
385 VLOG(1) << uuid_.canonical_value() << ": Connection is now pending."; | |
386 if (accept_request_) { | |
387 AcceptConnectionRequest(); | |
388 } | |
389 } | |
390 } | |
391 | |
392 void BluetoothSocketChromeOS::RequestDisconnection( | |
393 const dbus::ObjectPath& device_path, | |
394 const ConfirmationCallback& callback) { | |
395 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
396 DCHECK(profile_); | |
397 | |
398 VLOG(1) << profile_->object_path().value() << ": Request disconnection"; | |
399 callback.Run(SUCCESS); | |
400 } | |
401 | |
402 void BluetoothSocketChromeOS::Cancel() { | |
403 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
404 DCHECK(profile_); | |
405 | |
406 VLOG(1) << profile_->object_path().value() << ": Cancel"; | |
407 | |
408 if (!connection_request_queue_.size()) | |
409 return; | |
410 | |
411 // If the front request is being accepted mark it as cancelled, otherwise | |
412 // just pop it from the queue. | |
413 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
414 if (!request->accepting) { | |
415 request->cancelled = true; | |
416 } else { | |
417 connection_request_queue_.pop(); | |
418 } | |
419 } | |
420 | |
421 void BluetoothSocketChromeOS::AcceptConnectionRequest() { | |
422 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
423 DCHECK(accept_request_.get()); | |
424 DCHECK(connection_request_queue_.size() >= 1); | |
425 DCHECK(profile_); | |
426 | |
427 VLOG(1) << profile_->object_path().value() | |
428 << ": Accepting pending connection."; | |
429 | |
430 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
431 request->accepting = true; | |
432 | |
433 BluetoothDeviceChromeOS* device = | |
434 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())-> | |
435 GetDeviceWithPath(request->device_path); | |
436 DCHECK(device); | |
437 | |
438 scoped_refptr<BluetoothSocketChromeOS> client_socket = | |
439 BluetoothSocketChromeOS::CreateBluetoothSocket( | |
440 ui_task_runner(), socket_thread()); | |
441 | |
442 client_socket->device_address_ = device->GetAddress(); | |
443 client_socket->device_path_ = request->device_path; | |
444 client_socket->uuid_ = uuid_; | |
445 | |
446 socket_thread()->task_runner()->PostTask( | |
447 FROM_HERE, | |
448 base::Bind( | |
449 &BluetoothSocketChromeOS::DoNewConnection, | |
450 client_socket, | |
451 request->device_path, | |
452 base::Passed(&request->fd), | |
453 request->options, | |
454 base::Bind(&BluetoothSocketChromeOS::OnNewConnection, | |
455 this, | |
456 client_socket, | |
457 request->callback))); | |
458 } | |
459 | |
460 void BluetoothSocketChromeOS::DoNewConnection( | |
461 const dbus::ObjectPath& device_path, | |
462 scoped_ptr<dbus::FileDescriptor> fd, | |
463 const bluez::BluetoothProfileServiceProvider::Delegate::Options& options, | |
464 const ConfirmationCallback& callback) { | |
465 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | |
466 base::ThreadRestrictions::AssertIOAllowed(); | |
467 fd->CheckValidity(); | |
468 | |
469 VLOG(1) << uuid_.canonical_value() << ": Validity check complete."; | |
470 if (!fd->is_valid()) { | |
471 LOG(WARNING) << uuid_.canonical_value() << " :" << fd->value() | |
472 << ": Invalid file descriptor received from Bluetooth Daemon."; | |
473 ui_task_runner()->PostTask(FROM_HERE, | |
474 base::Bind(callback, REJECTED));; | |
475 return; | |
476 } | |
477 | |
478 if (tcp_socket()) { | |
479 LOG(WARNING) << uuid_.canonical_value() << ": Already connected"; | |
480 ui_task_runner()->PostTask(FROM_HERE, | |
481 base::Bind(callback, REJECTED));; | |
482 return; | |
483 } | |
484 | |
485 ResetTCPSocket(); | |
486 | |
487 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the | |
488 // TCPSocket implementation does not actually require one. | |
489 int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(), | |
490 net::IPEndPoint()); | |
491 if (net_result != net::OK) { | |
492 LOG(WARNING) << uuid_.canonical_value() << ": Error adopting socket: " | |
493 << std::string(net::ErrorToString(net_result)); | |
494 ui_task_runner()->PostTask(FROM_HERE, | |
495 base::Bind(callback, REJECTED));; | |
496 return; | |
497 } | |
498 | |
499 VLOG(2) << uuid_.canonical_value() | |
500 << ": Taking descriptor, confirming success."; | |
501 fd->TakeValue(); | |
502 ui_task_runner()->PostTask(FROM_HERE, | |
503 base::Bind(callback, SUCCESS));; | |
504 } | |
505 | |
506 void BluetoothSocketChromeOS::OnNewConnection( | |
507 scoped_refptr<BluetoothSocket> socket, | |
508 const ConfirmationCallback& callback, | |
509 Status status) { | |
510 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
511 DCHECK(accept_request_.get()); | |
512 DCHECK(connection_request_queue_.size() >= 1); | |
513 | |
514 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
515 if (status == SUCCESS && !request->cancelled) { | |
516 BluetoothDeviceChromeOS* device = | |
517 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())-> | |
518 GetDeviceWithPath(request->device_path); | |
519 DCHECK(device); | |
520 | |
521 accept_request_->success_callback.Run(device, socket); | |
522 } else { | |
523 accept_request_->error_callback.Run(kAcceptFailed); | |
524 } | |
525 | |
526 accept_request_.reset(nullptr); | |
527 connection_request_queue_.pop(); | |
528 | |
529 callback.Run(status); | |
530 } | |
531 | |
532 void BluetoothSocketChromeOS::DoCloseListening() { | |
533 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
534 | |
535 if (accept_request_) { | |
536 accept_request_->error_callback.Run( | |
537 net::ErrorToString(net::ERR_CONNECTION_CLOSED)); | |
538 accept_request_.reset(nullptr); | |
539 } | |
540 | |
541 while (connection_request_queue_.size() > 0) { | |
542 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
543 request->callback.Run(REJECTED); | |
544 connection_request_queue_.pop(); | |
545 } | |
546 } | |
547 | |
548 void BluetoothSocketChromeOS::UnregisterProfile() { | |
549 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
550 DCHECK(profile_); | |
551 | |
552 VLOG(1) << profile_->object_path().value() << ": Release profile"; | |
553 | |
554 static_cast<BluetoothAdapterChromeOS*>(adapter_.get()) | |
555 ->ReleaseProfile(device_path_, profile_); | |
556 profile_ = nullptr; | |
557 } | |
558 | |
559 } // namespace chromeos | |
OLD | NEW |