// 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_rfcomm_channel_mac.h" #include "base/logging.h" #include "device/bluetooth/bluetooth_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 { @private device::BluetoothRfcommChannelMac* channel_; // weak } - (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel; @end @implementation BluetoothRfcommChannelDelegate - (id)initWithChannel:(device::BluetoothRfcommChannelMac*)channel { if ((self = [super init])) channel_ = channel; return self; } - (void)rfcommChannelOpenComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel status:(IOReturn)error { channel_->OnChannelOpenComplete(rfcommChannel, error); } - (void)rfcommChannelWriteComplete:(IOBluetoothRFCOMMChannel*)rfcommChannel refcon:(void*)refcon status:(IOReturn)error { channel_->OnChannelWriteComplete(rfcommChannel, refcon, error); } - (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void*)dataPointer length:(size_t)dataLength { channel_->OnChannelDataReceived(rfcommChannel, dataPointer, dataLength); } - (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel { channel_->OnChannelClosed(rfcommChannel); } @end namespace device { BluetoothRfcommChannelMac::BluetoothRfcommChannelMac( BluetoothSocketMac* socket, IOBluetoothRFCOMMChannel* channel) : channel_(channel), delegate_(nil) { SetSocket(socket); } BluetoothRfcommChannelMac::~BluetoothRfcommChannelMac() { [channel_ setDelegate:nil]; [channel_ closeChannel]; } // static scoped_ptr BluetoothRfcommChannelMac::OpenAsync( BluetoothSocketMac* socket, IOBluetoothDevice* device, BluetoothRFCOMMChannelID channel_id, IOReturn* status) { DCHECK(socket); scoped_ptr channel( new BluetoothRfcommChannelMac(socket, nil)); // Retain the delegate, because IOBluetoothDevice's // |-openRFCOMMChannelAsync:withChannelID:delegate:| assumes that it can take // ownership of the delegate without calling |-retain| on it... DCHECK(channel->delegate_); [channel->delegate_ retain]; IOBluetoothRFCOMMChannel* rfcomm_channel; *status = [device openRFCOMMChannelAsync:&rfcomm_channel withChannelID:channel_id delegate:channel->delegate_]; if (*status == kIOReturnSuccess) { // Note: No need to retain the |rfcomm_channel| -- the returned channel is // already retained. channel->channel_.reset(rfcomm_channel); } else { channel.reset(); } return channel.Pass(); } 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_.reset( [[BluetoothRfcommChannelDelegate alloc] initWithChannel:this]); [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( BluetoothDeviceMac::GetDeviceAddress([channel getDevice]), status); } void BluetoothRfcommChannelMac::OnChannelClosed( IOBluetoothRFCOMMChannel* channel) { DCHECK_EQ(channel_, channel); 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