- add sources.
[platform/framework/web/crosswalk.git] / src / device / bluetooth / bluetooth_adapter_mac.mm
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.
4
5 #include "device/bluetooth/bluetooth_adapter_mac.h"
6
7 #import <IOBluetooth/objc/IOBluetoothDevice.h>
8 #import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
9 #import <IOBluetooth/objc/IOBluetoothHostController.h>
10
11 #include <string>
12
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
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
28
29 @interface IOBluetoothHostController (LionSDKDeclarations)
30 - (NSString*)nameAsString;
31 - (BluetoothHCIPowerState)powerState;
32 @end
33
34 @interface IOBluetoothDevice (LionSDKDeclarations)
35 - (NSString*)addressString;
36 @end
37
38 @protocol IOBluetoothDeviceInquiryDelegate
39 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
40 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
41                           device:(IOBluetoothDevice*)device;
42 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
43                         error:(IOReturn)error
44                       aborted:(BOOL)aborted;
45 @end
46
47 #endif  // MAC_OS_X_VERSION_10_7
48
49 @interface BluetoothAdapterMacDelegate
50     : NSObject <IOBluetoothDeviceInquiryDelegate> {
51  @private
52   device::BluetoothAdapterMac* adapter_;  // weak
53 }
54
55 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
56
57 @end
58
59 @implementation BluetoothAdapterMacDelegate
60
61 - (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
62   if ((self = [super init]))
63     adapter_ = adapter;
64
65   return self;
66 }
67
68 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
69   adapter_->DeviceInquiryStarted(sender);
70 }
71
72 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
73                           device:(IOBluetoothDevice*)device {
74   adapter_->DeviceFound(sender, device);
75 }
76
77 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
78                         error:(IOReturn)error
79                       aborted:(BOOL)aborted {
80   adapter_->DeviceInquiryComplete(sender, error, aborted);
81 }
82
83 @end
84
85 namespace {
86
87 const int kPollIntervalMs = 500;
88
89 }  // namespace
90
91 namespace device {
92
93 BluetoothAdapterMac::BluetoothAdapterMac()
94     : BluetoothAdapter(),
95       powered_(false),
96       discovery_status_(NOT_DISCOVERING),
97       adapter_delegate_(
98           [[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
99       device_inquiry_(
100           [[IOBluetoothDeviceInquiry
101               inquiryWithDelegate:adapter_delegate_] retain]),
102       recently_accessed_device_timestamp_(nil),
103       weak_ptr_factory_(this) {
104 }
105
106 BluetoothAdapterMac::~BluetoothAdapterMac() {
107   [device_inquiry_ release];
108   [adapter_delegate_ release];
109   [recently_accessed_device_timestamp_ release];
110 }
111
112 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
113   DCHECK(observer);
114   observers_.AddObserver(observer);
115 }
116
117 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
118   DCHECK(observer);
119   observers_.RemoveObserver(observer);
120 }
121
122 std::string BluetoothAdapterMac::GetAddress() const {
123   return address_;
124 }
125
126 std::string BluetoothAdapterMac::GetName() const {
127   return name_;
128 }
129
130 bool BluetoothAdapterMac::IsInitialized() const {
131   return true;
132 }
133
134 bool BluetoothAdapterMac::IsPresent() const {
135   return !address_.empty();
136 }
137
138 bool BluetoothAdapterMac::IsPowered() const {
139   return powered_;
140 }
141
142 void BluetoothAdapterMac::SetPowered(bool powered,
143                                      const base::Closure& callback,
144                                      const ErrorCallback& error_callback) {
145 }
146
147 bool BluetoothAdapterMac::IsDiscovering() const {
148   return discovery_status_ == DISCOVERING ||
149       discovery_status_ == DISCOVERY_STOPPING;
150 }
151
152 void BluetoothAdapterMac::StartDiscovering(
153     const base::Closure& callback,
154     const ErrorCallback& error_callback) {
155   if (discovery_status_ == DISCOVERING) {
156     num_discovery_listeners_++;
157     callback.Run();
158     return;
159   }
160   on_start_discovery_callbacks_.push_back(
161       std::make_pair(callback, error_callback));
162   MaybeStartDeviceInquiry();
163 }
164
165 void BluetoothAdapterMac::StopDiscovering(const base::Closure& callback,
166                                           const ErrorCallback& error_callback) {
167   if (discovery_status_ == NOT_DISCOVERING) {
168     error_callback.Run();
169     return;
170   }
171   on_stop_discovery_callbacks_.push_back(
172       std::make_pair(callback, error_callback));
173   MaybeStopDeviceInquiry();
174 }
175
176 void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
177     const BluetoothOutOfBandPairingDataCallback& callback,
178     const ErrorCallback& error_callback) {
179 }
180
181 void BluetoothAdapterMac::Init() {
182   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
183   PollAdapter();
184 }
185
186 void BluetoothAdapterMac::InitForTest(
187     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
188   ui_task_runner_ = ui_task_runner;
189   PollAdapter();
190 }
191
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];
199
200   if (controller != nil) {
201     name = base::SysNSStringToUTF8([controller nameAsString]);
202     address = base::SysNSStringToUTF8([controller addressAsString]);
203     powered = ([controller powerState] == kBluetoothHCIPowerStateON);
204   }
205
206   bool is_present = !address.empty();
207   name_ = name;
208   address_ = address;
209
210   if (was_present != is_present) {
211     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
212                       AdapterPresentChanged(this, is_present));
213   }
214   if (powered_ != powered) {
215     powered_ = powered;
216     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
217                       AdapterPoweredChanged(this, powered_));
218   }
219
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];
230   }
231
232   ui_task_runner_->PostDelayedTask(
233       FROM_HERE,
234       base::Bind(&BluetoothAdapterMac::PollAdapter,
235                  weak_ptr_factory_.GetWeakPtr()),
236       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
237 }
238
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);
245   }
246 }
247
248 void BluetoothAdapterMac::DeviceInquiryStarted(
249     IOBluetoothDeviceInquiry* inquiry) {
250   DCHECK(device_inquiry_ == inquiry);
251   if (discovery_status_ == DISCOVERING)
252     return;
253
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();
258
259   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
260                     AdapterDiscoveringChanged(this, true));
261   MaybeStopDeviceInquiry();
262 }
263
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);
273   }
274 }
275
276 void BluetoothAdapterMac::DeviceInquiryComplete(
277     IOBluetoothDeviceInquiry* inquiry,
278     IOReturn error,
279     bool aborted) {
280   DCHECK(device_inquiry_ == inquiry);
281   if (discovery_status_ == DISCOVERING &&
282       [device_inquiry_ start] == kIOReturnSuccess) {
283     return;
284   }
285
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();
295 }
296
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();
305     }
306   }
307 }
308
309 void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
310   if (discovery_status_ != DISCOVERING)
311     return;
312
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();
317     return;
318   }
319
320   discovery_status_ = DISCOVERY_STOPPING;
321   if ([device_inquiry_ stop] != kIOReturnSuccess) {
322     RunCallbacks(on_stop_discovery_callbacks_, false);
323     on_stop_discovery_callbacks_.clear();
324   }
325 }
326
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();
331        ++iter) {
332     if (success)
333       ui_task_runner_->PostTask(FROM_HERE, iter->first);
334     else
335       ui_task_runner_->PostTask(FROM_HERE, iter->second);
336   }
337 }
338
339 }  // namespace device