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/IOBluetooth.h>
13 #include "base/basictypes.h"
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "device/bluetooth/bluetooth_device_mac.h"
22 #include "device/bluetooth/bluetooth_service_record.h"
23 #include "device/bluetooth/bluetooth_service_record_mac.h"
24 #include "net/base/io_buffer.h"
26 @interface BluetoothRFCOMMChannelDelegate
27 : NSObject <IOBluetoothRFCOMMChannelDelegate> {
29 device::BluetoothSocketMac* socket_; // weak
32 - (id)initWithSocket:(device::BluetoothSocketMac*)socket;
36 @implementation BluetoothRFCOMMChannelDelegate
38 - (id)initWithSocket:(device::BluetoothSocketMac*)socket {
39 if ((self = [super init]))
45 - (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
46 status:(IOReturn)error {
47 socket_->OnChannelOpened(rfcommChannel, error);
50 - (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
52 status:(IOReturn)error {
53 socket_->OnChannelWriteComplete(rfcommChannel, refcon, error);
56 - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
57 data:(void*)dataPointer
58 length:(size_t)dataLength {
59 socket_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
62 - (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
63 socket_->OnChannelClosed(rfcommChannel);
70 const char kL2CAPNotSupported[] = "Bluetooth L2CAP protocol is not supported";
71 const char kSocketConnecting[] = "The socket is currently connecting";
72 const char kSocketAlreadyConnected[] = "The socket is already connected";
73 const char kSocketNotConnected[] = "The socket is not connected";
74 const char kReceivePending[] = "A Receive operation is pending";
77 void empty_queue(std::queue<T>& queue) {
79 std::swap(queue, empty);
86 BluetoothSocketMac::SendRequest::SendRequest()
87 : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
89 BluetoothSocketMac::SendRequest::~SendRequest() {}
91 BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {}
93 BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {}
95 BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {}
97 BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {}
100 void BluetoothSocketMac::Connect(
101 IOBluetoothSDPServiceRecord* record,
102 const ConnectSuccessCallback& success_callback,
103 const ErrorCompletionCallback& error_callback) {
104 scoped_refptr<BluetoothSocketMac> socket(new BluetoothSocketMac());
105 socket->ConnectImpl(record, success_callback, error_callback);
109 void BluetoothSocketMac::AcceptConnection(
110 IOBluetoothRFCOMMChannel* rfcomm_channel,
111 const ConnectSuccessCallback& success_callback,
112 const ErrorCompletionCallback& error_callback) {
113 scoped_refptr<BluetoothSocketMac> socket(new BluetoothSocketMac());
114 socket->AcceptConnectionImpl(
115 rfcomm_channel, success_callback, error_callback);
118 BluetoothSocketMac::BluetoothSocketMac()
119 : delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
122 BluetoothSocketMac::~BluetoothSocketMac() {
123 DCHECK(thread_checker_.CalledOnValidThread());
127 void BluetoothSocketMac::ReleaseChannel() {
128 DCHECK(thread_checker_.CalledOnValidThread());
129 if (rfcomm_channel_) {
130 [rfcomm_channel_ setDelegate:nil];
131 [rfcomm_channel_ closeChannel];
132 rfcomm_channel_.reset();
135 // Closing the channel above prevents the callback delegate from being called
136 // so it is now safe to release all callback state.
137 connect_callbacks_.reset();
138 receive_callbacks_.reset();
139 empty_queue(receive_queue_);
140 empty_queue(send_queue_);
143 void BluetoothSocketMac::ConnectImpl(
144 IOBluetoothSDPServiceRecord* record,
145 const ConnectSuccessCallback& success_callback,
146 const ErrorCompletionCallback& error_callback) {
147 DCHECK(thread_checker_.CalledOnValidThread());
150 error_callback.Run(kSocketConnecting);
154 if (rfcomm_channel_) {
155 error_callback.Run(kSocketAlreadyConnected);
159 uint8 rfcomm_channel_id;
160 IOReturn status = [record getRFCOMMChannelID:&rfcomm_channel_id];
161 if (status != kIOReturnSuccess) {
162 // TODO(youngki) add support for L2CAP sockets as well.
163 error_callback.Run(kL2CAPNotSupported);
167 IOBluetoothDevice* device = [record device];
168 IOBluetoothRFCOMMChannel* rfcomm_channel;
169 status = [device openRFCOMMChannelAsync:&rfcomm_channel
170 withChannelID:rfcomm_channel_id
172 if (status != kIOReturnSuccess) {
173 std::stringstream error;
174 error << "Failed to connect bluetooth socket ("
175 << BluetoothDeviceMac::GetDeviceAddress(device) << "): (" << status
177 error_callback.Run(error.str());
181 AcceptConnectionImpl(rfcomm_channel, success_callback, error_callback);
184 void BluetoothSocketMac::AcceptConnectionImpl(
185 IOBluetoothRFCOMMChannel* rfcomm_channel,
186 const ConnectSuccessCallback& success_callback,
187 const ErrorCompletionCallback& error_callback) {
188 connect_callbacks_.reset(new ConnectCallbacks());
189 connect_callbacks_->success_callback =
190 base::Bind(success_callback, scoped_refptr<BluetoothSocketMac>(this));
191 connect_callbacks_->error_callback = error_callback;
193 // Note: It's important to set the connect callbacks *prior* to setting the
194 // delegate, as setting the delegate can synchronously call into
195 // OnChannelOpened().
196 rfcomm_channel_.reset([rfcomm_channel retain]);
197 [rfcomm_channel_ setDelegate:delegate_];
200 void BluetoothSocketMac::OnChannelOpened(
201 IOBluetoothRFCOMMChannel* rfcomm_channel,
203 DCHECK(thread_checker_.CalledOnValidThread());
204 DCHECK_EQ(rfcomm_channel_, rfcomm_channel);
205 DCHECK(connecting());
207 scoped_ptr<ConnectCallbacks> temp = connect_callbacks_.Pass();
208 if (status != kIOReturnSuccess) {
210 std::stringstream error;
211 error << "Failed to connect bluetooth socket ("
212 << BluetoothDeviceMac::GetDeviceAddress([rfcomm_channel_ getDevice])
213 << "): (" << status << ")";
214 temp->error_callback.Run(error.str());
218 temp->success_callback.Run();
221 void BluetoothSocketMac::Close() {
222 DCHECK(thread_checker_.CalledOnValidThread());
227 void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
228 DCHECK(thread_checker_.CalledOnValidThread());
234 void BluetoothSocketMac::Receive(
235 int /* buffer_size */,
236 const ReceiveCompletionCallback& success_callback,
237 const ReceiveErrorCompletionCallback& error_callback) {
238 DCHECK(thread_checker_.CalledOnValidThread());
241 error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
245 if (!rfcomm_channel_) {
246 error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
250 // Only one pending read at a time
251 if (receive_callbacks_) {
252 error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
256 // If there is at least one packet, consume it and succeed right away.
257 if (!receive_queue_.empty()) {
258 scoped_refptr<net::IOBufferWithSize> buffer = receive_queue_.front();
259 receive_queue_.pop();
260 success_callback.Run(buffer->size(), buffer);
264 // Set the receive callback to use when data is received.
265 receive_callbacks_.reset(new ReceiveCallbacks());
266 receive_callbacks_->success_callback = success_callback;
267 receive_callbacks_->error_callback = error_callback;
270 void BluetoothSocketMac::OnChannelDataReceived(
271 IOBluetoothRFCOMMChannel* rfcomm_channel,
274 DCHECK(thread_checker_.CalledOnValidThread());
275 DCHECK_EQ(rfcomm_channel_, rfcomm_channel);
276 DCHECK(!connecting());
278 int data_size = base::checked_cast<int>(length);
279 scoped_refptr<net::IOBufferWithSize> buffer(
280 new net::IOBufferWithSize(data_size));
281 memcpy(buffer->data(), data, buffer->size());
283 // If there is a pending read callback, call it now.
284 if (receive_callbacks_) {
285 scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
286 temp->success_callback.Run(buffer->size(), buffer);
290 // Otherwise, enqueue the buffer for later use
291 receive_queue_.push(buffer);
294 void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
296 const SendCompletionCallback& success_callback,
297 const ErrorCompletionCallback& error_callback) {
298 DCHECK(thread_checker_.CalledOnValidThread());
301 error_callback.Run(kSocketConnecting);
305 if (!rfcomm_channel_) {
306 error_callback.Run(kSocketNotConnected);
310 // Create and enqueue request in preparation of async writes.
311 linked_ptr<SendRequest> request(new SendRequest());
312 request->buffer_size = buffer_size;
313 request->success_callback = success_callback;
314 request->error_callback = error_callback;
315 send_queue_.push(request);
317 // |writeAsync| accepts buffers of max. mtu bytes per call, so we need to emit
318 // multiple write operations if buffer_size > mtu.
319 BluetoothRFCOMMMTU mtu = [rfcomm_channel_ getMTU];
320 scoped_refptr<net::DrainableIOBuffer> send_buffer(
321 new net::DrainableIOBuffer(buffer, buffer_size));
322 while (send_buffer->BytesRemaining() > 0) {
323 int byte_count = send_buffer->BytesRemaining();
324 if (byte_count > mtu)
326 IOReturn status = [rfcomm_channel_ writeAsync:send_buffer->data()
328 refcon:request.get()];
329 if (status != kIOReturnSuccess) {
330 std::stringstream error;
331 error << "Failed to connect bluetooth socket ("
332 << BluetoothDeviceMac::GetDeviceAddress([rfcomm_channel_ getDevice])
333 << "): (" << status << ")";
334 // Remember the first error only
335 if (request->status == kIOReturnSuccess)
336 request->status = status;
337 request->error_signaled = true;
338 request->error_callback.Run(error.str());
339 // We may have failed to issue any write operation. In that case, there
340 // will be no corresponding completion callback for this particular
341 // request, so we must forget about it now.
342 if (request->active_async_writes == 0) {
348 request->active_async_writes++;
349 send_buffer->DidConsume(byte_count);
353 void BluetoothSocketMac::OnChannelWriteComplete(
354 IOBluetoothRFCOMMChannel* rfcomm_channel,
357 DCHECK(thread_checker_.CalledOnValidThread());
359 // Note: We use "CHECK" below to ensure we never run into unforeseen
360 // occurrences of asynchronous callbacks, which could lead to data
362 CHECK_EQ(rfcomm_channel_, rfcomm_channel);
363 CHECK_EQ(static_cast<SendRequest*>(refcon), send_queue_.front().get());
365 // Keep a local linked_ptr to avoid releasing the request too early if we end
366 // up removing it from the queue.
367 linked_ptr<SendRequest> request = send_queue_.front();
369 // Remember the first error only
370 if (status != kIOReturnSuccess) {
371 if (request->status == kIOReturnSuccess)
372 request->status = status;
375 // Figure out if we are done with this async request
376 request->active_async_writes--;
377 if (request->active_async_writes > 0)
380 // If this was the last active async write for this request, remove it from
381 // the queue and call the appropriate callback associated to the request.
383 if (request->status != kIOReturnSuccess) {
384 if (!request->error_signaled) {
385 std::stringstream error;
386 error << "Failed to connect bluetooth socket ("
387 << BluetoothDeviceMac::GetDeviceAddress([rfcomm_channel_ getDevice])
388 << "): (" << status << ")";
389 request->error_signaled = true;
390 request->error_callback.Run(error.str());
393 request->success_callback.Run(request->buffer_size);
397 void BluetoothSocketMac::OnChannelClosed(
398 IOBluetoothRFCOMMChannel* rfcomm_channel) {
399 DCHECK(thread_checker_.CalledOnValidThread());
400 DCHECK_EQ(rfcomm_channel_, rfcomm_channel);
402 if (receive_callbacks_) {
403 scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
404 temp->error_callback.Run(BluetoothSocket::kDisconnected,
405 kSocketNotConnected);
411 void BluetoothSocketMac::Accept(
412 const AcceptCompletionCallback& success_callback,
413 const ErrorCompletionCallback& error_callback) {
417 } // namespace device