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.
5 #include "device/bluetooth/bluetooth_classic_device_mac.h"
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"
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)
24 BluetoothHCIReadTransmitPowerLevel:(BluetoothConnectionHandle)connection
25 inType:(BluetoothHCITransmitPowerLevelType)type
26 outTransmitPowerLevel:(BluetoothHCITransmitPowerLevel*)level;
32 const char kApiUnavailable[] = "This API is not implemented on this platform.";
34 // Returns the first (should be, only) UUID contained within the
35 // |service_class_data|. Returns an invalid (empty) UUID if none is found.
36 BluetoothUUID ExtractUuid(IOBluetoothSDPDataElement* service_class_data) {
37 NSArray* inner_elements = [service_class_data getArrayValue];
38 IOBluetoothSDPUUID* sdp_uuid = nil;
39 for (IOBluetoothSDPDataElement* inner_element in inner_elements) {
40 if ([inner_element getTypeDescriptor] == kBluetoothSDPDataElementTypeUUID) {
41 sdp_uuid = [[inner_element getUUIDValue] getUUIDWithLength:16];
47 return BluetoothUUID();
49 const uint8* uuid_bytes = reinterpret_cast<const uint8*>([sdp_uuid bytes]);
50 std::string uuid_str = base::HexEncode(uuid_bytes, 16);
51 DCHECK_EQ(uuid_str.size(), 32U);
52 uuid_str.insert(8, "-");
53 uuid_str.insert(13, "-");
54 uuid_str.insert(18, "-");
55 uuid_str.insert(23, "-");
56 return BluetoothUUID(uuid_str);
61 BluetoothClassicDeviceMac::BluetoothClassicDeviceMac(IOBluetoothDevice* device)
62 : device_([device retain]) {
65 BluetoothClassicDeviceMac::~BluetoothClassicDeviceMac() {
68 uint32 BluetoothClassicDeviceMac::GetBluetoothClass() const {
69 return [device_ classOfDevice];
72 std::string BluetoothClassicDeviceMac::GetDeviceName() const {
73 return base::SysNSStringToUTF8([device_ name]);
76 std::string BluetoothClassicDeviceMac::GetAddress() const {
77 return GetDeviceAddress(device_);
80 BluetoothDevice::VendorIDSource BluetoothClassicDeviceMac::GetVendorIDSource()
82 return VENDOR_ID_UNKNOWN;
85 uint16 BluetoothClassicDeviceMac::GetVendorID() const {
89 uint16 BluetoothClassicDeviceMac::GetProductID() const {
93 uint16 BluetoothClassicDeviceMac::GetDeviceID() const {
97 bool BluetoothClassicDeviceMac::IsPaired() const {
98 return [device_ isPaired];
101 bool BluetoothClassicDeviceMac::IsConnected() const {
102 return [device_ isConnected];
105 bool BluetoothClassicDeviceMac::IsConnectable() const {
109 bool BluetoothClassicDeviceMac::IsConnecting() const {
113 BluetoothDevice::UUIDList BluetoothClassicDeviceMac::GetUUIDs() const {
115 for (IOBluetoothSDPServiceRecord* service_record in [device_ services]) {
116 IOBluetoothSDPDataElement* service_class_data =
117 [service_record getAttributeDataElement:
118 kBluetoothSDPAttributeIdentifierServiceClassIDList];
119 if ([service_class_data getTypeDescriptor] ==
120 kBluetoothSDPDataElementTypeDataElementSequence) {
121 BluetoothUUID uuid = ExtractUuid(service_class_data);
123 uuids.push_back(uuid);
129 int16 BluetoothClassicDeviceMac::GetInquiryRSSI() const {
130 return kUnknownPower;
133 int16 BluetoothClassicDeviceMac::GetInquiryTxPower() const {
135 return kUnknownPower;
138 bool BluetoothClassicDeviceMac::ExpectingPinCode() const {
143 bool BluetoothClassicDeviceMac::ExpectingPasskey() const {
148 bool BluetoothClassicDeviceMac::ExpectingConfirmation() const {
153 void BluetoothClassicDeviceMac::GetConnectionInfo(
154 const ConnectionInfoCallback& callback) {
155 ConnectionInfo connection_info;
156 if (![device_ isConnected]) {
157 callback.Run(connection_info);
161 connection_info.rssi = [device_ rawRSSI];
162 // The API guarantees that +127 is returned in case the RSSI is not readable:
163 // http://goo.gl/bpURYv
164 if (connection_info.rssi == 127)
165 connection_info.rssi = kUnknownPower;
167 connection_info.transmit_power =
168 GetHostTransmitPower(kReadCurrentTransmitPowerLevel);
169 connection_info.max_transmit_power =
170 GetHostTransmitPower(kReadMaximumTransmitPowerLevel);
172 callback.Run(connection_info);
175 void BluetoothClassicDeviceMac::Connect(
176 PairingDelegate* pairing_delegate,
177 const base::Closure& callback,
178 const ConnectErrorCallback& error_callback) {
182 void BluetoothClassicDeviceMac::SetPinCode(const std::string& pincode) {
186 void BluetoothClassicDeviceMac::SetPasskey(uint32 passkey) {
190 void BluetoothClassicDeviceMac::ConfirmPairing() {
194 void BluetoothClassicDeviceMac::RejectPairing() {
198 void BluetoothClassicDeviceMac::CancelPairing() {
202 void BluetoothClassicDeviceMac::Disconnect(
203 const base::Closure& callback,
204 const ErrorCallback& error_callback) {
208 void BluetoothClassicDeviceMac::Forget(const ErrorCallback& error_callback) {
212 void BluetoothClassicDeviceMac::ConnectToService(
213 const BluetoothUUID& uuid,
214 const ConnectToServiceCallback& callback,
215 const ConnectToServiceErrorCallback& error_callback) {
216 scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
217 socket->Connect(device_.get(), uuid, base::Bind(callback, socket),
221 void BluetoothClassicDeviceMac::ConnectToServiceInsecurely(
222 const BluetoothUUID& uuid,
223 const ConnectToServiceCallback& callback,
224 const ConnectToServiceErrorCallback& error_callback) {
225 error_callback.Run(kApiUnavailable);
228 void BluetoothClassicDeviceMac::CreateGattConnection(
229 const GattConnectionCallback& callback,
230 const ConnectErrorCallback& error_callback) {
231 // TODO(armansito): Implement.
232 error_callback.Run(ERROR_UNSUPPORTED_DEVICE);
235 NSDate* BluetoothClassicDeviceMac::GetLastUpdateTime() const {
236 return [device_ getLastInquiryUpdate];
239 int BluetoothClassicDeviceMac::GetHostTransmitPower(
240 BluetoothHCITransmitPowerLevelType power_level_type) const {
241 IOBluetoothHostController* controller =
242 [IOBluetoothHostController defaultController];
244 // Bail if the undocumented API is unavailable on this machine.
245 SEL selector = @selector(BluetoothHCIReadTransmitPowerLevel:
247 outTransmitPowerLevel:);
248 if (![controller respondsToSelector:selector])
249 return kUnknownPower;
251 BluetoothHCITransmitPowerLevel power_level;
253 [controller BluetoothHCIReadTransmitPowerLevel:[device_ connectionHandle]
254 inType:power_level_type
255 outTransmitPowerLevel:&power_level];
256 if (result != kIOReturnSuccess)
257 return kUnknownPower;
263 std::string BluetoothClassicDeviceMac::GetDeviceAddress(
264 IOBluetoothDevice* device) {
265 return CanonicalizeAddress(base::SysNSStringToUTF8([device addressString]));
268 } // namespace device