Add logging.store and logging.uploadStored to thunk.js for Hangouts.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac.mm
blobae557d0314b3395ce6d8ff4790fa15a19be63056
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 std::string BluetoothAdapterMac::GetAddress() const {
68   return address_;
71 std::string BluetoothAdapterMac::GetName() const {
72   return name_;
75 void BluetoothAdapterMac::SetName(const std::string& name,
76                                   const base::Closure& callback,
77                                   const ErrorCallback& error_callback) {
78   NOTIMPLEMENTED();
81 bool BluetoothAdapterMac::IsInitialized() const {
82   return true;
85 bool BluetoothAdapterMac::IsPresent() const {
86   return !address_.empty();
89 bool BluetoothAdapterMac::IsPowered() const {
90   return powered_;
93 void BluetoothAdapterMac::SetPowered(bool powered,
94                                      const base::Closure& callback,
95                                      const ErrorCallback& error_callback) {
96   NOTIMPLEMENTED();
99 bool BluetoothAdapterMac::IsDiscoverable() const {
100   NOTIMPLEMENTED();
101   return false;
104 void BluetoothAdapterMac::SetDiscoverable(
105     bool discoverable,
106     const base::Closure& callback,
107     const ErrorCallback& error_callback) {
108   NOTIMPLEMENTED();
111 bool BluetoothAdapterMac::IsDiscovering() const {
112   return classic_discovery_manager_->IsDiscovering();
115 void BluetoothAdapterMac::CreateRfcommService(
116     const BluetoothUUID& uuid,
117     const ServiceOptions& options,
118     const CreateServiceCallback& callback,
119     const CreateServiceErrorCallback& error_callback) {
120   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
121   socket->ListenUsingRfcomm(
122       this, uuid, options, base::Bind(callback, socket), error_callback);
125 void BluetoothAdapterMac::CreateL2capService(
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->ListenUsingL2cap(
132       this, uuid, options, base::Bind(callback, socket), error_callback);
135 void BluetoothAdapterMac::RegisterAudioSink(
136     const BluetoothAudioSink::Options& options,
137     const AcquiredCallback& callback,
138     const BluetoothAudioSink::ErrorCallback& error_callback) {
139   NOTIMPLEMENTED();
140   error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
143 void BluetoothAdapterMac::RegisterAdvertisement(
144     scoped_ptr<BluetoothAdvertisement::Data> advertisement_data,
145     const CreateAdvertisementCallback& callback,
146     const CreateAdvertisementErrorCallback& error_callback) {
147   NOTIMPLEMENTED();
148   error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
151 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
152   DeviceAdded(device);
155 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
156   if (unexpected) {
157     DVLOG(1) << "Discovery stopped unexpectedly";
158     num_discovery_sessions_ = 0;
159     MarkDiscoverySessionsAsInactive();
160   }
161   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
162                     observers_,
163                     AdapterDiscoveringChanged(this, false));
166 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
167   // TODO(isherman): Investigate whether this method can be replaced with a call
168   // to +registerForConnectNotifications:selector:.
169   DVLOG(1) << "Adapter registered a new connection from device with address: "
170            << BluetoothDeviceMac::GetDeviceAddress(device);
171   DeviceAdded(device);
174 void BluetoothAdapterMac::AddDiscoverySession(
175     BluetoothDiscoveryFilter* discovery_filter,
176     const base::Closure& callback,
177     const ErrorCallback& error_callback) {
178   DVLOG(1) << __func__;
179   if (num_discovery_sessions_ > 0) {
180     DCHECK(IsDiscovering());
181     num_discovery_sessions_++;
182     callback.Run();
183     return;
184   }
186   DCHECK_EQ(0, num_discovery_sessions_);
188   if (!classic_discovery_manager_->StartDiscovery()) {
189     DVLOG(1) << "Failed to add a discovery session";
190     error_callback.Run();
191     return;
192   }
194   DVLOG(1) << "Added a discovery session";
195   num_discovery_sessions_++;
196   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
197                     observers_,
198                     AdapterDiscoveringChanged(this, true));
199   callback.Run();
202 void BluetoothAdapterMac::RemoveDiscoverySession(
203     BluetoothDiscoveryFilter* discovery_filter,
204     const base::Closure& callback,
205     const ErrorCallback& error_callback) {
206   DVLOG(1) << __func__;
208   if (num_discovery_sessions_ > 1) {
209     // There are active sessions other than the one currently being removed.
210     DCHECK(IsDiscovering());
211     num_discovery_sessions_--;
212     callback.Run();
213     return;
214   }
216   if (num_discovery_sessions_ == 0) {
217     DVLOG(1) << "No active discovery sessions. Returning error.";
218     error_callback.Run();
219     return;
220   }
222   if (!classic_discovery_manager_->StopDiscovery()) {
223     DVLOG(1) << "Failed to stop discovery";
224     error_callback.Run();
225     return;
226   }
228   DVLOG(1) << "Discovery stopped";
229   num_discovery_sessions_--;
230   callback.Run();
233 void BluetoothAdapterMac::SetDiscoveryFilter(
234     scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
235     const base::Closure& callback,
236     const ErrorCallback& error_callback) {
237   NOTIMPLEMENTED();
238   error_callback.Run();
241 void BluetoothAdapterMac::RemovePairingDelegateInternal(
242     BluetoothDevice::PairingDelegate* pairing_delegate) {
245 void BluetoothAdapterMac::Init() {
246   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
247   PollAdapter();
250 void BluetoothAdapterMac::InitForTest(
251     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
252   ui_task_runner_ = ui_task_runner;
253   PollAdapter();
256 void BluetoothAdapterMac::PollAdapter() {
257   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
258   // is fixed.
259   tracked_objects::ScopedTracker tracking_profile1(
260       FROM_HERE_WITH_EXPLICIT_FUNCTION(
261           "461181 BluetoothAdapterMac::PollAdapter::Start"));
262   bool was_present = IsPresent();
263   std::string address;
264   bool powered = false;
265   IOBluetoothHostController* controller =
266       [IOBluetoothHostController defaultController];
268   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
269   // is fixed.
270   tracked_objects::ScopedTracker tracking_profile2(
271       FROM_HERE_WITH_EXPLICIT_FUNCTION(
272           "461181 BluetoothAdapterMac::PollAdapter::GetControllerStats"));
273   if (controller != nil) {
274     address = BluetoothDevice::CanonicalizeAddress(
275         base::SysNSStringToUTF8([controller addressAsString]));
276     powered = ([controller powerState] == kBluetoothHCIPowerStateON);
278     // For performance reasons, cache the adapter's name. It's not uncommon for
279     // a call to [controller nameAsString] to take tens of milliseconds. Note
280     // that this caching strategy might result in clients receiving a stale
281     // name. If this is a significant issue, then some more sophisticated
282     // workaround for the performance bottleneck will be needed. For additional
283     // context, see http://crbug.com/461181 and http://crbug.com/467316
284     if (address != address_ || (!address.empty() && name_.empty()))
285       name_ = base::SysNSStringToUTF8([controller nameAsString]);
286   }
288   bool is_present = !address.empty();
289   address_ = address;
291   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
292   // is fixed.
293   tracked_objects::ScopedTracker tracking_profile3(
294       FROM_HERE_WITH_EXPLICIT_FUNCTION(
295           "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged"));
296   if (was_present != is_present) {
297     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
298                       AdapterPresentChanged(this, is_present));
299   }
301   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
302   // is fixed.
303   tracked_objects::ScopedTracker tracking_profile4(
304       FROM_HERE_WITH_EXPLICIT_FUNCTION(
305           "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
306   if (powered_ != powered) {
307     powered_ = powered;
308     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
309                       AdapterPoweredChanged(this, powered_));
310   }
312   // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
313   // is fixed.
314   tracked_objects::ScopedTracker tracking_profile5(
315       FROM_HERE_WITH_EXPLICIT_FUNCTION(
316           "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices"));
317   UpdateDevices();
319   ui_task_runner_->PostDelayedTask(
320       FROM_HERE,
321       base::Bind(&BluetoothAdapterMac::PollAdapter,
322                  weak_ptr_factory_.GetWeakPtr()),
323       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
326 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
327   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
329   // Only notify observers once per device.
330   if (devices_.count(device_address))
331     return;
333   devices_[device_address] = new BluetoothDeviceMac(device);
334   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
335                     observers_,
336                     DeviceAdded(this, devices_[device_address]));
339 void BluetoothAdapterMac::UpdateDevices() {
340   // Notify observers if any previously seen devices are no longer available,
341   // i.e. if they are no longer paired, connected, nor recently discovered via
342   // an inquiry.
343   std::set<std::string> removed_devices;
344   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
345     BluetoothDevice* device = it->second;
346     if (device->IsPaired() || device->IsConnected())
347       continue;
349     NSDate* last_inquiry_update =
350         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
351     if (last_inquiry_update &&
352         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
353       continue;
355     FOR_EACH_OBSERVER(
356         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
357     delete device;
358     removed_devices.insert(it->first);
359     // The device will be erased from the map in the loop immediately below.
360   }
361   for (const std::string& device_address : removed_devices) {
362     size_t num_removed = devices_.erase(device_address);
363     DCHECK_EQ(num_removed, 1U);
364   }
366   // Add any new paired devices.
367   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
368     DeviceAdded(device);
369   }
372 }  // namespace device