Home | History | Annotate | Download | only in bluetooth
      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