blob: 43d9fb66d6b9cfe15ce7cb85b71a55518500b9f3 [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_device_mac.h"
#include <string>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/hash.h"
#include "base/mac/sdk_forward_declarations.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "device/bluetooth/bluetooth_socket_mac.h"
#include "device/bluetooth/bluetooth_uuid.h"
// Undocumented API for accessing the Bluetooth transmit power level.
// Similar to the API defined here [ http://goo.gl/20Q5vE ].
@interface IOBluetoothHostController (UndocumentedAPI)
- (IOReturn)
BluetoothHCIReadTransmitPowerLevel:(BluetoothConnectionHandle)connection
inType:(BluetoothHCITransmitPowerLevelType)type
outTransmitPowerLevel:(BluetoothHCITransmitPowerLevel*)level;
@end
namespace device {
namespace {
// Returns the first (should be, only) UUID contained within the
// |service_class_data|. Returns an invalid (empty) UUID if none is found.
BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) {
NSArray* inner_elements = [service_class_data getArrayValue];
IOBluetoothSDPUUID* sdp_uuid = nil;
for (IOBluetoothSDPDataElement* inner_element in inner_elements) {
if ([inner_element getTypeDescriptor] == kBluetoothSDPDataElementTypeUUID) {
sdp_uuid = [[inner_element getUUIDValue] getUUIDWithLength:16];
break;
}
}
if (!sdp_uuid)
return BluetoothUUID();
const uint8* uuid_bytes = reinterpret_cast<const uint8*>([sdp_uuid bytes]);
std::string uuid_str = base::HexEncode(uuid_bytes, 16);
DCHECK_EQ(uuid_str.size(), 32U);
uuid_str.insert(8, "-");
uuid_str.insert(13, "-");
uuid_str.insert(18, "-");
uuid_str.insert(23, "-");
return BluetoothUUID(uuid_str);
}
} // namespace
BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device)
: device_([device retain]) {
}
BluetoothDeviceMac::~BluetoothDeviceMac() {
}
void BluetoothDeviceMac::AddObserver(
device::BluetoothDevice::Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void BluetoothDeviceMac::RemoveObserver(
device::BluetoothDevice::Observer* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
uint32 BluetoothDeviceMac::GetBluetoothClass() const {
return [device_ classOfDevice];
}
std::string BluetoothDeviceMac::GetDeviceName() const {
return base::SysNSStringToUTF8([device_ name]);
}
std::string BluetoothDeviceMac::GetAddress() const {
return GetDeviceAddress(device_);
}
BluetoothDevice::VendorIDSource BluetoothDeviceMac::GetVendorIDSource() const {
return VENDOR_ID_UNKNOWN;
}
uint16 BluetoothDeviceMac::GetVendorID() const {
return 0;
}
uint16 BluetoothDeviceMac::GetProductID() const {
return 0;
}
uint16 BluetoothDeviceMac::GetDeviceID() const {
return 0;
}
int BluetoothDeviceMac::GetRSSI() const {
if (![device_ isConnected]) {
NOTIMPLEMENTED();
return kUnknownPower;
}
int rssi = [device_ rawRSSI];
// The API guarantees that +127 is returned in case the RSSI is not readable:
// http://goo.gl/bpURYv
if (rssi == 127)
return kUnknownPower;
return rssi;
}
int BluetoothDeviceMac::GetCurrentHostTransmitPower() const {
return GetHostTransmitPower(kReadCurrentTransmitPowerLevel);
}
int BluetoothDeviceMac::GetMaximumHostTransmitPower() const {
return GetHostTransmitPower(kReadMaximumTransmitPowerLevel);
}
bool BluetoothDeviceMac::IsPaired() const {
return [device_ isPaired];
}
bool BluetoothDeviceMac::IsConnected() const {
return [device_ isConnected];
}
bool BluetoothDeviceMac::IsConnectable() const {
return false;
}
bool BluetoothDeviceMac::IsConnecting() const {
return false;
}
BluetoothDevice::UUIDList BluetoothDeviceMac::GetUUIDs() const {
UUIDList uuids;
for (IOBluetoothSDPServiceRecord* service_record in [device_ services]) {
IOBluetoothSDPDataElement* service_class_data =
[service_record getAttributeDataElement:
kBluetoothSDPAttributeIdentifierServiceClassIDList];
if ([service_class_data getTypeDescriptor] ==
kBluetoothSDPDataElementTypeDataElementSequence) {
BluetoothUUID uuid = ExtractUuid(service_class_data);
if (uuid.IsValid())
uuids.push_back(uuid);
}
}
return uuids;
}
bool BluetoothDeviceMac::ExpectingPinCode() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceMac::ExpectingPasskey() const {
NOTIMPLEMENTED();
return false;
}
bool BluetoothDeviceMac::ExpectingConfirmation() const {
NOTIMPLEMENTED();
return false;
}
void BluetoothDeviceMac::Connect(
PairingDelegate* pairing_delegate,
const base::Closure& callback,
const ConnectErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::SetPinCode(const std::string& pincode) {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::SetPasskey(uint32 passkey) {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::ConfirmPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::RejectPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::CancelPairing() {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::Disconnect(const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::Forget(const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceMac::ConnectToService(
const BluetoothUUID& uuid,
const ConnectToServiceCallback& callback,
const ConnectToServiceErrorCallback& error_callback) {
scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
socket->Connect(
device_.get(), uuid, base::Bind(callback, socket), error_callback);
}
void BluetoothDeviceMac::CreateGattConnection(
const GattConnectionCallback& callback,
const ConnectErrorCallback& error_callback) {
// TODO(armansito): Implement.
error_callback.Run(ERROR_UNSUPPORTED_DEVICE);
}
void BluetoothDeviceMac::StartConnectionMonitor(
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
int BluetoothDeviceMac::GetHostTransmitPower(
BluetoothHCITransmitPowerLevelType power_level_type) const {
IOBluetoothHostController* controller =
[IOBluetoothHostController defaultController];
// Bail if the undocumented API is unavailable on this machine.
SEL selector = @selector(
BluetoothHCIReadTransmitPowerLevel:inType:outTransmitPowerLevel:);
if (![controller respondsToSelector:selector])
return kUnknownPower;
BluetoothHCITransmitPowerLevel power_level;
IOReturn result =
[controller BluetoothHCIReadTransmitPowerLevel:[device_ connectionHandle]
inType:power_level_type
outTransmitPowerLevel:&power_level];
if (result != kIOReturnSuccess)
return kUnknownPower;
return power_level;
}
// static
std::string BluetoothDeviceMac::GetDeviceAddress(IOBluetoothDevice* device) {
return CanonicalizeAddress(base::SysNSStringToUTF8([device addressString]));
}
} // namespace device