2d0556d97d0b0d0ee5a7b966d4fb791be089a3c6
[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/IOBluetoothHostController.h>
9
10 #include <string>
11
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/containers/hash_tables.h"
15 #include "base/location.h"
16 #include "base/mac/sdk_forward_declarations.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"
26
27 namespace {
28
29 // The frequency with which to poll the adapter for updates.
30 const int kPollIntervalMs = 500;
31
32 // The length of time that must elapse since the last Inquiry response before a
33 // discovered Classic device is considered to be no longer available.
34 const NSTimeInterval kDiscoveryTimeoutSec = 3 * 60;  // 3 minutes
35
36 }  // namespace
37
38 namespace device {
39
40 // static
41 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
42     const InitCallback& init_callback) {
43   return BluetoothAdapterMac::CreateAdapter();
44 }
45
46 // static
47 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
48   BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
49   adapter->Init();
50   return adapter->weak_ptr_factory_.GetWeakPtr();
51 }
52
53 BluetoothAdapterMac::BluetoothAdapterMac()
54     : BluetoothAdapter(),
55       powered_(false),
56       num_discovery_sessions_(0),
57       classic_discovery_manager_(
58           BluetoothDiscoveryManagerMac::CreateClassic(this)),
59       weak_ptr_factory_(this) {
60   DCHECK(classic_discovery_manager_.get());
61 }
62
63 BluetoothAdapterMac::~BluetoothAdapterMac() {
64 }
65
66 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
67   DCHECK(observer);
68   observers_.AddObserver(observer);
69 }
70
71 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
72   DCHECK(observer);
73   observers_.RemoveObserver(observer);
74 }
75
76 std::string BluetoothAdapterMac::GetAddress() const {
77   return address_;
78 }
79
80 std::string BluetoothAdapterMac::GetName() const {
81   return name_;
82 }
83
84 void BluetoothAdapterMac::SetName(const std::string& name,
85                                   const base::Closure& callback,
86                                   const ErrorCallback& error_callback) {
87   NOTIMPLEMENTED();
88 }
89
90 bool BluetoothAdapterMac::IsInitialized() const {
91   return true;
92 }
93
94 bool BluetoothAdapterMac::IsPresent() const {
95   return !address_.empty();
96 }
97
98 bool BluetoothAdapterMac::IsPowered() const {
99   return powered_;
100 }
101
102 void BluetoothAdapterMac::SetPowered(bool powered,
103                                      const base::Closure& callback,
104                                      const ErrorCallback& error_callback) {
105   NOTIMPLEMENTED();
106 }
107
108 bool BluetoothAdapterMac::IsDiscoverable() const {
109   NOTIMPLEMENTED();
110   return false;
111 }
112
113 void BluetoothAdapterMac::SetDiscoverable(
114     bool discoverable,
115     const base::Closure& callback,
116     const ErrorCallback& error_callback) {
117   NOTIMPLEMENTED();
118 }
119
120 bool BluetoothAdapterMac::IsDiscovering() const {
121   return classic_discovery_manager_->IsDiscovering();
122 }
123
124 void BluetoothAdapterMac::CreateRfcommService(
125     const BluetoothUUID& uuid,
126     const ServiceOptions& options,
127     const CreateServiceCallback& callback,
128     const CreateServiceErrorCallback& error_callback) {
129   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
130   socket->ListenUsingRfcomm(
131       this, uuid, options, base::Bind(callback, socket), error_callback);
132 }
133
134 void BluetoothAdapterMac::CreateL2capService(
135     const BluetoothUUID& uuid,
136     const ServiceOptions& options,
137     const CreateServiceCallback& callback,
138     const CreateServiceErrorCallback& error_callback) {
139   scoped_refptr<BluetoothSocketMac> socket = BluetoothSocketMac::CreateSocket();
140   socket->ListenUsingL2cap(
141       this, uuid, options, base::Bind(callback, socket), error_callback);
142 }
143
144 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
145   DeviceAdded(device);
146 }
147
148 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
149   if (unexpected) {
150     DVLOG(1) << "Discovery stopped unexpectedly";
151     num_discovery_sessions_ = 0;
152     MarkDiscoverySessionsAsInactive();
153   }
154   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
155                     observers_,
156                     AdapterDiscoveringChanged(this, false));
157 }
158
159 void BluetoothAdapterMac::DeviceConnected(IOBluetoothDevice* device) {
160   // TODO(isherman): Investigate whether this method can be replaced with a call
161   // to +registerForConnectNotifications:selector:.
162   DVLOG(1) << "Adapter registered a new connection from device with address: "
163            << BluetoothDeviceMac::GetDeviceAddress(device);
164   DeviceAdded(device);
165 }
166
167 void BluetoothAdapterMac::AddDiscoverySession(
168     const base::Closure& callback,
169     const ErrorCallback& error_callback) {
170   DVLOG(1) << __func__;
171   if (num_discovery_sessions_ > 0) {
172     DCHECK(IsDiscovering());
173     num_discovery_sessions_++;
174     callback.Run();
175     return;
176   }
177
178   DCHECK_EQ(0, num_discovery_sessions_);
179
180   if (!classic_discovery_manager_->StartDiscovery()) {
181     DVLOG(1) << "Failed to add a discovery session";
182     error_callback.Run();
183     return;
184   }
185
186   DVLOG(1) << "Added a discovery session";
187   num_discovery_sessions_++;
188   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
189                     observers_,
190                     AdapterDiscoveringChanged(this, true));
191   callback.Run();
192 }
193
194 void BluetoothAdapterMac::RemoveDiscoverySession(
195     const base::Closure& callback,
196     const ErrorCallback& error_callback) {
197   DVLOG(1) << __func__;
198
199   if (num_discovery_sessions_ > 1) {
200     // There are active sessions other than the one currently being removed.
201     DCHECK(IsDiscovering());
202     num_discovery_sessions_--;
203     callback.Run();
204     return;
205   }
206
207   if (num_discovery_sessions_ == 0) {
208     DVLOG(1) << "No active discovery sessions. Returning error.";
209     error_callback.Run();
210     return;
211   }
212
213   if (!classic_discovery_manager_->StopDiscovery()) {
214     DVLOG(1) << "Failed to stop discovery";
215     error_callback.Run();
216     return;
217   }
218
219   DVLOG(1) << "Discovery stopped";
220   num_discovery_sessions_--;
221   callback.Run();
222 }
223
224 void BluetoothAdapterMac::RemovePairingDelegateInternal(
225     BluetoothDevice::PairingDelegate* pairing_delegate) {
226 }
227
228 void BluetoothAdapterMac::Init() {
229   ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
230   PollAdapter();
231 }
232
233 void BluetoothAdapterMac::InitForTest(
234     scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
235   ui_task_runner_ = ui_task_runner;
236   PollAdapter();
237 }
238
239 void BluetoothAdapterMac::PollAdapter() {
240   bool was_present = IsPresent();
241   std::string name;
242   std::string address;
243   bool powered = false;
244   IOBluetoothHostController* controller =
245       [IOBluetoothHostController defaultController];
246
247   if (controller != nil) {
248     name = base::SysNSStringToUTF8([controller nameAsString]);
249     address = BluetoothDevice::CanonicalizeAddress(
250         base::SysNSStringToUTF8([controller addressAsString]));
251     powered = ([controller powerState] == kBluetoothHCIPowerStateON);
252   }
253
254   bool is_present = !address.empty();
255   name_ = name;
256   address_ = address;
257
258   if (was_present != is_present) {
259     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
260                       AdapterPresentChanged(this, is_present));
261   }
262   if (powered_ != powered) {
263     powered_ = powered;
264     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
265                       AdapterPoweredChanged(this, powered_));
266   }
267
268   UpdateDevices();
269
270   ui_task_runner_->PostDelayedTask(
271       FROM_HERE,
272       base::Bind(&BluetoothAdapterMac::PollAdapter,
273                  weak_ptr_factory_.GetWeakPtr()),
274       base::TimeDelta::FromMilliseconds(kPollIntervalMs));
275 }
276
277 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
278   std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
279
280   // Only notify observers once per device.
281   if (devices_.count(device_address))
282     return;
283
284   devices_[device_address] = new BluetoothDeviceMac(device);
285   FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
286                     observers_,
287                     DeviceAdded(this, devices_[device_address]));
288 }
289
290 void BluetoothAdapterMac::UpdateDevices() {
291   // Notify observers if any previously seen devices are no longer available,
292   // i.e. if they are no longer paired, connected, nor recently discovered via
293   // an inquiry.
294   std::set<std::string> removed_devices;
295   for (DevicesMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
296     BluetoothDevice* device = it->second;
297     if (device->IsPaired() || device->IsConnected())
298       continue;
299
300     NSDate* last_inquiry_update =
301         static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
302     if (last_inquiry_update &&
303         -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
304       continue;
305
306     FOR_EACH_OBSERVER(
307         BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
308     delete device;
309     removed_devices.insert(it->first);
310     // The device will be erased from the map in the loop immediately below.
311   }
312   for (const std::string& device_address : removed_devices) {
313     size_t num_removed = devices_.erase(device_address);
314     DCHECK_EQ(num_removed, 1U);
315   }
316
317   // Add any new paired devices.
318   for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
319     DeviceAdded(device);
320   }
321 }
322
323 }  // namespace device