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"
24 #include "device/bluetooth/bluetooth_socket_mac.h"
25 #include "device/bluetooth/bluetooth_uuid.h"
27 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
28 #if !defined(MAC_OS_X_VERSION_10_7) || \
29 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
31 @interface IOBluetoothHostController (LionSDKDeclarations)
32 - (NSString*)nameAsString;
33 - (BluetoothHCIPowerState)powerState;
36 @protocol IOBluetoothDeviceInquiryDelegate
37 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
38 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
39 device:(IOBluetoothDevice*)device;
40 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
42 aborted:(BOOL)aborted;
45 #endif // MAC_OS_X_VERSION_10_7
47 @interface BluetoothAdapterMacDelegate
48 : NSObject <IOBluetoothDeviceInquiryDelegate> {
50 device::BluetoothAdapterMac* adapter_; // weak
53 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
57 @implementation BluetoothAdapterMacDelegate
59 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
60 if ((self = [super init]))
66 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
67 adapter_->DeviceInquiryStarted(sender);
70 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
71 device:(IOBluetoothDevice*)device {
72 adapter_->DeviceFound(sender, device);
75 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
77 aborted:(BOOL)aborted {
78 adapter_->DeviceInquiryComplete(sender, error, aborted);
85 const int kPollIntervalMs = 500;
92 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
93 const InitCallback& init_callback) {
94 return BluetoothAdapterMac::CreateAdapter();
98 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
99 BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
101 return adapter->weak_ptr_factory_.GetWeakPtr();
104 BluetoothAdapterMac::BluetoothAdapterMac()
105 : BluetoothAdapter(),
107 discovery_status_(NOT_DISCOVERING),
109 [[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
111 [[IOBluetoothDeviceInquiry
112 inquiryWithDelegate:adapter_delegate_] retain]),
113 weak_ptr_factory_(this) {
116 BluetoothAdapterMac::~BluetoothAdapterMac() {
119 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
121 observers_.AddObserver(observer);
124 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
126 observers_.RemoveObserver(observer);
129 std::string BluetoothAdapterMac::GetAddress() const {
133 std::string BluetoothAdapterMac::GetName() const {
137 void BluetoothAdapterMac::SetName(const std::string& name,
138 const base::Closure& callback,
139 const ErrorCallback& error_callback) {
143 bool BluetoothAdapterMac::IsInitialized() const {
147 bool BluetoothAdapterMac::IsPresent() const {
148 return !address_.empty();
151 bool BluetoothAdapterMac::IsPowered() const {
155 void BluetoothAdapterMac::SetPowered(bool powered,
156 const base::Closure& callback,
157 const ErrorCallback& error_callback) {
161 bool BluetoothAdapterMac::IsDiscoverable() const {
166 void BluetoothAdapterMac::SetDiscoverable(
168 const base::Closure& callback,
169 const ErrorCallback& error_callback) {
173 bool BluetoothAdapterMac::IsDiscovering() const {
174 return discovery_status_ == DISCOVERING ||
175 discovery_status_ == DISCOVERY_STOPPING;
178 void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
179 const BluetoothOutOfBandPairingDataCallback& callback,
180 const ErrorCallback& error_callback) {
183 void BluetoothAdapterMac::CreateRfcommService(
184 const BluetoothUUID& uuid,
187 const CreateServiceCallback& callback,
188 const CreateServiceErrorCallback& error_callback) {
189 // TODO(keybuk): implement.
193 void BluetoothAdapterMac::CreateL2capService(
194 const BluetoothUUID& uuid,
196 const CreateServiceCallback& callback,
197 const CreateServiceErrorCallback& error_callback) {
198 // TODO(keybuk): implement.
202 void BluetoothAdapterMac::AddDiscoverySession(
203 const base::Closure& callback,
204 const ErrorCallback& error_callback) {
205 if (discovery_status_ == DISCOVERING) {
206 num_discovery_listeners_++;
210 on_start_discovery_callbacks_.push_back(
211 std::make_pair(callback, error_callback));
212 MaybeStartDeviceInquiry();
215 void BluetoothAdapterMac::RemoveDiscoverySession(
216 const base::Closure& callback,
217 const ErrorCallback& error_callback) {
218 if (discovery_status_ == NOT_DISCOVERING) {
219 error_callback.Run();
222 on_stop_discovery_callbacks_.push_back(
223 std::make_pair(callback, error_callback));
224 MaybeStopDeviceInquiry();
227 void BluetoothAdapterMac::RemovePairingDelegateInternal(
228 BluetoothDevice::PairingDelegate* pairing_delegate) {
231 void BluetoothAdapterMac::Init() {
232 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
236 void BluetoothAdapterMac::InitForTest(
237 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
238 ui_task_runner_ = ui_task_runner;
242 void BluetoothAdapterMac::PollAdapter() {
243 bool was_present = IsPresent();
246 bool powered = false;
247 IOBluetoothHostController* controller =
248 [IOBluetoothHostController defaultController];
250 if (controller != nil) {
251 name = base::SysNSStringToUTF8([controller nameAsString]);
252 address = BluetoothDeviceMac::NormalizeAddress(
253 base::SysNSStringToUTF8([controller addressAsString]));
254 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
257 bool is_present = !address.empty();
261 if (was_present != is_present) {
262 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
263 AdapterPresentChanged(this, is_present));
265 if (powered_ != powered) {
267 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
268 AdapterPoweredChanged(this, powered_));
271 IOBluetoothDevice* recent_device =
272 [[IOBluetoothDevice recentDevices:1] lastObject];
273 NSDate* access_timestamp = [recent_device recentAccessDate];
274 if (recently_accessed_device_timestamp_ == nil ||
275 access_timestamp == nil ||
276 [recently_accessed_device_timestamp_ compare:access_timestamp] ==
277 NSOrderedAscending) {
278 UpdateDevices([IOBluetoothDevice pairedDevices]);
279 recently_accessed_device_timestamp_.reset([access_timestamp copy]);
282 ui_task_runner_->PostDelayedTask(
284 base::Bind(&BluetoothAdapterMac::PollAdapter,
285 weak_ptr_factory_.GetWeakPtr()),
286 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
289 void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
290 STLDeleteValues(&devices_);
291 for (IOBluetoothDevice* device in devices) {
292 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
293 devices_[device_address] = new BluetoothDeviceMac(device);
297 void BluetoothAdapterMac::DeviceInquiryStarted(
298 IOBluetoothDeviceInquiry* inquiry) {
299 DCHECK_EQ(device_inquiry_, inquiry);
300 if (discovery_status_ == DISCOVERING)
303 discovery_status_ = DISCOVERING;
304 RunCallbacks(on_start_discovery_callbacks_, true);
305 num_discovery_listeners_ = on_start_discovery_callbacks_.size();
306 on_start_discovery_callbacks_.clear();
308 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
309 AdapterDiscoveringChanged(this, true));
310 MaybeStopDeviceInquiry();
313 void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
314 IOBluetoothDevice* device) {
315 DCHECK_EQ(device_inquiry_, inquiry);
316 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
317 if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
318 BluetoothDeviceMac device_mac(device);
319 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
320 DeviceAdded(this, &device_mac));
321 discovered_devices_.insert(device_address);
325 void BluetoothAdapterMac::DeviceInquiryComplete(
326 IOBluetoothDeviceInquiry* inquiry,
329 DCHECK_EQ(device_inquiry_, inquiry);
330 if (discovery_status_ == DISCOVERING &&
331 [device_inquiry_ start] == kIOReturnSuccess) {
335 // Device discovery is done.
336 discovered_devices_.clear();
337 discovery_status_ = NOT_DISCOVERING;
338 RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
339 num_discovery_listeners_ = 0;
340 on_stop_discovery_callbacks_.clear();
341 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
342 AdapterDiscoveringChanged(this, false));
343 MaybeStartDeviceInquiry();
346 void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
347 if (discovery_status_ == NOT_DISCOVERING &&
348 !on_start_discovery_callbacks_.empty()) {
349 discovery_status_ = DISCOVERY_STARTING;
350 if ([device_inquiry_ start] != kIOReturnSuccess) {
351 discovery_status_ = NOT_DISCOVERING;
352 RunCallbacks(on_start_discovery_callbacks_, false);
353 on_start_discovery_callbacks_.clear();
358 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
359 if (discovery_status_ != DISCOVERING)
362 if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
363 RunCallbacks(on_stop_discovery_callbacks_, true);
364 num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
365 on_stop_discovery_callbacks_.clear();
369 discovery_status_ = DISCOVERY_STOPPING;
370 if ([device_inquiry_ stop] != kIOReturnSuccess) {
371 RunCallbacks(on_stop_discovery_callbacks_, false);
372 on_stop_discovery_callbacks_.clear();
376 void BluetoothAdapterMac::RunCallbacks(
377 const DiscoveryCallbackList& callback_list, bool success) const {
378 for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
379 iter != callback_list.end();
382 ui_task_runner_->PostTask(FROM_HERE, iter->first);
384 ui_task_runner_->PostTask(FROM_HERE, iter->second);
388 } // namespace device