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::DeviceFound(IOBluetoothDevice* device) {
147 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
149 DVLOG(1) << "Discovery stopped unexpectedly";
150 num_discovery_sessions_ = 0;
151 MarkDiscoverySessionsAsInactive();
153 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
155 AdapterDiscoveringChanged(this, false));
158 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
159 // TODO(isherman): Investigate whether this method can be replaced with a call
160 // to +registerForConnectNotifications:selector:.
161 DVLOG(1) << "Adapter registered a new connection from device with address: "
162 << BluetoothDeviceMac::GetDeviceAddress(device);
166 void BluetoothAdapterMac::AddDiscoverySession(
167 BluetoothDiscoveryFilter* discovery_filter,
168 const base::Closure& callback,
169 const ErrorCallback& error_callback) {
170 DVLOG(1) << __func__;
171 if (num_discovery_sessions_ > 0) {
172 DCHECK(IsDiscovering());
173 num_discovery_sessions_++;
178 DCHECK_EQ(0, num_discovery_sessions_);
180 if (!classic_discovery_manager_->StartDiscovery()) {
181 DVLOG(1) << "Failed to add a discovery session";
182 error_callback.Run();
186 DVLOG(1) << "Added a discovery session";
187 num_discovery_sessions_++;
188 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
190 AdapterDiscoveringChanged(this, true));
194 void BluetoothAdapterMac::RemoveDiscoverySession(
195 BluetoothDiscoveryFilter* discovery_filter,
196 const base::Closure& callback,
197 const ErrorCallback& error_callback) {
198 DVLOG(1) << __func__;
200 if (num_discovery_sessions_ > 1) {
201 // There are active sessions other than the one currently being removed.
202 DCHECK(IsDiscovering());
203 num_discovery_sessions_--;
208 if (num_discovery_sessions_ == 0) {
209 DVLOG(1) << "No active discovery sessions. Returning error.";
210 error_callback.Run();
214 if (!classic_discovery_manager_->StopDiscovery()) {
215 DVLOG(1) << "Failed to stop discovery";
216 error_callback.Run();
220 DVLOG(1) << "Discovery stopped";
221 num_discovery_sessions_--;
225 void BluetoothAdapterMac::SetDiscoveryFilter(
226 scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
227 const base::Closure& callback,
228 const ErrorCallback& error_callback) {
230 error_callback.Run();
233 void BluetoothAdapterMac::RemovePairingDelegateInternal(
234 BluetoothDevice::PairingDelegate* pairing_delegate) {
237 void BluetoothAdapterMac::Init() {
238 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
242 void BluetoothAdapterMac::InitForTest(
243 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
244 ui_task_runner_ = ui_task_runner;
248 void BluetoothAdapterMac::PollAdapter() {
249 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
251 tracked_objects::ScopedTracker tracking_profile1(
252 FROM_HERE_WITH_EXPLICIT_FUNCTION(
253 "461181 BluetoothAdapterMac::PollAdapter::Start"));
254 bool was_present = IsPresent();
257 bool powered = false;
258 IOBluetoothHostController* controller =
259 [IOBluetoothHostController defaultController];
261 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
263 tracked_objects::ScopedTracker tracking_profile2(
264 FROM_HERE_WITH_EXPLICIT_FUNCTION(
265 "461181 BluetoothAdapterMac::PollAdapter::GetControllerStats"));
266 if (controller != nil) {
267 name = base::SysNSStringToUTF8([controller nameAsString]);
268 address = BluetoothDevice::CanonicalizeAddress(
269 base::SysNSStringToUTF8([controller addressAsString]));
270 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
273 bool is_present = !address.empty();
277 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
279 tracked_objects::ScopedTracker tracking_profile3(
280 FROM_HERE_WITH_EXPLICIT_FUNCTION(
281 "461181 BluetoothAdapterMac::PollAdapter::AdapterPresentChanged"));
282 if (was_present != is_present) {
283 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
284 AdapterPresentChanged(this, is_present));
287 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
289 tracked_objects::ScopedTracker tracking_profile4(
290 FROM_HERE_WITH_EXPLICIT_FUNCTION(
291 "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
292 if (powered_ != powered) {
294 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
295 AdapterPoweredChanged(this, powered_));
298 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
300 tracked_objects::ScopedTracker tracking_profile5(
301 FROM_HERE_WITH_EXPLICIT_FUNCTION(
302 "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices"));
305 ui_task_runner_->PostDelayedTask(
307 base::Bind(&BluetoothAdapterMac::PollAdapter,
308 weak_ptr_factory_.GetWeakPtr()),
309 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
312 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
313 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
315 // Only notify observers once per device.
316 if (devices_.count(device_address))
319 devices_[device_address] = new BluetoothDeviceMac(device);
320 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
322 DeviceAdded(this, devices_[device_address]));
325 void BluetoothAdapterMac::UpdateDevices() {
326 // Notify observers if any previously seen devices are no longer available,
327 // i.e. if they are no longer paired, connected, nor recently discovered via
329 std::set<std::string> removed_devices;
330 for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
331 BluetoothDevice* device = it->second;
332 if (device->IsPaired() || device->IsConnected())
335 NSDate* last_inquiry_update =
336 static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
337 if (last_inquiry_update &&
338 -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
342 BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
344 removed_devices.insert(it->first);
345 // The device will be erased from the map in the loop immediately below.
347 for (const std::string& device_address : removed_devices) {
348 size_t num_removed = devices_.erase(device_address);
349 DCHECK_EQ(num_removed, 1U);
352 // Add any new paired devices.
353 for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
358 } // namespace device