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