Process Alt-Svc headers.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac.mm
blobbdbca4694f5322940349080f92124f3e488c51ac
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/mac_util.h"
17 #include "base/mac/sdk_forward_declarations.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/profiler/scoped_tracker.h"
20 #include "base/sequenced_task_runner.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/thread_task_runner_handle.h"
24 #include "base/time/time.h"
25 #include "device/bluetooth/bluetooth_classic_device_mac.h"
26 #include "device/bluetooth/bluetooth_discovery_session.h"
27 #include "device/bluetooth/bluetooth_low_energy_central_manager_delegate.h"
28 #include "device/bluetooth/bluetooth_socket_mac.h"
29 #include "device/bluetooth/bluetooth_uuid.h"
31 namespace {
33 // The frequency with which to poll the adapter for updates.
34 const int kPollIntervalMs = 500;
36 }  // namespace
38 namespace device {
40 // static
41 const NSTimeInterval BluetoothAdapterMac::kDiscoveryTimeoutSec =
42     180;  // 3 minutes
44 // static
45 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
46     const InitCallback& init_callback) {
47   return BluetoothAdapterMac::CreateAdapter();
50 // static
51 base::WeakPtr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapter() {
52   BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
53   adapter->Init();
54   return adapter->weak_ptr_factory_.GetWeakPtr();
57 // static
58 base::WeakPtr<BluetoothAdapterMac> BluetoothAdapterMac::CreateAdapterForTest(
59     std::string name,
60     std::string address,
61     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
62   BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
63   adapter->InitForTest(ui_task_runner);
64   adapter->name_ = name;
65   adapter->address_ = address;
66   return adapter->weak_ptr_factory_.GetWeakPtr();
69 BluetoothAdapterMac::BluetoothAdapterMac()
70     : BluetoothAdapter(),
71       classic_powered_(false),
72       num_discovery_sessions_(0),
73       classic_discovery_manager_(
74           BluetoothDiscoveryManagerMac::CreateClassic(this)),
75       weak_ptr_factory_(this) {
76   if (IsLowEnergyAvailable()) {
77     low_energy_discovery_manager_.reset(
78         BluetoothLowEnergyDiscoveryManagerMac::Create(this));
79     low_energy_central_manager_delegate_.reset(
80         [[BluetoothLowEnergyCentralManagerDelegate alloc]
81             initWithDiscoveryManager:low_energy_discovery_manager_.get()
82                           andAdapter:this]);
83     Class aClass = NSClassFromString(@"CBCentralManager");
84     low_energy_central_manager_.reset([[aClass alloc]
85         initWithDelegate:low_energy_central_manager_delegate_.get()
86                    queue:dispatch_get_main_queue()]);
87     low_energy_discovery_manager_->SetCentralManager(
88         low_energy_central_manager_.get());
89   }
90   DCHECK(classic_discovery_manager_.get());
93 BluetoothAdapterMac::~BluetoothAdapterMac() {
96 std::string BluetoothAdapterMac::GetAddress() const {
97   return address_;
100 std::string BluetoothAdapterMac::GetName() const {
101   return name_;
104 void BluetoothAdapterMac::SetName(const std::string& name,
105                                   const base::Closure& callback,
106                                   const ErrorCallback& error_callback) {
107   NOTIMPLEMENTED();
110 bool BluetoothAdapterMac::IsInitialized() const {
111   return true;
114 bool BluetoothAdapterMac::IsPresent() const {
115   bool is_present = !address_.empty();
116   if (IsLowEnergyAvailable()) {
117     is_present = is_present || ([low_energy_central_manager_ state] ==
118                                 CBCentralManagerStatePoweredOn);
119   }
120   return is_present;
123 bool BluetoothAdapterMac::IsPowered() const {
124   bool is_powered = classic_powered_;
125   if (IsLowEnergyAvailable()) {
126     is_powered = is_powered || ([low_energy_central_manager_ state] ==
127                                 CBCentralManagerStatePoweredOn);
128   }
129   return is_powered;
132 void BluetoothAdapterMac::SetPowered(bool powered,
133                                      const base::Closure& callback,
134                                      const ErrorCallback& error_callback) {
135   NOTIMPLEMENTED();
138 // TODO(krstnmnlsn): If this information is retrievable form IOBluetooth we
139 // should return the discoverable status.
140 bool BluetoothAdapterMac::IsDiscoverable() const {
141   return false;
144 void BluetoothAdapterMac::SetDiscoverable(
145     bool discoverable,
146     const base::Closure& callback,
147     const ErrorCallback& error_callback) {
148   NOTIMPLEMENTED();
151 bool BluetoothAdapterMac::IsDiscovering() const {
152   bool is_discovering = classic_discovery_manager_->IsDiscovering();
153   if (IsLowEnergyAvailable())
154     is_discovering =
155         is_discovering || low_energy_discovery_manager_->IsDiscovering();
156   return is_discovering;
159 void BluetoothAdapterMac::CreateRfcommService(
160     const BluetoothUUID& uuid,
161     const ServiceOptions& options,
162     const CreateServiceCallback& callback,
163     const CreateServiceErrorCallback& error_callback) {
164   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
165   socket->ListenUsingRfcomm(
166       this, uuid, options, base::Bind(callback, socket), error_callback);
169 void BluetoothAdapterMac::CreateL2capService(
170     const BluetoothUUID& uuid,
171     const ServiceOptions& options,
172     const CreateServiceCallback& callback,
173     const CreateServiceErrorCallback& error_callback) {
174   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
175   socket->ListenUsingL2cap(
176       this, uuid, options, base::Bind(callback, socket), error_callback);
179 void BluetoothAdapterMac::RegisterAudioSink(
180     const BluetoothAudioSink::Options& options,
181     const AcquiredCallback& callback,
182     const BluetoothAudioSink::ErrorCallback& error_callback) {
183   NOTIMPLEMENTED();
184   error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
187 void BluetoothAdapterMac::RegisterAdvertisement(
188     scoped_ptr<BluetoothAdvertisement::Data> advertisement_data,
189     const CreateAdvertisementCallback& callback,
190     const CreateAdvertisementErrorCallback& error_callback) {
191   NOTIMPLEMENTED();
192   error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
195 void BluetoothAdapterMac::ClassicDeviceFound(IOBluetoothDevice* device) {
196   ClassicDeviceAdded(device);
199 void BluetoothAdapterMac::ClassicDiscoveryStopped(bool unexpected) {
200   if (unexpected) {
201     DVLOG(1) << "Discovery stopped unexpectedly";
202     num_discovery_sessions_ = 0;
203     MarkDiscoverySessionsAsInactive();
204   }
205   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
206                     observers_,
207                     AdapterDiscoveringChanged(this, false));
210 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
211   // TODO(isherman): Investigate whether this method can be replaced with a call
212   // to +registerForConnectNotifications:selector:.
213   DVLOG(1) << "Adapter registered a new connection from device with address: "
214            << BluetoothClassicDeviceMac::GetDeviceAddress(device);
215   ClassicDeviceAdded(device);
218 // static
219 bool BluetoothAdapterMac::IsLowEnergyAvailable() {
220   return base::mac::IsOSYosemiteOrLater();
223 void BluetoothAdapterMac::SetCentralManagerForTesting(
224     CBCentralManager* central_manager) {
225   CHECK(BluetoothAdapterMac::IsLowEnergyAvailable());
226   [central_manager performSelector:@selector(setDelegate:)
227                         withObject:low_energy_central_manager_delegate_];
228   low_energy_central_manager_.reset(central_manager);
229   low_energy_discovery_manager_->SetCentralManager(
230       low_energy_central_manager_.get());
233 void BluetoothAdapterMac::RemovePairingDelegateInternal(
234     BluetoothDevice::PairingDelegate* pairing_delegate) {
237 void BluetoothAdapterMac::AddDiscoverySession(
238     BluetoothDiscoveryFilter* discovery_filter,
239     const base::Closure& callback,
240     const ErrorCallback& error_callback) {
241   DVLOG(1) << __func__;
242   if (num_discovery_sessions_ > 0) {
243     DCHECK(IsDiscovering());
244     num_discovery_sessions_++;
245     // We are already running a discovery session, notify the system if the
246     // filter has changed.
247     if (!StartDiscovery(discovery_filter)) {
248       error_callback.Run();
249       return;
250     }
251     callback.Run();
252     return;
253   }
255   DCHECK_EQ(0, num_discovery_sessions_);
257   if (!StartDiscovery(discovery_filter)) {
258     error_callback.Run();
259     return;
260   }
262   DVLOG(1) << "Added a discovery session";
263   num_discovery_sessions_++;
264   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
265                     observers_,
266                     AdapterDiscoveringChanged(this, true));
267   callback.Run();
270 void BluetoothAdapterMac::RemoveDiscoverySession(
271     BluetoothDiscoveryFilter* discovery_filter,
272     const base::Closure& callback,
273     const ErrorCallback& error_callback) {
274   DVLOG(1) << __func__;
276   if (num_discovery_sessions_ > 1) {
277     // There are active sessions other than the one currently being removed.
278     DCHECK(IsDiscovering());
279     num_discovery_sessions_--;
280     callback.Run();
281     return;
282   }
284   if (num_discovery_sessions_ == 0) {
285     DVLOG(1) << "No active discovery sessions. Returning error.";
286     error_callback.Run();
287     return;
288   }
290   // Default to dual discovery if |discovery_filter| is NULL.
291   BluetoothDiscoveryFilter::TransportMask transport =
292       BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL;
293   if (discovery_filter)
294     transport = discovery_filter->GetTransport();
296   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC) {
297     if (!classic_discovery_manager_->StopDiscovery()) {
298       DVLOG(1) << "Failed to stop classic discovery";
299       error_callback.Run();
300       return;
301     }
302   }
303   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_LE) {
304     if (IsLowEnergyAvailable())
305       low_energy_discovery_manager_->StopDiscovery();
306   }
308   DVLOG(1) << "Discovery stopped";
309   num_discovery_sessions_--;
310   callback.Run();
313 void BluetoothAdapterMac::SetDiscoveryFilter(
314     scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
315     const base::Closure& callback,
316     const ErrorCallback& error_callback) {
317   NOTIMPLEMENTED();
318   error_callback.Run();
321 bool BluetoothAdapterMac::StartDiscovery(
322     BluetoothDiscoveryFilter* discovery_filter) {
323   // Default to dual discovery if |discovery_filter| is NULL.  IOBluetooth seems
324   // allow starting low energy and classic discovery at once.
325   BluetoothDiscoveryFilter::TransportMask transport =
326       BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL;
327   if (discovery_filter)
328     transport = discovery_filter->GetTransport();
330   if ((transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC) &&
331       !classic_discovery_manager_->IsDiscovering()) {
332     // TODO(krstnmnlsn): If a classic discovery session is already running then
333     // we should update its filter. crbug.com/498056
334     if (!classic_discovery_manager_->StartDiscovery()) {
335       DVLOG(1) << "Failed to add a classic discovery session";
336       return false;
337     }
338   }
339   if (transport & BluetoothDiscoveryFilter::Transport::TRANSPORT_LE) {
340     // Begin a low energy discovery session or update it if one is already
341     // running.
342     if (IsLowEnergyAvailable())
343       low_energy_discovery_manager_->StartDiscovery(
344           BluetoothDevice::UUIDList());
345   }
346   return true;
349 void BluetoothAdapterMac::Init() {
350   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
351   PollAdapter();
354 void BluetoothAdapterMac::InitForTest(
355     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
356   ui_task_runner_ = ui_task_runner;
359 void BluetoothAdapterMac::PollAdapter() {
360   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
361   // is fixed.
362   tracked_objects::ScopedTracker tracking_profile1(
363       FROM_HERE_WITH_EXPLICIT_FUNCTION(
364           "461181 BluetoothAdapterMac::PollAdapter::Start"));
365   bool was_present = IsPresent();
366   std::string address;
367   bool classic_powered = false;
368   IOBluetoothHostController* controller =
369       [IOBluetoothHostController defaultController];
371   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
372   // is fixed.
373   tracked_objects::ScopedTracker tracking_profile2(
374       FROM_HERE_WITH_EXPLICIT_FUNCTION(
375           "461181 BluetoothAdapterMac::PollAdapter::GetControllerStats"));
376   if (controller != nil) {
377     address = BluetoothDevice::CanonicalizeAddress(
378         base::SysNSStringToUTF8([controller addressAsString]));
379     classic_powered = ([controller powerState] == kBluetoothHCIPowerStateON);
381     // For performance reasons, cache the adapter's name. It's not uncommon for
382     // a call to [controller nameAsString] to take tens of milliseconds. Note
383     // that this caching strategy might result in clients receiving a stale
384     // name. If this is a significant issue, then some more sophisticated
385     // workaround for the performance bottleneck will be needed. For additional
386     // context, see http://crbug.com/461181 and http://crbug.com/467316
387     if (address != address_ || (!address.empty() && name_.empty()))
388       name_ = base::SysNSStringToUTF8([controller nameAsString]);
389   }
391   bool is_present = !address.empty();
392   address_ = address;
394   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
395   // is fixed.
396   tracked_objects::ScopedTracker tracking_profile3(
397       FROM_HERE_WITH_EXPLICIT_FUNCTION(
398           "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged"));
399   if (was_present != is_present) {
400     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
401                       AdapterPresentChanged(this, is_present));
402   }
404   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
405   // is fixed.
406   tracked_objects::ScopedTracker tracking_profile4(
407       FROM_HERE_WITH_EXPLICIT_FUNCTION(
408           "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
409   if (classic_powered_ != classic_powered) {
410     classic_powered_ = classic_powered;
411     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
412                       AdapterPoweredChanged(this, classic_powered_));
413   }
415   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
416   // is fixed.
417   tracked_objects::ScopedTracker tracking_profile5(
418       FROM_HERE_WITH_EXPLICIT_FUNCTION(
419           "461181 BluetoothAdapterMac::PollAdapter::RemoveTimedOutDevices"));
420   RemoveTimedOutDevices();
422   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
423   // is fixed.
424   tracked_objects::ScopedTracker tracking_profile6(
425       FROM_HERE_WITH_EXPLICIT_FUNCTION(
426           "461181 BluetoothAdapterMac::PollAdapter::AddPairedDevices"));
427   AddPairedDevices();
429   ui_task_runner_->PostDelayedTask(
430       FROM_HERE,
431       base::Bind(&BluetoothAdapterMac::PollAdapter,
432                  weak_ptr_factory_.GetWeakPtr()),
433       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
436 void BluetoothAdapterMac::ClassicDeviceAdded(IOBluetoothDevice* device) {
437   std::string device_address =
438       BluetoothClassicDeviceMac::GetDeviceAddress(device);
440   // Only notify observers once per device.
441   if (devices_.count(device_address))
442     return;
444   devices_[device_address] = new BluetoothClassicDeviceMac(device);
445   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
446                     observers_,
447                     DeviceAdded(this, devices_[device_address]));
450 void BluetoothAdapterMac::LowEnergyDeviceUpdated(
451     CBPeripheral* peripheral,
452     NSDictionary* advertisement_data,
453     int rssi) {
454   std::string device_address =
455       BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
456   // Get a reference to the actual device pointer held by |devices_| (if
457   // |device_address| has no entry in the map a NULL pointer is created by the
458   // std::map [] operator).
459   BluetoothDevice*& device_reference = devices_[device_address];
460   if (!device_reference) {
461     // A new device has been found.
462     device_reference =
463         new BluetoothLowEnergyDeviceMac(peripheral, advertisement_data, rssi);
464     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
465                       DeviceAdded(this, device_reference));
466     return;
467   }
469   if (static_cast<BluetoothLowEnergyDeviceMac*>(device_reference)
470           ->GetIdentifier() !=
471       BluetoothLowEnergyDeviceMac::GetPeripheralIdentifier(peripheral)) {
472     // Collision, two identifiers map to the same hash address.  With a 48 bit
473     // hash the probability of this occuring with 10,000 devices
474     // simultaneously present is 1e-6 (see
475     // https://en.wikipedia.org/wiki/Birthday_problem#Probability_table).  We
476     // ignore the second device by returning.
477     return;
478   }
480   // A device has an update.
481   static_cast<BluetoothLowEnergyDeviceMac*>(device_reference)
482       ->Update(peripheral, advertisement_data, rssi);
483   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
484                     DeviceChanged(this, device_reference));
487 // TODO(krstnmnlsn): Implement. crbug.com/511025
488 void BluetoothAdapterMac::LowEnergyCentralManagerUpdatedState() {}
490 void BluetoothAdapterMac::RemoveTimedOutDevices() {
491   // Notify observers if any previously seen devices are no longer available,
492   // i.e. if they are no longer paired, connected, nor recently discovered via
493   // an inquiry.
494   std::set<std::string> removed_devices;
495   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
496     BluetoothDevice* device = it->second;
497     if (device->IsPaired() || device->IsConnected())
498       continue;
500     NSDate* last_update_time =
501         static_cast<BluetoothDeviceMac*>(device)->GetLastUpdateTime();
502     if (last_update_time &&
503         -[last_update_time timeIntervalSinceNow] < kDiscoveryTimeoutSec)
504       continue;
506     FOR_EACH_OBSERVER(
507         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
508     delete device;
509     removed_devices.insert(it->first);
510     // The device will be erased from the map in the loop immediately below.
511   }
512   for (const std::string& device_address : removed_devices) {
513     size_t num_removed = devices_.erase(device_address);
514     DCHECK_EQ(num_removed, 1U);
515   }
518 void BluetoothAdapterMac::AddPairedDevices() {
519   // Add any new paired devices.
520   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
521     ClassicDeviceAdded(device);
522   }
525 }  // namespace device