summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-10 17:09:55 +0000
committerrpaquay@chromium.org <rpaquay@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-10 17:09:55 +0000
commit3b0175620040e14c736c31488098afbb2933f93c (patch)
treed0256e72bf8caf657899e1e0570dffcc81382bb5 /device
parent0e343edaec4e71ce240e72229d38f362277f97e1 (diff)
downloadchromium_src-3b0175620040e14c736c31488098afbb2933f93c.zip
chromium_src-3b0175620040e14c736c31488098afbb2933f93c.tar.gz
chromium_src-3b0175620040e14c736c31488098afbb2933f93c.tar.bz2
MacOS implementation of BluetoothSocket.
BUG=343725,343651 Review URL: https://codereview.chromium.org/229463003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@263018 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device')
-rw-r--r--device/bluetooth/bluetooth_adapter_mac.mm5
-rw-r--r--device/bluetooth/bluetooth_device_mac.h10
-rw-r--r--device/bluetooth/bluetooth_device_mac.mm22
-rw-r--r--device/bluetooth/bluetooth_profile_mac.h17
-rw-r--r--device/bluetooth/bluetooth_profile_mac.mm112
-rw-r--r--device/bluetooth/bluetooth_socket_mac.h97
-rw-r--r--device/bluetooth/bluetooth_socket_mac.mm413
7 files changed, 527 insertions, 149 deletions
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 9ac7caa..f8eba23 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -265,7 +265,7 @@ void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
for (IOBluetoothDevice* device in devices) {
std::string device_address =
base::SysNSStringToUTF8([device addressString]);
- devices_[device_address] = new BluetoothDeviceMac(device);
+ devices_[device_address] = new BluetoothDeviceMac(ui_task_runner_, device);
}
}
@@ -290,7 +290,8 @@ void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
DCHECK(device_inquiry_ == inquiry);
std::string device_address = base::SysNSStringToUTF8([device addressString]);
if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
- scoped_ptr<BluetoothDeviceMac> device_mac(new BluetoothDeviceMac(device));
+ scoped_ptr<BluetoothDeviceMac> device_mac(
+ new BluetoothDeviceMac(ui_task_runner_, device));
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
DeviceAdded(this, device_mac.get()));
discovered_devices_.insert(device_address);
diff --git a/device/bluetooth/bluetooth_device_mac.h b/device/bluetooth/bluetooth_device_mac.h
index e0ed024..e7bec55 100644
--- a/device/bluetooth/bluetooth_device_mac.h
+++ b/device/bluetooth/bluetooth_device_mac.h
@@ -17,11 +17,17 @@
class IOBluetoothDevice;
#endif
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
namespace device {
class BluetoothDeviceMac : public BluetoothDevice {
public:
- explicit BluetoothDeviceMac(IOBluetoothDevice* device);
+ explicit BluetoothDeviceMac(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothDevice* device);
virtual ~BluetoothDeviceMac();
// BluetoothDevice override
@@ -81,6 +87,8 @@ class BluetoothDeviceMac : public BluetoothDevice {
// List of observers interested in event notifications from us.
ObserverList<Observer> observers_;
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ // (retained)
IOBluetoothDevice* device_;
DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceMac);
diff --git a/device/bluetooth/bluetooth_device_mac.mm b/device/bluetooth/bluetooth_device_mac.mm
index bd25449..bd54495 100644
--- a/device/bluetooth/bluetooth_device_mac.mm
+++ b/device/bluetooth/bluetooth_device_mac.mm
@@ -13,6 +13,7 @@
#include "base/basictypes.h"
#include "base/hash.h"
+#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
@@ -36,8 +37,6 @@
namespace {
-const char kFailedToConnect[] = "Connection failed";
-
// Converts |uuid| to a IOBluetoothSDPUUID instance.
//
// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
@@ -64,9 +63,12 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
namespace device {
-BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device)
- : BluetoothDevice(), device_([device retain]) {
-}
+BluetoothDeviceMac::BluetoothDeviceMac(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothDevice* device)
+ : BluetoothDevice(),
+ ui_task_runner_(ui_task_runner),
+ device_([device retain]) {}
BluetoothDeviceMac::~BluetoothDeviceMac() {
[device_ release];
@@ -199,9 +201,8 @@ void BluetoothDeviceMac::ConnectToService(
[device_ getServiceRecordForUUID:GetIOBluetoothSDPUUID(
service_uuid.canonical_value())];
if (record != nil) {
- BluetoothServiceRecordMac service_record(record);
scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketMac::CreateBluetoothSocket(service_record));
+ BluetoothSocketMac::CreateBluetoothSocket(ui_task_runner_, record));
if (socket.get() != NULL)
callback.Run(socket);
}
@@ -211,10 +212,9 @@ void BluetoothDeviceMac::ConnectToProfile(
BluetoothProfile* profile,
const base::Closure& callback,
const ConnectToProfileErrorCallback& error_callback) {
- if (static_cast<BluetoothProfileMac*>(profile)->Connect(device_))
- callback.Run();
- else
- error_callback.Run(kFailedToConnect);
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ static_cast<BluetoothProfileMac*>(profile)
+ ->Connect(ui_task_runner_, device_, callback, error_callback);
}
void BluetoothDeviceMac::SetOutOfBandPairingData(
diff --git a/device/bluetooth/bluetooth_profile_mac.h b/device/bluetooth/bluetooth_profile_mac.h
index bad10ce..673abeb 100644
--- a/device/bluetooth/bluetooth_profile_mac.h
+++ b/device/bluetooth/bluetooth_profile_mac.h
@@ -18,6 +18,10 @@
class IOBluetoothDevice;
#endif
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
namespace device {
class BluetoothProfileMac : public BluetoothProfile {
@@ -27,10 +31,15 @@ class BluetoothProfileMac : public BluetoothProfile {
virtual void SetConnectionCallback(
const ConnectionCallback& callback) OVERRIDE;
- // Makes an outgoing connection to |device|.
- // This method runs |socket_callback_| with the socket and returns true if the
- // connection is made successfully.
- bool Connect(IOBluetoothDevice* device);
+ typedef base::Callback<void(const std::string&)> ErrorCallback;
+
+ // Makes an outgoing connection to |device|, calling |callback| on succes or
+ // |error_callback| on error. If successful, this method also calls
+ // |connection_callback_| before calling |callback|.
+ void Connect(const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothDevice* device,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback);
private:
friend BluetoothProfile;
diff --git a/device/bluetooth/bluetooth_profile_mac.mm b/device/bluetooth/bluetooth_profile_mac.mm
index 54781e4..b666877 100644
--- a/device/bluetooth/bluetooth_profile_mac.mm
+++ b/device/bluetooth/bluetooth_profile_mac.mm
@@ -12,14 +12,32 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device_mac.h"
#include "device/bluetooth/bluetooth_socket_mac.h"
+// Replicate specific 10.7 SDK declarations for building with prior SDKs.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+@interface IOBluetoothDevice (LionSDKDeclarations)
+- (NSString*)addressString;
+@end
+
+#endif // MAC_OS_X_VERSION_10_7
+
namespace {
+const char kNoConnectionCallback[] = "Connection callback not set";
+const char kProfileNotFound[] = "Profile not found";
+
// Converts |uuid| to a IOBluetoothSDPUUID instance.
//
// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
@@ -42,6 +60,54 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
length:uuid_bytes_vector.size()];
}
+void OnSocketConnectUI(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ scoped_refptr<device::BluetoothSocketMac> socket,
+ const base::Closure& success_callback,
+ const device::BluetoothProfileMac::ErrorCallback& error_callback) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ socket->Connect(success_callback, error_callback);
+}
+
+void OnConnectSuccessUIWithAdapter(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ const base::Closure& callback,
+ const device::BluetoothProfileMac::ConnectionCallback& connection_callback,
+ const std::string& device_address,
+ scoped_refptr<device::BluetoothSocketMac> socket,
+ scoped_refptr<device::BluetoothAdapter> adapter) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ const device::BluetoothDevice* device = adapter->GetDevice(device_address);
+ if (device) {
+ connection_callback.Run(device, socket);
+ callback.Run();
+ }
+}
+
+void OnConnectSuccessUI(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ const base::Closure& callback,
+ const device::BluetoothProfileMac::ConnectionCallback& connection_callback,
+ const std::string& device_address,
+ scoped_refptr<device::BluetoothSocketMac> socket) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ device::BluetoothAdapterFactory::GetAdapter(
+ base::Bind(&OnConnectSuccessUIWithAdapter,
+ ui_task_runner,
+ callback,
+ connection_callback,
+ device_address,
+ socket));
+}
+
+void OnConnectErrorUI(
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
+ const device::BluetoothProfileMac::ErrorCallback& error_callback,
+ const std::string& error) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ error_callback.Run(error);
+}
+
} // namespace
namespace device {
@@ -63,23 +129,37 @@ void BluetoothProfileMac::SetConnectionCallback(
connection_callback_ = callback;
}
-bool BluetoothProfileMac::Connect(IOBluetoothDevice* device) {
- if (connection_callback_.is_null())
- return false;
-
- IOBluetoothSDPServiceRecord* record =
- [device getServiceRecordForUUID:GetIOBluetoothSDPUUID(
- uuid_.canonical_value())];
- if (record != nil) {
- scoped_refptr<BluetoothSocket> socket(
- BluetoothSocketMac::CreateBluetoothSocket(record));
- if (socket.get() != NULL) {
- BluetoothDeviceMac device_mac(device);
- connection_callback_.Run(&device_mac, socket);
- return true;
- }
+void BluetoothProfileMac::Connect(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothDevice* device,
+ const base::Closure& success_callback,
+ const ErrorCallback& error_callback) {
+ DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
+ if (connection_callback_.is_null()) {
+ error_callback.Run(kNoConnectionCallback);
+ return;
+ }
+
+ IOBluetoothSDPServiceRecord* record = [device
+ getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_.canonical_value())];
+ if (record == nil) {
+ error_callback.Run(kProfileNotFound);
+ return;
}
- return false;
+
+ std::string device_address = base::SysNSStringToUTF8([device addressString]);
+ scoped_refptr<BluetoothSocketMac> socket(
+ BluetoothSocketMac::CreateBluetoothSocket(ui_task_runner, record));
+ OnSocketConnectUI(
+ ui_task_runner,
+ socket,
+ base::Bind(OnConnectSuccessUI,
+ ui_task_runner,
+ success_callback,
+ connection_callback_,
+ device_address,
+ socket),
+ base::Bind(OnConnectErrorUI, ui_task_runner, error_callback));
}
} // namespace device
diff --git a/device/bluetooth/bluetooth_socket_mac.h b/device/bluetooth/bluetooth_socket_mac.h
index 806eceb..76e197d 100644
--- a/device/bluetooth/bluetooth_socket_mac.h
+++ b/device/bluetooth/bluetooth_socket_mac.h
@@ -5,9 +5,15 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
+#include <queue>
#include <string>
+#include <IOKit/IOreturn.h>
+
+#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/thread_checker.h"
#include "device/bluetooth/bluetooth_socket.h"
#ifdef __OBJC__
@@ -21,8 +27,8 @@ class IOBluetoothSDPServiceRecord;
#endif
namespace net {
-class GrowableIOBuffer;
class IOBuffer;
+class IOBufferWithSize;
} // namespace net
namespace device {
@@ -30,16 +36,20 @@ namespace device {
class BluetoothServiceRecord;
// This class is an implementation of BluetoothSocket class for OSX platform.
+// All methods of this class must all be called on the UI thread, as per Chrome
+// guidelines on performing Async IO on UI thread on MacOS.
class BluetoothSocketMac : public BluetoothSocket {
public:
- // TODO(youngki): This method is deprecated; remove this method when
- // BluetoothServiceRecord is removed.
- static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record);
-
- static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
+ static scoped_refptr<BluetoothSocketMac> CreateBluetoothSocket(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
IOBluetoothSDPServiceRecord* record);
+ // Connect to the peer device and calls |success_callback| when the
+ // connection has been established successfully. If an error occurs, calls
+ // |error_callback| with a system error message.
+ void Connect(const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback);
+
// Overriden from BluetoothSocket:
virtual void Close() OVERRIDE;
virtual void Disconnect(const base::Closure& callback) OVERRIDE;
@@ -53,22 +63,77 @@ class BluetoothSocketMac : public BluetoothSocket {
const ErrorCompletionCallback& error_callback) OVERRIDE;
// called by BluetoothRFCOMMChannelDelegate.
- void OnDataReceived(IOBluetoothRFCOMMChannel* rfcomm_channel,
- void* data,
- size_t length);
+ void OnChannelOpened(IOBluetoothRFCOMMChannel* rfcomm_channel,
+ IOReturn status);
+
+ // called by BluetoothRFCOMMChannelDelegate.
+ void OnChannelClosed(IOBluetoothRFCOMMChannel* rfcomm_channel);
+
+ // called by BluetoothRFCOMMChannelDelegate.
+ void OnChannelDataReceived(IOBluetoothRFCOMMChannel* rfcomm_channel,
+ void* data,
+ size_t length);
+
+ // called by BluetoothRFCOMMChannelDelegate.
+ void OnChannelWriteComplete(IOBluetoothRFCOMMChannel* rfcomm_channel,
+ void* refcon,
+ IOReturn status);
protected:
virtual ~BluetoothSocketMac();
private:
- explicit BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel);
-
- void ResetIncomingDataBuffer();
+ BluetoothSocketMac(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothSDPServiceRecord* record);
- IOBluetoothRFCOMMChannel* rfcomm_channel_;
+ struct SendRequest {
+ SendRequest();
+ ~SendRequest();
+ int buffer_size;
+ SendCompletionCallback success_callback;
+ ErrorCompletionCallback error_callback;
+ IOReturn status;
+ int active_async_writes;
+ bool error_signaled;
+ };
+
+ struct ReceiveCallbacks {
+ ReceiveCallbacks();
+ ~ReceiveCallbacks();
+ ReceiveCompletionCallback success_callback;
+ ReceiveErrorCompletionCallback error_callback;
+ };
+
+ struct ConnectCallbacks {
+ ConnectCallbacks();
+ ~ConnectCallbacks();
+ base::Closure success_callback;
+ ErrorCompletionCallback error_callback;
+ };
+
+ void ReleaseChannel();
+
+ bool connecting() const { return connect_callbacks_; }
+
+ // Used to verify all methods are called on the same thread.
+ base::ThreadChecker thread_checker_;
+ // Task Runner for the UI thread, used to post tasks.
+ scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
+ // (retained) The Bluetooth Service definition.
+ IOBluetoothSDPServiceRecord* record_;
+ // (weak) The RFCOMM channel delegate. Released when the channel is closed.
BluetoothRFCOMMChannelDelegate* delegate_;
- scoped_refptr<net::GrowableIOBuffer> incoming_data_buffer_;
- std::string error_message_;
+ // (retained) The IOBluetooth RFCOMM channel used to issue commands.
+ IOBluetoothRFCOMMChannel* rfcomm_channel_;
+ // Connection callbacks -- when a pending async connection is active.
+ scoped_ptr<ConnectCallbacks> connect_callbacks_;
+ // Packets received while there is no pending "receive" callback.
+ std::queue<scoped_refptr<net::IOBufferWithSize> > receive_queue_;
+ // Receive callbacks -- when a receive call is active.
+ scoped_ptr<ReceiveCallbacks> receive_callbacks_;
+ // Send queue -- one entry per pending send operation.
+ std::queue<linked_ptr<SendRequest> > send_queue_;
DISALLOW_COPY_AND_ASSIGN(BluetoothSocketMac);
};
diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm
index b4a88c0..cee05a5 100644
--- a/device/bluetooth/bluetooth_socket_mac.mm
+++ b/device/bluetooth/bluetooth_socket_mac.mm
@@ -9,12 +9,15 @@
#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
#include <limits>
+#include <sstream>
#include <string>
#include "base/basictypes.h"
+#include "base/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
#include "device/bluetooth/bluetooth_service_record.h"
#include "device/bluetooth/bluetooth_service_record_mac.h"
#include "net/base/io_buffer.h"
@@ -48,156 +51,368 @@
return self;
}
+- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
+ status:(IOReturn)error {
+ socket_->OnChannelOpened(rfcommChannel, error);
+}
+
+- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
+ refcon:(void*)refcon
+ status:(IOReturn)error {
+ socket_->OnChannelWriteComplete(rfcommChannel, refcon, error);
+}
+
- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
data:(void*)dataPointer
length:(size_t)dataLength {
- socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength);
+ socket_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
+}
+
+- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
+ socket_->OnChannelClosed(rfcommChannel);
}
@end
+namespace {
+
+const char kL2CAPNotSupported[] = "Bluetooth L2CAP protocol is not supported";
+const char kSocketConnecting[] = "The socket is currently connecting";
+const char kSocketAlreadyConnected[] = "The socket is already connected";
+const char kSocketNotConnected[] = "The socket is not connected";
+const char kReceivePending[] = "A Receive operation is pending";
+
+template <class T>
+void empty_queue(std::queue<T>& queue) {
+ std::queue<T> empty;
+ std::swap(queue, empty);
+}
+
+} // namespace
+
namespace device {
-BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel)
- : rfcomm_channel_(rfcomm_channel),
- delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
- [rfcomm_channel_ setDelegate:delegate_];
- ResetIncomingDataBuffer();
+BluetoothSocketMac::SendRequest::SendRequest()
+ : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
+
+BluetoothSocketMac::SendRequest::~SendRequest() {}
+
+BluetoothSocketMac::ReceiveCallbacks::ReceiveCallbacks() {}
+
+BluetoothSocketMac::ReceiveCallbacks::~ReceiveCallbacks() {}
+
+BluetoothSocketMac::ConnectCallbacks::ConnectCallbacks() {}
+
+BluetoothSocketMac::ConnectCallbacks::~ConnectCallbacks() {}
+
+// static
+scoped_refptr<BluetoothSocketMac> BluetoothSocketMac::CreateBluetoothSocket(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothSDPServiceRecord* record) {
+ return new BluetoothSocketMac(ui_task_runner, record);
+}
+
+BluetoothSocketMac::BluetoothSocketMac(
+ const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
+ IOBluetoothSDPServiceRecord* record)
+ : ui_task_runner_(ui_task_runner),
+ record_(record),
+ delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]),
+ rfcomm_channel_(nil) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ [record_ retain];
}
BluetoothSocketMac::~BluetoothSocketMac() {
- [rfcomm_channel_ setDelegate:nil];
- [rfcomm_channel_ closeChannel];
- [rfcomm_channel_ release];
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ ReleaseChannel();
[delegate_ release];
+ [record_ release];
}
-// static
-scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
- const BluetoothServiceRecord& service_record) {
- BluetoothSocketMac* bluetooth_socket = NULL;
- if (service_record.SupportsRfcomm()) {
- const BluetoothServiceRecordMac* service_record_mac =
- static_cast<const BluetoothServiceRecordMac*>(&service_record);
- IOBluetoothDevice* device = service_record_mac->GetIOBluetoothDevice();
- IOBluetoothRFCOMMChannel* rfcomm_channel;
- IOReturn status =
- [device openRFCOMMChannelAsync:&rfcomm_channel
- withChannelID:service_record.rfcomm_channel()
- delegate:nil];
- if (status == kIOReturnSuccess) {
- bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
- } else {
- LOG(ERROR) << "Failed to connect bluetooth socket ("
- << service_record.address() << "): (" << status << ")";
- }
+void BluetoothSocketMac::ReleaseChannel() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ if (rfcomm_channel_ != nil) {
+ [rfcomm_channel_ setDelegate:nil];
+ [rfcomm_channel_ closeChannel];
+ [rfcomm_channel_ release];
+ rfcomm_channel_ = nil;
}
- // TODO(youngki): add support for L2CAP sockets as well.
- return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
+ // Closing the channel above prevents the callback delegate from being called
+ // so it is now safe to release all callback state.
+ connect_callbacks_.reset(NULL);
+ receive_callbacks_.reset(NULL);
+ empty_queue(receive_queue_);
+ empty_queue(send_queue_);
}
-// static
-scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
- IOBluetoothSDPServiceRecord* record) {
- BluetoothSocketMac* bluetooth_socket = NULL;
+void BluetoothSocketMac::Connect(
+ const base::Closure& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ if (connecting()) {
+ error_callback.Run(kSocketConnecting);
+ return;
+ }
+
+ if (rfcomm_channel_ != nil) {
+ error_callback.Run(kSocketAlreadyConnected);
+ return;
+ }
+
uint8 rfcomm_channel_id;
- if ([record getRFCOMMChannelID:&rfcomm_channel_id] == kIOReturnSuccess) {
- IOBluetoothDevice* device = [record device];
- IOBluetoothRFCOMMChannel* rfcomm_channel;
- IOReturn status =
- [device openRFCOMMChannelAsync:&rfcomm_channel
- withChannelID:rfcomm_channel_id
- delegate:nil];
- if (status == kIOReturnSuccess) {
- bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
- } else {
- LOG(ERROR) << "Failed to connect bluetooth socket ("
+ IOReturn status = [record_ getRFCOMMChannelID:&rfcomm_channel_id];
+ if (status != kIOReturnSuccess) {
+ // TODO(youngki) add support for L2CAP sockets as well.
+ error_callback.Run(kL2CAPNotSupported);
+ return;
+ }
+
+ IOBluetoothDevice* device = [record_ device];
+ IOBluetoothRFCOMMChannel* rfcomm_channel;
+ status = [device openRFCOMMChannelAsync:&rfcomm_channel
+ withChannelID:rfcomm_channel_id
+ delegate:delegate_];
+ if (status != kIOReturnSuccess) {
+ std::stringstream error;
+ error << std::string("Failed to connect bluetooth socket (")
<< base::SysNSStringToUTF8([device addressString]) << "): (" << status
- << ")";
- }
+ << std::string(")");
+ error_callback.Run(error.str());
+ return;
}
- // TODO(youngki): Add support for L2CAP sockets as well.
+ connect_callbacks_.reset(new ConnectCallbacks());
+ connect_callbacks_->success_callback = success_callback;
+ connect_callbacks_->error_callback = error_callback;
+ rfcomm_channel_ = rfcomm_channel;
+ [rfcomm_channel_ setDelegate:delegate_];
+}
+
+void BluetoothSocketMac::OnChannelOpened(
+ IOBluetoothRFCOMMChannel* rfcomm_channel,
+ IOReturn status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(rfcomm_channel_ == rfcomm_channel);
+ DCHECK(connecting());
+
+ scoped_ptr<ConnectCallbacks> temp = connect_callbacks_.Pass();
+ if (status != kIOReturnSuccess) {
+ ReleaseChannel();
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << base::SysNSStringToUTF8([[record_ device] addressString]) << "): ("
+ << status << ")";
+ temp->error_callback.Run(error.str());
+ return;
+ }
- return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
+ temp->success_callback.Run();
}
-void BluetoothSocketMac::Close() { NOTIMPLEMENTED(); }
+void BluetoothSocketMac::Close() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ ReleaseChannel();
+}
void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
- NOTIMPLEMENTED();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ ReleaseChannel();
+ callback.Run();
}
void BluetoothSocketMac::Receive(
int count,
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback) {
- NOTIMPLEMENTED();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ if (connecting()) {
+ error_callback.Run(BluetoothSocket::kSystemError, kSocketConnecting);
+ return;
+ }
+
+ if (rfcomm_channel_ == nil) {
+ error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
+ return;
+ }
+
+ // Only one pending read at a time
+ if (receive_callbacks_) {
+ error_callback.Run(BluetoothSocket::kIOPending, kReceivePending);
+ return;
+ }
+
+ // If there is at least one packet, consume it and succeed right away.
+ if (!receive_queue_.empty()) {
+ scoped_refptr<net::IOBufferWithSize> buffer = receive_queue_.front();
+ receive_queue_.pop();
+ success_callback.Run(buffer->size(), buffer);
+ return;
+ }
+
+ // Set the receive callback to use when data is received.
+ receive_callbacks_.reset(new ReceiveCallbacks());
+ receive_callbacks_->success_callback = success_callback;
+ receive_callbacks_->error_callback = error_callback;
+}
+
+void BluetoothSocketMac::OnChannelDataReceived(
+ IOBluetoothRFCOMMChannel* rfcomm_channel,
+ void* data,
+ size_t length) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+ DCHECK(rfcomm_channel_ == rfcomm_channel);
+ DCHECK(!connecting());
+ CHECK_LT(length, static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ int data_size = static_cast<int>(length);
+ scoped_refptr<net::IOBufferWithSize> buffer(
+ new net::IOBufferWithSize(data_size));
+ memcpy(buffer->data(), data, buffer->size());
+
+ // If there is a pending read callback, call it now.
+ if (receive_callbacks_) {
+ scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
+ temp->success_callback.Run(buffer->size(), buffer);
+ return;
+ }
+
+ // Otherwise, enqueue the buffer for later use
+ receive_queue_.push(buffer);
}
void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
int buffer_size,
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
- NOTIMPLEMENTED();
-}
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ if (connecting()) {
+ error_callback.Run(kSocketConnecting);
+ return;
+ }
-#if 0
-bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) {
- CHECK(buffer->offset() == 0);
- int length = incoming_data_buffer_->offset();
- if (length > 0) {
- buffer->SetCapacity(length);
- memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length);
- buffer->set_offset(length);
+ if (rfcomm_channel_ == nil) {
+ error_callback.Run(kSocketNotConnected);
+ return;
+ }
- ResetIncomingDataBuffer();
+ // Create and enqueue request in preparation of async writes.
+ linked_ptr<SendRequest> request(new SendRequest());
+ request->buffer_size = buffer_size;
+ request->success_callback = success_callback;
+ request->error_callback = error_callback;
+ send_queue_.push(request);
+
+ // |writeAsync| accepts buffers of max. mtu bytes per call, so we need to emit
+ // multiple write operations if buffer_size > mtu.
+ BluetoothRFCOMMMTU mtu = [rfcomm_channel_ getMTU];
+ scoped_refptr<net::DrainableIOBuffer> send_buffer(
+ new net::DrainableIOBuffer(buffer, buffer_size));
+ while (send_buffer->BytesRemaining() > 0) {
+ int byte_count = send_buffer->BytesRemaining();
+ if (byte_count > mtu)
+ byte_count = mtu;
+ IOReturn status = [rfcomm_channel_ writeAsync:send_buffer->data()
+ length:byte_count
+ refcon:request.get()];
+ if (status != kIOReturnSuccess) {
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << base::SysNSStringToUTF8([[record_ device] addressString])
+ << "): (" << status << ")";
+ // Remember the first error only
+ if (request->status == kIOReturnSuccess)
+ request->status = status;
+ request->error_signaled = true;
+ request->error_callback.Run(error.str());
+ // We may have failed to issue any write operation. In that case, there
+ // will be no corresponding completion callback for this particular
+ // request, so we must forget about it now.
+ if (request->active_async_writes == 0) {
+ send_queue_.pop();
+ }
+ return;
+ }
+
+ request->active_async_writes++;
+ send_buffer->DidConsume(byte_count);
}
- return true;
}
-bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) {
- int bytes_written = buffer->BytesRemaining();
- IOReturn status = [rfcomm_channel_ writeAsync:buffer->data()
- length:bytes_written
- refcon:nil];
+void BluetoothSocketMac::OnChannelWriteComplete(
+ IOBluetoothRFCOMMChannel* rfcomm_channel,
+ void* refcon,
+ IOReturn status) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
+
+ // Note: We use "CHECK" below to ensure we never run into unforeseen
+ // occurrences of asynchronous callbacks, which could lead to data
+ // corruption.
+ CHECK(rfcomm_channel_ == rfcomm_channel);
+ CHECK(static_cast<SendRequest*>(refcon) == send_queue_.front().get());
+
+ // Keep a local linked_ptr to avoid releasing the request too early if we end
+ // up removing it from the queue.
+ linked_ptr<SendRequest> request = send_queue_.front();
+
+ // Remember the first error only
if (status != kIOReturnSuccess) {
- error_message_ = base::StringPrintf(
- "Failed to send data. IOReturn code: %u", status);
- return false;
+ if (request->status == kIOReturnSuccess)
+ request->status = status;
}
- buffer->DidConsume(bytes_written);
- return true;
+ // Figure out if we are done with this async request
+ request->active_async_writes--;
+ if (request->active_async_writes > 0)
+ return;
+
+ // If this was the last active async write for this request, remove it from
+ // the queue and call the appropriate callback associated to the request.
+ send_queue_.pop();
+ if (request->status != kIOReturnSuccess) {
+ if (!request->error_signaled) {
+ std::stringstream error;
+ error << "Failed to connect bluetooth socket ("
+ << base::SysNSStringToUTF8([[record_ device] addressString])
+ << "): (" << status << ")";
+ request->error_signaled = true;
+ request->error_callback.Run(error.str());
+ }
+ } else {
+ request->success_callback.Run(request->buffer_size);
+ }
}
-std::string BluetoothSocketMac::GetLastErrorMessage() const {
- return error_message_;
-}
-#endif
-void BluetoothSocketMac::OnDataReceived(
- IOBluetoothRFCOMMChannel* rfcomm_channel, void* data, size_t length) {
+void BluetoothSocketMac::OnChannelClosed(
+ IOBluetoothRFCOMMChannel* rfcomm_channel) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
DCHECK(rfcomm_channel_ == rfcomm_channel);
- CHECK_LT(length, static_cast<size_t>(std::numeric_limits<int>::max()));
- int data_size = static_cast<int>(length);
- if (incoming_data_buffer_->RemainingCapacity() < data_size) {
- int additional_capacity =
- std::max(data_size, incoming_data_buffer_->capacity());
- CHECK_LT(
- additional_capacity,
- std::numeric_limits<int>::max() - incoming_data_buffer_->capacity());
- incoming_data_buffer_->SetCapacity(
- incoming_data_buffer_->capacity() + additional_capacity);
- }
- memcpy(incoming_data_buffer_->data(), data, data_size);
- incoming_data_buffer_->set_offset(
- incoming_data_buffer_->offset() + data_size);
-}
-
-void BluetoothSocketMac::ResetIncomingDataBuffer() {
- incoming_data_buffer_ = new net::GrowableIOBuffer();
- incoming_data_buffer_->SetCapacity(1024);
+
+ if (receive_callbacks_) {
+ scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
+ temp->error_callback.Run(BluetoothSocket::kDisconnected,
+ kSocketNotConnected);
+ }
+
+ ReleaseChannel();
}
} // namespace device