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 324