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.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, &registered_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, &registered_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