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 340