1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "device/bluetooth/bluetooth_device_mac.h" 6 7#include <string> 8 9#include "base/basictypes.h" 10#include "base/bind.h" 11#include "base/hash.h" 12#include "base/mac/sdk_forward_declarations.h" 13#include "base/sequenced_task_runner.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/string_util.h" 16#include "base/strings/sys_string_conversions.h" 17#include "device/bluetooth/bluetooth_socket_mac.h" 18#include "device/bluetooth/bluetooth_uuid.h" 19 20// Undocumented API for accessing the Bluetooth transmit power level. 21// Similar to the API defined here [ http://goo.gl/20Q5vE ]. 22@interface IOBluetoothHostController (UndocumentedAPI) 23- (IOReturn) 24 BluetoothHCIReadTransmitPowerLevel:(BluetoothConnectionHandle)connection 25 inType:(BluetoothHCITransmitPowerLevelType)type 26 outTransmitPowerLevel:(BluetoothHCITransmitPowerLevel*)level; 27@end 28 29namespace device { 30namespace { 31 32// Returns the first (should be, only) UUID contained within the 33// |service_class_data|. Returns an invalid (empty) UUID if none is found. 34BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) { 35 NSArray* inner_elements = [service_class_data getArrayValue]; 36 IOBluetoothSDPUUID* sdp_uuid = nil; 37 for (IOBluetoothSDPDataElement* inner_element in inner_elements) { 38 if ([inner_element getTypeDescriptor] == kBluetoothSDPDataElementTypeUUID) { 39 sdp_uuid = [[inner_element getUUIDValue] getUUIDWithLength:16]; 40 break; 41 } 42 } 43 44 if (!sdp_uuid) 45 return BluetoothUUID(); 46 47 const uint8* uuid_bytes = reinterpret_cast<const uint8*>([sdp_uuid bytes]); 48 std::string uuid_str = base::HexEncode(uuid_bytes, 16); 49 DCHECK_EQ(uuid_str.size(), 32U); 50 uuid_str.insert(8, "-"); 51 uuid_str.insert(13, "-"); 52 uuid_str.insert(18, "-"); 53 uuid_str.insert(23, "-"); 54 return BluetoothUUID(uuid_str); 55} 56 57} // namespace 58 59BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device) 60 : device_([device retain]) { 61} 62 63BluetoothDeviceMac::~BluetoothDeviceMac() { 64} 65 66uint32 BluetoothDeviceMac::GetBluetoothClass() const { 67 return [device_ classOfDevice]; 68} 69 70std::string BluetoothDeviceMac::GetDeviceName() const { 71 return base::SysNSStringToUTF8([device_ name]); 72} 73 74std::string BluetoothDeviceMac::GetAddress() const { 75 return GetDeviceAddress(device_); 76} 77 78BluetoothDevice::VendorIDSource BluetoothDeviceMac::GetVendorIDSource() const { 79 return VENDOR_ID_UNKNOWN; 80} 81 82uint16 BluetoothDeviceMac::GetVendorID() const { 83 return 0; 84} 85 86uint16 BluetoothDeviceMac::GetProductID() const { 87 return 0; 88} 89 90uint16 BluetoothDeviceMac::GetDeviceID() const { 91 return 0; 92} 93 94int BluetoothDeviceMac::GetRSSI() const { 95 if (![device_ isConnected]) { 96 NOTIMPLEMENTED(); 97 return kUnknownPower; 98 } 99 100 int rssi = [device_ rawRSSI]; 101 102 // The API guarantees that +127 is returned in case the RSSI is not readable: 103 // http://goo.gl/bpURYv 104 if (rssi == 127) 105 return kUnknownPower; 106 107 return rssi; 108} 109 110int BluetoothDeviceMac::GetCurrentHostTransmitPower() const { 111 return GetHostTransmitPower(kReadCurrentTransmitPowerLevel); 112} 113 114int BluetoothDeviceMac::GetMaximumHostTransmitPower() const { 115 return GetHostTransmitPower(kReadMaximumTransmitPowerLevel); 116} 117 118bool BluetoothDeviceMac::IsPaired() const { 119 return [device_ isPaired]; 120} 121 122bool BluetoothDeviceMac::IsConnected() const { 123 return [device_ isConnected]; 124} 125 126bool BluetoothDeviceMac::IsConnectable() const { 127 return false; 128} 129 130bool BluetoothDeviceMac::IsConnecting() const { 131 return false; 132} 133 134BluetoothDevice::UUIDList BluetoothDeviceMac::GetUUIDs() const { 135 UUIDList uuids; 136 for (IOBluetoothSDPServiceRecord* service_record in [device_ services]) { 137 IOBluetoothSDPDataElement* service_class_data = 138 [service_record getAttributeDataElement: 139 kBluetoothSDPAttributeIdentifierServiceClassIDList]; 140 if ([service_class_data getTypeDescriptor] == 141 kBluetoothSDPDataElementTypeDataElementSequence) { 142 BluetoothUUID uuid = ExtractUuid(service_class_data); 143 if (uuid.IsValid()) 144 uuids.push_back(uuid); 145 } 146 } 147 return uuids; 148} 149 150bool BluetoothDeviceMac::ExpectingPinCode() const { 151 NOTIMPLEMENTED(); 152 return false; 153} 154 155bool BluetoothDeviceMac::ExpectingPasskey() const { 156 NOTIMPLEMENTED(); 157 return false; 158} 159 160bool BluetoothDeviceMac::ExpectingConfirmation() const { 161 NOTIMPLEMENTED(); 162 return false; 163} 164 165void BluetoothDeviceMac::Connect( 166 PairingDelegate* pairing_delegate, 167 const base::Closure& callback, 168 const ConnectErrorCallback& error_callback) { 169 NOTIMPLEMENTED(); 170} 171 172void BluetoothDeviceMac::SetPinCode(const std::string& pincode) { 173 NOTIMPLEMENTED(); 174} 175 176void BluetoothDeviceMac::SetPasskey(uint32 passkey) { 177 NOTIMPLEMENTED(); 178} 179 180void BluetoothDeviceMac::ConfirmPairing() { 181 NOTIMPLEMENTED(); 182} 183 184void BluetoothDeviceMac::RejectPairing() { 185 NOTIMPLEMENTED(); 186} 187 188void BluetoothDeviceMac::CancelPairing() { 189 NOTIMPLEMENTED(); 190} 191 192void BluetoothDeviceMac::Disconnect(const base::Closure& callback, 193 const ErrorCallback& error_callback) { 194 NOTIMPLEMENTED(); 195} 196 197void BluetoothDeviceMac::Forget(const ErrorCallback& error_callback) { 198 NOTIMPLEMENTED(); 199} 200 201void BluetoothDeviceMac::ConnectToService( 202 const BluetoothUUID& uuid, 203 const ConnectToServiceCallback& callback, 204 const ConnectToServiceErrorCallback& error_callback) { 205 scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket(); 206 socket->Connect( 207 device_.get(), uuid, base::Bind(callback, socket), error_callback); 208} 209 210void BluetoothDeviceMac::CreateGattConnection( 211 const GattConnectionCallback& callback, 212 const ConnectErrorCallback& error_callback) { 213 // TODO(armansito): Implement. 214 error_callback.Run(ERROR_UNSUPPORTED_DEVICE); 215} 216 217void BluetoothDeviceMac::StartConnectionMonitor( 218 const base::Closure& callback, 219 const ErrorCallback& error_callback) { 220 NOTIMPLEMENTED(); 221} 222 223NSDate* BluetoothDeviceMac::GetLastInquiryUpdate() { 224 return [device_ getLastInquiryUpdate]; 225} 226 227int BluetoothDeviceMac::GetHostTransmitPower( 228 BluetoothHCITransmitPowerLevelType power_level_type) const { 229 IOBluetoothHostController* controller = 230 [IOBluetoothHostController defaultController]; 231 232 // Bail if the undocumented API is unavailable on this machine. 233 SEL selector = @selector( 234 BluetoothHCIReadTransmitPowerLevel:inType:outTransmitPowerLevel:); 235 if (![controller respondsToSelector:selector]) 236 return kUnknownPower; 237 238 BluetoothHCITransmitPowerLevel power_level; 239 IOReturn result = 240 [controller BluetoothHCIReadTransmitPowerLevel:[device_ connectionHandle] 241 inType:power_level_type 242 outTransmitPowerLevel:&power_level]; 243 if (result != kIOReturnSuccess) 244 return kUnknownPower; 245 246 return power_level; 247} 248 249// static 250std::string BluetoothDeviceMac::GetDeviceAddress(IOBluetoothDevice* device) { 251 return CanonicalizeAddress(base::SysNSStringToUTF8([device addressString])); 252} 253 254} // namespace device 255