blob: 77cf7e1484720c709e7a12b279070cbbad1eb898 [file] [log] [blame]
// 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 <memory>
#include "base/logging.h"
#include "base/mac/sdk_forward_declarations.h"
#include "device/bluetooth/bluetooth_classic_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
std::unique_ptr<BluetoothL2capChannelMac> BluetoothL2capChannelMac::OpenAsync(
BluetoothSocketMac* socket,
IOBluetoothDevice* device,
BluetoothL2CAPPSM psm,
IOReturn* status) {
DCHECK(socket);
std::unique_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;
}
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_];
}
IOBluetoothDevice* BluetoothL2capChannelMac::GetDevice() {
return [channel_ device];
}
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(
BluetoothClassicDeviceMac::GetDeviceAddress([channel device]), 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