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::CreateRfcommService(
179 const BluetoothUUID& uuid,
182 const CreateServiceCallback& callback,
183 const CreateServiceErrorCallback& error_callback) {
184 // TODO(keybuk): implement.
188 void BluetoothAdapterMac::CreateL2capService(
189 const BluetoothUUID& uuid,
191 const CreateServiceCallback& callback,
192 const CreateServiceErrorCallback& error_callback) {
193 // TODO(keybuk): implement.
197 void BluetoothAdapterMac::AddDiscoverySession(
198 const base::Closure& callback,
199 const ErrorCallback& error_callback) {
200 if (discovery_status_ == DISCOVERING) {
201 num_discovery_listeners_++;
205 on_start_discovery_callbacks_.push_back(
206 std::make_pair(callback, error_callback));
207 MaybeStartDeviceInquiry();
210 void BluetoothAdapterMac::RemoveDiscoverySession(
211 const base::Closure& callback,
212 const ErrorCallback& error_callback) {
213 if (discovery_status_ == NOT_DISCOVERING) {
214 error_callback.Run();
217 on_stop_discovery_callbacks_.push_back(
218 std::make_pair(callback, error_callback));
219 MaybeStopDeviceInquiry();
222 void BluetoothAdapterMac::RemovePairingDelegateInternal(
223 BluetoothDevice::PairingDelegate* pairing_delegate) {
226 void BluetoothAdapterMac::Init() {
227 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
231 void BluetoothAdapterMac::InitForTest(
232 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
233 ui_task_runner_ = ui_task_runner;
237 void BluetoothAdapterMac::PollAdapter() {
238 bool was_present = IsPresent();
241 bool powered = false;
242 IOBluetoothHostController* controller =
243 [IOBluetoothHostController defaultController];
245 if (controller != nil) {
246 name = base::SysNSStringToUTF8([controller nameAsString]);
247 address = BluetoothDevice::CanonicalizeAddress(
248 base::SysNSStringToUTF8([controller addressAsString]));
249 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
252 bool is_present = !address.empty();
256 if (was_present != is_present) {
257 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
258 AdapterPresentChanged(this, is_present));
260 if (powered_ != powered) {
262 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
263 AdapterPoweredChanged(this, powered_));
266 IOBluetoothDevice* recent_device =
267 [[IOBluetoothDevice recentDevices:1] lastObject];
268 NSDate* access_timestamp = [recent_device recentAccessDate];
269 if (recently_accessed_device_timestamp_ == nil ||
270 access_timestamp == nil ||
271 [recently_accessed_device_timestamp_ compare:access_timestamp] ==
272 NSOrderedAscending) {
273 UpdateDevices([IOBluetoothDevice pairedDevices]);
274 recently_accessed_device_timestamp_.reset([access_timestamp copy]);
277 ui_task_runner_->PostDelayedTask(
279 base::Bind(&BluetoothAdapterMac::PollAdapter,
280 weak_ptr_factory_.GetWeakPtr()),
281 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
284 void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
285 STLDeleteValues(&devices_);
286 for (IOBluetoothDevice* device in devices) {
287 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
288 devices_[device_address] = new BluetoothDeviceMac(device);
292 void BluetoothAdapterMac::DeviceInquiryStarted(
293 IOBluetoothDeviceInquiry* inquiry) {
294 DCHECK_EQ(device_inquiry_, inquiry);
295 if (discovery_status_ == DISCOVERING)
298 discovery_status_ = DISCOVERING;
299 RunCallbacks(on_start_discovery_callbacks_, true);
300 num_discovery_listeners_ = on_start_discovery_callbacks_.size();
301 on_start_discovery_callbacks_.clear();
303 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
304 AdapterDiscoveringChanged(this, true));
305 MaybeStopDeviceInquiry();
308 void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
309 IOBluetoothDevice* device) {
310 DCHECK_EQ(device_inquiry_, inquiry);
311 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
312 if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
313 BluetoothDeviceMac device_mac(device);
314 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
315 DeviceAdded(this, &device_mac));
316 discovered_devices_.insert(device_address);
320 void BluetoothAdapterMac::DeviceInquiryComplete(
321 IOBluetoothDeviceInquiry* inquiry,
324 DCHECK_EQ(device_inquiry_, inquiry);
325 if (discovery_status_ == DISCOVERING &&
326 [device_inquiry_ start] == kIOReturnSuccess) {
330 // Device discovery is done.
331 discovered_devices_.clear();
332 discovery_status_ = NOT_DISCOVERING;
333 RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
334 num_discovery_listeners_ = 0;
335 on_stop_discovery_callbacks_.clear();
336 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
337 AdapterDiscoveringChanged(this, false));
338 MaybeStartDeviceInquiry();
341 void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
342 if (discovery_status_ == NOT_DISCOVERING &&
343 !on_start_discovery_callbacks_.empty()) {
344 discovery_status_ = DISCOVERY_STARTING;
345 if ([device_inquiry_ start] != kIOReturnSuccess) {
346 discovery_status_ = NOT_DISCOVERING;
347 RunCallbacks(on_start_discovery_callbacks_, false);
348 on_start_discovery_callbacks_.clear();
353 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
354 if (discovery_status_ != DISCOVERING)
357 if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
358 RunCallbacks(on_stop_discovery_callbacks_, true);
359 num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
360 on_stop_discovery_callbacks_.clear();
364 discovery_status_ = DISCOVERY_STOPPING;
365 if ([device_inquiry_ stop] != kIOReturnSuccess) {
366 RunCallbacks(on_stop_discovery_callbacks_, false);
367 on_stop_discovery_callbacks_.clear();
371 void BluetoothAdapterMac::RunCallbacks(
372 const DiscoveryCallbackList& callback_list, bool success) const {
373 for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
374 iter != callback_list.end();
377 ui_task_runner_->PostTask(FROM_HERE, iter->first);
379 ui_task_runner_->PostTask(FROM_HERE, iter->second);
383 } // namespace device