Mark //testing/perf target testonly.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac.mm
blob6aa450cf59234381599b49861a63edc68670fff8
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_adapter_mac.h"
7 #import <IOBluetooth/objc/IOBluetoothDevice.h>
8 #import <IOBluetooth/objc/IOBluetoothHostController.h>
10 #include <string>
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/containers/hash_tables.h"
15 #include "base/location.h"
16 #include "base/mac/sdk_forward_declarations.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "device/bluetooth/bluetooth_device_mac.h"
24 #include "device/bluetooth/bluetooth_socket_mac.h"
25 #include "device/bluetooth/bluetooth_uuid.h"
27 namespace {
29 // The frequency with which to poll the adapter for updates.
30 const int kPollIntervalMs = 500;
32 // The length of time that must elapse since the last Inquiry response before a
33 // discovered Classic device is considered to be no longer available.
34 const NSTimeInterval kDiscoveryTimeoutSec = 3 * 60;  // 3 minutes
36 }  // namespace
38 namespace device {
40 // static
41 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
42     const InitCallback& init_callback) {
43   return BluetoothAdapterMac::CreateAdapter();
46 // static
47 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
48   BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
49   adapter->Init();
50   return adapter->weak_ptr_factory_.GetWeakPtr();
53 BluetoothAdapterMac::BluetoothAdapterMac()
54     : BluetoothAdapter(),
55       powered_(false),
56       num_discovery_sessions_(0),
57       classic_discovery_manager_(
58           BluetoothDiscoveryManagerMac::CreateClassic(this)),
59       weak_ptr_factory_(this) {
60   DCHECK(classic_discovery_manager_.get());
63 BluetoothAdapterMac::~BluetoothAdapterMac() {
66 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
67   DCHECK(observer);
68   observers_.AddObserver(observer);
71 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
72   DCHECK(observer);
73   observers_.RemoveObserver(observer);
76 std::string BluetoothAdapterMac::GetAddress() const {
77   return address_;
80 std::string BluetoothAdapterMac::GetName() const {
81   return name_;
84 void BluetoothAdapterMac::SetName(const std::string& name,
85                                   const base::Closure& callback,
86                                   const ErrorCallback& error_callback) {
87   NOTIMPLEMENTED();
90 bool BluetoothAdapterMac::IsInitialized() const {
91   return true;
94 bool BluetoothAdapterMac::IsPresent() const {
95   return !address_.empty();
98 bool BluetoothAdapterMac::IsPowered() const {
99   return powered_;
102 void BluetoothAdapterMac::SetPowered(bool powered,
103                                      const base::Closure& callback,
104                                      const ErrorCallback& error_callback) {
105   NOTIMPLEMENTED();
108 bool BluetoothAdapterMac::IsDiscoverable() const {
109   NOTIMPLEMENTED();
110   return false;
113 void BluetoothAdapterMac::SetDiscoverable(
114     bool discoverable,
115     const base::Closure& callback,
116     const ErrorCallback& error_callback) {
117   NOTIMPLEMENTED();
120 bool BluetoothAdapterMac::IsDiscovering() const {
121   return classic_discovery_manager_->IsDiscovering();
124 void BluetoothAdapterMac::CreateRfcommService(
125     const BluetoothUUID& uuid,
126     const ServiceOptions& options,
127     const CreateServiceCallback& callback,
128     const CreateServiceErrorCallback& error_callback) {
129   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
130   socket->ListenUsingRfcomm(
131       this, uuid, options, base::Bind(callback, socket), error_callback);
134 void BluetoothAdapterMac::CreateL2capService(
135     const BluetoothUUID& uuid,
136     const ServiceOptions& options,
137     const CreateServiceCallback& callback,
138     const CreateServiceErrorCallback& error_callback) {
139   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
140   socket->ListenUsingL2cap(
141       this, uuid, options, base::Bind(callback, socket), error_callback);
144 void BluetoothAdapterMac::RegisterAudioSink(
145     const BluetoothAudioSink::Options& options,
146     const AcquiredCallback& callback,
147     const BluetoothAudioSink::ErrorCallback& error_callback) {
148   NOTIMPLEMENTED();
149   error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
152 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
153   DeviceAdded(device);
156 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
157   if (unexpected) {
158     DVLOG(1) << "Discovery stopped unexpectedly";
159     num_discovery_sessions_ = 0;
160     MarkDiscoverySessionsAsInactive();
161   }
162   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
163                     observers_,
164                     AdapterDiscoveringChanged(this, false));
167 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
168   // TODO(isherman): Investigate whether this method can be replaced with a call
169   // to +registerForConnectNotifications:selector:.
170   DVLOG(1) << "Adapter registered a new connection from device with address: "
171            << BluetoothDeviceMac::GetDeviceAddress(device);
172   DeviceAdded(device);
175 void BluetoothAdapterMac::DeleteOnCorrectThread() const {
176   if (ui_task_runner_->RunsTasksOnCurrentThread() ||
177       !ui_task_runner_->DeleteSoon(FROM_HERE, this))
178     delete this;
181 void BluetoothAdapterMac::AddDiscoverySession(
182     const base::Closure& callback,
183     const ErrorCallback& error_callback) {
184   DVLOG(1) << __func__;
185   if (num_discovery_sessions_ > 0) {
186     DCHECK(IsDiscovering());
187     num_discovery_sessions_++;
188     callback.Run();
189     return;
190   }
192   DCHECK_EQ(0, num_discovery_sessions_);
194   if (!classic_discovery_manager_->StartDiscovery()) {
195     DVLOG(1) << "Failed to add a discovery session";
196     error_callback.Run();
197     return;
198   }
200   DVLOG(1) << "Added a discovery session";
201   num_discovery_sessions_++;
202   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
203                     observers_,
204                     AdapterDiscoveringChanged(this, true));
205   callback.Run();
208 void BluetoothAdapterMac::RemoveDiscoverySession(
209     const base::Closure& callback,
210     const ErrorCallback& error_callback) {
211   DVLOG(1) << __func__;
213   if (num_discovery_sessions_ > 1) {
214     // There are active sessions other than the one currently being removed.
215     DCHECK(IsDiscovering());
216     num_discovery_sessions_--;
217     callback.Run();
218     return;
219   }
221   if (num_discovery_sessions_ == 0) {
222     DVLOG(1) << "No active discovery sessions. Returning error.";
223     error_callback.Run();
224     return;
225   }
227   if (!classic_discovery_manager_->StopDiscovery()) {
228     DVLOG(1) << "Failed to stop discovery";
229     error_callback.Run();
230     return;
231   }
233   DVLOG(1) << "Discovery stopped";
234   num_discovery_sessions_--;
235   callback.Run();
238 void BluetoothAdapterMac::RemovePairingDelegateInternal(
239     BluetoothDevice::PairingDelegate* pairing_delegate) {
242 void BluetoothAdapterMac::Init() {
243   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
244   PollAdapter();
247 void BluetoothAdapterMac::InitForTest(
248     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
249   ui_task_runner_ = ui_task_runner;
250   PollAdapter();
253 void BluetoothAdapterMac::PollAdapter() {
254   bool was_present = IsPresent();
255   std::string name;
256   std::string address;
257   bool powered = false;
258   IOBluetoothHostController* controller =
259       [IOBluetoothHostController defaultController];
261   if (controller != nil) {
262     name = base::SysNSStringToUTF8([controller nameAsString]);
263     address = BluetoothDevice::CanonicalizeAddress(
264         base::SysNSStringToUTF8([controller addressAsString]));
265     powered = ([controller powerState] == kBluetoothHCIPowerStateON);
266   }
268   bool is_present = !address.empty();
269   name_ = name;
270   address_ = address;
272   if (was_present != is_present) {
273     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
274                       AdapterPresentChanged(this, is_present));
275   }
276   if (powered_ != powered) {
277     powered_ = powered;
278     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
279                       AdapterPoweredChanged(this, powered_));
280   }
282   UpdateDevices();
284   ui_task_runner_->PostDelayedTask(
285       FROM_HERE,
286       base::Bind(&BluetoothAdapterMac::PollAdapter,
287                  weak_ptr_factory_.GetWeakPtr()),
288       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
291 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
292   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
294   // Only notify observers once per device.
295   if (devices_.count(device_address))
296     return;
298   devices_[device_address] = new BluetoothDeviceMac(device);
299   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
300                     observers_,
301                     DeviceAdded(this, devices_[device_address]));
304 void BluetoothAdapterMac::UpdateDevices() {
305   // Notify observers if any previously seen devices are no longer available,
306   // i.e. if they are no longer paired, connected, nor recently discovered via
307   // an inquiry.
308   std::set<std::string> removed_devices;
309   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
310     BluetoothDevice* device = it->second;
311     if (device->IsPaired() || device->IsConnected())
312       continue;
314     NSDate* last_inquiry_update =
315         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
316     if (last_inquiry_update &&
317         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
318       continue;
320     FOR_EACH_OBSERVER(
321         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
322     delete device;
323     removed_devices.insert(it->first);
324     // The device will be erased from the map in the loop immediately below.
325   }
326   for (const std::string& device_address : removed_devices) {
327     size_t num_removed = devices_.erase(device_address);
328     DCHECK_EQ(num_removed, 1U);
329   }
331   // Add any new paired devices.
332   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
333     DeviceAdded(device);
334   }
337 }  // namespace device