Roll WebRTC 8837:8852, Libjingle 8836:8851
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac.mm
blobfc7541405162f2bd75b63884cb4c5f66b64b2741
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/profiler/scoped_tracker.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/strings/sys_string_conversions.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "device/bluetooth/bluetooth_device_mac.h"
25 #include "device/bluetooth/bluetooth_socket_mac.h"
26 #include "device/bluetooth/bluetooth_uuid.h"
28 namespace {
30 // The frequency with which to poll the adapter for updates.
31 const int kPollIntervalMs = 500;
33 // The length of time that must elapse since the last Inquiry response before a
34 // discovered Classic device is considered to be no longer available.
35 const NSTimeInterval kDiscoveryTimeoutSec = 3 * 60;  // 3 minutes
37 }  // namespace
39 namespace device {
41 // static
42 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
43     const InitCallback& init_callback) {
44   return BluetoothAdapterMac::CreateAdapter();
47 // static
48 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
49   BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
50   adapter->Init();
51   return adapter->weak_ptr_factory_.GetWeakPtr();
54 BluetoothAdapterMac::BluetoothAdapterMac()
55     : BluetoothAdapter(),
56       powered_(false),
57       num_discovery_sessions_(0),
58       classic_discovery_manager_(
59           BluetoothDiscoveryManagerMac::CreateClassic(this)),
60       weak_ptr_factory_(this) {
61   DCHECK(classic_discovery_manager_.get());
64 BluetoothAdapterMac::~BluetoothAdapterMac() {
67 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
68   DCHECK(observer);
69   observers_.AddObserver(observer);
72 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
73   DCHECK(observer);
74   observers_.RemoveObserver(observer);
77 std::string BluetoothAdapterMac::GetAddress() const {
78   return address_;
81 std::string BluetoothAdapterMac::GetName() const {
82   return name_;
85 void BluetoothAdapterMac::SetName(const std::string& name,
86                                   const base::Closure& callback,
87                                   const ErrorCallback& error_callback) {
88   NOTIMPLEMENTED();
91 bool BluetoothAdapterMac::IsInitialized() const {
92   return true;
95 bool BluetoothAdapterMac::IsPresent() const {
96   return !address_.empty();
99 bool BluetoothAdapterMac::IsPowered() const {
100   return powered_;
103 void BluetoothAdapterMac::SetPowered(bool powered,
104                                      const base::Closure& callback,
105                                      const ErrorCallback& error_callback) {
106   NOTIMPLEMENTED();
109 bool BluetoothAdapterMac::IsDiscoverable() const {
110   NOTIMPLEMENTED();
111   return false;
114 void BluetoothAdapterMac::SetDiscoverable(
115     bool discoverable,
116     const base::Closure& callback,
117     const ErrorCallback& error_callback) {
118   NOTIMPLEMENTED();
121 bool BluetoothAdapterMac::IsDiscovering() const {
122   return classic_discovery_manager_->IsDiscovering();
125 void BluetoothAdapterMac::CreateRfcommService(
126     const BluetoothUUID& uuid,
127     const ServiceOptions& options,
128     const CreateServiceCallback& callback,
129     const CreateServiceErrorCallback& error_callback) {
130   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
131   socket->ListenUsingRfcomm(
132       this, uuid, options, base::Bind(callback, socket), error_callback);
135 void BluetoothAdapterMac::CreateL2capService(
136     const BluetoothUUID& uuid,
137     const ServiceOptions& options,
138     const CreateServiceCallback& callback,
139     const CreateServiceErrorCallback& error_callback) {
140   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
141   socket->ListenUsingL2cap(
142       this, uuid, options, base::Bind(callback, socket), error_callback);
145 void BluetoothAdapterMac::RegisterAudioSink(
146     const BluetoothAudioSink::Options& options,
147     const AcquiredCallback& callback,
148     const BluetoothAudioSink::ErrorCallback& error_callback) {
149   NOTIMPLEMENTED();
150   error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
153 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
154   DeviceAdded(device);
157 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
158   if (unexpected) {
159     DVLOG(1) << "Discovery stopped unexpectedly";
160     num_discovery_sessions_ = 0;
161     MarkDiscoverySessionsAsInactive();
162   }
163   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
164                     observers_,
165                     AdapterDiscoveringChanged(this, false));
168 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
169   // TODO(isherman): Investigate whether this method can be replaced with a call
170   // to +registerForConnectNotifications:selector:.
171   DVLOG(1) << "Adapter registered a new connection from device with address: "
172            << BluetoothDeviceMac::GetDeviceAddress(device);
173   DeviceAdded(device);
176 void BluetoothAdapterMac::DeleteOnCorrectThread() const {
177   if (ui_task_runner_->RunsTasksOnCurrentThread() ||
178       !ui_task_runner_->DeleteSoon(FROM_HERE, this))
179     delete this;
182 void BluetoothAdapterMac::AddDiscoverySession(
183     const base::Closure& callback,
184     const ErrorCallback& error_callback) {
185   DVLOG(1) << __func__;
186   if (num_discovery_sessions_ > 0) {
187     DCHECK(IsDiscovering());
188     num_discovery_sessions_++;
189     callback.Run();
190     return;
191   }
193   DCHECK_EQ(0, num_discovery_sessions_);
195   if (!classic_discovery_manager_->StartDiscovery()) {
196     DVLOG(1) << "Failed to add a discovery session";
197     error_callback.Run();
198     return;
199   }
201   DVLOG(1) << "Added a discovery session";
202   num_discovery_sessions_++;
203   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
204                     observers_,
205                     AdapterDiscoveringChanged(this, true));
206   callback.Run();
209 void BluetoothAdapterMac::RemoveDiscoverySession(
210     const base::Closure& callback,
211     const ErrorCallback& error_callback) {
212   DVLOG(1) << __func__;
214   if (num_discovery_sessions_ > 1) {
215     // There are active sessions other than the one currently being removed.
216     DCHECK(IsDiscovering());
217     num_discovery_sessions_--;
218     callback.Run();
219     return;
220   }
222   if (num_discovery_sessions_ == 0) {
223     DVLOG(1) << "No active discovery sessions. Returning error.";
224     error_callback.Run();
225     return;
226   }
228   if (!classic_discovery_manager_->StopDiscovery()) {
229     DVLOG(1) << "Failed to stop discovery";
230     error_callback.Run();
231     return;
232   }
234   DVLOG(1) << "Discovery stopped";
235   num_discovery_sessions_--;
236   callback.Run();
239 void BluetoothAdapterMac::RemovePairingDelegateInternal(
240     BluetoothDevice::PairingDelegate* pairing_delegate) {
243 void BluetoothAdapterMac::Init() {
244   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
245   PollAdapter();
248 void BluetoothAdapterMac::InitForTest(
249     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
250   ui_task_runner_ = ui_task_runner;
251   PollAdapter();
254 void BluetoothAdapterMac::PollAdapter() {
255   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
256   // is fixed.
257   tracked_objects::ScopedTracker tracking_profile1(
258       FROM_HERE_WITH_EXPLICIT_FUNCTION(
259           "461181 BluetoothAdapterMac::PollAdapter::Start"));
260   bool was_present = IsPresent();
261   std::string name;
262   std::string address;
263   bool powered = false;
264   IOBluetoothHostController* controller =
265       [IOBluetoothHostController defaultController];
267   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
268   // is fixed.
269   tracked_objects::ScopedTracker tracking_profile2(
270       FROM_HERE_WITH_EXPLICIT_FUNCTION(
271           "461181 BluetoothAdapterMac::PollAdapter::GetControllerStats"));
272   if (controller != nil) {
273     name = base::SysNSStringToUTF8([controller nameAsString]);
274     address = BluetoothDevice::CanonicalizeAddress(
275         base::SysNSStringToUTF8([controller addressAsString]));
276     powered = ([controller powerState] == kBluetoothHCIPowerStateON);
277   }
279   bool is_present = !address.empty();
280   name_ = name;
281   address_ = address;
283   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
284   // is fixed.
285   tracked_objects::ScopedTracker tracking_profile3(
286       FROM_HERE_WITH_EXPLICIT_FUNCTION(
287           "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged"));
288   if (was_present != is_present) {
289     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
290                       AdapterPresentChanged(this, is_present));
291   }
293   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
294   // is fixed.
295   tracked_objects::ScopedTracker tracking_profile4(
296       FROM_HERE_WITH_EXPLICIT_FUNCTION(
297           "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
298   if (powered_ != powered) {
299     powered_ = powered;
300     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
301                       AdapterPoweredChanged(this, powered_));
302   }
304   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
305   // is fixed.
306   tracked_objects::ScopedTracker tracking_profile5(
307       FROM_HERE_WITH_EXPLICIT_FUNCTION(
308           "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices"));
309   UpdateDevices();
311   ui_task_runner_->PostDelayedTask(
312       FROM_HERE,
313       base::Bind(&BluetoothAdapterMac::PollAdapter,
314                  weak_ptr_factory_.GetWeakPtr()),
315       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
318 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
319   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
321   // Only notify observers once per device.
322   if (devices_.count(device_address))
323     return;
325   devices_[device_address] = new BluetoothDeviceMac(device);
326   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
327                     observers_,
328                     DeviceAdded(this, devices_[device_address]));
331 void BluetoothAdapterMac::UpdateDevices() {
332   // Notify observers if any previously seen devices are no longer available,
333   // i.e. if they are no longer paired, connected, nor recently discovered via
334   // an inquiry.
335   std::set<std::string> removed_devices;
336   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
337     BluetoothDevice* device = it->second;
338     if (device->IsPaired() || device->IsConnected())
339       continue;
341     NSDate* last_inquiry_update =
342         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
343     if (last_inquiry_update &&
344         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
345       continue;
347     FOR_EACH_OBSERVER(
348         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
349     delete device;
350     removed_devices.insert(it->first);
351     // The device will be erased from the map in the loop immediately below.
352   }
353   for (const std::string& device_address : removed_devices) {
354     size_t num_removed = devices_.erase(device_address);
355     DCHECK_EQ(num_removed, 1U);
356   }
358   // Add any new paired devices.
359   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
360     DeviceAdded(device);
361   }
364 }  // namespace device