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/IOBluetoothHostController.h>
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"
29 // The frequency with which to poll the adapter for updates.
30 const int kPollIntervalMs = 500;
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
41 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
42 const InitCallback& init_callback) {
43 return BluetoothAdapterMac::CreateAdapter();
47 base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
48 BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
50 return adapter->weak_ptr_factory_.GetWeakPtr();
53 BluetoothAdapterMac::BluetoothAdapterMac()
56 num_discovery_sessions_(0),
57 classic_discovery_manager_(
58 BluetoothDiscoveryManagerMac::CreateClassic(this)),
59 weak_ptr_factory_(this) {
60 DCHECK(classic_discovery_manager_.get());
63 BluetoothAdapterMac::~BluetoothAdapterMac() {
66 void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
68 observers_.AddObserver(observer);
71 void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
73 observers_.RemoveObserver(observer);
76 std::string BluetoothAdapterMac::GetAddress() const {
80 std::string BluetoothAdapterMac::GetName() const {
84 void BluetoothAdapterMac::SetName(const std::string& name,
85 const base::Closure& callback,
86 const ErrorCallback& error_callback) {
90 bool BluetoothAdapterMac::IsInitialized() const {
94 bool BluetoothAdapterMac::IsPresent() const {
95 return !address_.empty();
98 bool BluetoothAdapterMac::IsPowered() const {
102 void BluetoothAdapterMac::SetPowered(bool powered,
103 const base::Closure& callback,
104 const ErrorCallback& error_callback) {
108 bool BluetoothAdapterMac::IsDiscoverable() const {
113 void BluetoothAdapterMac::SetDiscoverable(
115 const base::Closure& callback,
116 const ErrorCallback& error_callback) {
120 bool BluetoothAdapterMac::IsDiscovering() const {
121 return classic_discovery_manager_->IsDiscovering();
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);
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);
144 void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) {
148 void BluetoothAdapterMac::DiscoveryStopped(bool unexpected) {
150 DVLOG(1) << "Discovery stopped unexpectedly";
151 num_discovery_sessions_ = 0;
152 MarkDiscoverySessionsAsInactive();
154 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
156 AdapterDiscoveringChanged(this, false));
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);
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_++;
178 DCHECK_EQ(0, num_discovery_sessions_);
180 if (!classic_discovery_manager_->StartDiscovery()) {
181 DVLOG(1) << "Failed to add a discovery session";
182 error_callback.Run();
186 DVLOG(1) << "Added a discovery session";
187 num_discovery_sessions_++;
188 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
190 AdapterDiscoveringChanged(this, true));
194 void BluetoothAdapterMac::RemoveDiscoverySession(
195 const base::Closure& callback,
196 const ErrorCallback& error_callback) {
197 DVLOG(1) << __func__;
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_--;
207 if (num_discovery_sessions_ == 0) {
208 DVLOG(1) << "No active discovery sessions. Returning error.";
209 error_callback.Run();
213 if (!classic_discovery_manager_->StopDiscovery()) {
214 DVLOG(1) << "Failed to stop discovery";
215 error_callback.Run();
219 DVLOG(1) << "Discovery stopped";
220 num_discovery_sessions_--;
224 void BluetoothAdapterMac::RemovePairingDelegateInternal(
225 BluetoothDevice::PairingDelegate* pairing_delegate) {
228 void BluetoothAdapterMac::Init() {
229 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
233 void BluetoothAdapterMac::InitForTest(
234 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
235 ui_task_runner_ = ui_task_runner;
239 void BluetoothAdapterMac::PollAdapter() {
240 bool was_present = IsPresent();
243 bool powered = false;
244 IOBluetoothHostController* controller =
245 [IOBluetoothHostController defaultController];
247 if (controller != nil) {
248 name = base::SysNSStringToUTF8([controller nameAsString]);
249 address = BluetoothDevice::CanonicalizeAddress(
250 base::SysNSStringToUTF8([controller addressAsString]));
251 powered = ([controller powerState] == kBluetoothHCIPowerStateON);
254 bool is_present = !address.empty();
258 if (was_present != is_present) {
259 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
260 AdapterPresentChanged(this, is_present));
262 if (powered_ != powered) {
264 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
265 AdapterPoweredChanged(this, powered_));
270 ui_task_runner_->PostDelayedTask(
272 base::Bind(&BluetoothAdapterMac::PollAdapter,
273 weak_ptr_factory_.GetWeakPtr()),
274 base::TimeDelta::FromMilliseconds(kPollIntervalMs));
277 void BluetoothAdapterMac::DeviceAdded(IOBluetoothDevice* device) {
278 std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
280 // Only notify observers once per device.
281 if (devices_.count(device_address))
284 devices_[device_address] = new BluetoothDeviceMac(device);
285 FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
287 DeviceAdded(this, devices_[device_address]));
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
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())
300 NSDate* last_inquiry_update =
301 static_cast<BluetoothDeviceMac*>(device)->GetLastInquiryUpdate();
302 if (last_inquiry_update &&
303 -[last_inquiry_update timeIntervalSinceNow] < kDiscoveryTimeoutSec)
307 BluetoothAdapter::Observer, observers_, DeviceRemoved(this, device));
309 removed_devices.insert(it->first);
310 // The device will be erased from the map in the loop immediately below.
312 for (const std::string& device_address : removed_devices) {
313 size_t num_removed = devices_.erase(device_address);
314 DCHECK_EQ(num_removed, 1U);
317 // Add any new paired devices.
318 for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
323 } // namespace device