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/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