- add sources.
[platform/framework/web/crosswalk.git] / src / device / bluetooth / bluetooth_socket_mac.mm
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