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_mac.h" 6 7#import <IOBluetooth/IOBluetooth.h> 8#include <stdint.h> 9 10#include <limits> 11#include <memory> 12#include <sstream> 13#include <string> 14#include <utility> 15 16#include "base/bind.h" 17#include "base/callback.h" 18#include "base/callback_helpers.h" 19#include "base/containers/queue.h" 20#include "base/mac/scoped_cftyperef.h" 21#include "base/memory/ref_counted.h" 22#include "base/numerics/safe_conversions.h" 23#include "base/strings/string_number_conversions.h" 24#include "base/strings/stringprintf.h" 25#include "base/strings/sys_string_conversions.h" 26#include "base/threading/thread_restrictions.h" 27#include "device/bluetooth/bluetooth_adapter_mac.h" 28#include "device/bluetooth/bluetooth_channel_mac.h" 29#include "device/bluetooth/bluetooth_classic_device_mac.h" 30#include "device/bluetooth/bluetooth_device.h" 31#include "device/bluetooth/bluetooth_l2cap_channel_mac.h" 32#include "device/bluetooth/bluetooth_rfcomm_channel_mac.h" 33#include "net/base/io_buffer.h" 34#include "net/base/net_errors.h" 35 36using device::BluetoothSocket; 37 38// A simple helper class that forwards SDP query completed notifications to its 39// wrapped |socket_|. 40@interface SDPQueryListener : NSObject { 41 @private 42 // The socket that registered for notifications. 43 scoped_refptr<device::BluetoothSocketMac> _socket; 44 45 // Callbacks associated with the request that triggered this SDP query. 46 base::OnceClosure _success_callback; 47 BluetoothSocket::ErrorCompletionCallback _error_callback; 48 49 // The device being queried. 50 IOBluetoothDevice* _device; // weak 51} 52 53- (id)initWithSocket:(scoped_refptr<device::BluetoothSocketMac>)socket 54 device:(IOBluetoothDevice*)device 55 success_callback:(base::OnceClosure)success_callback 56 error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback; 57- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status; 58 59@end 60 61@implementation SDPQueryListener 62 63- (id)initWithSocket:(scoped_refptr<device::BluetoothSocketMac>)socket 64 device:(IOBluetoothDevice*)device 65 success_callback:(base::OnceClosure)success_callback 66 error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback { 67 if ((self = [super init])) { 68 _socket = socket; 69 _device = device; 70 _success_callback = std::move(success_callback); 71 _error_callback = error_callback; 72 } 73 74 return self; 75} 76 77- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status { 78 DCHECK_EQ(device, _device); 79 _socket->OnSDPQueryComplete(status, device, std::move(_success_callback), 80 _error_callback); 81} 82 83@end 84 85// A simple helper class that forwards RFCOMM channel opened notifications to 86// its wrapped |socket_|. 87@interface BluetoothRfcommConnectionListener : NSObject { 88 @private 89 // The socket that owns |self|. 90 device::BluetoothSocketMac* _socket; // weak 91 92 // The OS mechanism used to subscribe to and unsubscribe from RFCOMM channel 93 // creation notifications. 94 IOBluetoothUserNotification* _rfcommNewChannelNotification; // weak 95} 96 97- (id)initWithSocket:(device::BluetoothSocketMac*)socket 98 channelID:(BluetoothRFCOMMChannelID)channelID; 99- (void)rfcommChannelOpened:(IOBluetoothUserNotification*)notification 100 channel:(IOBluetoothRFCOMMChannel*)rfcommChannel; 101 102@end 103 104@implementation BluetoothRfcommConnectionListener 105 106- (id)initWithSocket:(device::BluetoothSocketMac*)socket 107 channelID:(BluetoothRFCOMMChannelID)channelID { 108 if ((self = [super init])) { 109 _socket = socket; 110 111 SEL selector = @selector(rfcommChannelOpened:channel:); 112 const auto kIncomingDirection = 113 kIOBluetoothUserNotificationChannelDirectionIncoming; 114 _rfcommNewChannelNotification = 115 [IOBluetoothRFCOMMChannel 116 registerForChannelOpenNotifications:self 117 selector:selector 118 withChannelID:channelID 119 direction:kIncomingDirection]; 120 } 121 122 return self; 123} 124 125- (void)dealloc { 126 [_rfcommNewChannelNotification unregister]; 127 [super dealloc]; 128} 129 130- (void)rfcommChannelOpened:(IOBluetoothUserNotification*)notification 131 channel:(IOBluetoothRFCOMMChannel*)rfcommChannel { 132 if (notification != _rfcommNewChannelNotification) { 133 // This case is reachable if there are pre-existing RFCOMM channels open at 134 // the time that the listener is created. In that case, each existing 135 // channel calls into this method with a different notification than the one 136 // this class registered with. Ignore those; this class is only interested 137 // in channels that have opened since it registered for notifications. 138 return; 139 } 140 141 _socket->OnChannelOpened(std::unique_ptr<device::BluetoothChannelMac>( 142 new device::BluetoothRfcommChannelMac(NULL, [rfcommChannel retain]))); 143} 144 145@end 146 147// A simple helper class that forwards L2CAP channel opened notifications to 148// its wrapped |socket_|. 149@interface BluetoothL2capConnectionListener : NSObject { 150 @private 151 // The socket that owns |self|. 152 device::BluetoothSocketMac* _socket; // weak 153 154 // The OS mechanism used to subscribe to and unsubscribe from L2CAP channel 155 // creation notifications. 156 IOBluetoothUserNotification* _l2capNewChannelNotification; // weak 157} 158 159- (id)initWithSocket:(device::BluetoothSocketMac*)socket 160 psm:(BluetoothL2CAPPSM)psm; 161- (void)l2capChannelOpened:(IOBluetoothUserNotification*)notification 162 channel:(IOBluetoothL2CAPChannel*)l2capChannel; 163 164@end 165 166@implementation BluetoothL2capConnectionListener 167 168- (id)initWithSocket:(device::BluetoothSocketMac*)socket 169 psm:(BluetoothL2CAPPSM)psm { 170 if ((self = [super init])) { 171 _socket = socket; 172 173 SEL selector = @selector(l2capChannelOpened:channel:); 174 const auto kIncomingDirection = 175 kIOBluetoothUserNotificationChannelDirectionIncoming; 176 _l2capNewChannelNotification = 177 [IOBluetoothL2CAPChannel 178 registerForChannelOpenNotifications:self 179 selector:selector 180 withPSM:psm 181 direction:kIncomingDirection]; 182 } 183 184 return self; 185} 186 187- (void)dealloc { 188 [_l2capNewChannelNotification unregister]; 189 [super dealloc]; 190} 191 192- (void)l2capChannelOpened:(IOBluetoothUserNotification*)notification 193 channel:(IOBluetoothL2CAPChannel*)l2capChannel { 194 if (notification != _l2capNewChannelNotification) { 195 // This case is reachable if there are pre-existing L2CAP channels open at 196 // the time that the listener is created. In that case, each existing 197 // channel calls into this method with a different notification than the one 198 // this class registered with. Ignore those; this class is only interested 199 // in channels that have opened since it registered for notifications. 200 return; 201 } 202 203 _socket->OnChannelOpened(std::unique_ptr<device::BluetoothChannelMac>( 204 new device::BluetoothL2capChannelMac(NULL, [l2capChannel retain]))); 205} 206 207@end 208 209namespace device { 210namespace { 211 212// It's safe to use 0 to represent invalid channel or PSM port numbers, as both 213// are required to be non-zero for valid services. 214const BluetoothRFCOMMChannelID kInvalidRfcommChannelId = 0; 215const BluetoothL2CAPPSM kInvalidL2capPsm = 0; 216 217const char kInvalidOrUsedChannel[] = "Invalid channel or already in use"; 218const char kInvalidOrUsedPsm[] = "Invalid PSM or already in use"; 219const char kProfileNotFound[] = "Profile not found"; 220const char kSDPQueryFailed[] = "SDP query failed"; 221const char kSocketConnecting[] = "The socket is currently connecting"; 222const char kSocketAlreadyConnected[] = "The socket is already connected"; 223const char kSocketNotConnected[] = "The socket is not connected"; 224const char kReceivePending[] = "A Receive operation is pending"; 225 226template <class T> 227void empty_queue(base::queue<T>& queue) { 228 base::queue<T> empty; 229 std::swap(queue, empty); 230} 231 232// Converts |uuid| to a IOBluetoothSDPUUID instance. 233IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const BluetoothUUID& uuid) { 234 std::vector<uint8_t> uuid_bytes_vector = uuid.GetBytes(); 235 return [IOBluetoothSDPUUID uuidWithBytes:uuid_bytes_vector.data() 236 length:uuid_bytes_vector.size()]; 237} 238 239// Converts the given |integer| to a string. 240NSString* IntToNSString(int integer) { 241 return [@(integer) stringValue]; 242} 243 244// Returns a dictionary containing the Bluetooth service definition 245// corresponding to the provided |uuid|, |name|, and |protocol_definition|. Does 246// not include a service name in the definition if |name| is null. 247NSDictionary* BuildServiceDefinition(const BluetoothUUID& uuid, 248 const std::string* name, 249 NSArray* protocol_definition) { 250 NSMutableDictionary* service_definition = [NSMutableDictionary dictionary]; 251 252 if (name) { 253 // TODO(isherman): The service's language is currently hardcoded to English. 254 // The language should ideally be specified in the chrome.bluetooth API 255 // instead. 256 const int kEnglishLanguageBase = 100; 257 const int kServiceNameKey = 258 kEnglishLanguageBase + kBluetoothSDPAttributeIdentifierServiceName; 259 NSString* service_name = base::SysUTF8ToNSString(*name); 260 [service_definition setObject:service_name 261 forKey:IntToNSString(kServiceNameKey)]; 262 } 263 264 const int kUUIDsKey = kBluetoothSDPAttributeIdentifierServiceClassIDList; 265 NSArray* uuids = @[GetIOBluetoothSDPUUID(uuid)]; 266 [service_definition setObject:uuids forKey:IntToNSString(kUUIDsKey)]; 267 268 const int kProtocolDefinitionsKey = 269 kBluetoothSDPAttributeIdentifierProtocolDescriptorList; 270 [service_definition setObject:protocol_definition 271 forKey:IntToNSString(kProtocolDefinitionsKey)]; 272 273 return service_definition; 274} 275 276// Returns a dictionary containing the Bluetooth RFCOMM service definition 277// corresponding to the provided |uuid| and |options|. 278NSDictionary* BuildRfcommServiceDefinition( 279 const BluetoothUUID& uuid, 280 const BluetoothAdapter::ServiceOptions& options) { 281 int channel_id = options.channel ? *options.channel : kInvalidRfcommChannelId; 282 NSArray* rfcomm_protocol_definition = @[ 283 @[ [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP] ], 284 @[ 285 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM], 286 @{ 287 @"DataElementType" : @1, // Unsigned integer. 288 @"DataElementSize" : @1, // 1 byte. 289 @"DataElementValue" : @(channel_id), 290 }, 291 ], 292 ]; 293 return BuildServiceDefinition( 294 uuid, options.name.get(), rfcomm_protocol_definition); 295} 296 297// Returns a dictionary containing the Bluetooth L2CAP service definition 298// corresponding to the provided |uuid| and |options|. 299NSDictionary* BuildL2capServiceDefinition( 300 const BluetoothUUID& uuid, 301 const BluetoothAdapter::ServiceOptions& options) { 302 int psm = options.psm ? *options.psm : kInvalidL2capPsm; 303 NSArray* l2cap_protocol_definition = @[ 304 @[ 305 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP], 306 @{ 307 @"DataElementType" : @1, // Unsigned integer. 308 @"DataElementSize" : @2, // 2 bytes. 309 @"DataElementValue" : @(psm), 310 }, 311 ], 312 ]; 313 return BuildServiceDefinition( 314 uuid, options.name.get(), l2cap_protocol_definition); 315} 316 317// Registers a Bluetooth service with the specified |service_definition| in the 318// system SDP server. Returns the registered service on success. If the service 319// could not be registered, or if |verify_service_callback| indicates that the 320// to-be-registered service was not configured correctly, returns nil. 321IOBluetoothSDPServiceRecord* RegisterService( 322 NSDictionary* service_definition, 323 const base::Callback<bool(IOBluetoothSDPServiceRecord*)>& 324 verify_service_callback) { 325 // Attempt to register the service. 326 IOBluetoothSDPServiceRecord* service_record = [IOBluetoothSDPServiceRecord 327 publishedServiceRecordWithDictionary:service_definition]; 328 329 // Verify that the registered service was configured correctly. If not, 330 // withdraw the service. 331 if (!service_record || !verify_service_callback.Run(service_record)) { 332 [service_record removeServiceRecord]; 333 service_record = nil; 334 } 335 336 return service_record; 337} 338 339// Returns true iff the |requested_channel_id| was registered in the RFCOMM 340// |service_record|. If it was, also updates |registered_channel_id| with the 341// registered value, as the requested id may have been left unspecified. 342bool VerifyRfcommService(const int* requested_channel_id, 343 BluetoothRFCOMMChannelID* registered_channel_id, 344 IOBluetoothSDPServiceRecord* service_record) { 345 // Test whether the requested channel id was available. 346 // TODO(isherman): The OS doesn't seem to actually pick a random channel if we 347 // pass in |kInvalidRfcommChannelId|. 348 BluetoothRFCOMMChannelID rfcomm_channel_id; 349 IOReturn result = [service_record getRFCOMMChannelID:&rfcomm_channel_id]; 350 if (result != kIOReturnSuccess || 351 (requested_channel_id && rfcomm_channel_id != *requested_channel_id)) { 352 return false; 353 } 354 355 *registered_channel_id = rfcomm_channel_id; 356 return true; 357} 358 359// Registers an RFCOMM service with the specified |uuid|, |options.channel_id|, 360// and |options.name| in the system SDP server. Automatically allocates a 361// channel if |options.channel_id| is null. Does not specify a name if 362// |options.name| is null. Returns a handle to the registered service and 363// updates |registered_channel_id| to the actual channel id, or returns nil if 364// the service could not be registered. 365IOBluetoothSDPServiceRecord* RegisterRfcommService( 366 const BluetoothUUID& uuid, 367 const BluetoothAdapter::ServiceOptions& options, 368 BluetoothRFCOMMChannelID* registered_channel_id) { 369 return RegisterService( 370 BuildRfcommServiceDefinition(uuid, options), 371 base::Bind( 372 &VerifyRfcommService, options.channel.get(), registered_channel_id)); 373} 374 375// Returns true iff the |requested_psm| was registered in the L2CAP 376// |service_record|. If it was, also updates |registered_psm| with the 377// registered value, as the requested PSM may have been left unspecified. 378bool VerifyL2capService(const int* requested_psm, 379 BluetoothL2CAPPSM* registered_psm, 380 IOBluetoothSDPServiceRecord* service_record) { 381 // Test whether the requested PSM was available. 382 // TODO(isherman): The OS doesn't seem to actually pick a random PSM if we 383 // pass in |kInvalidL2capPsm|. 384 BluetoothL2CAPPSM l2cap_psm; 385 IOReturn result = [service_record getL2CAPPSM:&l2cap_psm]; 386 if (result != kIOReturnSuccess || 387 (requested_psm && l2cap_psm != *requested_psm)) { 388 return false; 389 } 390 391 *registered_psm = l2cap_psm; 392 return true; 393} 394 395// Registers an L2CAP service with the specified |uuid|, |options.psm|, and 396// |options.name| in the system SDP server. Automatically allocates a PSM if 397// |options.psm| is null. Does not register a name if |options.name| is null. 398// Returns a handle to the registered service and updates |registered_psm| to 399// the actual PSM, or returns nil if the service could not be registered. 400IOBluetoothSDPServiceRecord* RegisterL2capService( 401 const BluetoothUUID& uuid, 402 const BluetoothAdapter::ServiceOptions& options, 403 BluetoothL2CAPPSM* registered_psm) { 404 return RegisterService( 405 BuildL2capServiceDefinition(uuid, options), 406 base::Bind(&VerifyL2capService, options.psm.get(), registered_psm)); 407} 408 409} // namespace 410 411// static 412scoped_refptr<BluetoothSocketMac> BluetoothSocketMac::CreateSocket() { 413 return base::WrapRefCounted(new BluetoothSocketMac()); 414} 415 416void BluetoothSocketMac::Connect( 417 IOBluetoothDevice* device, 418 const BluetoothUUID& uuid, 419 base::OnceClosure success_callback, 420 const ErrorCompletionCallback& error_callback) { 421 DCHECK(thread_checker_.CalledOnValidThread()); 422 423 uuid_ = uuid; 424 425 // Perform an SDP query on the |device| to refresh the cache, in case the 426 // services that the |device| advertises have changed since the previous 427 // query. 428 DVLOG(1) << BluetoothClassicDeviceMac::GetDeviceAddress(device) << " " 429 << uuid_.canonical_value() << ": Sending SDP query."; 430 SDPQueryListener* listener = 431 [[SDPQueryListener alloc] initWithSocket:this 432 device:device 433 success_callback:std::move(success_callback) 434 error_callback:error_callback]; 435 [device performSDPQuery:[listener autorelease] 436 uuids:@[GetIOBluetoothSDPUUID(uuid_)]]; 437} 438 439void BluetoothSocketMac::ListenUsingRfcomm( 440 scoped_refptr<BluetoothAdapterMac> adapter, 441 const BluetoothUUID& uuid, 442 const BluetoothAdapter::ServiceOptions& options, 443 const base::Closure& success_callback, 444 const ErrorCompletionCallback& error_callback) { 445 DCHECK(thread_checker_.CalledOnValidThread()); 446 447 adapter_ = adapter; 448 uuid_ = uuid; 449 450 DVLOG(1) << uuid_.canonical_value() << ": Registering RFCOMM service."; 451 BluetoothRFCOMMChannelID registered_channel_id; 452 service_record_.reset( 453 RegisterRfcommService(uuid, options, ®istered_channel_id)); 454 if (!service_record_.get()) { 455 error_callback.Run(kInvalidOrUsedChannel); 456 return; 457 } 458 459 rfcomm_connection_listener_.reset( 460 [[BluetoothRfcommConnectionListener alloc] 461 initWithSocket:this 462 channelID:registered_channel_id]); 463 464 success_callback.Run(); 465} 466 467void BluetoothSocketMac::ListenUsingL2cap( 468 scoped_refptr<BluetoothAdapterMac> adapter, 469 const BluetoothUUID& uuid, 470 const BluetoothAdapter::ServiceOptions& options, 471 const base::Closure& success_callback, 472 const ErrorCompletionCallback& error_callback) { 473 DCHECK(thread_checker_.CalledOnValidThread()); 474 475 adapter_ = adapter; 476 uuid_ = uuid; 477 478 DVLOG(1) << uuid_.canonical_value() << ": Registering L2CAP service."; 479 BluetoothL2CAPPSM registered_psm; 480 service_record_.reset(RegisterL2capService(uuid, options, ®istered_psm)); 481 if (!service_record_.get()) { 482 error_callback.Run(kInvalidOrUsedPsm); 483 return; 484 } 485 486 l2cap_connection_listener_.reset( 487 [[BluetoothL2capConnectionListener alloc] initWithSocket:this 488 psm:registered_psm]); 489 490 success_callback.Run(); 491} 492 493void BluetoothSocketMac::OnSDPQueryComplete( 494 IOReturn status, 495 IOBluetoothDevice* device, 496 base::OnceClosure success_callback, 497 const ErrorCompletionCallback& error_callback) { 498 DCHECK(thread_checker_.CalledOnValidThread()); 499 DVLOG(1) << BluetoothClassicDeviceMac::GetDeviceAddress(device) << " " 500 << uuid_.canonical_value() << ": SDP query complete."; 501 502 if (status != kIOReturnSuccess) { 503 error_callback.Run(kSDPQueryFailed); 504 return; 505 } 506 507 IOBluetoothSDPServiceRecord* record = [device 508 getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)]; 509 if (record == nil) { 510 error_callback.Run(kProfileNotFound); 511 return; 512 } 513 514 if (is_connecting()) { 515 error_callback.Run(kSocketConnecting); 516 return; 517 } 518 519 if (channel_) { 520 error_callback.Run(kSocketAlreadyConnected); 521 return; 522 } 523 524 // Since RFCOMM is built on top of L2CAP, a service record with both should 525 // always be treated as RFCOMM. 526 BluetoothRFCOMMChannelID rfcomm_channel_id = kInvalidRfcommChannelId; 527 BluetoothL2CAPPSM l2cap_psm = kInvalidL2capPsm; 528 status = [record getRFCOMMChannelID:&rfcomm_channel_id]; 529 if (status != kIOReturnSuccess) { 530 status = [record getL2CAPPSM:&l2cap_psm]; 531 if (status != kIOReturnSuccess) { 532 error_callback.Run(kProfileNotFound); 533 return; 534 } 535 } 536 537 if (rfcomm_channel_id != kInvalidRfcommChannelId) { 538 DVLOG(1) << BluetoothClassicDeviceMac::GetDeviceAddress(device) << " " 539 << uuid_.canonical_value() 540 << ": Opening RFCOMM channel: " << rfcomm_channel_id; 541 } else { 542 DCHECK_NE(l2cap_psm, kInvalidL2capPsm); 543 DVLOG(1) << BluetoothClassicDeviceMac::GetDeviceAddress(device) << " " 544 << uuid_.canonical_value() 545 << ": Opening L2CAP channel: " << l2cap_psm; 546 } 547 548 // Note: It's important to set the connect callbacks *prior* to opening the 549 // channel, as opening the channel can synchronously call into 550 // OnChannelOpenComplete(). 551 connect_callbacks_.reset(new ConnectCallbacks()); 552 connect_callbacks_->success_callback = std::move(success_callback); 553 connect_callbacks_->error_callback = error_callback; 554 555 if (rfcomm_channel_id != kInvalidRfcommChannelId) { 556 channel_ = BluetoothRfcommChannelMac::OpenAsync( 557 this, device, rfcomm_channel_id, &status); 558 } else { 559 DCHECK_NE(l2cap_psm, kInvalidL2capPsm); 560 channel_ = 561 BluetoothL2capChannelMac::OpenAsync(this, device, l2cap_psm, &status); 562 } 563 if (status != kIOReturnSuccess) { 564 ReleaseChannel(); 565 std::stringstream error; 566 error << "Failed to connect bluetooth socket (" 567 << BluetoothClassicDeviceMac::GetDeviceAddress(device) << "): (" 568 << status << ")"; 569 error_callback.Run(error.str()); 570 return; 571 } 572 573 DVLOG(1) << BluetoothClassicDeviceMac::GetDeviceAddress(device) << " " 574 << uuid_.canonical_value() << ": channel opening in background."; 575} 576 577void BluetoothSocketMac::OnChannelOpened( 578 std::unique_ptr<BluetoothChannelMac> channel) { 579 DCHECK(thread_checker_.CalledOnValidThread()); 580 DVLOG(1) << uuid_.canonical_value() << ": Incoming channel pending."; 581 582 accept_queue_.push(std::move(channel)); 583 if (accept_request_) 584 AcceptConnectionRequest(); 585 586 // TODO(isherman): Currently, the socket remains alive even after the app that 587 // requested it is closed. That's not great, as a misbehaving app could 588 // saturate all of the system's RFCOMM channels, and then they would not be 589 // freed until the user restarts Chrome. http://crbug.com/367316 590 // TODO(isherman): Likewise, the socket currently remains alive even if the 591 // underlying channel is closed, e.g. via the client disconnecting, or the 592 // user closing the Bluetooth connection via the system menu. This functions 593 // essentially as a minor memory leak. http://crbug.com/367319 594} 595 596void BluetoothSocketMac::OnChannelOpenComplete( 597 const std::string& device_address, 598 IOReturn status) { 599 DCHECK(thread_checker_.CalledOnValidThread()); 600 DCHECK(is_connecting()); 601 602 DVLOG(1) << device_address << " " << uuid_.canonical_value() 603 << ": channel open complete."; 604 605 std::unique_ptr<ConnectCallbacks> temp = std::move(connect_callbacks_); 606 if (status != kIOReturnSuccess) { 607 ReleaseChannel(); 608 std::stringstream error; 609 error << "Failed to connect bluetooth socket (" << device_address << "): (" 610 << status << ")"; 611 temp->error_callback.Run(error.str()); 612 return; 613 } 614 615 std::move(temp->success_callback).Run(); 616} 617 618void BluetoothSocketMac::Close() { 619 DCHECK(thread_checker_.CalledOnValidThread()); 620 621 if (channel_) 622 ReleaseChannel(); 623 else if (service_record_.get()) 624 ReleaseListener(); 625} 626 627void BluetoothSocketMac::Disconnect(const base::Closure& callback) { 628 DCHECK(thread_checker_.CalledOnValidThread()); 629 630 Close(); 631 callback.Run(); 632} 633 634void BluetoothSocketMac::Receive( 635 int /* buffer_size */, 636 const ReceiveCompletionCallback& success_callback, 637 const ReceiveErrorCompletionCallback& error_callback) { 638 DCHECK(thread_checker_.CalledOnValidThread()); 639 640 if (is_connecting()) { 641 error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting); 642 return; 643 } 644 645 if (!channel_) { 646 error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected); 647 return; 648 } 649 650 // Only one pending read at a time 651 if (receive_callbacks_) { 652 error_callback.Run(BluetoothSocket::kIOPending, kReceivePending); 653 return; 654 } 655 656 // If there is at least one packet, consume it and succeed right away. 657 if (!receive_queue_.empty()) { 658 scoped_refptr<net::IOBufferWithSize> buffer = receive_queue_.front(); 659 receive_queue_.pop(); 660 success_callback.Run(buffer->size(), buffer); 661 return; 662 } 663 664 // Set the receive callback to use when data is received. 665 receive_callbacks_.reset(new ReceiveCallbacks()); 666 receive_callbacks_->success_callback = success_callback; 667 receive_callbacks_->error_callback = error_callback; 668} 669 670void BluetoothSocketMac::OnChannelDataReceived(void* data, size_t length) { 671 DCHECK(thread_checker_.CalledOnValidThread()); 672 DCHECK(!is_connecting()); 673 674 int data_size = base::checked_cast<int>(length); 675 auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(data_size); 676 memcpy(buffer->data(), data, buffer->size()); 677 678 // If there is a pending read callback, call it now. 679 if (receive_callbacks_) { 680 std::unique_ptr<ReceiveCallbacks> temp = std::move(receive_callbacks_); 681 temp->success_callback.Run(buffer->size(), buffer); 682 return; 683 } 684 685 // Otherwise, enqueue the buffer for later use 686 receive_queue_.push(buffer); 687} 688 689void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer, 690 int buffer_size, 691 const SendCompletionCallback& success_callback, 692 const ErrorCompletionCallback& error_callback) { 693 DCHECK(thread_checker_.CalledOnValidThread()); 694 695 if (is_connecting()) { 696 error_callback.Run(kSocketConnecting); 697 return; 698 } 699 700 if (!channel_) { 701 error_callback.Run(kSocketNotConnected); 702 return; 703 } 704 705 // Create and enqueue request in preparation of async writes. 706 auto request = std::make_unique<SendRequest>(); 707 SendRequest* request_ptr = request.get(); 708 request->buffer_size = buffer_size; 709 request->success_callback = success_callback; 710 request->error_callback = error_callback; 711 send_queue_.push(std::move(request)); 712 713 // |writeAsync| accepts buffers of max. mtu bytes per call, so we need to emit 714 // multiple write operations if buffer_size > mtu. 715 uint16_t mtu = channel_->GetOutgoingMTU(); 716 auto send_buffer = 717 base::MakeRefCounted<net::DrainableIOBuffer>(buffer.get(), buffer_size); 718 while (send_buffer->BytesRemaining() > 0) { 719 int byte_count = send_buffer->BytesRemaining(); 720 if (byte_count > mtu) 721 byte_count = mtu; 722 IOReturn status = 723 channel_->WriteAsync(send_buffer->data(), byte_count, request_ptr); 724 725 if (status != kIOReturnSuccess) { 726 std::stringstream error; 727 error << "Failed to connect bluetooth socket (" 728 << channel_->GetDeviceAddress() << "): (" << status << ")"; 729 // Remember the first error only 730 if (request_ptr->status == kIOReturnSuccess) 731 request_ptr->status = status; 732 request_ptr->error_signaled = true; 733 request_ptr->error_callback.Run(error.str()); 734 // We may have failed to issue any write operation. In that case, there 735 // will be no corresponding completion callback for this particular 736 // request, so we must forget about it now. 737 if (request_ptr->active_async_writes == 0) { 738 send_queue_.pop(); 739 } 740 return; 741 } 742 743 request_ptr->active_async_writes++; 744 send_buffer->DidConsume(byte_count); 745 } 746} 747 748void BluetoothSocketMac::OnChannelWriteComplete(void* refcon, IOReturn status) { 749 DCHECK(thread_checker_.CalledOnValidThread()); 750 751 SendRequest* request_ptr = send_queue_.front().get(); 752 // Note: We use "CHECK" below to ensure we never run into unforeseen 753 // occurrences of asynchronous callbacks, which could lead to data 754 // corruption. 755 CHECK_EQ(static_cast<SendRequest*>(refcon), request_ptr); 756 757 // Remember the first error only. 758 if (status != kIOReturnSuccess) { 759 if (request_ptr->status == kIOReturnSuccess) 760 request_ptr->status = status; 761 } 762 763 // Figure out if we are done with this async request. 764 request_ptr->active_async_writes--; 765 if (request_ptr->active_async_writes > 0) 766 return; 767 768 // If this was the last active async write for this request, remove it from 769 // the queue and call the appropriate callback associated to the request. 770 std::unique_ptr<SendRequest> request = std::move(send_queue_.front()); 771 send_queue_.pop(); 772 if (request->status != kIOReturnSuccess) { 773 if (!request->error_signaled) { 774 std::stringstream error; 775 error << "Failed to connect bluetooth socket (" 776 << channel_->GetDeviceAddress() << "): (" << status << ")"; 777 request->error_signaled = true; 778 request->error_callback.Run(error.str()); 779 } 780 } else { 781 request->success_callback.Run(request->buffer_size); 782 } 783} 784 785void BluetoothSocketMac::OnChannelClosed() { 786 DCHECK(thread_checker_.CalledOnValidThread()); 787 788 if (receive_callbacks_) { 789 std::unique_ptr<ReceiveCallbacks> temp = std::move(receive_callbacks_); 790 temp->error_callback.Run(BluetoothSocket::kDisconnected, 791 kSocketNotConnected); 792 } 793 794 ReleaseChannel(); 795} 796 797void BluetoothSocketMac::Accept( 798 const AcceptCompletionCallback& success_callback, 799 const ErrorCompletionCallback& error_callback) { 800 DCHECK(thread_checker_.CalledOnValidThread()); 801 802 // Allow only one pending accept at a time. 803 if (accept_request_) { 804 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING)); 805 return; 806 } 807 808 accept_request_.reset(new AcceptRequest); 809 accept_request_->success_callback = success_callback; 810 accept_request_->error_callback = error_callback; 811 812 if (accept_queue_.size() >= 1) 813 AcceptConnectionRequest(); 814} 815 816void BluetoothSocketMac::AcceptConnectionRequest() { 817 DCHECK(thread_checker_.CalledOnValidThread()); 818 DVLOG(1) << uuid_.canonical_value() << ": Accepting pending connection."; 819 820 std::unique_ptr<BluetoothChannelMac> channel = 821 std::move(accept_queue_.front()); 822 accept_queue_.pop(); 823 824 adapter_->DeviceConnected(channel->GetDevice()); 825 BluetoothDevice* device = adapter_->GetDevice(channel->GetDeviceAddress()); 826 DCHECK(device); 827 828 scoped_refptr<BluetoothSocketMac> client_socket = 829 BluetoothSocketMac::CreateSocket(); 830 831 client_socket->uuid_ = uuid_; 832 client_socket->channel_ = std::move(channel); 833 834 // Associating the socket can synchronously call into OnChannelOpenComplete(). 835 // Make sure to first set the new socket to be connecting and hook it up to 836 // run the accept callback with the device object. 837 client_socket->connect_callbacks_.reset(new ConnectCallbacks()); 838 client_socket->connect_callbacks_->success_callback = 839 base::Bind(accept_request_->success_callback, device, client_socket); 840 client_socket->connect_callbacks_->error_callback = 841 accept_request_->error_callback; 842 accept_request_.reset(); 843 844 // Now it's safe to associate the socket with the channel. 845 client_socket->channel_->SetSocket(client_socket.get()); 846 847 DVLOG(1) << uuid_.canonical_value() << ": Accept complete."; 848} 849 850BluetoothSocketMac::AcceptRequest::AcceptRequest() {} 851 852BluetoothSocketMac::AcceptRequest::~AcceptRequest() {} 853 854BluetoothSocketMac::SendRequest::SendRequest() 855 : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {} 856 857BluetoothSocketMac::SendRequest::~SendRequest() {} 858 859BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {} 860 861BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {} 862 863BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {} 864 865BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {} 866 867BluetoothSocketMac::BluetoothSocketMac() {} 868 869BluetoothSocketMac::~BluetoothSocketMac() { 870 DCHECK(thread_checker_.CalledOnValidThread()); 871 DCHECK(!channel_); 872 DCHECK(!rfcomm_connection_listener_); 873} 874 875void BluetoothSocketMac::ReleaseChannel() { 876 DCHECK(thread_checker_.CalledOnValidThread()); 877 channel_.reset(); 878 879 // Closing the channel above prevents the callback delegate from being called 880 // so it is now safe to release all callback state. 881 connect_callbacks_.reset(); 882 receive_callbacks_.reset(); 883 empty_queue(receive_queue_); 884 empty_queue(send_queue_); 885} 886 887void BluetoothSocketMac::ReleaseListener() { 888 DCHECK(thread_checker_.CalledOnValidThread()); 889 DCHECK(service_record_.get()); 890 891 [service_record_ removeServiceRecord]; 892 service_record_.reset(); 893 rfcomm_connection_listener_.reset(); 894 l2cap_connection_listener_.reset(); 895 896 // Destroying the listener above prevents the callback delegate from being 897 // called so it is now safe to release all callback state. 898 accept_request_.reset(); 899 empty_queue(accept_queue_); 900} 901 902} // namespace device 903