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