Commit 3b017562 authored by rpaquay@chromium.org's avatar rpaquay@chromium.org

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
parent 0e343eda
...@@ -265,7 +265,7 @@ void BluetoothAdapterMac::UpdateDevices(NSArray* devices) { ...@@ -265,7 +265,7 @@ void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
for (IOBluetoothDevice* device in devices) { for (IOBluetoothDevice* device in devices) {
std::string device_address = std::string device_address =
base::SysNSStringToUTF8([device addressString]); 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, ...@@ -290,7 +290,8 @@ void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
DCHECK(device_inquiry_ == inquiry); DCHECK(device_inquiry_ == inquiry);
std::string device_address = base::SysNSStringToUTF8([device addressString]); std::string device_address = base::SysNSStringToUTF8([device addressString]);
if (discovered_devices_.find(device_address) == discovered_devices_.end()) { 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_, FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
DeviceAdded(this, device_mac.get())); DeviceAdded(this, device_mac.get()));
discovered_devices_.insert(device_address); discovered_devices_.insert(device_address);
......
...@@ -17,11 +17,17 @@ ...@@ -17,11 +17,17 @@
class IOBluetoothDevice; class IOBluetoothDevice;
#endif #endif
namespace base {
class SequencedTaskRunner;
} // namespace base
namespace device { namespace device {
class BluetoothDeviceMac : public BluetoothDevice { class BluetoothDeviceMac : public BluetoothDevice {
public: public:
explicit BluetoothDeviceMac(IOBluetoothDevice* device); explicit BluetoothDeviceMac(
const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
IOBluetoothDevice* device);
virtual ~BluetoothDeviceMac(); virtual ~BluetoothDeviceMac();
// BluetoothDevice override // BluetoothDevice override
...@@ -81,6 +87,8 @@ class BluetoothDeviceMac : public BluetoothDevice { ...@@ -81,6 +87,8 @@ class BluetoothDeviceMac : public BluetoothDevice {
// List of observers interested in event notifications from us. // List of observers interested in event notifications from us.
ObserverList<Observer> observers_; ObserverList<Observer> observers_;
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
// (retained)
IOBluetoothDevice* device_; IOBluetoothDevice* device_;
DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceMac); DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceMac);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/hash.h" #include "base/hash.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
...@@ -36,8 +37,6 @@ ...@@ -36,8 +37,6 @@
namespace { namespace {
const char kFailedToConnect[] = "Connection failed";
// Converts |uuid| to a IOBluetoothSDPUUID instance. // Converts |uuid| to a IOBluetoothSDPUUID instance.
// //
// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. // |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
...@@ -64,9 +63,12 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) { ...@@ -64,9 +63,12 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
namespace device { namespace device {
BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device) BluetoothDeviceMac::BluetoothDeviceMac(
: BluetoothDevice(), device_([device retain]) { const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
} IOBluetoothDevice* device)
: BluetoothDevice(),
ui_task_runner_(ui_task_runner),
device_([device retain]) {}
BluetoothDeviceMac::~BluetoothDeviceMac() { BluetoothDeviceMac::~BluetoothDeviceMac() {
[device_ release]; [device_ release];
...@@ -199,9 +201,8 @@ void BluetoothDeviceMac::ConnectToService( ...@@ -199,9 +201,8 @@ void BluetoothDeviceMac::ConnectToService(
[device_ getServiceRecordForUUID:GetIOBluetoothSDPUUID( [device_ getServiceRecordForUUID:GetIOBluetoothSDPUUID(
service_uuid.canonical_value())]; service_uuid.canonical_value())];
if (record != nil) { if (record != nil) {
BluetoothServiceRecordMac service_record(record);
scoped_refptr<BluetoothSocket> socket( scoped_refptr<BluetoothSocket> socket(
BluetoothSocketMac::CreateBluetoothSocket(service_record)); BluetoothSocketMac::CreateBluetoothSocket(ui_task_runner_, record));
if (socket.get() != NULL) if (socket.get() != NULL)
callback.Run(socket); callback.Run(socket);
} }
...@@ -211,10 +212,9 @@ void BluetoothDeviceMac::ConnectToProfile( ...@@ -211,10 +212,9 @@ void BluetoothDeviceMac::ConnectToProfile(
BluetoothProfile* profile, BluetoothProfile* profile,
const base::Closure& callback, const base::Closure& callback,
const ConnectToProfileErrorCallback& error_callback) { const ConnectToProfileErrorCallback& error_callback) {
if (static_cast<BluetoothProfileMac*>(profile)->Connect(device_)) DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
callback.Run(); static_cast<BluetoothProfileMac*>(profile)
else ->Connect(ui_task_runner_, device_, callback, error_callback);
error_callback.Run(kFailedToConnect);
} }
void BluetoothDeviceMac::SetOutOfBandPairingData( void BluetoothDeviceMac::SetOutOfBandPairingData(
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
class IOBluetoothDevice; class IOBluetoothDevice;
#endif #endif
namespace base {
class SequencedTaskRunner;
} // namespace base
namespace device { namespace device {
class BluetoothProfileMac : public BluetoothProfile { class BluetoothProfileMac : public BluetoothProfile {
...@@ -27,10 +31,15 @@ class BluetoothProfileMac : public BluetoothProfile { ...@@ -27,10 +31,15 @@ class BluetoothProfileMac : public BluetoothProfile {
virtual void SetConnectionCallback( virtual void SetConnectionCallback(
const ConnectionCallback& callback) OVERRIDE; const ConnectionCallback& callback) OVERRIDE;
// Makes an outgoing connection to |device|. typedef base::Callback<void(const std::string&)> ErrorCallback;
// This method runs |socket_callback_| with the socket and returns true if the
// connection is made successfully. // Makes an outgoing connection to |device|, calling |callback| on succes or
bool Connect(IOBluetoothDevice* device); // |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: private:
friend BluetoothProfile; friend BluetoothProfile;
......
...@@ -12,14 +12,32 @@ ...@@ -12,14 +12,32 @@
#include <vector> #include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/strings/string_number_conversions.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_device_mac.h"
#include "device/bluetooth/bluetooth_socket_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 { namespace {
const char kNoConnectionCallback[] = "Connection callback not set";
const char kProfileNotFound[] = "Profile not found";
// Converts |uuid| to a IOBluetoothSDPUUID instance. // Converts |uuid| to a IOBluetoothSDPUUID instance.
// //
// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. // |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
...@@ -42,6 +60,54 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) { ...@@ -42,6 +60,54 @@ IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
length:uuid_bytes_vector.size()]; 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
namespace device { namespace device {
...@@ -63,23 +129,37 @@ void BluetoothProfileMac::SetConnectionCallback( ...@@ -63,23 +129,37 @@ void BluetoothProfileMac::SetConnectionCallback(
connection_callback_ = callback; connection_callback_ = callback;
} }
bool BluetoothProfileMac::Connect(IOBluetoothDevice* device) { void BluetoothProfileMac::Connect(
if (connection_callback_.is_null()) const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
return false; IOBluetoothDevice* device,
const base::Closure& success_callback,
IOBluetoothSDPServiceRecord* record = const ErrorCallback& error_callback) {
[device getServiceRecordForUUID:GetIOBluetoothSDPUUID( DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
uuid_.canonical_value())]; if (connection_callback_.is_null()) {
if (record != nil) { error_callback.Run(kNoConnectionCallback);
scoped_refptr<BluetoothSocket> socket( return;
BluetoothSocketMac::CreateBluetoothSocket(record));
if (socket.get() != NULL) {
BluetoothDeviceMac device_mac(device);
connection_callback_.Run(&device_mac, socket);
return true;
} }
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 } // namespace device
...@@ -5,9 +5,15 @@ ...@@ -5,9 +5,15 @@
#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_ #ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_ #define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_MAC_H_
#include <queue>
#include <string> #include <string>
#include <IOKit/IOreturn.h>
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.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" #include "device/bluetooth/bluetooth_socket.h"
#ifdef __OBJC__ #ifdef __OBJC__
...@@ -21,8 +27,8 @@ class IOBluetoothSDPServiceRecord; ...@@ -21,8 +27,8 @@ class IOBluetoothSDPServiceRecord;
#endif #endif
namespace net { namespace net {
class GrowableIOBuffer;
class IOBuffer; class IOBuffer;
class IOBufferWithSize;
} // namespace net } // namespace net
namespace device { namespace device {
...@@ -30,16 +36,20 @@ namespace device { ...@@ -30,16 +36,20 @@ namespace device {
class BluetoothServiceRecord; class BluetoothServiceRecord;
// This class is an implementation of BluetoothSocket class for OSX platform. // 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 { class BluetoothSocketMac : public BluetoothSocket {
public: public:
// TODO(youngki): This method is deprecated; remove this method when static scoped_refptr<BluetoothSocketMac> CreateBluetoothSocket(
// BluetoothServiceRecord is removed. const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
const BluetoothServiceRecord& service_record);
static scoped_refptr<BluetoothSocket> CreateBluetoothSocket(
IOBluetoothSDPServiceRecord* record); 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: // Overriden from BluetoothSocket:
virtual void Close() OVERRIDE; virtual void Close() OVERRIDE;
virtual void Disconnect(const base::Closure& callback) OVERRIDE; virtual void Disconnect(const base::Closure& callback) OVERRIDE;
...@@ -53,22 +63,77 @@ class BluetoothSocketMac : public BluetoothSocket { ...@@ -53,22 +63,77 @@ class BluetoothSocketMac : public BluetoothSocket {
const ErrorCompletionCallback& error_callback) OVERRIDE; const ErrorCompletionCallback& error_callback) OVERRIDE;
// called by BluetoothRFCOMMChannelDelegate. // called by BluetoothRFCOMMChannelDelegate.
void OnDataReceived(IOBluetoothRFCOMMChannel* rfcomm_channel, 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, void* data,
size_t length); size_t length);
// called by BluetoothRFCOMMChannelDelegate.
void OnChannelWriteComplete(IOBluetoothRFCOMMChannel* rfcomm_channel,
void* refcon,
IOReturn status);
protected: protected:
virtual ~BluetoothSocketMac(); virtual ~BluetoothSocketMac();
private: private:
explicit BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel); BluetoothSocketMac(
const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
void ResetIncomingDataBuffer(); 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_; BluetoothRFCOMMChannelDelegate* delegate_;
scoped_refptr<net::GrowableIOBuffer> incoming_data_buffer_; // (retained) The IOBluetooth RFCOMM channel used to issue commands.
std::string error_message_; 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); DISALLOW_COPY_AND_ASSIGN(BluetoothSocketMac);
}; };
......
...@@ -9,12 +9,15 @@ ...@@ -9,12 +9,15 @@
#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h> #import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
#include <limits> #include <limits>
#include <sstream>
#include <string> #include <string>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback_helpers.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.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.h"
#include "device/bluetooth/bluetooth_service_record_mac.h" #include "device/bluetooth/bluetooth_service_record_mac.h"
#include "net/base/io_buffer.h" #include "net/base/io_buffer.h"
...@@ -48,156 +51,368 @@ ...@@ -48,156 +51,368 @@
return self; 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 - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
data:(void*)dataPointer data:(void*)dataPointer
length:(size_t)dataLength { length:(size_t)dataLength {
socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength); socket_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
}
- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
socket_->OnChannelClosed(rfcommChannel);
} }
@end @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 { namespace device {
BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel) BluetoothSocketMac::SendRequest::SendRequest()
: rfcomm_channel_(rfcomm_channel), : status(kIOReturnSuccess), active_async_writes(0), error_signaled(false) {}
delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
[rfcomm_channel_ setDelegate:delegate_]; BluetoothSocketMac::SendRequest::~SendRequest() {}
ResetIncomingDataBuffer();
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() { BluetoothSocketMac::~BluetoothSocketMac() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
ReleaseChannel();
[delegate_ release];
[record_ release];
}
void BluetoothSocketMac::ReleaseChannel() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
if (rfcomm_channel_ != nil) {
[rfcomm_channel_ setDelegate:nil]; [rfcomm_channel_ setDelegate:nil];
[rfcomm_channel_ closeChannel]; [rfcomm_channel_ closeChannel];
[rfcomm_channel_ release]; [rfcomm_channel_ release];
[delegate_ release]; rfcomm_channel_ = nil;
}
// 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 void BluetoothSocketMac::Connect(
scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket( const base::Closure& success_callback,
const BluetoothServiceRecord& service_record) { const ErrorCompletionCallback& error_callback) {
BluetoothSocketMac* bluetooth_socket = NULL; DCHECK(thread_checker_.CalledOnValidThread());
if (service_record.SupportsRfcomm()) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
const BluetoothServiceRecordMac* service_record_mac =
static_cast<const BluetoothServiceRecordMac*>(&service_record); if (connecting()) {
IOBluetoothDevice* device = service_record_mac->GetIOBluetoothDevice(); error_callback.Run(kSocketConnecting);
IOBluetoothRFCOMMChannel* rfcomm_channel; return;
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 << ")";
}
} }
// TODO(youngki): add support for L2CAP sockets as well.
return scoped_refptr<BluetoothSocketMac>(bluetooth_socket); if (rfcomm_channel_ != nil) {
} error_callback.Run(kSocketAlreadyConnected);
return;
}
// static
scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
IOBluetoothSDPServiceRecord* record) {
BluetoothSocketMac* bluetooth_socket = NULL;
uint8 rfcomm_channel_id; uint8 rfcomm_channel_id;
if ([record getRFCOMMChannelID:&rfcomm_channel_id] == kIOReturnSuccess) { IOReturn status = [record_ getRFCOMMChannelID:&rfcomm_channel_id];
IOBluetoothDevice* device = [record device]; if (status != kIOReturnSuccess) {
// TODO(youngki) add support for L2CAP sockets as well.
error_callback.Run(kL2CAPNotSupported);
return;
}
IOBluetoothDevice* device = [record_ device];
IOBluetoothRFCOMMChannel* rfcomm_channel; IOBluetoothRFCOMMChannel* rfcomm_channel;
IOReturn status = status = [device openRFCOMMChannelAsync:&rfcomm_channel
[device openRFCOMMChannelAsync:&rfcomm_channel
withChannelID:rfcomm_channel_id withChannelID:rfcomm_channel_id
delegate:nil]; delegate:delegate_];
if (status == kIOReturnSuccess) { if (status != kIOReturnSuccess) {
bluetooth_socket = new BluetoothSocketMac(rfcomm_channel); std::stringstream error;
} else { error << std::string("Failed to connect bluetooth socket (")
LOG(ERROR) << "Failed to connect bluetooth socket ("
<< base::SysNSStringToUTF8([device addressString]) << "): (" << status << 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_];
}
return scoped_refptr<BluetoothSocketMac>(bluetooth_socket); 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;
}
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) { void BluetoothSocketMac::Disconnect(const base::Closure& callback) {
NOTIMPLEMENTED(); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
ReleaseChannel();
callback.Run();
} }
void BluetoothSocketMac::Receive( void BluetoothSocketMac::Receive(
int count, int count,
const ReceiveCompletionCallback& success_callback, const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_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, void BluetoothSocketMac::Send(scoped_refptr<net::IOBuffer> buffer,
int buffer_size, int buffer_size,
const SendCompletionCallback& success_callback, const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) { const ErrorCompletionCallback& error_callback) {
NOTIMPLEMENTED(); DCHECK(thread_checker_.CalledOnValidThread());
} DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
#if 0 if (connecting()) {
bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) { error_callback.Run(kSocketConnecting);
CHECK(buffer->offset() == 0); return;
int length = incoming_data_buffer_->offset(); }
if (length > 0) {
buffer->SetCapacity(length);
memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length);
buffer->set_offset(length);
ResetIncomingDataBuffer(); if (rfcomm_channel_ == nil) {
error_callback.Run(kSocketNotConnected);
return;
} }
return true;
}
bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) { // Create and enqueue request in preparation of async writes.
int bytes_written = buffer->BytesRemaining(); linked_ptr<SendRequest> request(new SendRequest());
IOReturn status = [rfcomm_channel_ writeAsync:buffer->data() request->buffer_size = buffer_size;
length:bytes_written request->success_callback = success_callback;
refcon:nil]; 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) { if (status != kIOReturnSuccess) {
error_message_ = base::StringPrintf( std::stringstream error;
"Failed to send data. IOReturn code: %u", status); error << "Failed to connect bluetooth socket ("
return false; << 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;
} }
buffer->DidConsume(bytes_written); request->active_async_writes++;
return true; send_buffer->DidConsume(byte_count);
}
} }
std::string BluetoothSocketMac::GetLastErrorMessage() const { void BluetoothSocketMac::OnChannelWriteComplete(
return error_message_; 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) {
if (request->status == kIOReturnSuccess)
request->status = status;
}
// 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);
}
} }
#endif
void BluetoothSocketMac::OnDataReceived( void BluetoothSocketMac::OnChannelClosed(
IOBluetoothRFCOMMChannel* rfcomm_channel, void* data, size_t length) { IOBluetoothRFCOMMChannel* rfcomm_channel) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
DCHECK(rfcomm_channel_ == rfcomm_channel); 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 (receive_callbacks_) {
if (incoming_data_buffer_->RemainingCapacity() < data_size) { scoped_ptr<ReceiveCallbacks> temp = receive_callbacks_.Pass();
int additional_capacity = temp->error_callback.Run(BluetoothSocket::kDisconnected,
std::max(data_size, incoming_data_buffer_->capacity()); kSocketNotConnected);
CHECK_LT( }
additional_capacity,
std::numeric_limits<int>::max() - incoming_data_buffer_->capacity()); ReleaseChannel();
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);
} }
} // namespace device } // namespace device
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment