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 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
69 observers_.AddObserver(observer);
72 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
74 observers_.RemoveObserver(observer);
77 std::string BluetoothAdapterMac::GetAddress() const {
81 std::string BluetoothAdapterMac::GetName() const {
85 void BluetoothAdapterMac::SetName(const std::string& name,
86 const base::Closure& callback,
87 const ErrorCallback& error_callback) {
91 bool BluetoothAdapterMac::IsInitialized() const {
95 bool BluetoothAdapterMac::IsPresent() const {
96 return !address_.empty();
99 bool BluetoothAdapterMac::IsPowered() const {
103 void BluetoothAdapterMac::SetPowered(bool powered,
104 const base::Closure& callback,
105 const ErrorCallback& error_callback) {
109 bool BluetoothAdapterMac::IsDiscoverable() const {
114 void BluetoothAdapterMac::SetDiscoverable(
116 const base::Closure& callback,
117 const ErrorCallback& error_callback) {
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) {
150 error_callback.Run(BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM);
153 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
157 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
159 DVLOG(1) << "Discovery stopped unexpectedly";
160 num_discovery_sessions_ = 0;
161 MarkDiscoverySessionsAsInactive();
163 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
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);
176 void BluetoothAdapterMac::DeleteOnCorrectThread() const {
177 if (ui_task_runner_->RunsTasksOnCurrentThread() ||
178 !ui_task_runner_->DeleteSoon(FROM_HERE, 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_++;
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();
201 DVLOG(1) << "Added a discovery session";
202 num_discovery_sessions_++;
203 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
205 AdapterDiscoveringChanged(this, true));
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_--;
222 if (num_discovery_sessions_ == 0) {
223 DVLOG(1) << "No active discovery sessions. Returning error.";
224 error_callback.Run();
228 if (!classic_discovery_manager_->StopDiscovery()) {
229 DVLOG(1) << "Failed to stop discovery";
230 error_callback.Run();
234 DVLOG(1) << "Discovery stopped";
235 num_discovery_sessions_--;
239 void BluetoothAdapterMac::RemovePairingDelegateInternal(
240 BluetoothDevice::PairingDelegate* pairing_delegate) {
243 void BluetoothAdapterMac::Init() {
244 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
248 void BluetoothAdapterMac::InitForTest(
249 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
250 ui_task_runner_ = ui_task_runner;
254 void BluetoothAdapterMac::PollAdapter() {
255 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
257 tracked_objects::ScopedTracker tracking_profile1(
258 FROM_HERE_WITH_EXPLICIT_FUNCTION(
259 "461181 BluetoothAdapterMac::PollAdapter::Start"));
260 bool was_present = IsPresent();
263 bool powered = false;
264 IOBluetoothHostController* controller =
265 [IOBluetoothHostController defaultController];
267 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
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);
279 bool is_present = !address.empty();
283 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
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));
293 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
295 tracked_objects::ScopedTracker tracking_profile4(
296 FROM_HERE_WITH_EXPLICIT_FUNCTION(
297 "461181 BluetoothAdapterMac::PollAdapter::AdapterPowerChanged"));
298 if (powered_ != powered) {
300 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
301 AdapterPoweredChanged(this, powered_));
304 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/461181
306 tracked_objects::ScopedTracker tracking_profile5(
307 FROM_HERE_WITH_EXPLICIT_FUNCTION(
308 "461181 BluetoothAdapterMac::PollAdapter::UpdateDevices"));
311 ui_task_runner_->PostDelayedTask(
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))
325 devices_[device_address] = new BluetoothDeviceMac(device);
326 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
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
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())
341 NSDate* last_inquiry_update =
342 static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
343 if (last_inquiry_update &&
344 -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
348 BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
350 removed_devices.insert(it->first);
351 // The device will be erased from the map in the loop immediately below.
353 for (const std::string& device_address : removed_devices) {
354 size_t num_removed = devices_.erase(device_address);
355 DCHECK_EQ(num_removed, 1U);
358 // Add any new paired devices.
359 for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
364 } // namespace device