blob: 3751441f5217c4a85ddcd1a6fb95dfe45a252a5e [file] [log] [blame]
// Copyright 2013 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_socket_mac.h"
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h>
#include <limits>
#include <string>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "device/bluetooth/bluetooth_service_record.h"
#include "device/bluetooth/bluetooth_service_record_mac.h"
#include "net/base/io_buffer.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)
- (NSString*)addressString;
@end
#endif // MAC_OS_X_VERSION_10_7
@interface BluetoothRFCOMMChannelDelegate
: NSObject <IOBluetoothRFCOMMChannelDelegate> {
@private
device::BluetoothSocketMac* socket_; // weak
}
- (id)initWithSocket:(device::BluetoothSocketMac*)socket;
@end
@implementation BluetoothRFCOMMChannelDelegate
- (id)initWithSocket:(device::BluetoothSocketMac*)socket {
if ((self = [super init]))
socket_ = socket;
return self;
}
- (void)rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel
data:(void*)dataPointer
length:(size_t)dataLength {
socket_->OnDataReceived(rfcommChannel, dataPointer, dataLength);
}
@end
namespace device {
BluetoothSocketMac::BluetoothSocketMac(IOBluetoothRFCOMMChannel* rfcomm_channel)
: rfcomm_channel_(rfcomm_channel),
delegate_([[BluetoothRFCOMMChannelDelegate alloc] initWithSocket:this]) {
[rfcomm_channel_ setDelegate:delegate_];
ResetIncomingDataBuffer();
}
BluetoothSocketMac::~BluetoothSocketMac() {
[rfcomm_channel_ setDelegate:nil];
[rfcomm_channel_ closeChannel];
[rfcomm_channel_ release];
[delegate_ release];
}
// static
scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
const BluetoothServiceRecord& service_record) {
BluetoothSocketMac* bluetooth_socket = NULL;
if (service_record.SupportsRfcomm()) {
const BluetoothServiceRecordMac* service_record_mac =
static_cast<const BluetoothServiceRecordMac*>(&service_record);
IOBluetoothDevice* device = service_record_mac->GetIOBluetoothDevice();
IOBluetoothRFCOMMChannel* rfcomm_channel;
IOReturn status =
[device openRFCOMMChannelAsync:&rfcomm_channel
withChannelID:service_record.rfcomm_channel()
delegate:nil];
if (status == kIOReturnSuccess) {
bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
} else {
LOG(ERROR) << "Failed to connect bluetooth socket ("
<< service_record.address() << "): (" << status << ")";
}
}
// TODO(youngki): add support for L2CAP sockets as well.
return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
}
// static
scoped_refptr<BluetoothSocket> BluetoothSocketMac::CreateBluetoothSocket(
IOBluetoothSDPServiceRecord* record) {
BluetoothSocketMac* bluetooth_socket = NULL;
uint8 rfcomm_channel_id;
if ([record getRFCOMMChannelID:&rfcomm_channel_id] == kIOReturnSuccess) {
IOBluetoothDevice* device = [record device];
IOBluetoothRFCOMMChannel* rfcomm_channel;
IOReturn status =
[device openRFCOMMChannelAsync:&rfcomm_channel
withChannelID:rfcomm_channel_id
delegate:nil];
if (status == kIOReturnSuccess) {
bluetooth_socket = new BluetoothSocketMac(rfcomm_channel);
} else {
LOG(ERROR) << "Failed to connect bluetooth socket ("
<< base::SysNSStringToUTF8([device addressString]) << "): (" << status
<< ")";
}
}
// TODO(youngki): Add support for L2CAP sockets as well.
return scoped_refptr<BluetoothSocketMac>(bluetooth_socket);
}
bool BluetoothSocketMac::Receive(net::GrowableIOBuffer* buffer) {
CHECK(buffer->offset() == 0);
int length = incoming_data_buffer_->offset();
if (length > 0) {
buffer->SetCapacity(length);
memcpy(buffer->data(), incoming_data_buffer_->StartOfBuffer(), length);
buffer->set_offset(length);
ResetIncomingDataBuffer();
}
return true;
}
bool BluetoothSocketMac::Send(net::DrainableIOBuffer* buffer) {
int bytes_written = buffer->BytesRemaining();
IOReturn status = [rfcomm_channel_ writeAsync:buffer->data()
length:bytes_written
refcon:nil];
if (status != kIOReturnSuccess) {
error_message_ = base::StringPrintf(
"Failed to send data. IOReturn code: %u", status);
return false;
}
buffer->DidConsume(bytes_written);
return true;
}
std::string BluetoothSocketMac::GetLastErrorMessage() const {
return error_message_;
}
void BluetoothSocketMac::OnDataReceived(
IOBluetoothRFCOMMChannel* rfcomm_channel, void* data, size_t length) {
DCHECK(rfcomm_channel_ == rfcomm_channel);
CHECK_LT(length, static_cast<size_t>(std::numeric_limits<int>::max()));
int data_size = static_cast<int>(length);
if (incoming_data_buffer_->RemainingCapacity() < data_size) {
int additional_capacity =
std::max(data_size, incoming_data_buffer_->capacity());
CHECK_LT(
additional_capacity,
std::numeric_limits<int>::max() - incoming_data_buffer_->capacity());
incoming_data_buffer_->SetCapacity(
incoming_data_buffer_->capacity() + additional_capacity);
}
memcpy(incoming_data_buffer_->data(), data, data_size);
incoming_data_buffer_->set_offset(
incoming_data_buffer_->offset() + data_size);
}
void BluetoothSocketMac::ResetIncomingDataBuffer() {
incoming_data_buffer_ = new net::GrowableIOBuffer();
incoming_data_buffer_->SetCapacity(1024);
}
} // namespace device