summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--device/bluetooth/bluetooth_profile_mac.h21
-rw-r--r--device/bluetooth/bluetooth_profile_mac.mm144
2 files changed, 138 insertions, 27 deletions
diff --git a/device/bluetooth/bluetooth_profile_mac.h b/device/bluetooth/bluetooth_profile_mac.h
index 4fef256..a49d7c0 100644
--- a/device/bluetooth/bluetooth_profile_mac.h
+++ b/device/bluetooth/bluetooth_profile_mac.h
@@ -12,12 +12,13 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/mac/scoped_nsobject.h"
+#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
#include "device/bluetooth/bluetooth_profile.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/bluetooth_uuid.h"
-@class BluetoothProfileMacHelper;
+@class RFCOMMConnectionListener;
@class IOBluetoothDevice;
@class IOBluetoothRFCOMMChannel;
@@ -37,6 +38,16 @@ class BluetoothProfileMac : public BluetoothProfile {
const base::Closure& success_callback,
const BluetoothSocket::ErrorCompletionCallback& error_callback);
+ // Callback that is invoked when the OS completes an SDP query.
+ // |status| is the returned status from the SDP query. The remaining
+ // parameters are those from |Connect()|.
+ void OnSDPQueryComplete(
+ IOReturn status,
+ IOBluetoothDevice* device,
+ const ConnectionCallback& connection_callback,
+ const base::Closure& success_callback,
+ const BluetoothSocket::ErrorCompletionCallback& error_callback);
+
// Callback that is invoked when the OS detects a new incoming RFCOMM channel
// connection. |rfcomm_channel| is the newly opened channel.
void OnRFCOMMChannelOpened(IOBluetoothRFCOMMChannel* rfcomm_channel);
@@ -54,11 +65,17 @@ class BluetoothProfileMac : public BluetoothProfile {
// A simple helper that registers for OS notifications and forwards them to
// |this| profile.
- base::scoped_nsobject<BluetoothProfileMacHelper> helper_;
+ base::scoped_nsobject<RFCOMMConnectionListener> rfcomm_connection_listener_;
// A handle to the service record registered in the system SDP server.
// Used to eventually unregister the service.
const BluetoothSDPServiceRecordHandle service_record_handle_;
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<BluetoothProfileMac> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BluetoothProfileMac);
};
} // namespace device
diff --git a/device/bluetooth/bluetooth_profile_mac.mm b/device/bluetooth/bluetooth_profile_mac.mm
index f6d5ee7..5ac6414 100644
--- a/device/bluetooth/bluetooth_profile_mac.mm
+++ b/device/bluetooth/bluetooth_profile_mac.mm
@@ -18,10 +18,21 @@
#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)
+- (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids;
+@end
+
+#endif // MAC_OS_X_VERSION_10_7
+
namespace device {
namespace {
const char kNoConnectionCallback[] = "Connection callback not set";
+const char kSDPQueryFailed[] = "SDP query failed";
const char kProfileNotFound[] = "Profile not found";
// It's safe to use 0 to represent an unregistered service, as implied by the
@@ -29,22 +40,22 @@ const char kProfileNotFound[] = "Profile not found";
const BluetoothSDPServiceRecordHandle kInvalidServiceRecordHandle = 0;
// Converts |uuid| to a IOBluetoothSDPUUID instance.
-//
-// |uuid| must be in the format of XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
-IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const std::string& uuid) {
- DCHECK(uuid.size() == 36);
- DCHECK(uuid[8] == '-');
- DCHECK(uuid[13] == '-');
- DCHECK(uuid[18] == '-');
- DCHECK(uuid[23] == '-');
- std::string numbers_only = uuid;
+IOBluetoothSDPUUID* GetIOBluetoothSDPUUID(const BluetoothUUID& uuid) {
+ // The canonical UUID format is XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.
+ const std::string uuid_str = uuid.canonical_value();
+ DCHECK_EQ(uuid_str.size(), 36U);
+ DCHECK_EQ(uuid_str[8], '-');
+ DCHECK_EQ(uuid_str[13], '-');
+ DCHECK_EQ(uuid_str[18], '-');
+ DCHECK_EQ(uuid_str[23], '-');
+ std::string numbers_only = uuid_str;
numbers_only.erase(23, 1);
numbers_only.erase(18, 1);
numbers_only.erase(13, 1);
numbers_only.erase(8, 1);
std::vector<uint8> uuid_bytes_vector;
base::HexStringToBytes(numbers_only, &uuid_bytes_vector);
- DCHECK(uuid_bytes_vector.size() == 16);
+ DCHECK_EQ(uuid_bytes_vector.size(), 16U);
return [IOBluetoothSDPUUID uuidWithBytes:&uuid_bytes_vector.front()
length:uuid_bytes_vector.size()];
@@ -100,7 +111,7 @@ NSDictionary* BuildServiceDefinition(const BluetoothUUID& uuid,
forKey:IntToNSString(kServiceNameKey)];
const int kUUIDsKey = kBluetoothSDPAttributeIdentifierServiceClassIDList;
- NSArray* uuids = @[GetIOBluetoothSDPUUID(uuid.canonical_value())];
+ NSArray* uuids = @[GetIOBluetoothSDPUUID(uuid)];
[service_definition setObject:uuids forKey:IntToNSString(kUUIDsKey)];
const int kProtocolDefinitionsKey =
@@ -159,9 +170,67 @@ BluetoothSDPServiceRecordHandle RegisterService(
} // namespace
} // namespace device
-// A simple helper class that forwards system notifications to its wrapped
-// |profile_|.
-@interface BluetoothProfileMacHelper : NSObject {
+using device::BluetoothProfile;
+using device::BluetoothProfileMac;
+using device::BluetoothSocket;
+
+// A simple helper class that forwards SDP query completed notifications to its
+// wrapped |profile_|.
+@interface SDPQueryListener : NSObject {
+ @private
+ // The profile that registered for notifications.
+ base::WeakPtr<BluetoothProfileMac> profile_;
+
+ // Callbacks associated with the connect request that triggered this SDP
+ // query.
+ BluetoothProfile::ConnectionCallback connection_callback_;
+ base::Closure success_callback_;
+ BluetoothSocket::ErrorCompletionCallback error_callback_;
+
+ // The device being queried.
+ IOBluetoothDevice* device_; // weak
+}
+
+- (id)initWithProfile:(base::WeakPtr<BluetoothProfileMac>)profile
+ device:(IOBluetoothDevice*)device
+ connection_callback:(BluetoothProfile::ConnectionCallback)connection_callback
+ success_callback:(base::Closure)success_callback
+ error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback;
+- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status;
+
+@end
+
+@implementation SDPQueryListener
+
+- (id)initWithProfile:(base::WeakPtr<BluetoothProfileMac>)profile
+ device:(IOBluetoothDevice*)device
+ connection_callback:(BluetoothProfile::ConnectionCallback)connection_callback
+ success_callback:(base::Closure)success_callback
+ error_callback:(BluetoothSocket::ErrorCompletionCallback)error_callback {
+ if ((self = [super init])) {
+ profile_ = profile;
+ device_ = device;
+ connection_callback_ = connection_callback;
+ success_callback_ = success_callback;
+ error_callback_ = error_callback;
+ }
+
+ return self;
+}
+
+- (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status {
+ DCHECK_EQ(device, device_);
+ if (profile_) {
+ profile_->OnSDPQueryComplete(status, device, connection_callback_,
+ success_callback_, error_callback_);
+ }
+}
+
+@end
+
+// A simple helper class that forwards RFCOMM channel opened notifications to
+// its wrapped |profile_|.
+@interface RFCOMMConnectionListener : NSObject {
@private
// The profile that owns |self|.
device::BluetoothProfileMac* profile_; // weak
@@ -178,7 +247,7 @@ BluetoothSDPServiceRecordHandle RegisterService(
@end
-@implementation BluetoothProfileMacHelper
+@implementation RFCOMMConnectionListener
- (id)initWithProfile:(device::BluetoothProfileMac*)profile
channelID:(BluetoothRFCOMMChannelID)channelID {
@@ -208,10 +277,10 @@ BluetoothSDPServiceRecordHandle RegisterService(
channel:(IOBluetoothRFCOMMChannel*)rfcommChannel {
if (notification != rfcommNewChannelNotification_) {
// This case is reachable if there are pre-existing RFCOMM channels open at
- // the time that the helper is created. In that case, each existing channel
- // calls into this method with a different notification than the one this
- // class registered with. Ignore those; this class is only interested in
- // channels that have opened since it registered for notifications.
+ // the time that the listener is created. In that case, each existing
+ // channel calls into this method with a different notification than the one
+ // this class registered with. Ignore those; this class is only interested
+ // in channels that have opened since it registered for notifications.
return;
}
@@ -232,10 +301,11 @@ BluetoothProfileMac::BluetoothProfileMac(const BluetoothUUID& uuid,
const Options& options)
: uuid_(uuid),
rfcomm_channel_id_(options.channel),
- helper_([[BluetoothProfileMacHelper alloc]
- initWithProfile:this
- channelID:rfcomm_channel_id_]),
- service_record_handle_(RegisterService(uuid, options)) {
+ rfcomm_connection_listener_([[RFCOMMConnectionListener alloc]
+ initWithProfile:this
+ channelID:rfcomm_channel_id_]),
+ service_record_handle_(RegisterService(uuid, options)),
+ weak_ptr_factory_(this) {
// TODO(isherman): What should happen if there was an error registering the
// service, i.e. if |service_record_handle_| is |kInvalidServiceRecordHandle|?
// http://crbug.com/367290
@@ -264,8 +334,32 @@ void BluetoothProfileMac::Connect(
return;
}
+ // Perform an SDP query on the |device| to refresh the cache, in case the
+ // services that the |device| advertises have changed since the previous
+ // query.
+ SDPQueryListener* listener =
+ [[SDPQueryListener alloc] initWithProfile:weak_ptr_factory_.GetWeakPtr()
+ device:device
+ connection_callback:connection_callback_
+ success_callback:success_callback
+ error_callback:error_callback];
+ [device performSDPQuery:[listener autorelease]
+ uuids:@[GetIOBluetoothSDPUUID(uuid_)]];
+}
+
+void BluetoothProfileMac::OnSDPQueryComplete(
+ IOReturn status,
+ IOBluetoothDevice* device,
+ const ConnectionCallback& connection_callback,
+ const base::Closure& success_callback,
+ const BluetoothSocket::ErrorCompletionCallback& error_callback) {
+ if (status != kIOReturnSuccess) {
+ error_callback.Run(kSDPQueryFailed);
+ return;
+ }
+
IOBluetoothSDPServiceRecord* record = [device
- getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_.canonical_value())];
+ getServiceRecordForUUID:GetIOBluetoothSDPUUID(uuid_)];
if (record == nil) {
error_callback.Run(kProfileNotFound);
return;
@@ -276,7 +370,7 @@ void BluetoothProfileMac::Connect(
record,
base::Bind(OnConnectSuccess,
success_callback,
- connection_callback_,
+ connection_callback,
device_address),
error_callback);
}