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 // 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;
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
47 @interface SDPQueryListener : NSObject {
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;
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])) {
77 success_callback_ = success_callback;
78 error_callback_ = error_callback;
84 - (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
85 DCHECK_EQ(device, device_);
86 socket_->OnSDPQueryComplete(
87 status, device, success_callback_, error_callback_);
92 // A simple helper class that forwards RFCOMM channel opened notifications to
93 // its wrapped |socket_|.
94 @interface BluetoothRfcommConnectionListener : NSObject {
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;
111 @implementation BluetoothRfcommConnectionListener
113 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
114 channelID:(BluetoothRFCOMMChannelID)channelID {
115 if ((self = [super init])) {
118 SEL selector = @selector(rfcommChannelOpened:channel:);
119 const auto kIncomingDirection =
120 kIOBluetoothUserNotificationChannelDirectionIncoming;
121 rfcommNewChannelNotification_ =
122 [IOBluetoothRFCOMMChannel
123 registerForChannelOpenNotifications:self
125 withChannelID:channelID
126 direction:kIncomingDirection];
133 [rfcommNewChannelNotification_ unregister];
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.
148 socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
149 new device::BluetoothRfcommChannelMac(NULL, [rfcommChannel retain])));
154 // A simple helper class that forwards L2CAP channel opened notifications to
155 // its wrapped |socket_|.
156 @interface BluetoothL2capConnectionListener : NSObject {
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;
173 @implementation BluetoothL2capConnectionListener
175 - (id)initWithSocket:(device::BluetoothSocketMac*)socket
176 psm:(BluetoothL2CAPPSM)psm {
177 if ((self = [super init])) {
180 SEL selector = @selector(l2capChannelOpened:channel:);
181 const auto kIncomingDirection =
182 kIOBluetoothUserNotificationChannelDirectionIncoming;
183 l2capNewChannelNotification_ =
184 [IOBluetoothL2CAPChannel
185 registerForChannelOpenNotifications:self
188 direction:kIncomingDirection];
195 [l2capNewChannelNotification_ unregister];
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.
210 socket_->OnChannelOpened(scoped_ptr<device::BluetoothChannelMac>(
211 new device::BluetoothL2capChannelMac(NULL, [l2capChannel retain])));
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";
238 void empty_queue(std::queue<T>& queue) {
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];
279 // TODO(isherman): The service's language is currently hardcoded to English.
280 // The language should ideally be specified in the chrome.bluetooth API
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)];
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 =
311 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP]
314 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM],
316 @"DataElementType": @1, // Unsigned integer.
317 @"DataElementSize": @1, // 1 byte.
318 @"DataElementValue": [NSNumber numberWithInt:channel_id]
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 =
335 [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP],
337 @"DataElementType": @1, // Unsigned integer.
338 @"DataElementSize": @2, // 2 bytes.
339 @"DataElementValue": [NSNumber numberWithInt:psm]
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;
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;
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)) {
401 *registered_channel_id = rfcomm_channel_id;
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),
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)) {
437 *registered_psm = l2cap_psm;
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));
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());
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
475 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
476 << uuid_.canonical_value() << ": Sending SDP query.";
477 SDPQueryListener* listener =
478 [[SDPQueryListener alloc] initWithSocket:this
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());
497 DVLOG(1) << uuid_.canonical_value() << ": Registering RFCOMM service.";
498 BluetoothRFCOMMChannelID registered_channel_id;
499 service_record_handle_ =
500 RegisterRfcommService(uuid, options, ®istered_channel_id);
501 if (service_record_handle_ == kInvalidServiceRecordHandle) {
502 error_callback.Run(kInvalidOrUsedChannel);
506 rfcomm_connection_listener_.reset(
507 [[BluetoothRfcommConnectionListener alloc]
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());
525 DVLOG(1) << uuid_.canonical_value() << ": Registering L2CAP service.";
526 BluetoothL2CAPPSM registered_psm;
527 service_record_handle_ = RegisterL2capService(uuid, options, ®istered_psm);
528 if (service_record_handle_ == kInvalidServiceRecordHandle) {
529 error_callback.Run(kInvalidOrUsedPsm);
533 l2cap_connection_listener_.reset(
534 [[BluetoothL2capConnectionListener alloc] initWithSocket:this
535 psm:registered_psm]);
537 success_callback.Run();
540 void BluetoothSocketMac::OnSDPQueryComplete(
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);
554 IOBluetoothSDPServiceRecord* record = [device
555 getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
557 error_callback.Run(kProfileNotFound);
561 if (is_connecting()) {
562 error_callback.Run(kSocketConnecting);
567 error_callback.Run(kSocketAlreadyConnected);
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);
584 if (rfcomm_channel_id != kInvalidRfcommChannelId) {
585 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
586 << uuid_.canonical_value() << ": Opening RFCOMM channel: "
587 << rfcomm_channel_id;
589 DCHECK_NE(l2cap_psm, kInvalidL2capPsm);
590 DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " "
591 << uuid_.canonical_value() << ": Opening L2CAP channel: "
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);
606 DCHECK_NE(l2cap_psm, kInvalidL2capPsm);
608 BluetoothL2capChannelMac::OpenAsync(this, device, l2cap_psm, &status);
610 if (status != kIOReturnSuccess) {
612 std::stringstream error;
613 error << "Failed to connect bluetooth socket ("
614 << BluetoothDeviceMac::GetDeviceAddress(device) << "): (" << status
616 error_callback.Run(error.str());
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()));
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,
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) {
656 std::stringstream error;
657 error << "Failed to connect bluetooth socket (" << device_address << "): ("
659 temp->error_callback.Run(error.str());
663 temp->success_callback.Run();
666 void BluetoothSocketMac::Close() {
667 DCHECK(thread_checker_.CalledOnValidThread());
671 else if (service_record_handle_ != kInvalidServiceRecordHandle)
675 void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
676 DCHECK(thread_checker_.CalledOnValidThread());
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);
694 error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
698 // Only one pending read at a time
699 if (receive_callbacks_) {
700 error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
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);
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);
734 // Otherwise, enqueue the buffer for later use
735 receive_queue_.push(buffer);
738 void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
740 const SendCompletionCallback& success_callback,
741 const ErrorCompletionCallback& error_callback) {
742 DCHECK(thread_checker_.CalledOnValidThread());
744 if (is_connecting()) {
745 error_callback.Run(kSocketConnecting);
750 error_callback.Run(kSocketNotConnected);
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)
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) {
791 request->active_async_writes++;
792 send_buffer->DidConsume(byte_count);
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
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;
814 // Figure out if we are done with this async request
815 request->active_async_writes--;
816 if (request->active_async_writes > 0)
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.
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());
831 request->success_callback.Run(request->buffer_size);
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);
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));
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();
873 adapter_->DeviceConnected(channel->GetDevice());
874 BluetoothDevice* device = adapter_->GetDevice(channel->GetDeviceAddress());
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());
923 DCHECK(!rfcomm_connection_listener_);
926 void BluetoothSocketMac::ReleaseChannel() {
927 DCHECK(thread_checker_.CalledOnValidThread());
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