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>
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"
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
42 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
43 const InitCallback& init_callback) {
44 return BluetoothAdapterMac::CreateAdapter();
48 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
49 BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
51 return adapter->weak_ptr_factory_.GetWeakPtr();
54 BluetoothAdapterMac::BluetoothAdapterMac()
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 {
71 std::string BluetoothAdapterMac::GetName() const {
75 void BluetoothAdapterMac::SetName(const std::string& name,
76 const base::Closure& callback,
77 const ErrorCallback& error_callback) {
81 bool BluetoothAdapterMac::IsInitialized() const {
85 bool BluetoothAdapterMac::IsPresent() const {
86 return !address_.empty();
89 bool BluetoothAdapterMac::IsPowered() const {
93 void BluetoothAdapterMac::SetPowered(bool powered,
94 const base::Closure& callback,
95 const ErrorCallback& error_callback) {
99 bool BluetoothAdapterMac::IsDiscoverable() const {
104 void BluetoothAdapterMac::SetDiscoverable(
106 const base::Closure& callback,
107 const ErrorCallback& error_callback) {
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) {
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) {
148 error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
151 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
155 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
157 DVLOG(1) << "Discovery stopped unexpectedly";
158 num_discovery_sessions_ = 0;
159 MarkDiscoverySessionsAsInactive();
161 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
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);
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_++;
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();
194 DVLOG(1) << "Added a discovery session";
195 num_discovery_sessions_++;
196 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
198 AdapterDiscoveringChanged(this, true));
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_--;
216 if (num_discovery_sessions_ == 0) {
217 DVLOG(1) << "No active discovery sessions. Returning error.";
218 error_callback.Run();
222 if (!classic_discovery_manager_->StopDiscovery()) {
223 DVLOG(1) << "Failed to stop discovery";
224 error_callback.Run();
228 DVLOG(1) << "Discovery stopped";
229 num_discovery_sessions_--;
233 void BluetoothAdapterMac::SetDiscoveryFilter(
234 scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
235 const base::Closure& callback,
236 const ErrorCallback& error_callback) {
238 error_callback.Run();
241 void BluetoothAdapterMac::RemovePairingDelegateInternal(
242 BluetoothDevice::PairingDelegate* pairing_delegate) {
245 void BluetoothAdapterMac::Init() {
246 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
250 void BluetoothAdapterMac::InitForTest(
251 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
252 ui_task_runner_ = ui_task_runner;
256 void BluetoothAdapterMac::PollAdapter() {
257 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
259 tracked_objects::ScopedTracker tracking_profile1(
260 FROM_HERE_WITH_EXPLICIT_FUNCTION(
261 "461181 BluetoothAdapterMac::PollAdapter::Start"));
262 bool was_present = IsPresent();
264 bool powered = false;
265 IOBluetoothHostController* controller =
266 [IOBluetoothHostController defaultController];
268 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
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]);
288 bool is_present = !address.empty();
291 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
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));
301 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
303 tracked_objects::ScopedTracker tracking_profile4(
304 FROM_HERE_WITH_EXPLICIT_FUNCTION(
305 "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
306 if (powered_ != powered) {
308 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
309 AdapterPoweredChanged(this, powered_));
312 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
314 tracked_objects::ScopedTracker tracking_profile5(
315 FROM_HERE_WITH_EXPLICIT_FUNCTION(
316 "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices"));
319 ui_task_runner_->PostDelayedTask(
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))
333 devices_[device_address] = new BluetoothDeviceMac(device);
334 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
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
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())
349 NSDate* last_inquiry_update =
350 static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
351 if (last_inquiry_update &&
352 -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
356 BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
358 removed_devices.insert(it->first);
359 // The device will be erased from the map in the loop immediately below.
361 for (const std::string& device_address : removed_devices) {
362 size_t num_removed = devices_.erase(device_address);
363 DCHECK_EQ(num_removed, 1U);
366 // Add any new paired devices.
367 for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
372 } // namespace device