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 29 namespace device { 30 namespace { 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. 34 BluetoothUUID 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 59 BluetoothDeviceMac::BluetoothDeviceMac(IOBluetoothDevice* device) 60 : device_([device retain]) { 61 } 62 63 BluetoothDeviceMac::~BluetoothDeviceMac() { 64 } 65 66 uint32 BluetoothDeviceMac::GetBluetoothClass() const { 67 return [device_ classOfDevice]; 68 } 69 70 std::string BluetoothDeviceMac::GetDeviceName() const { 71 return base::SysNSStringToUTF8([device_ name]); 72 } 73 74 std::string BluetoothDeviceMac::GetAddress() const { 75 return GetDeviceAddress(device_); 76 } 77 78 BluetoothDevice::VendorIDSource BluetoothDeviceMac::GetVendorIDSource() const { 79 return VENDOR_ID_UNKNOWN; 80 } 81 82 uint16 BluetoothDeviceMac::GetVendorID() const { 83 return 0; 84 } 85 86 uint16 BluetoothDeviceMac::GetProductID() const { 87 return 0; 88 } 89 90 uint16 BluetoothDeviceMac::GetDeviceID() const { 91 return 0; 92 } 93 94 int 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 110 int BluetoothDeviceMac::GetCurrentHostTransmitPower() const { 111 return GetHostTransmitPower(kReadCurrentTransmitPowerLevel); 112 } 113 114 int BluetoothDeviceMac::GetMaximumHostTransmitPower() const { 115 return GetHostTransmitPower(kReadMaximumTransmitPowerLevel); 116 } 117 118 bool BluetoothDeviceMac::IsPaired() const { 119 return [device_ isPaired]; 120 } 121 122 bool BluetoothDeviceMac::IsConnected() const { 123 return [device_ isConnected]; 124 } 125 126 bool BluetoothDeviceMac::IsConnectable() const { 127 return false; 128 } 129 130 bool BluetoothDeviceMac::IsConnecting() const { 131 return false; 132 } 133 134 BluetoothDevice::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 150 bool BluetoothDeviceMac::ExpectingPinCode() const { 151 NOTIMPLEMENTED(); 152 return false; 153 } 154 155 bool BluetoothDeviceMac::ExpectingPasskey() const { 156 NOTIMPLEMENTED(); 157 return false; 158 } 159 160 bool BluetoothDeviceMac::ExpectingConfirmation() const { 161 NOTIMPLEMENTED(); 162 return false; 163 } 164 165 void BluetoothDeviceMac::Connect( 166 PairingDelegate* pairing_delegate, 167 const base::Closure& callback, 168 const ConnectErrorCallback& error_callback) { 169 NOTIMPLEMENTED(); 170 } 171 172 void BluetoothDeviceMac::SetPinCode(const std::string& pincode) { 173 NOTIMPLEMENTED(); 174 } 175 176 void BluetoothDeviceMac::SetPasskey(uint32 passkey) { 177 NOTIMPLEMENTED(); 178 } 179 180 void BluetoothDeviceMac::ConfirmPairing() { 181 NOTIMPLEMENTED(); 182 } 183 184 void BluetoothDeviceMac::RejectPairing() { 185 NOTIMPLEMENTED(); 186 } 187 188 void BluetoothDeviceMac::CancelPairing() { 189 NOTIMPLEMENTED(); 190 } 191 192 void BluetoothDeviceMac::Disconnect(const base::Closure& callback, 193 const ErrorCallback& error_callback) { 194 NOTIMPLEMENTED(); 195 } 196 197 void BluetoothDeviceMac::Forget(const ErrorCallback& error_callback) { 198 NOTIMPLEMENTED(); 199 } 200 201 void 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 210 void BluetoothDeviceMac::CreateGattConnection( 211 const GattConnectionCallback& callback, 212 const ConnectErrorCallback& error_callback) { 213 // TODO(armansito): Implement. 214 error_callback.Run(ERROR_UNSUPPORTED_DEVICE); 215 } 216 217 void BluetoothDeviceMac::StartConnectionMonitor( 218 const base::Closure& callback, 219 const ErrorCallback& error_callback) { 220 NOTIMPLEMENTED(); 221 } 222 223 NSDate* BluetoothDeviceMac::GetLastInquiryUpdate() { 224 return [device_ getLastInquiryUpdate]; 225 } 226 227 int 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 250 std::string BluetoothDeviceMac::GetDeviceAddress(IOBluetoothDevice* device) { 251 return CanonicalizeAddress(base::SysNSStringToUTF8([device addressString])); 252 } 253 254 } // namespace device 255