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/objc/IOBluetoothDevice.h>
8 #import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
9 #import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
14 #include "base/basictypes.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "device/bluetooth/bluetooth_service_record.h"
19 #include "device/bluetooth/bluetooth_service_record_mac.h"
20 #include "net/base/io_buffer.h"
22 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
23 #if !defined(MAC_OS_X_VERSION_10_7) || \
24 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
26 @interface IOBluetoothDevice (LionSDKDeclarations)
27 - (NSString*)addressString;
30 #endif // MAC_OS_X_VERSION_10_7
32 @interface BluetoothRFCOMMChannelDelegate
33 : NSObject <IOBluetoothRFCOMMChannelDelegate> {
35 device::BluetoothSocketMac* socket_; // weak
38 - (id)initWithSocket:(device::BluetoothSocketMac*)socket;
42 @implementation BluetoothRFCOMMChannelDelegate
44 - (id)initWithSocket:(device::BluetoothSocketMac*)socket {
45 if ((self = [super init]))
51 - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
52 data:(void*)dataPointer
53 length:(size_t)dataLength {
54 socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength);
61 BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel)
62 : rfcomm_channel_(rfcomm_channel),
63 delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
64 [rfcomm_channel_ setDelegate:delegate_];
65 ResetIncomingDataBuffer();
68 BluetoothSocketMac::~BluetoothSocketMac() {
69 [rfcomm_channel_ setDelegate:nil];
70 [rfcomm_channel_ closeChannel];
71 [rfcomm_channel_ release];
76 scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
77 const BluetoothServiceRecord& service_record) {
78 BluetoothSocketMac* bluetooth_socket = NULL;
79 if (service_record.SupportsRfcomm()) {
80 const BluetoothServiceRecordMac* service_record_mac =
81 static_cast<const BluetoothServiceRecordMac*>(&service_record);
82 IOBluetoothDevice* device = service_record_mac->GetIOBluetoothDevice();
83 IOBluetoothRFCOMMChannel* rfcomm_channel;
85 [device openRFCOMMChannelAsync:&rfcomm_channel
86 withChannelID:service_record.rfcomm_channel()
88 if (status == kIOReturnSuccess) {
89 bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
91 LOG(ERROR) << "Failed to connect bluetooth socket ("
92 << service_record.address() << "): (" << status << ")";
95 // TODO(youngki): add support for L2CAP sockets as well.
97 return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
101 scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
102 IOBluetoothSDPServiceRecord* record) {
103 BluetoothSocketMac* bluetooth_socket = NULL;
104 uint8 rfcomm_channel_id;
105 if ([record getRFCOMMChannelID:&rfcomm_channel_id] == kIOReturnSuccess) {
106 IOBluetoothDevice* device = [record device];
107 IOBluetoothRFCOMMChannel* rfcomm_channel;
109 [device openRFCOMMChannelAsync:&rfcomm_channel
110 withChannelID:rfcomm_channel_id
112 if (status == kIOReturnSuccess) {
113 bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
115 LOG(ERROR) << "Failed to connect bluetooth socket ("
116 << base::SysNSStringToUTF8([device addressString]) << "): (" << status
121 // TODO(youngki): Add support for L2CAP sockets as well.
123 return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
126 bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) {
127 CHECK(buffer->offset() == 0);
128 int length = incoming_data_buffer_->offset();
130 buffer->SetCapacity(length);
131 memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length);
132 buffer->set_offset(length);
134 ResetIncomingDataBuffer();
139 bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) {
140 int bytes_written = buffer->BytesRemaining();
141 IOReturn status = [rfcomm_channel_ writeAsync:buffer->data()
144 if (status != kIOReturnSuccess) {
145 error_message_ = base::StringPrintf(
146 "Failed to send data. IOReturn code: %u", status);
150 buffer->DidConsume(bytes_written);
154 std::string BluetoothSocketMac::GetLastErrorMessage() const {
155 return error_message_;
158 void BluetoothSocketMac::OnDataReceived(
159 IOBluetoothRFCOMMChannel* rfcomm_channel, void* data, size_t length) {
160 DCHECK(rfcomm_channel_ == rfcomm_channel);
161 CHECK_LT(length, static_cast<size_t>(std::numeric_limits<int>::max()));
162 int data_size = static_cast<int>(length);
163 if (incoming_data_buffer_->RemainingCapacity() < data_size) {
164 int additional_capacity =
165 std::max(data_size, incoming_data_buffer_->capacity());
168 std::numeric_limits<int>::max() - incoming_data_buffer_->capacity());
169 incoming_data_buffer_->SetCapacity(
170 incoming_data_buffer_->capacity() + additional_capacity);
172 memcpy(incoming_data_buffer_->data(), data, data_size);
173 incoming_data_buffer_->set_offset(
174 incoming_data_buffer_->offset() + data_size);
177 void BluetoothSocketMac::ResetIncomingDataBuffer() {
178 incoming_data_buffer_ = new net::GrowableIOBuffer();
179 incoming_data_buffer_->SetCapacity(1024);
182 } // namespace device