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 @protocol IOBluetoothDeviceInquiryDelegate
35 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
36 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
37 device:(IOBluetoothDevice*)device;
38 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
40 aborted:(BOOL)aborted;
43 #endif // MAC_OS_X_VERSION_10_7
45 @interface BluetoothAdapterMacDelegate
46 : NSObject <IOBluetoothDeviceInquiryDelegate> {
48 device::BluetoothAdapterMac* adapter_; // weak
51 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
55 @implementation BluetoothAdapterMacDelegate
57 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
58 if ((self = [super init]))
64 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
65 adapter_->DeviceInquiryStarted(sender);
68 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
69 device:(IOBluetoothDevice*)device {
70 adapter_->DeviceFound(sender, device);
73 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
75 aborted:(BOOL)aborted {
76 adapter_->DeviceInquiryComplete(sender, error, aborted);
83 const int kPollIntervalMs = 500;
90 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
91 const InitCallback& init_callback) {
92 return BluetoothAdapterMac::CreateAdapter();
96 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
97 BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
99 return adapter->weak_ptr_factory_.GetWeakPtr();
102 BluetoothAdapterMac::BluetoothAdapterMac()
103 : BluetoothAdapter(),
105 discovery_status_(NOT_DISCOVERING),
107 [[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
109 [[IOBluetoothDeviceInquiry
110 inquiryWithDelegate:adapter_delegate_] retain]),
111 weak_ptr_factory_(this) {
114 BluetoothAdapterMac::~BluetoothAdapterMac() {
117 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
119 observers_.AddObserver(observer);
122 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
124 observers_.RemoveObserver(observer);
127 std::string BluetoothAdapterMac::GetAddress() const {
131 std::string BluetoothAdapterMac::GetName() const {
135 void BluetoothAdapterMac::SetName(const std::string& name,
136 const base::Closure& callback,
137 const ErrorCallback& error_callback) {
141 bool BluetoothAdapterMac::IsInitialized() const {
145 bool BluetoothAdapterMac::IsPresent() const {
146 return !address_.empty();
149 bool BluetoothAdapterMac::IsPowered() const {
153 void BluetoothAdapterMac::SetPowered(bool powered,
154 const base::Closure& callback,
155 const ErrorCallback& error_callback) {
159 bool BluetoothAdapterMac::IsDiscoverable() const {
164 void BluetoothAdapterMac::SetDiscoverable(
166 const base::Closure& callback,
167 const ErrorCallback& error_callback) {
171 bool BluetoothAdapterMac::IsDiscovering() const {
172 return discovery_status_ == DISCOVERING ||
173 discovery_status_ == DISCOVERY_STOPPING;
176 void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
177 const BluetoothOutOfBandPairingDataCallback& callback,
178 const ErrorCallback& error_callback) {
181 void BluetoothAdapterMac::AddDiscoverySession(
182 const base::Closure& callback,
183 const ErrorCallback& error_callback) {
184 if (discovery_status_ == DISCOVERING) {
185 num_discovery_listeners_++;
189 on_start_discovery_callbacks_.push_back(
190 std::make_pair(callback, error_callback));
191 MaybeStartDeviceInquiry();
194 void BluetoothAdapterMac::RemoveDiscoverySession(
195 const base::Closure& callback,
196 const ErrorCallback& error_callback) {
197 if (discovery_status_ == NOT_DISCOVERING) {
198 error_callback.Run();
201 on_stop_discovery_callbacks_.push_back(
202 std::make_pair(callback, error_callback));
203 MaybeStopDeviceInquiry();
206 void BluetoothAdapterMac::RemovePairingDelegateInternal(
207 BluetoothDevice::PairingDelegate* pairing_delegate) {
210 void BluetoothAdapterMac::Init() {
211 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
215 void BluetoothAdapterMac::InitForTest(
216 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
217 ui_task_runner_ = ui_task_runner;
221 void BluetoothAdapterMac::PollAdapter() {
222 bool was_present = IsPresent();
225 bool powered = false;
226 IOBluetoothHostController* controller =
227 [IOBluetoothHostController defaultController];
229 if (controller != nil) {
230 name = base::SysNSStringToUTF8([controller nameAsString]);
231 address = BluetoothDeviceMac::NormalizeAddress(
232 base::SysNSStringToUTF8([controller addressAsString]));
233 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
236 bool is_present = !address.empty();
240 if (was_present != is_present) {
241 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
242 AdapterPresentChanged(this, is_present));
244 if (powered_ != powered) {
246 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
247 AdapterPoweredChanged(this, powered_));
250 IOBluetoothDevice* recent_device =
251 [[IOBluetoothDevice recentDevices:1] lastObject];
252 NSDate* access_timestamp = [recent_device recentAccessDate];
253 if (recently_accessed_device_timestamp_ == nil ||
254 access_timestamp == nil ||
255 [recently_accessed_device_timestamp_ compare:access_timestamp] ==
256 NSOrderedAscending) {
257 UpdateDevices([IOBluetoothDevice pairedDevices]);
258 recently_accessed_device_timestamp_.reset([access_timestamp copy]);
261 ui_task_runner_->PostDelayedTask(
263 base::Bind(&BluetoothAdapterMac::PollAdapter,
264 weak_ptr_factory_.GetWeakPtr()),
265 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
268 void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
269 STLDeleteValues(&devices_);
270 for (IOBluetoothDevice* device in devices) {
271 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
272 devices_[device_address] = new BluetoothDeviceMac(device);
276 void BluetoothAdapterMac::DeviceInquiryStarted(
277 IOBluetoothDeviceInquiry* inquiry) {
278 DCHECK_EQ(device_inquiry_, inquiry);
279 if (discovery_status_ == DISCOVERING)
282 discovery_status_ = DISCOVERING;
283 RunCallbacks(on_start_discovery_callbacks_, true);
284 num_discovery_listeners_ = on_start_discovery_callbacks_.size();
285 on_start_discovery_callbacks_.clear();
287 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
288 AdapterDiscoveringChanged(this, true));
289 MaybeStopDeviceInquiry();
292 void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
293 IOBluetoothDevice* device) {
294 DCHECK_EQ(device_inquiry_, inquiry);
295 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
296 if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
297 BluetoothDeviceMac device_mac(device);
298 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
299 DeviceAdded(this, &device_mac));
300 discovered_devices_.insert(device_address);
304 void BluetoothAdapterMac::DeviceInquiryComplete(
305 IOBluetoothDeviceInquiry* inquiry,
308 DCHECK_EQ(device_inquiry_, inquiry);
309 if (discovery_status_ == DISCOVERING &&
310 [device_inquiry_ start] == kIOReturnSuccess) {
314 // Device discovery is done.
315 discovered_devices_.clear();
316 discovery_status_ = NOT_DISCOVERING;
317 RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
318 num_discovery_listeners_ = 0;
319 on_stop_discovery_callbacks_.clear();
320 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
321 AdapterDiscoveringChanged(this, false));
322 MaybeStartDeviceInquiry();
325 void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
326 if (discovery_status_ == NOT_DISCOVERING &&
327 !on_start_discovery_callbacks_.empty()) {
328 discovery_status_ = DISCOVERY_STARTING;
329 if ([device_inquiry_ start] != kIOReturnSuccess) {
330 discovery_status_ = NOT_DISCOVERING;
331 RunCallbacks(on_start_discovery_callbacks_, false);
332 on_start_discovery_callbacks_.clear();
337 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
338 if (discovery_status_ != DISCOVERING)
341 if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
342 RunCallbacks(on_stop_discovery_callbacks_, true);
343 num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
344 on_stop_discovery_callbacks_.clear();
348 discovery_status_ = DISCOVERY_STOPPING;
349 if ([device_inquiry_ stop] != kIOReturnSuccess) {
350 RunCallbacks(on_stop_discovery_callbacks_, false);
351 on_stop_discovery_callbacks_.clear();
355 void BluetoothAdapterMac::RunCallbacks(
356 const DiscoveryCallbackList& callback_list, bool success) const {
357 for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
358 iter != callback_list.end();
361 ui_task_runner_->PostTask(FROM_HERE, iter->first);
363 ui_task_runner_->PostTask(FROM_HERE, iter->second);
367 } // namespace device