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/IOBluetoothDeviceInquiry.h>
9 #import <IOBluetooth/objc/IOBluetoothHostController.h>
13 #include "base/bind.h"
14 #include "base/compiler_specific.h"
15 #include "base/containers/hash_tables.h"
16 #include "base/location.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "device/bluetooth/bluetooth_device_mac.h"
25 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
26 #if !defined(MAC_OS_X_VERSION_10_7) || \
27 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
29 @interface IOBluetoothHostController (LionSDKDeclarations)
30 - (NSString*)nameAsString;
31 - (BluetoothHCIPowerState)powerState;
34 @interface IOBluetoothDevice (LionSDKDeclarations)
35 - (NSString*)addressString;
38 @protocol IOBluetoothDeviceInquiryDelegate
39 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
40 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
41 device:(IOBluetoothDevice*)device;
42 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
44 aborted:(BOOL)aborted;
47 #endif // MAC_OS_X_VERSION_10_7
49 @interface BluetoothAdapterMacDelegate
50 : NSObject <IOBluetoothDeviceInquiryDelegate> {
52 device::BluetoothAdapterMac* adapter_; // weak
55 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
59 @implementation BluetoothAdapterMacDelegate
61 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
62 if ((self = [super init]))
68 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
69 adapter_->DeviceInquiryStarted(sender);
72 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
73 device:(IOBluetoothDevice*)device {
74 adapter_->DeviceFound(sender, device);
77 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
79 aborted:(BOOL)aborted {
80 adapter_->DeviceInquiryComplete(sender, error, aborted);
87 const int kPollIntervalMs = 500;
93 BluetoothAdapterMac::BluetoothAdapterMac()
96 discovery_status_(NOT_DISCOVERING),
98 [[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
100 [[IOBluetoothDeviceInquiry
101 inquiryWithDelegate:adapter_delegate_] retain]),
102 recently_accessed_device_timestamp_(nil),
103 weak_ptr_factory_(this) {
106 BluetoothAdapterMac::~BluetoothAdapterMac() {
107 [device_inquiry_ release];
108 [adapter_delegate_ release];
109 [recently_accessed_device_timestamp_ release];
112 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
114 observers_.AddObserver(observer);
117 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
119 observers_.RemoveObserver(observer);
122 std::string BluetoothAdapterMac::GetAddress() const {
126 std::string BluetoothAdapterMac::GetName() const {
130 void BluetoothAdapterMac::SetName(const std::string& name,
131 const base::Closure& callback,
132 const ErrorCallback& error_callback) {
136 bool BluetoothAdapterMac::IsInitialized() const {
140 bool BluetoothAdapterMac::IsPresent() const {
141 return !address_.empty();
144 bool BluetoothAdapterMac::IsPowered() const {
148 void BluetoothAdapterMac::SetPowered(bool powered,
149 const base::Closure& callback,
150 const ErrorCallback& error_callback) {
154 bool BluetoothAdapterMac::IsDiscoverable() const {
159 void BluetoothAdapterMac::SetDiscoverable(
161 const base::Closure& callback,
162 const ErrorCallback& error_callback) {
166 bool BluetoothAdapterMac::IsDiscovering() const {
167 return discovery_status_ == DISCOVERING ||
168 discovery_status_ == DISCOVERY_STOPPING;
171 void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
172 const BluetoothOutOfBandPairingDataCallback& callback,
173 const ErrorCallback& error_callback) {
176 void BluetoothAdapterMac::AddDiscoverySession(
177 const base::Closure& callback,
178 const ErrorCallback& error_callback) {
179 if (discovery_status_ == DISCOVERING) {
180 num_discovery_listeners_++;
184 on_start_discovery_callbacks_.push_back(
185 std::make_pair(callback, error_callback));
186 MaybeStartDeviceInquiry();
189 void BluetoothAdapterMac::RemoveDiscoverySession(
190 const base::Closure& callback,
191 const ErrorCallback& error_callback) {
192 if (discovery_status_ == NOT_DISCOVERING) {
193 error_callback.Run();
196 on_stop_discovery_callbacks_.push_back(
197 std::make_pair(callback, error_callback));
198 MaybeStopDeviceInquiry();
201 void BluetoothAdapterMac::RemovePairingDelegateInternal(
202 BluetoothDevice::PairingDelegate* pairing_delegate) {
205 void BluetoothAdapterMac::Init() {
206 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
210 void BluetoothAdapterMac::InitForTest(
211 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
212 ui_task_runner_ = ui_task_runner;
216 void BluetoothAdapterMac::PollAdapter() {
217 bool was_present = IsPresent();
218 std::string name = "";
219 std::string address = "";
220 bool powered = false;
221 IOBluetoothHostController* controller =
222 [IOBluetoothHostController defaultController];
224 if (controller != nil) {
225 name = base::SysNSStringToUTF8([controller nameAsString]);
226 address = base::SysNSStringToUTF8([controller addressAsString]);
227 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
230 bool is_present = !address.empty();
234 if (was_present != is_present) {
235 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
236 AdapterPresentChanged(this, is_present));
238 if (powered_ != powered) {
240 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
241 AdapterPoweredChanged(this, powered_));
244 IOBluetoothDevice* recent_device =
245 [[IOBluetoothDevice recentDevices:1] lastObject];
246 NSDate* access_timestamp = [recent_device recentAccessDate];
247 if (recently_accessed_device_timestamp_ == nil ||
248 access_timestamp == nil ||
249 [recently_accessed_device_timestamp_ compare:access_timestamp] ==
250 NSOrderedAscending) {
251 UpdateDevices([IOBluetoothDevice pairedDevices]);
252 [recently_accessed_device_timestamp_ release];
253 recently_accessed_device_timestamp_ = [access_timestamp copy];
256 ui_task_runner_->PostDelayedTask(
258 base::Bind(&BluetoothAdapterMac::PollAdapter,
259 weak_ptr_factory_.GetWeakPtr()),
260 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
263 void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
264 STLDeleteValues(&devices_);
265 for (IOBluetoothDevice* device in devices) {
266 std::string device_address =
267 base::SysNSStringToUTF8([device addressString]);
268 devices_[device_address] = new BluetoothDeviceMac(device);
272 void BluetoothAdapterMac::DeviceInquiryStarted(
273 IOBluetoothDeviceInquiry* inquiry) {
274 DCHECK(device_inquiry_ == inquiry);
275 if (discovery_status_ == DISCOVERING)
278 discovery_status_ = DISCOVERING;
279 RunCallbacks(on_start_discovery_callbacks_, true);
280 num_discovery_listeners_ = on_start_discovery_callbacks_.size();
281 on_start_discovery_callbacks_.clear();
283 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
284 AdapterDiscoveringChanged(this, true));
285 MaybeStopDeviceInquiry();
288 void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
289 IOBluetoothDevice* device) {
290 DCHECK(device_inquiry_ == inquiry);
291 std::string device_address = base::SysNSStringToUTF8([device addressString]);
292 if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
293 scoped_ptr<BluetoothDeviceMac> device_mac(new BluetoothDeviceMac(device));
294 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
295 DeviceAdded(this, device_mac.get()));
296 discovered_devices_.insert(device_address);
300 void BluetoothAdapterMac::DeviceInquiryComplete(
301 IOBluetoothDeviceInquiry* inquiry,
304 DCHECK(device_inquiry_ == inquiry);
305 if (discovery_status_ == DISCOVERING &&
306 [device_inquiry_ start] == kIOReturnSuccess) {
310 // Device discovery is done.
311 discovered_devices_.clear();
312 discovery_status_ = NOT_DISCOVERING;
313 RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
314 num_discovery_listeners_ = 0;
315 on_stop_discovery_callbacks_.clear();
316 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
317 AdapterDiscoveringChanged(this, false));
318 MaybeStartDeviceInquiry();
321 void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
322 if (discovery_status_ == NOT_DISCOVERING &&
323 !on_start_discovery_callbacks_.empty()) {
324 discovery_status_ = DISCOVERY_STARTING;
325 if ([device_inquiry_ start] != kIOReturnSuccess) {
326 discovery_status_ = NOT_DISCOVERING;
327 RunCallbacks(on_start_discovery_callbacks_, false);
328 on_start_discovery_callbacks_.clear();
333 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
334 if (discovery_status_ != DISCOVERING)
337 if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
338 RunCallbacks(on_stop_discovery_callbacks_, true);
339 num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
340 on_stop_discovery_callbacks_.clear();
344 discovery_status_ = DISCOVERY_STOPPING;
345 if ([device_inquiry_ stop] != kIOReturnSuccess) {
346 RunCallbacks(on_stop_discovery_callbacks_, false);
347 on_stop_discovery_callbacks_.clear();
351 void BluetoothAdapterMac::RunCallbacks(
352 const DiscoveryCallbackList& callback_list, bool success) const {
353 for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
354 iter != callback_list.end();
357 ui_task_runner_->PostTask(FROM_HERE, iter->first);
359 ui_task_runner_->PostTask(FROM_HERE, iter->second);
363 } // namespace device