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>
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 #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;
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
46 @interface SDPQueryListener : NSObject {
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;
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])) {
76 success_callback_ = success_callback;
77 error_callback_ = error_callback;
83 - (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
84 DCHECK_EQ(device, device_);
85 socket_->OnSDPQueryComplete(
86 status, device, success_callback_, error_callback_);
91 // A simple helper class that forwards RFCOMM channel opened notifications to
92 // its wrapped |socket_|.
93 @interface BluetoothRfcommConnectionListener : NSObject {
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;
110 @implementation BluetoothRfcommConnectionListener
112 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
113 channelID:(BluetoothRFCOMMChannelID)channelID {
114 if ((self = [super init])) {
117 SEL selector = @selector(rfcommChannelOpened:channel:);
118 const auto kIncomingDirection =
119 kIOBluetoothUserNotificationChannelDirectionIncoming;
120 rfcommNewChannelNotification_ =
121 [IOBluetoothRFCOMMChannel
122 registerForChannelOpenNotifications:self
124 withChannelID:channelID
125 direction:kIncomingDirection];
132 [rfcommNewChannelNotification_ unregister];
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.
147 socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
148 new device::BluetoothRfcommChannelMac(NULL, [rfcommChannel retain])));
153 // A simple helper class that forwards L2CAP channel opened notifications to
154 // its wrapped |socket_|.
155 @interface BluetoothL2capConnectionListener : NSObject {
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;
172 @implementation BluetoothL2capConnectionListener
174 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
175 psm:(BluetoothL2CAPPSM)psm {
176 if ((self = [super init])) {
179 SEL selector = @selector(l2capChannelOpened:channel:);
180 const auto kIncomingDirection =
181 kIOBluetoothUserNotificationChannelDirectionIncoming;
182 l2capNewChannelNotification_ =
183 [IOBluetoothL2CAPChannel
184 registerForChannelOpenNotifications:self
187 direction:kIncomingDirection];
194 [l2capNewChannelNotification_ unregister];
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.
209 socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
210 new device::BluetoothL2capChannelMac(NULL, [l2capChannel retain])));
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";
237 void empty_queue(std::queue<T>& queue) {
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];
278 // TODO(isherman): The service's language is currently hardcoded to English.
279 // The language should ideally be specified in the chrome.bluetooth API
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)];
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 =
310 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP]
313 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM],
315 @"DataElementType": @1, // Unsigned integer.
316 @"DataElementSize": @1, // 1 byte.
317 @"DataElementValue": [NSNumber numberWithInt:channel_id]
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 =
334 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP],
336 @"DataElementType": @1, // Unsigned integer.
337 @"DataElementSize": @2, // 2 bytes.
338 @"DataElementValue": [NSNumber numberWithInt:psm]
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;
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;
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)) {
400 *registered_channel_id = rfcomm_channel_id;
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),
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)) {
436 *registered_psm = l2cap_psm;
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));
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());
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
474 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
475 << uuid_.canonical_value() << ": Sending SDP query.";
476 SDPQueryListener* listener =
477 [[SDPQueryListener alloc] initWithSocket:this
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());
496 DVLOG(1) << uuid_.canonical_value() << ": Registering RFCOMM service.";
497 BluetoothRFCOMMChannelID registered_channel_id;
498 service_record_handle_ =
499 RegisterRfcommService(uuid, options, ®istered_channel_id);
500 if (service_record_handle_ == kInvalidServiceRecordHandle) {
501 error_callback.Run(kInvalidOrUsedChannel);
505 rfcomm_connection_listener_.reset(
506 [[BluetoothRfcommConnectionListener alloc]
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());
524 DVLOG(1) << uuid_.canonical_value() << ": Registering L2CAP service.";
525 BluetoothL2CAPPSM registered_psm;
526 service_record_handle_ = RegisterL2capService(uuid, options, ®istered_psm);
527 if (service_record_handle_ == kInvalidServiceRecordHandle) {
528 error_callback.Run(kInvalidOrUsedPsm);
532 l2cap_connection_listener_.reset(
533 [[BluetoothL2capConnectionListener alloc] initWithSocket:this
534 psm:registered_psm]);
536 success_callback.Run();
539 void BluetoothSocketMac::OnSDPQueryComplete(
541 IOBluetoothDevice* device,
542 const base::Closure& success_callback,
543 const ErrorCompletionCallback& error_callback) {
544 DCHECK(thread_checker_.CalledOnValidThread());
545 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
546 << uuid_.canonical_value() << ": SDP query complete.";
548 if (status != kIOReturnSuccess) {
549 error_callback.Run(kSDPQueryFailed);
553 IOBluetoothSDPServiceRecord* record = [device
554 getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
556 error_callback.Run(kProfileNotFound);
560 if (is_connecting()) {
561 error_callback.Run(kSocketConnecting);
566 error_callback.Run(kSocketAlreadyConnected);
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);
583 if (rfcomm_channel_id != kInvalidRfcommChannelId) {
584 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
585 << uuid_.canonical_value() << ": Opening RFCOMM channel: "
586 << rfcomm_channel_id;
588 DCHECK_NE(l2cap_psm, kInvalidL2capPsm);
589 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
590 << uuid_.canonical_value() << ": Opening L2CAP channel: "
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);
605 DCHECK_NE(l2cap_psm, kInvalidL2capPsm);
607 BluetoothL2capChannelMac::OpenAsync(this, device, l2cap_psm, &status);
609 if (status != kIOReturnSuccess) {
611 std::stringstream error;
612 error << "Failed to connect bluetooth socket ("
613 << BluetoothDeviceMac::GetDeviceAddress(device) << "): (" << status
615 error_callback.Run(error.str());
619 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
620 << uuid_.canonical_value()
621 << ": channel opening in background.";
624 void BluetoothSocketMac::OnChannelOpened(
625 scoped_ptr<BluetoothChannelMac> channel) {
626 DCHECK(thread_checker_.CalledOnValidThread());
627 DVLOG(1) << uuid_.canonical_value() << ": Incoming channel pending.";
629 accept_queue_.push(linked_ptr<BluetoothChannelMac>(channel.release()));
631 AcceptConnectionRequest();
633 // TODO(isherman): Currently, the socket remains alive even after the app that
634 // requested it is closed. That's not great, as a misbehaving app could
635 // saturate all of the system's RFCOMM channels, and then they would not be
636 // freed until the user restarts Chrome. http://crbug.com/367316
637 // TODO(isherman): Likewise, the socket currently remains alive even if the
638 // underlying channel is closed, e.g. via the client disconnecting, or the
639 // user closing the Bluetooth connection via the system menu. This functions
640 // essentially as a minor memory leak. http://crbug.com/367319
643 void BluetoothSocketMac::OnChannelOpenComplete(
644 const std::string& device_address,
646 DCHECK(thread_checker_.CalledOnValidThread());
647 DCHECK(is_connecting());
649 DVLOG(1) << device_address << " " << uuid_.canonical_value()
650 << ": channel open complete.";
652 scoped_ptr<ConnectCallbacks> temp = connect_callbacks_.Pass();
653 if (status != kIOReturnSuccess) {
655 std::stringstream error;
656 error << "Failed to connect bluetooth socket (" << device_address << "): ("
658 temp->error_callback.Run(error.str());
662 temp->success_callback.Run();
665 void BluetoothSocketMac::Close() {
666 DCHECK(thread_checker_.CalledOnValidThread());
670 else if (service_record_handle_ != kInvalidServiceRecordHandle)
674 void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
675 DCHECK(thread_checker_.CalledOnValidThread());
681 void BluetoothSocketMac::Receive(
682 int /* buffer_size */,
683 const ReceiveCompletionCallback& success_callback,
684 const ReceiveErrorCompletionCallback& error_callback) {
685 DCHECK(thread_checker_.CalledOnValidThread());
687 if (is_connecting()) {
688 error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
693 error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
697 // Only one pending read at a time
698 if (receive_callbacks_) {
699 error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
703 // If there is at least one packet, consume it and succeed right away.
704 if (!receive_queue_.empty()) {
705 scoped_refptr<net::IOBufferWithSize> buffer = receive_queue_.front();
706 receive_queue_.pop();
707 success_callback.Run(buffer->size(), buffer);
711 // Set the receive callback to use when data is received.
712 receive_callbacks_.reset(new ReceiveCallbacks());
713 receive_callbacks_->success_callback = success_callback;
714 receive_callbacks_->error_callback = error_callback;
717 void BluetoothSocketMac::OnChannelDataReceived(void* data, size_t length) {
718 DCHECK(thread_checker_.CalledOnValidThread());
719 DCHECK(!is_connecting());
721 int data_size = base::checked_cast<int>(length);
722 scoped_refptr<net::IOBufferWithSize> buffer(
723 new net::IOBufferWithSize(data_size));
724 memcpy(buffer->data(), data, buffer->size());
726 // If there is a pending read callback, call it now.
727 if (receive_callbacks_) {
728 scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
729 temp->success_callback.Run(buffer->size(), buffer);
733 // Otherwise, enqueue the buffer for later use
734 receive_queue_.push(buffer);
737 void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
739 const SendCompletionCallback& success_callback,
740 const ErrorCompletionCallback& error_callback) {
741 DCHECK(thread_checker_.CalledOnValidThread());
743 if (is_connecting()) {
744 error_callback.Run(kSocketConnecting);
749 error_callback.Run(kSocketNotConnected);
753 // Create and enqueue request in preparation of async writes.
754 linked_ptr<SendRequest> request(new SendRequest());
755 request->buffer_size = buffer_size;
756 request->success_callback = success_callback;
757 request->error_callback = error_callback;
758 send_queue_.push(request);
760 // |writeAsync| accepts buffers of max. mtu bytes per call, so we need to emit
761 // multiple write operations if buffer_size > mtu.
762 uint16_t mtu = channel_->GetOutgoingMTU();
763 scoped_refptr<net::DrainableIOBuffer> send_buffer(
764 new net::DrainableIOBuffer(buffer.get(), buffer_size));
765 while (send_buffer->BytesRemaining() > 0) {
766 int byte_count = send_buffer->BytesRemaining();
767 if (byte_count > mtu)
770 channel_->WriteAsync(send_buffer->data(), byte_count, request.get());
772 if (status != kIOReturnSuccess) {
773 std::stringstream error;
774 error << "Failed to connect bluetooth socket ("
775 << channel_->GetDeviceAddress() << "): (" << status << ")";
776 // Remember the first error only
777 if (request->status == kIOReturnSuccess)
778 request->status = status;
779 request->error_signaled = true;
780 request->error_callback.Run(error.str());
781 // We may have failed to issue any write operation. In that case, there
782 // will be no corresponding completion callback for this particular
783 // request, so we must forget about it now.
784 if (request->active_async_writes == 0) {
790 request->active_async_writes++;
791 send_buffer->DidConsume(byte_count);
795 void BluetoothSocketMac::OnChannelWriteComplete(void* refcon, IOReturn status) {
796 DCHECK(thread_checker_.CalledOnValidThread());
798 // Note: We use "CHECK" below to ensure we never run into unforeseen
799 // occurrences of asynchronous callbacks, which could lead to data
801 CHECK_EQ(static_cast<SendRequest*>(refcon), send_queue_.front().get());
803 // Keep a local linked_ptr to avoid releasing the request too early if we end
804 // up removing it from the queue.
805 linked_ptr<SendRequest> request = send_queue_.front();
807 // Remember the first error only
808 if (status != kIOReturnSuccess) {
809 if (request->status == kIOReturnSuccess)
810 request->status = status;
813 // Figure out if we are done with this async request
814 request->active_async_writes--;
815 if (request->active_async_writes > 0)
818 // If this was the last active async write for this request, remove it from
819 // the queue and call the appropriate callback associated to the request.
821 if (request->status != kIOReturnSuccess) {
822 if (!request->error_signaled) {
823 std::stringstream error;
824 error << "Failed to connect bluetooth socket ("
825 << channel_->GetDeviceAddress() << "): (" << status << ")";
826 request->error_signaled = true;
827 request->error_callback.Run(error.str());
830 request->success_callback.Run(request->buffer_size);
834 void BluetoothSocketMac::OnChannelClosed() {
835 DCHECK(thread_checker_.CalledOnValidThread());
837 if (receive_callbacks_) {
838 scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
839 temp->error_callback.Run(BluetoothSocket::kDisconnected,
840 kSocketNotConnected);
846 void BluetoothSocketMac::Accept(
847 const AcceptCompletionCallback& success_callback,
848 const ErrorCompletionCallback& error_callback) {
849 DCHECK(thread_checker_.CalledOnValidThread());
851 // Allow only one pending accept at a time.
852 if (accept_request_) {
853 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
857 accept_request_.reset(new AcceptRequest);
858 accept_request_->success_callback = success_callback;
859 accept_request_->error_callback = error_callback;
861 if (accept_queue_.size() >= 1)
862 AcceptConnectionRequest();
865 void BluetoothSocketMac::AcceptConnectionRequest() {
866 DCHECK(thread_checker_.CalledOnValidThread());
867 DVLOG(1) << uuid_.canonical_value() << ": Accepting pending connection.";
869 linked_ptr<BluetoothChannelMac> channel = accept_queue_.front();
872 adapter_->DeviceConnected(channel->GetDevice());
873 BluetoothDevice* device = adapter_->GetDevice(channel->GetDeviceAddress());
876 scoped_refptr<BluetoothSocketMac> client_socket =
877 BluetoothSocketMac::CreateSocket();
879 client_socket->uuid_ = uuid_;
880 client_socket->channel_.reset(channel.release());
882 // Associating the socket can synchronously call into OnChannelOpenComplete().
883 // Make sure to first set the new socket to be connecting and hook it up to
884 // run the accept callback with the device object.
885 client_socket->connect_callbacks_.reset(new ConnectCallbacks());
886 client_socket->connect_callbacks_->success_callback =
887 base::Bind(accept_request_->success_callback, device, client_socket);
888 client_socket->connect_callbacks_->error_callback =
889 accept_request_->error_callback;
890 accept_request_.reset();
892 // Now it's safe to associate the socket with the channel.
893 client_socket->channel_->SetSocket(client_socket.get());
895 DVLOG(1) << uuid_.canonical_value() << ": Accept complete.";
898 BluetoothSocketMac::AcceptRequest::AcceptRequest() {}
900 BluetoothSocketMac::AcceptRequest::~AcceptRequest() {}
902 BluetoothSocketMac::SendRequest::SendRequest()
903 : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
905 BluetoothSocketMac::SendRequest::~SendRequest() {}
907 BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {}
909 BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {}
911 BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {}
913 BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {}
915 BluetoothSocketMac::BluetoothSocketMac()
916 : service_record_handle_(kInvalidServiceRecordHandle) {
919 BluetoothSocketMac::~BluetoothSocketMac() {
920 DCHECK(thread_checker_.CalledOnValidThread());
922 DCHECK(!rfcomm_connection_listener_);
925 void BluetoothSocketMac::ReleaseChannel() {
926 DCHECK(thread_checker_.CalledOnValidThread());
929 // Closing the channel above prevents the callback delegate from being called
930 // so it is now safe to release all callback state.
931 connect_callbacks_.reset();
932 receive_callbacks_.reset();
933 empty_queue(receive_queue_);
934 empty_queue(send_queue_);
937 void BluetoothSocketMac::ReleaseListener() {
938 DCHECK(thread_checker_.CalledOnValidThread());
939 DCHECK_NE(service_record_handle_, kInvalidServiceRecordHandle);
941 IOBluetoothRemoveServiceWithRecordHandle(service_record_handle_);
942 rfcomm_connection_listener_.reset();
943 l2cap_connection_listener_.reset();
945 // Destroying the listener above prevents the callback delegate from being
946 // called so it is now safe to release all callback state.
947 accept_request_.reset();
948 empty_queue(accept_queue_);
951 } // namespace device