• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
59namespace device {
60
61BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel)
62    : rfcomm_channel_(rfcomm_channel),
63      delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
64  [rfcomm_channel_ setDelegate:delegate_];
65  ResetIncomingDataBuffer();
66}
67
68BluetoothSocketMac::~BluetoothSocketMac() {
69  [rfcomm_channel_ setDelegate:nil];
70  [rfcomm_channel_ closeChannel];
71  [rfcomm_channel_ release];
72  [delegate_ release];
73}
74
75// static
76scoped_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
101scoped_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
126bool 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
139bool 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
154std::string BluetoothSocketMac::GetLastErrorMessage() const {
155  return error_message_;
156}
157
158void 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
177void BluetoothSocketMac::ResetIncomingDataBuffer() {
178  incoming_data_buffer_ = new net::GrowableIOBuffer();
179  incoming_data_buffer_->SetCapacity(1024);
180}
181
182}  // namespace device
183