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 27namespace { 28 29// The frequency with which to poll the adapter for updates. 30const 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. 34const NSTimeInterval kDiscoveryTimeoutSec = 3 * 60; // 3 minutes 35 36} // namespace 37 38namespace device { 39 40// static 41base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter( 42 const InitCallback& init_callback) { 43 return BluetoothAdapterMac::CreateAdapter(); 44} 45 46// static 47base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() { 48 BluetoothAdapterMac* adapter = new BluetoothAdapterMac(); 49 adapter->Init(); 50 return adapter->weak_ptr_factory_.GetWeakPtr(); 51} 52 53BluetoothAdapterMac::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 63BluetoothAdapterMac::~BluetoothAdapterMac() { 64} 65 66void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) { 67 DCHECK(observer); 68 observers_.AddObserver(observer); 69} 70 71void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) { 72 DCHECK(observer); 73 observers_.RemoveObserver(observer); 74} 75 76std::string BluetoothAdapterMac::GetAddress() const { 77 return address_; 78} 79 80std::string BluetoothAdapterMac::GetName() const { 81 return name_; 82} 83 84void BluetoothAdapterMac::SetName(const std::string& name, 85 const base::Closure& callback, 86 const ErrorCallback& error_callback) { 87 NOTIMPLEMENTED(); 88} 89 90bool BluetoothAdapterMac::IsInitialized() const { 91 return true; 92} 93 94bool BluetoothAdapterMac::IsPresent() const { 95 return !address_.empty(); 96} 97 98bool BluetoothAdapterMac::IsPowered() const { 99 return powered_; 100} 101 102void BluetoothAdapterMac::SetPowered(bool powered, 103 const base::Closure& callback, 104 const ErrorCallback& error_callback) { 105 NOTIMPLEMENTED(); 106} 107 108bool BluetoothAdapterMac::IsDiscoverable() const { 109 NOTIMPLEMENTED(); 110 return false; 111} 112 113void BluetoothAdapterMac::SetDiscoverable( 114 bool discoverable, 115 const base::Closure& callback, 116 const ErrorCallback& error_callback) { 117 NOTIMPLEMENTED(); 118} 119 120bool BluetoothAdapterMac::IsDiscovering() const { 121 return classic_discovery_manager_->IsDiscovering(); 122} 123 124void 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 134void 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 144void BluetoothAdapterMac::DeviceFound(IOBluetoothDevice* device) { 145 DeviceAdded(device); 146} 147 148void 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 159void 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 167void 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 194void 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 224void BluetoothAdapterMac::RemovePairingDelegateInternal( 225 BluetoothDevice::PairingDelegate* pairing_delegate) { 226} 227 228void BluetoothAdapterMac::Init() { 229 ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); 230 PollAdapter(); 231} 232 233void BluetoothAdapterMac::InitForTest( 234 scoped_refptr<base::SequencedTaskRunner> ui_task_runner) { 235 ui_task_runner_ = ui_task_runner; 236 PollAdapter(); 237} 238 239void 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 277void 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 290void 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 324