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