Mark //testing/perf target testonly.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_socket_mac.mm
blob73f210aba4be4f4488a2107f84913e5eb3bc635e
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/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"
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
37 @interface IOBluetoothDevice (LionSDKDeclarations)
38 - (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids;
39 @end
41 #endif  // MAC_OS_X_VERSION_10_7
43 using device::BluetoothSocket;
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_;
52   // Callbacks associated with the request that triggered this SDP query.
53   base::Closure success_callback_;
54   BluetoothSocket::ErrorCompletionCallback error_callback_;
56   // The device being queried.
57   IOBluetoothDevice* device_;  // weak
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;
66 @end
68 @implementation SDPQueryListener
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   }
81   return self;
84 - (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
85   DCHECK_EQ(device, device_);
86   socket_->OnSDPQueryComplete(
87       status, device, success_callback_, error_callback_);
90 @end
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
99   // The OS mechanism used to subscribe to and unsubscribe from RFCOMM channel
100   // creation notifications.
101   IOBluetoothUserNotification* rfcommNewChannelNotification_;  // weak
104 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
105            channelID:(BluetoothRFCOMMChannelID)channelID;
106 - (void)rfcommChannelOpened:(IOBluetoothUserNotification*)notification
107                     channel:(IOBluetoothRFCOMMChannel*)rfcommChannel;
109 @end
111 @implementation BluetoothRfcommConnectionListener
113 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
114            channelID:(BluetoothRFCOMMChannelID)channelID {
115   if ((self = [super init])) {
116     socket_ = socket;
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   }
129   return self;
132 - (void)dealloc {
133   [rfcommNewChannelNotification_ unregister];
134   [super dealloc];
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   }
148   socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
149       new device::BluetoothRfcommChannelMac(NULL, [rfcommChannel retain])));
152 @end
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
161   // The OS mechanism used to subscribe to and unsubscribe from L2CAP channel
162   // creation notifications.
163   IOBluetoothUserNotification* l2capNewChannelNotification_;  // weak
166 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
167                  psm:(BluetoothL2CAPPSM)psm;
168 - (void)l2capChannelOpened:(IOBluetoothUserNotification*)notification
169                    channel:(IOBluetoothL2CAPChannel*)l2capChannel;
171 @end
173 @implementation BluetoothL2capConnectionListener
175 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
176                  psm:(BluetoothL2CAPPSM)psm {
177   if ((self = [super init])) {
178     socket_ = socket;
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   }
191   return self;
194 - (void)dealloc {
195   [l2capNewChannelNotification_ unregister];
196   [super dealloc];
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   }
210   socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
211       new device::BluetoothL2capChannelMac(NULL, [l2capChannel retain])));
214 @end
216 namespace device {
217 namespace {
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;
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;
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";
237 template <class T>
238 void empty_queue(std::queue<T>& queue) {
239   std::queue<T> empty;
240   std::swap(queue, empty);
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);
261   return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector.front()
262                                     length:uuid_bytes_vector.size()];
265 // Converts the given |integer| to a string.
266 NSString* IntToNSString(int integer) {
267   return [[NSNumber numberWithInt:integer] stringValue];
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];
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   }
290   const int kUUIDsKey = kBluetoothSDPAttributeIdentifierServiceClassIDList;
291   NSArray* uuids = @[GetIOBluetoothSDPUUID(uuid)];
292   [service_definition setObject:uuids forKey:IntToNSString(kUUIDsKey)];
294   const int kProtocolDefinitionsKey =
295       kBluetoothSDPAttributeIdentifierProtocolDescriptorList;
296   [service_definition setObject:protocol_definition
297                          forKey:IntToNSString(kProtocolDefinitionsKey)];
299   return service_definition;
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);
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);
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);
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;
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   }
382   return service_record_handle;
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   }
401   *registered_channel_id = rfcomm_channel_id;
402   return true;
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));
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   }
437   *registered_psm = l2cap_psm;
438   return true;
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));
456 }  // namespace
458 // static
459 scoped_refptr<BluetoothSocketMac> BluetoothSocketMac::CreateSocket() {
460   return make_scoped_refptr(new BluetoothSocketMac());
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());
470   uuid_ = uuid;
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_)]];
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());
494   adapter_ = adapter;
495   uuid_ = uuid;
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   }
506   rfcomm_connection_listener_.reset(
507       [[BluetoothRfcommConnectionListener alloc]
508           initWithSocket:this
509                channelID:registered_channel_id]);
511   success_callback.Run();
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());
522   adapter_ = adapter;
523   uuid_ = uuid;
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   }
533   l2cap_connection_listener_.reset(
534       [[BluetoothL2capConnectionListener alloc] initWithSocket:this
535                                                            psm:registered_psm]);
537   success_callback.Run();
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.";
549   if (status != kIOReturnSuccess) {
550     error_callback.Run(kSDPQueryFailed);
551     return;
552   }
554   IOBluetoothSDPServiceRecord* record = [device
555       getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
556   if (record == nil) {
557     error_callback.Run(kProfileNotFound);
558     return;
559   }
561   if (is_connecting()) {
562     error_callback.Run(kSocketConnecting);
563     return;
564   }
566   if (channel_) {
567     error_callback.Run(kSocketAlreadyConnected);
568     return;
569   }
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   }
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   }
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;
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   }
620   DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
621            << uuid_.canonical_value()
622            << ": channel opening in background.";
625 void BluetoothSocketMac::OnChannelOpened(
626     scoped_ptr<BluetoothChannelMac> channel) {
627   DCHECK(thread_checker_.CalledOnValidThread());
628   DVLOG(1) << uuid_.canonical_value() << ": Incoming channel pending.";
630   accept_queue_.push(linked_ptr<BluetoothChannelMac>(channel.release()));
631   if (accept_request_)
632     AcceptConnectionRequest();
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
644 void BluetoothSocketMac::OnChannelOpenComplete(
645     const std::string& device_address,
646     IOReturn status) {
647   DCHECK(thread_checker_.CalledOnValidThread());
648   DCHECK(is_connecting());
650   DVLOG(1) << device_address << " " << uuid_.canonical_value()
651            << ": channel open complete.";
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   }
663   temp->success_callback.Run();
666 void BluetoothSocketMac::Close() {
667   DCHECK(thread_checker_.CalledOnValidThread());
669   if (channel_)
670     ReleaseChannel();
671   else if (service_record_handle_ != kInvalidServiceRecordHandle)
672     ReleaseListener();
675 void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
676   DCHECK(thread_checker_.CalledOnValidThread());
678   Close();
679   callback.Run();
682 void BluetoothSocketMac::Receive(
683     int /* buffer_size */,
684     const ReceiveCompletionCallback& success_callback,
685     const ReceiveErrorCompletionCallback& error_callback) {
686   DCHECK(thread_checker_.CalledOnValidThread());
688   if (is_connecting()) {
689     error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
690     return;
691   }
693   if (!channel_) {
694     error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
695     return;
696   }
698   // Only one pending read at a time
699   if (receive_callbacks_) {
700     error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
701     return;
702   }
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   }
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;
718 void BluetoothSocketMac::OnChannelDataReceived(void* data, size_t length) {
719   DCHECK(thread_checker_.CalledOnValidThread());
720   DCHECK(!is_connecting());
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());
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   }
734   // Otherwise, enqueue the buffer for later use
735   receive_queue_.push(buffer);
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());
744   if (is_connecting()) {
745     error_callback.Run(kSocketConnecting);
746     return;
747   }
749   if (!channel_) {
750     error_callback.Run(kSocketNotConnected);
751     return;
752   }
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);
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());
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     }
791     request->active_async_writes++;
792     send_buffer->DidConsume(byte_count);
793   }
796 void BluetoothSocketMac::OnChannelWriteComplete(void* refcon, IOReturn status) {
797   DCHECK(thread_checker_.CalledOnValidThread());
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());
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();
808   // Remember the first error only
809   if (status != kIOReturnSuccess) {
810     if (request->status == kIOReturnSuccess)
811       request->status = status;
812   }
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;
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   }
835 void BluetoothSocketMac::OnChannelClosed() {
836   DCHECK(thread_checker_.CalledOnValidThread());
838   if (receive_callbacks_) {
839     scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
840     temp->error_callback.Run(BluetoothSocket::kDisconnected,
841                              kSocketNotConnected);
842   }
844   ReleaseChannel();
847 void BluetoothSocketMac::Accept(
848     const AcceptCompletionCallback& success_callback,
849     const ErrorCompletionCallback& error_callback) {
850   DCHECK(thread_checker_.CalledOnValidThread());
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   }
858   accept_request_.reset(new AcceptRequest);
859   accept_request_->success_callback = success_callback;
860   accept_request_->error_callback = error_callback;
862   if (accept_queue_.size() >= 1)
863     AcceptConnectionRequest();
866 void BluetoothSocketMac::AcceptConnectionRequest() {
867   DCHECK(thread_checker_.CalledOnValidThread());
868   DVLOG(1) << uuid_.canonical_value() << ": Accepting pending connection.";
870   linked_ptr<BluetoothChannelMac> channel = accept_queue_.front();
871   accept_queue_.pop();
873   adapter_->DeviceConnected(channel->GetDevice());
874   BluetoothDevice* device = adapter_->GetDevice(channel->GetDeviceAddress());
875   DCHECK(device);
877   scoped_refptr<BluetoothSocketMac> client_socket =
878       BluetoothSocketMac::CreateSocket();
880   client_socket->uuid_ = uuid_;
881   client_socket->channel_.reset(channel.release());
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();
893   // Now it's safe to associate the socket with the channel.
894   client_socket->channel_->SetSocket(client_socket.get());
896   DVLOG(1) << uuid_.canonical_value() << ": Accept complete.";
899 BluetoothSocketMac::AcceptRequest::AcceptRequest() {}
901 BluetoothSocketMac::AcceptRequest::~AcceptRequest() {}
903 BluetoothSocketMac::SendRequest::SendRequest()
904     : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
906 BluetoothSocketMac::SendRequest::~SendRequest() {}
908 BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {}
910 BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {}
912 BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {}
914 BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {}
916 BluetoothSocketMac::BluetoothSocketMac()
917     : service_record_handle_(kInvalidServiceRecordHandle) {
920 BluetoothSocketMac::~BluetoothSocketMac() {
921   DCHECK(thread_checker_.CalledOnValidThread());
922   DCHECK(!channel_);
923   DCHECK(!rfcomm_connection_listener_);
926 void BluetoothSocketMac::ReleaseChannel() {
927   DCHECK(thread_checker_.CalledOnValidThread());
928   channel_.reset();
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_);
938 void BluetoothSocketMac::ReleaseListener() {
939   DCHECK(thread_checker_.CalledOnValidThread());
940   DCHECK_NE(service_record_handle_, kInvalidServiceRecordHandle);
942   IOBluetoothRemoveServiceWithRecordHandle(service_record_handle_);
943   rfcomm_connection_listener_.reset();
944   l2cap_connection_listener_.reset();
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_);
952 }  // namespace device