1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/bluetooth_rfcomm_channel_mac.h"
#include <memory>
#include "base/check_op.h"
#include "base/memory/raw_ptr.h"
#include "device/bluetooth/bluetooth_classic_device_mac.h"
#include "device/bluetooth/bluetooth_socket_mac.h"
// A simple delegate class for an open RFCOMM channel that forwards methods to
// its wrapped `_channel`.
@interface BluetoothRfcommChannelDelegate
: NSObject <IOBluetoothRFCOMMChannelDelegate> {
@private
raw_ptr<device::BluetoothRfcommChannelMac> _channel; // weak
IOBluetoothRFCOMMChannel* __strong _rfcommChannel;
// While `_rfcommChannel` is open, the delegate holds a strong reference to
// itself to ensure it is not destroyed before rfcommChannelClosed is
// received. This is a workaround for a macOS bug, see Apple Feedback report
// FB13705522.
BluetoothRfcommChannelDelegate* __strong _strongSelf;
}
- (instancetype)initWithChannel:(device::BluetoothRfcommChannelMac*)channel
rfcommChannel:(IOBluetoothRFCOMMChannel*)rfcommChannel;
- (void)setRfcommChannel:(IOBluetoothRFCOMMChannel*)rfcommChannel;
@end
@implementation BluetoothRfcommChannelDelegate
- (instancetype)initWithChannel:(device::BluetoothRfcommChannelMac*)channel
rfcommChannel:(IOBluetoothRFCOMMChannel*)rfcommChannel {
if ((self = [super init])) {
_channel = channel;
_rfcommChannel = rfcommChannel;
}
return self;
}
- (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
status:(IOReturn)error {
CHECK(_rfcommChannel);
if (error == kIOReturnSuccess) {
// Keep the delegate alive until rfcommChannelClosed.
_strongSelf = self;
}
if (_channel) {
_channel->OnChannelOpenComplete(rfcommChannel, error);
}
}
- (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel
refcon:(void*)refcon
status:(IOReturn)error {
if (_channel) {
_channel->OnChannelWriteComplete(rfcommChannel, refcon, error);
}
}
- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
data:(void*)dataPointer
length:(size_t)dataLength {
if (_channel) {
_channel->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength);
}
}
- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel {
[_rfcommChannel setDelegate:nil];
// If `_channel` still exists, notify it that the channel was closed so it
// can release its strong references to `rfcommChannel` and the channel
// delegate (this object). In the typical case we expect `_channel` has
// already been destroyed.
if (_channel) {
_channel->OnChannelClosed(rfcommChannel);
}
// Remove the last owning references to the channel and delegate. After
// releasing `_strongSelf` this object may be destroyed, so the only safe
// thing to do is return.
_rfcommChannel = nil;
_strongSelf = nil;
}
- (void)resetOwner {
_channel = nullptr;
}
- (void)setRfcommChannel:(IOBluetoothRFCOMMChannel*)rfcommChannel {
CHECK(!_rfcommChannel);
_rfcommChannel = rfcommChannel;
}
@end
namespace device {
BluetoothRfcommChannelMac::BluetoothRfcommChannelMac(
BluetoothSocketMac* socket,
IOBluetoothRFCOMMChannel* channel)
: channel_(channel),
delegate_(nil) {
SetSocket(socket);
}
BluetoothRfcommChannelMac::~BluetoothRfcommChannelMac() {
// If `channel_` is opened, `delegate_` and `channel_` are allowed to persist
// until the delegate is notified that the channel has been closed. Reset the
// delegate's reference to this object so the delegate will not notify us
// for events that occur after our destruction.
[delegate_ resetOwner];
[channel_ closeChannel];
}
// static
std::unique_ptr<BluetoothRfcommChannelMac> BluetoothRfcommChannelMac::OpenAsync(
BluetoothSocketMac* socket,
IOBluetoothDevice* device,
BluetoothRFCOMMChannelID channel_id,
IOReturn* status) {
DCHECK(socket);
std::unique_ptr<BluetoothRfcommChannelMac> channel(
new BluetoothRfcommChannelMac(socket, /*channel=*/nil));
DCHECK(channel->delegate_);
IOBluetoothRFCOMMChannel* rfcomm_channel;
*status = [device openRFCOMMChannelAsync:&rfcomm_channel
withChannelID:channel_id
delegate:channel->delegate_];
if (*status == kIOReturnSuccess) {
channel->channel_ = rfcomm_channel;
[channel->delegate_ setRfcommChannel:rfcomm_channel];
} else {
channel.reset();
}
return channel;
}
void BluetoothRfcommChannelMac::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_ = [[BluetoothRfcommChannelDelegate alloc] initWithChannel:this
rfcommChannel:channel_];
[channel_ setDelegate:delegate_];
}
IOBluetoothDevice* BluetoothRfcommChannelMac::GetDevice() {
return [channel_ getDevice];
}
uint16_t BluetoothRfcommChannelMac::GetOutgoingMTU() {
return [channel_ getMTU];
}
IOReturn BluetoothRfcommChannelMac::WriteAsync(void* data,
uint16_t length,
void* refcon) {
DCHECK_LE(length, GetOutgoingMTU());
return [channel_ writeAsync:data length:length refcon:refcon];
}
void BluetoothRfcommChannelMac::OnChannelOpenComplete(
IOBluetoothRFCOMMChannel* 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 getDevice]), status);
}
void BluetoothRfcommChannelMac::OnChannelClosed(
IOBluetoothRFCOMMChannel* channel) {
DCHECK_EQ(channel_, channel);
channel_ = nil;
[delegate_ resetOwner];
delegate_ = nil;
socket()->OnChannelClosed();
}
void BluetoothRfcommChannelMac::OnChannelDataReceived(
IOBluetoothRFCOMMChannel* channel,
void* data,
size_t length) {
DCHECK_EQ(channel_, channel);
socket()->OnChannelDataReceived(data, length);
}
void BluetoothRfcommChannelMac::OnChannelWriteComplete(
IOBluetoothRFCOMMChannel* 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
|