Home | History | Annotate | Download | only in bluetooth
      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, &registered_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, &registered_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