diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-14 19:31:48 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-14 19:31:48 +0000 |
commit | edc0de329de9d0a1fe3d2a4c07db9eee17666150 (patch) | |
tree | 1b7ed2e27b24d65ef85c2f1671b259199b2532d1 | |
parent | da8bc29b7285278a09fce6796fd9ccd39ca6a92f (diff) | |
download | chromium_src-edc0de329de9d0a1fe3d2a4c07db9eee17666150.zip chromium_src-edc0de329de9d0a1fe3d2a4c07db9eee17666150.tar.gz chromium_src-edc0de329de9d0a1fe3d2a4c07db9eee17666150.tar.bz2 |
Add support for client L2CAP sockets on Mac.
BUG=372495
TEST=BluetoothTest app
R=keybuk@chromium.org
TBR=tapted@chromium.org
Review URL: https://codereview.chromium.org/326313002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277247 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | apps/app_shim/chrome_main_app_mode_mac.mm | 2 | ||||
-rw-r--r-- | base/mac/sdk_forward_declarations.h | 8 | ||||
-rw-r--r-- | device/bluetooth/bluetooth.gyp | 6 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_l2cap_channel_mac.h | 69 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_l2cap_channel_mac.mm | 172 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_rfcomm_channel_mac.h | 4 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_rfcomm_channel_mac.mm | 2 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_socket_mac.mm | 45 |
8 files changed, 286 insertions, 22 deletions
diff --git a/apps/app_shim/chrome_main_app_mode_mac.mm b/apps/app_shim/chrome_main_app_mode_mac.mm index 1092142..ee81b77 100644 --- a/apps/app_shim/chrome_main_app_mode_mac.mm +++ b/apps/app_shim/chrome_main_app_mode_mac.mm @@ -155,7 +155,7 @@ AppShimController::AppShimController() AppShimController::~AppShimController() { // Un-set the delegate since NSApplication does not retain it. - [NSApp setDelegate:nil]; + [[NSApplication sharedApplication] setDelegate:nil]; } void AppShimController::OnPingChromeReply(bool success) { diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h index 70df98e..e53fdbd 100644 --- a/base/mac/sdk_forward_declarations.h +++ b/base/mac/sdk_forward_declarations.h @@ -14,9 +14,7 @@ #import <AppKit/AppKit.h> #import <CoreWLAN/CoreWLAN.h> #import <ImageCaptureCore/ImageCaptureCore.h> -#import <IOBluetooth/objc/IOBluetoothDevice.h> -#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h> -#import <IOBluetooth/objc/IOBluetoothHostController.h> +#import <IOBluetooth/IOBluetooth.h> #if !defined(MAC_OS_X_VERSION_10_7) || \ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 @@ -169,6 +167,10 @@ enum CWChannelBand { aborted:(BOOL)aborted; @end +@interface IOBluetoothL2CAPChannel (LionSDK) +@property(readonly) BluetoothL2CAPMTU outgoingMTU; +@end + @interface IOBluetoothDevice (LionSDK) - (NSString*)addressString; - (unsigned int)classOfDevice; diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp index 350dfe0..efcbe59 100644 --- a/device/bluetooth/bluetooth.gyp +++ b/device/bluetooth/bluetooth.gyp @@ -41,6 +41,8 @@ 'bluetooth_device_mac.mm', 'bluetooth_device_win.cc', 'bluetooth_device_win.h', + 'bluetooth_discovery_manager_mac.mm', + 'bluetooth_discovery_manager_mac.h', 'bluetooth_discovery_session.cc', 'bluetooth_discovery_session.h', 'bluetooth_gatt_characteristic.cc', @@ -51,8 +53,8 @@ 'bluetooth_gatt_service.h', 'bluetooth_init_win.cc', 'bluetooth_init_win.h', - 'bluetooth_discovery_manager_mac.mm', - 'bluetooth_discovery_manager_mac.h', + 'bluetooth_l2cap_channel_mac.mm', + 'bluetooth_l2cap_channel_mac.h', 'bluetooth_pairing_chromeos.cc', 'bluetooth_pairing_chromeos.h', 'bluetooth_remote_gatt_characteristic_chromeos.cc', diff --git a/device/bluetooth/bluetooth_l2cap_channel_mac.h b/device/bluetooth/bluetooth_l2cap_channel_mac.h new file mode 100644 index 0000000..1d489a0 --- /dev/null +++ b/device/bluetooth/bluetooth_l2cap_channel_mac.h @@ -0,0 +1,69 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_ + +#import <IOBluetooth/IOBluetooth.h> +#import <IOKit/IOReturn.h> + +#include "base/mac/scoped_nsobject.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "device/bluetooth/bluetooth_channel_mac.h" + +@class BluetoothL2capChannelDelegate; + +namespace device { + +class BluetoothL2capChannelMac : public BluetoothChannelMac { + public: + // Creates a new L2CAP channel wrapper with the given |socket| and native + // |channel|. + // NOTE: The |channel| is expected to already be retained. + BluetoothL2capChannelMac(BluetoothSocketMac* socket, + IOBluetoothL2CAPChannel* channel); + virtual ~BluetoothL2capChannelMac(); + + // Opens a new L2CAP channel with Channel ID |channel_id| to the target + // |device|. Returns the opened channel and sets |status| to kIOReturnSuccess + // if the open process was successfully started (or if an existing L2CAP + // channel was found). Otherwise, sets |status| to an error status. + static scoped_ptr<BluetoothL2capChannelMac> OpenAsync( + BluetoothSocketMac* socket, + IOBluetoothDevice* device, + BluetoothL2CAPPSM psm, + IOReturn* status); + + // BluetoothChannelMac: + virtual void SetSocket(BluetoothSocketMac* socket) OVERRIDE; + virtual std::string GetDeviceAddress() OVERRIDE; + virtual uint16_t GetOutgoingMTU() OVERRIDE; + virtual IOReturn WriteAsync(void* data, + uint16_t length, + void* refcon) OVERRIDE; + + void OnChannelOpenComplete(IOBluetoothL2CAPChannel* channel, + IOReturn status); + void OnChannelClosed(IOBluetoothL2CAPChannel* channel); + void OnChannelDataReceived(IOBluetoothL2CAPChannel* channel, + void* data, + size_t length); + void OnChannelWriteComplete(IOBluetoothL2CAPChannel* channel, + void* refcon, + IOReturn status); + + private: + // The wrapped native L2CAP channel. + base::scoped_nsobject<IOBluetoothL2CAPChannel> channel_; + + // The delegate for the native channel. + base::scoped_nsobject<BluetoothL2capChannelDelegate> delegate_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothL2capChannelMac); +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_L2CAP_CHANNEL_MAC_H_ diff --git a/device/bluetooth/bluetooth_l2cap_channel_mac.mm b/device/bluetooth/bluetooth_l2cap_channel_mac.mm new file mode 100644 index 0000000..ff137ba --- /dev/null +++ b/device/bluetooth/bluetooth_l2cap_channel_mac.mm @@ -0,0 +1,172 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_l2cap_channel_mac.h" + +#include "base/logging.h" +#include "base/mac/sdk_forward_declarations.h" +#include "device/bluetooth/bluetooth_device_mac.h" +#include "device/bluetooth/bluetooth_socket_mac.h" + +// A simple delegate class for an open L2CAP channel that forwards methods to +// its wrapped |channel_|. +@interface BluetoothL2capChannelDelegate + : NSObject <IOBluetoothL2CAPChannelDelegate> { + @private + device::BluetoothL2capChannelMac* channel_; // weak +} + +- (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel; + +@end + +@implementation BluetoothL2capChannelDelegate + +- (id)initWithChannel:(device::BluetoothL2capChannelMac*)channel { + if ((self = [super init])) + channel_ = channel; + + return self; +} + +- (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel*)l2capChannel + status:(IOReturn)error { + channel_->OnChannelOpenComplete(l2capChannel, error); +} + +- (void)l2capChannelWriteComplete:(IOBluetoothL2CAPChannel*)l2capChannel + refcon:(void*)refcon + status:(IOReturn)error { + channel_->OnChannelWriteComplete(l2capChannel, refcon, error); +} + +- (void)l2capChannelData:(IOBluetoothL2CAPChannel*)l2capChannel + data:(void*)dataPointer + length:(size_t)dataLength { + channel_->OnChannelDataReceived(l2capChannel, dataPointer, dataLength); +} + +- (void)l2capChannelClosed:(IOBluetoothL2CAPChannel*)l2capChannel { + channel_->OnChannelClosed(l2capChannel); +} + +// These methods are marked as optional in the 10.8 SDK, but not in the 10.6 +// SDK. These empty implementations can be removed once we drop the 10.6 SDK. +- (void)l2capChannelReconfigured:(IOBluetoothL2CAPChannel*)l2capChannel { +} +- (void)l2capChannelQueueSpaceAvailable:(IOBluetoothL2CAPChannel*)l2capChannel { +} +@end + +namespace device { + +BluetoothL2capChannelMac::BluetoothL2capChannelMac( + BluetoothSocketMac* socket, + IOBluetoothL2CAPChannel* channel) + : channel_(channel), + delegate_(nil) { + SetSocket(socket); +} + +BluetoothL2capChannelMac::~BluetoothL2capChannelMac() { + [channel_ setDelegate:nil]; + [channel_ closeChannel]; +} + +// static +scoped_ptr<BluetoothL2capChannelMac> BluetoothL2capChannelMac::OpenAsync( + BluetoothSocketMac* socket, + IOBluetoothDevice* device, + BluetoothL2CAPPSM psm, + IOReturn* status) { + DCHECK(socket); + scoped_ptr<BluetoothL2capChannelMac> channel( + new BluetoothL2capChannelMac(socket, nil)); + + // Retain the delegate, because IOBluetoothDevice's + // |-openL2CAPChannelAsync:withPSM:delegate:| assumes that it can take + // ownership of the delegate without calling |-retain| on it... + DCHECK(channel->delegate_); + [channel->delegate_ retain]; + IOBluetoothL2CAPChannel* l2cap_channel; + *status = [device openL2CAPChannelAsync:&l2cap_channel + withPSM:psm + delegate:channel->delegate_]; + if (*status == kIOReturnSuccess) + channel->channel_.reset([l2cap_channel retain]); + else + channel.reset(); + + return channel.Pass(); +} + +void BluetoothL2capChannelMac::SetSocket(BluetoothSocketMac* socket) { + BluetoothChannelMac::SetSocket(socket); + if (!this->socket()) + return; + + // Now that the socket is set, it's safe to associate a delegate, which can + // call back to the socket. + DCHECK(!delegate_); + delegate_.reset( + [[BluetoothL2capChannelDelegate alloc] initWithChannel:this]); + [channel_ setDelegate:delegate_]; +} + +std::string BluetoothL2capChannelMac::GetDeviceAddress() { + return BluetoothDeviceMac::GetDeviceAddress([channel_ getDevice]); +} + +uint16_t BluetoothL2capChannelMac::GetOutgoingMTU() { + return [channel_ outgoingMTU]; +} + +IOReturn BluetoothL2capChannelMac::WriteAsync(void* data, + uint16_t length, + void* refcon) { + DCHECK_LE(length, GetOutgoingMTU()); + return [channel_ writeAsync:data length:length refcon:refcon]; +} + +void BluetoothL2capChannelMac::OnChannelOpenComplete( + IOBluetoothL2CAPChannel* channel, + IOReturn status) { + if (channel_) { + DCHECK_EQ(channel_, channel); + } else { + // The (potentially) asynchronous connection occurred synchronously. + // Should only be reachable from OpenAsync(). + DCHECK_EQ(status, kIOReturnSuccess); + } + + socket()->OnChannelOpenComplete( + BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status); +} + +void BluetoothL2capChannelMac::OnChannelClosed( + IOBluetoothL2CAPChannel* channel) { + DCHECK_EQ(channel_, channel); + socket()->OnChannelClosed(); +} + +void BluetoothL2capChannelMac::OnChannelDataReceived( + IOBluetoothL2CAPChannel* channel, + void* data, + size_t length) { + DCHECK_EQ(channel_, channel); + socket()->OnChannelDataReceived(data, length); +} + +void BluetoothL2capChannelMac::OnChannelWriteComplete( + IOBluetoothL2CAPChannel* channel, + void* refcon, + IOReturn status) { + // Note: We use "CHECK" below to ensure we never run into unforeseen + // occurrences of asynchronous callbacks, which could lead to data + // corruption. + CHECK_EQ(channel_, channel); + socket()->OnChannelWriteComplete(refcon, status); +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_rfcomm_channel_mac.h b/device/bluetooth/bluetooth_rfcomm_channel_mac.h index d381496..572d915 100644 --- a/device/bluetooth/bluetooth_rfcomm_channel_mac.h +++ b/device/bluetooth/bluetooth_rfcomm_channel_mac.h @@ -33,7 +33,7 @@ class BluetoothRfcommChannelMac : public BluetoothChannelMac { static scoped_ptr<BluetoothRfcommChannelMac> OpenAsync( BluetoothSocketMac* socket, IOBluetoothDevice* device, - uint8 channel_id, + BluetoothRFCOMMChannelID channel_id, IOReturn* status); // BluetoothChannelMac: @@ -57,6 +57,8 @@ class BluetoothRfcommChannelMac : public BluetoothChannelMac { private: // The wrapped native RFCOMM channel. base::scoped_nsobject<IOBluetoothRFCOMMChannel> channel_; + + // The delegate for the native channel. base::scoped_nsobject<BluetoothRfcommChannelDelegate> delegate_; DISALLOW_COPY_AND_ASSIGN(BluetoothRfcommChannelMac); diff --git a/device/bluetooth/bluetooth_rfcomm_channel_mac.mm b/device/bluetooth/bluetooth_rfcomm_channel_mac.mm index 8697ef6..655fe57 100644 --- a/device/bluetooth/bluetooth_rfcomm_channel_mac.mm +++ b/device/bluetooth/bluetooth_rfcomm_channel_mac.mm @@ -71,7 +71,7 @@ BluetoothRfcommChannelMac::~BluetoothRfcommChannelMac() { scoped_ptr<BluetoothRfcommChannelMac> BluetoothRfcommChannelMac::OpenAsync( BluetoothSocketMac* socket, IOBluetoothDevice* device, - uint8 channel_id, + BluetoothRFCOMMChannelID channel_id, IOReturn* status) { DCHECK(socket); scoped_ptr<BluetoothRfcommChannelMac> channel( diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm index 67fb949..4868134 100644 --- a/device/bluetooth/bluetooth_socket_mac.mm +++ b/device/bluetooth/bluetooth_socket_mac.mm @@ -24,6 +24,7 @@ #include "device/bluetooth/bluetooth_channel_mac.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_device_mac.h" +#include "device/bluetooth/bluetooth_l2cap_channel_mac.h" #include "device/bluetooth/bluetooth_rfcomm_channel_mac.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -317,7 +318,7 @@ void BluetoothSocketMac::ListenUsingRfcomm( adapter_ = adapter; uuid_ = uuid; - DVLOG(1) << uuid_.canonical_value() << ": Registering service."; + DVLOG(1) << uuid_.canonical_value() << ": Registering RFCOMM service."; BluetoothRFCOMMChannelID registered_channel_id; service_record_handle_ = RegisterRfcommService(uuid, channel_id, ®istered_channel_id); @@ -375,17 +376,29 @@ void BluetoothSocketMac::OnSDPQueryComplete( return; } - uint8 rfcomm_channel_id; + // Since RFCOMM is built on top of L2CAP, a service record with both should + // always be treated as RFCOMM. + BluetoothRFCOMMChannelID rfcomm_channel_id = BluetoothAdapter::kChannelAuto; + BluetoothL2CAPPSM l2cap_psm = BluetoothAdapter::kPsmAuto; status = [record getRFCOMMChannelID:&rfcomm_channel_id]; if (status != kIOReturnSuccess) { - // TODO(isherman): Add support for L2CAP sockets as well. - error_callback.Run(kL2capNotSupported); - return; + status = [record getL2CAPPSM:&l2cap_psm]; + if (status != kIOReturnSuccess) { + error_callback.Run(kProfileNotFound); + return; + } } - DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " " - << uuid_.canonical_value() << ": Opening RFCOMM channel: " - << rfcomm_channel_id; + if (rfcomm_channel_id != BluetoothAdapter::kChannelAuto) { + DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " " + << uuid_.canonical_value() << ": Opening RFCOMM channel: " + << rfcomm_channel_id; + } else { + DCHECK_NE(l2cap_psm, BluetoothAdapter::kPsmAuto); + DVLOG(1) << BluetoothDeviceMac::GetDeviceAddress(device) << " " + << uuid_.canonical_value() << ": Opening L2CAP channel: " + << l2cap_psm; + } // Note: It's important to set the connect callbacks *prior* to opening the // channel as the delegate is passed in and can synchronously call into @@ -394,8 +407,14 @@ void BluetoothSocketMac::OnSDPQueryComplete( connect_callbacks_->success_callback = success_callback; connect_callbacks_->error_callback = error_callback; - channel_ = BluetoothRfcommChannelMac::OpenAsync( - this, device, rfcomm_channel_id, &status); + if (rfcomm_channel_id != BluetoothAdapter::kChannelAuto) { + channel_ = BluetoothRfcommChannelMac::OpenAsync( + this, device, rfcomm_channel_id, &status); + } else { + DCHECK_NE(l2cap_psm, BluetoothAdapter::kPsmAuto); + channel_ = + BluetoothL2capChannelMac::OpenAsync(this, device, l2cap_psm, &status); + } if (status != kIOReturnSuccess) { std::stringstream error; error << "Failed to connect bluetooth socket (" @@ -407,7 +426,7 @@ void BluetoothSocketMac::OnSDPQueryComplete( DVLOG(2) << BluetoothDeviceMac::GetDeviceAddress(device) << " " << uuid_.canonical_value() - << ": RFCOMM channel opening in background."; + << ": channel opening in background."; } void BluetoothSocketMac::OnChannelOpened( @@ -506,9 +525,7 @@ void BluetoothSocketMac::Receive( receive_callbacks_->error_callback = error_callback; } -void BluetoothSocketMac::OnChannelDataReceived( - void* data, - size_t length) { +void BluetoothSocketMac::OnChannelDataReceived(void* data, size_t length) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!is_connecting()); |