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