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