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/IOBluetoothDeviceInquiry.h>
8 #import <IOBluetooth/objc/IOBluetoothDevice.h>
9 #import <IOBluetooth/objc/IOBluetoothHostController.h>
13 #include "base/bind.h"
14 #include "base/compiler_specific.h"
15 #include "base/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/thread_task_runner_handle.h"
21 #include "base/time.h"
22 #include "base/strings/sys_string_conversions.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 bool BluetoothAdapterMac::IsInitialized() const {
134 bool BluetoothAdapterMac::IsPresent() const {
135 return !address_.empty();
138 bool BluetoothAdapterMac::IsPowered() const {
142 void BluetoothAdapterMac::SetPowered(bool powered,
143 const base::Closure& callback,
144 const ErrorCallback& error_callback) {
147 bool BluetoothAdapterMac::IsDiscovering() const {
148 return discovery_status_ == DISCOVERING ||
149 discovery_status_ == DISCOVERY_STOPPING;
152 void BluetoothAdapterMac::StartDiscovering(
153 const base::Closure& callback,
154 const ErrorCallback& error_callback) {
155 if (discovery_status_ == DISCOVERING) {
156 num_discovery_listeners_++;
160 on_start_discovery_callbacks_.push_back(
161 std::make_pair(callback, error_callback));
162 MaybeStartDeviceInquiry();
165 void BluetoothAdapterMac::StopDiscovering(const base::Closure& callback,
166 const ErrorCallback& error_callback) {
167 if (discovery_status_ == NOT_DISCOVERING) {
168 error_callback.Run();
171 on_stop_discovery_callbacks_.push_back(
172 std::make_pair(callback, error_callback));
173 MaybeStopDeviceInquiry();
176 void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
177 const BluetoothOutOfBandPairingDataCallback& callback,
178 const ErrorCallback& error_callback) {
181 void BluetoothAdapterMac::Init() {
182 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
186 void BluetoothAdapterMac::InitForTest(
187 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
188 ui_task_runner_ = ui_task_runner;
192 void BluetoothAdapterMac::PollAdapter() {
193 bool was_present = IsPresent();
194 std::string name = "";
195 std::string address = "";
196 bool powered = false;
197 IOBluetoothHostController* controller =
198 [IOBluetoothHostController defaultController];
200 if (controller != nil) {
201 name = base::SysNSStringToUTF8([controller nameAsString]);
202 address = base::SysNSStringToUTF8([controller addressAsString]);
203 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
206 bool is_present = !address.empty();
210 if (was_present != is_present) {
211 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
212 AdapterPresentChanged(this, is_present));
214 if (powered_ != powered) {
216 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
217 AdapterPoweredChanged(this, powered_));
220 IOBluetoothDevice* recent_device =
221 [[IOBluetoothDevice recentDevices:1] lastObject];
222 NSDate* access_timestamp = [recent_device recentAccessDate];
223 if (recently_accessed_device_timestamp_ == nil ||
224 access_timestamp == nil ||
225 [recently_accessed_device_timestamp_ compare:access_timestamp] ==
226 NSOrderedAscending) {
227 UpdateDevices([IOBluetoothDevice pairedDevices]);
228 [recently_accessed_device_timestamp_ release];
229 recently_accessed_device_timestamp_ = [access_timestamp copy];
232 ui_task_runner_->PostDelayedTask(
234 base::Bind(&BluetoothAdapterMac::PollAdapter,
235 weak_ptr_factory_.GetWeakPtr()),
236 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
239 void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
240 STLDeleteValues(&devices_);
241 for (IOBluetoothDevice* device in devices) {
242 std::string device_address =
243 base::SysNSStringToUTF8([device addressString]);
244 devices_[device_address] = new BluetoothDeviceMac(device);
248 void BluetoothAdapterMac::DeviceInquiryStarted(
249 IOBluetoothDeviceInquiry* inquiry) {
250 DCHECK(device_inquiry_ == inquiry);
251 if (discovery_status_ == DISCOVERING)
254 discovery_status_ = DISCOVERING;
255 RunCallbacks(on_start_discovery_callbacks_, true);
256 num_discovery_listeners_ = on_start_discovery_callbacks_.size();
257 on_start_discovery_callbacks_.clear();
259 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
260 AdapterDiscoveringChanged(this, true));
261 MaybeStopDeviceInquiry();
264 void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
265 IOBluetoothDevice* device) {
266 DCHECK(device_inquiry_ == inquiry);
267 std::string device_address = base::SysNSStringToUTF8([device addressString]);
268 if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
269 scoped_ptr<BluetoothDeviceMac> device_mac(new BluetoothDeviceMac(device));
270 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
271 DeviceAdded(this, device_mac.get()));
272 discovered_devices_.insert(device_address);
276 void BluetoothAdapterMac::DeviceInquiryComplete(
277 IOBluetoothDeviceInquiry* inquiry,
280 DCHECK(device_inquiry_ == inquiry);
281 if (discovery_status_ == DISCOVERING &&
282 [device_inquiry_ start] == kIOReturnSuccess) {
286 // Device discovery is done.
287 discovered_devices_.clear();
288 discovery_status_ = NOT_DISCOVERING;
289 RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
290 num_discovery_listeners_ = 0;
291 on_stop_discovery_callbacks_.clear();
292 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
293 AdapterDiscoveringChanged(this, false));
294 MaybeStartDeviceInquiry();
297 void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
298 if (discovery_status_ == NOT_DISCOVERING &&
299 !on_start_discovery_callbacks_.empty()) {
300 discovery_status_ = DISCOVERY_STARTING;
301 if ([device_inquiry_ start] != kIOReturnSuccess) {
302 discovery_status_ = NOT_DISCOVERING;
303 RunCallbacks(on_start_discovery_callbacks_, false);
304 on_start_discovery_callbacks_.clear();
309 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
310 if (discovery_status_ != DISCOVERING)
313 if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
314 RunCallbacks(on_stop_discovery_callbacks_, true);
315 num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
316 on_stop_discovery_callbacks_.clear();
320 discovery_status_ = DISCOVERY_STOPPING;
321 if ([device_inquiry_ stop] != kIOReturnSuccess) {
322 RunCallbacks(on_stop_discovery_callbacks_, false);
323 on_stop_discovery_callbacks_.clear();
327 void BluetoothAdapterMac::RunCallbacks(
328 const DiscoveryCallbackList& callback_list, bool success) const {
329 for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
330 iter != callback_list.end();
333 ui_task_runner_->PostTask(FROM_HERE, iter->first);
335 ui_task_runner_->PostTask(FROM_HERE, iter->second);
339 } // namespace device