Home | History | Annotate | Download | only in bluetooth
      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