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/objc/IOBluetoothDevice.h> 8 #import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h> 9 #import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h> 10 11 #include <limits> 12 #include <string> 13 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" 21 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 25 26 @interface IOBluetoothDevice (LionSDKDeclarations) 27 - (NSString*)addressString; 28 @end 29 30 #endif // MAC_OS_X_VERSION_10_7 31 32 @interface BluetoothRFCOMMChannelDelegate 33 : NSObject <IOBluetoothRFCOMMChannelDelegate> { 34 @private 35 device::BluetoothSocketMac* socket_; // weak 36 } 37 38 - (id)initWithSocket:(device::BluetoothSocketMac*)socket; 39 40 @end 41 42 @implementation BluetoothRFCOMMChannelDelegate 43 44 - (id)initWithSocket:(device::BluetoothSocketMac*)socket { 45 if ((self = [super init])) 46 socket_ = socket; 47 48 return self; 49 } 50 51 - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel 52 data:(void*)dataPointer 53 length:(size_t)dataLength { 54 socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength); 55 } 56 57 @end 58 59 namespace device { 60 61 BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel) 62 : rfcomm_channel_(rfcomm_channel), 63 delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) { 64 [rfcomm_channel_ setDelegate:delegate_]; 65 ResetIncomingDataBuffer(); 66 } 67 68 BluetoothSocketMac::~BluetoothSocketMac() { 69 [rfcomm_channel_ setDelegate:nil]; 70 [rfcomm_channel_ closeChannel]; 71 [rfcomm_channel_ release]; 72 [delegate_ release]; 73 } 74 75 // static 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; 84 IOReturn status = 85 [device openRFCOMMChannelAsync:&rfcomm_channel 86 withChannelID:service_record.rfcomm_channel() 87 delegate:nil]; 88 if (status == kIOReturnSuccess) { 89 bluetooth_socket = new BluetoothSocketMac(rfcomm_channel); 90 } else { 91 LOG(ERROR) << "Failed to connect bluetooth socket (" 92 << service_record.address() << "): (" << status << ")"; 93 } 94 } 95 // TODO(youngki): add support for L2CAP sockets as well. 96 97 return scoped_refptr<BluetoothSocketMac>(bluetooth_socket); 98 } 99 100 // static 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; 108 IOReturn status = 109 [device openRFCOMMChannelAsync:&rfcomm_channel 110 withChannelID:rfcomm_channel_id 111 delegate:nil]; 112 if (status == kIOReturnSuccess) { 113 bluetooth_socket = new BluetoothSocketMac(rfcomm_channel); 114 } else { 115 LOG(ERROR) << "Failed to connect bluetooth socket (" 116 << base::SysNSStringToUTF8([device addressString]) << "): (" << status 117 << ")"; 118 } 119 } 120 121 // TODO(youngki): Add support for L2CAP sockets as well. 122 123 return scoped_refptr<BluetoothSocketMac>(bluetooth_socket); 124 } 125 126 bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) { 127 CHECK(buffer->offset() == 0); 128 int length = incoming_data_buffer_->offset(); 129 if (length > 0) { 130 buffer->SetCapacity(length); 131 memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length); 132 buffer->set_offset(length); 133 134 ResetIncomingDataBuffer(); 135 } 136 return true; 137 } 138 139 bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) { 140 int bytes_written = buffer->BytesRemaining(); 141 IOReturn status = [rfcomm_channel_ writeAsync:buffer->data() 142 length:bytes_written 143 refcon:nil]; 144 if (status != kIOReturnSuccess) { 145 error_message_ = base::StringPrintf( 146 "Failed to send data. IOReturn code: %u", status); 147 return false; 148 } 149 150 buffer->DidConsume(bytes_written); 151 return true; 152 } 153 154 std::string BluetoothSocketMac::GetLastErrorMessage() const { 155 return error_message_; 156 } 157 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()); 166 CHECK_LT( 167 additional_capacity, 168 std::numeric_limits<int>::max() - incoming_data_buffer_->capacity()); 169 incoming_data_buffer_->SetCapacity( 170 incoming_data_buffer_->capacity() + additional_capacity); 171 } 172 memcpy(incoming_data_buffer_->data(), data, data_size); 173 incoming_data_buffer_->set_offset( 174 incoming_data_buffer_->offset() + data_size); 175 } 176 177 void BluetoothSocketMac::ResetIncomingDataBuffer() { 178 incoming_data_buffer_ = new net::GrowableIOBuffer(); 179 incoming_data_buffer_->SetCapacity(1024); 180 } 181 182 } // namespace device 183