Revert 269361 "Fix WebURLLoaderImpl::Context leak if a pending r..."
[chromium-blink-merge.git] / device / bluetooth / bluetooth_socket_mac.mm
blobd258f0e586a7be9d018bb664d31c1910407803ef
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>
9 #include <limits>
10 #include <sstream>
11 #include <string>
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> {
28  @private
29   device::BluetoothSocketMac* socket_;  // weak
32 - (id)initWithSocket:(device::BluetoothSocketMac*)socket;
34 @end
36 @implementation BluetoothRFCOMMChannelDelegate
38 - (id)initWithSocket:(device::BluetoothSocketMac*)socket {
39   if ((self = [super init]))
40     socket_ = socket;
42   return self;
45 - (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
46                            status:(IOReturn)error {
47   socket_->OnChannelOpened(rfcommChannel, error);
50 - (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
51                             refcon:(void*)refcon
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);
66 @end
68 namespace {
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";
76 template <class T>
77 void empty_queue(std::queue<T>& queue) {
78   std::queue<T> empty;
79   std::swap(queue, empty);
82 }  // namespace
84 namespace device {
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() {}
99 // static
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);
108 // static
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());
124   ReleaseChannel();
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();
133   }
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());
149   if (connecting()) {
150     error_callback.Run(kSocketConnecting);
151     return;
152   }
154   if (rfcomm_channel_) {
155     error_callback.Run(kSocketAlreadyConnected);
156     return;
157   }
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);
164     return;
165   }
167   IOBluetoothDevice* device = [record device];
168   IOBluetoothRFCOMMChannel* rfcomm_channel;
169   status = [device openRFCOMMChannelAsync:&rfcomm_channel
170                             withChannelID:rfcomm_channel_id
171                                  delegate:delegate_];
172   if (status != kIOReturnSuccess) {
173     std::stringstream error;
174     error << "Failed to connect bluetooth socket ("
175           << BluetoothDeviceMac::GetDeviceAddress(device) << "): (" << status
176           << ")";
177     error_callback.Run(error.str());
178     return;
179   }
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,
202     IOReturn status) {
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) {
209     ReleaseChannel();
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());
215     return;
216   }
218   temp->success_callback.Run();
221 void BluetoothSocketMac::Close() {
222   DCHECK(thread_checker_.CalledOnValidThread());
224   ReleaseChannel();
227 void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
228   DCHECK(thread_checker_.CalledOnValidThread());
230   ReleaseChannel();
231   callback.Run();
234 void BluetoothSocketMac::Receive(
235     int /* buffer_size */,
236     const ReceiveCompletionCallback& success_callback,
237     const ReceiveErrorCompletionCallback& error_callback) {
238   DCHECK(thread_checker_.CalledOnValidThread());
240   if (connecting()) {
241     error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
242     return;
243   }
245   if (!rfcomm_channel_) {
246     error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
247     return;
248   }
250   // Only one pending read at a time
251   if (receive_callbacks_) {
252     error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
253     return;
254   }
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);
261     return;
262   }
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,
272     void* data,
273     size_t length) {
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);
287     return;
288   }
290   // Otherwise, enqueue the buffer for later use
291   receive_queue_.push(buffer);
294 void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
295                               int buffer_size,
296                               const SendCompletionCallback& success_callback,
297                               const ErrorCompletionCallback& error_callback) {
298   DCHECK(thread_checker_.CalledOnValidThread());
300   if (connecting()) {
301     error_callback.Run(kSocketConnecting);
302     return;
303   }
305   if (!rfcomm_channel_) {
306     error_callback.Run(kSocketNotConnected);
307     return;
308   }
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)
325       byte_count = mtu;
326     IOReturn status = [rfcomm_channel_ writeAsync:send_buffer->data()
327                                            length:byte_count
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) {
343         send_queue_.pop();
344       }
345       return;
346     }
348     request->active_async_writes++;
349     send_buffer->DidConsume(byte_count);
350   }
353 void BluetoothSocketMac::OnChannelWriteComplete(
354     IOBluetoothRFCOMMChannel* rfcomm_channel,
355     void* refcon,
356     IOReturn status) {
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
361   // corruption.
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;
373   }
375   // Figure out if we are done with this async request
376   request->active_async_writes--;
377   if (request->active_async_writes > 0)
378     return;
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.
382   send_queue_.pop();
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());
391     }
392   } else {
393     request->success_callback.Run(request->buffer_size);
394   }
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);
406   }
408   ReleaseChannel();
411 void BluetoothSocketMac::Accept(
412     const AcceptCompletionCallback& success_callback,
413     const ErrorCompletionCallback& error_callback) {
414   NOTIMPLEMENTED();
417 }  // namespace device