Home | History | Annotate | Download | only in bluetooth
      1 // Copyright 2014 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_discovery_manager_mac.h"
      6 
      7 #import <IOBluetooth/objc/IOBluetoothDevice.h>
      8 #import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
      9 
     10 #include "base/mac/scoped_nsobject.h"
     11 #include "base/mac/sdk_forward_declarations.h"
     12 #include "base/logging.h"
     13 
     14 namespace device {
     15 
     16 class BluetoothDiscoveryManagerMacClassic;
     17 
     18 }  // namespace device
     19 
     20 // IOBluetoothDeviceInquiryDelegate implementation.
     21 @interface BluetoothDeviceInquiryDelegate
     22     : NSObject<IOBluetoothDeviceInquiryDelegate> {
     23  @private
     24   device::BluetoothDiscoveryManagerMacClassic* manager_;  // weak
     25 }
     26 
     27 - (id)initWithManager:(device::BluetoothDiscoveryManagerMacClassic*)manager;
     28 
     29 @end
     30 
     31 namespace device {
     32 
     33 // ementation of BluetoothDiscoveryManagerMac for Bluetooth classic device
     34 // discovery, using the IOBluetooth framework.
     35 class BluetoothDiscoveryManagerMacClassic
     36     : public BluetoothDiscoveryManagerMac {
     37  public:
     38   explicit BluetoothDiscoveryManagerMacClassic(Observer* observer)
     39       : BluetoothDiscoveryManagerMac(observer),
     40         should_do_discovery_(false),
     41         inquiry_running_(false),
     42         inquiry_delegate_(
     43             [[BluetoothDeviceInquiryDelegate alloc] initWithManager:this]),
     44         inquiry_([[IOBluetoothDeviceInquiry alloc]
     45             initWithDelegate:inquiry_delegate_]) {}
     46 
     47   virtual ~BluetoothDiscoveryManagerMacClassic() {}
     48 
     49   // BluetoothDiscoveryManagerMac override.
     50   virtual bool IsDiscovering() const OVERRIDE { return should_do_discovery_; }
     51 
     52   // BluetoothDiscoveryManagerMac override.
     53   virtual bool StartDiscovery() OVERRIDE {
     54     DVLOG(1) << "Bluetooth Classic: StartDiscovery";
     55     DCHECK(!should_do_discovery_);
     56 
     57     DVLOG(1) << "Discovery requested";
     58     should_do_discovery_ = true;
     59 
     60     if (inquiry_running_) {
     61       DVLOG(1) << "Device inquiry already running";
     62       return true;
     63     }
     64 
     65     DVLOG(1) << "Requesting to start device inquiry";
     66     if ([inquiry_ start] != kIOReturnSuccess) {
     67       DVLOG(1) << "Failed to start device inquiry";
     68 
     69       // Set |should_do_discovery_| to false here. Since we're reporting an
     70       // error, we're indicating that the adapter call StartDiscovery again
     71       // if needed.
     72       should_do_discovery_ = false;
     73       return false;
     74     }
     75 
     76     DVLOG(1) << "Device inquiry start was successful";
     77     return true;
     78   }
     79 
     80   // BluetoothDiscoveryManagerMac override.
     81   virtual bool StopDiscovery() OVERRIDE {
     82     DVLOG(1) << "Bluetooth Classic: StopDiscovery";
     83     DCHECK(should_do_discovery_);
     84 
     85     should_do_discovery_ = false;
     86 
     87     if (!inquiry_running_) {
     88       DVLOG(1) << "No device inquiry running; discovery stopped";
     89       return true;
     90     }
     91 
     92     DVLOG(1) << "Requesting to stop device inquiry";
     93     IOReturn status = [inquiry_ stop];
     94     if (status == kIOReturnSuccess) {
     95       DVLOG(1) << "Device inquiry stop was successful";
     96       return true;
     97     }
     98 
     99     if (status == kIOReturnNotPermitted) {
    100       DVLOG(1) << "Device inquiry was already stopped";
    101       return true;
    102     }
    103 
    104     LOG(WARNING) << "Failed to stop device inquiry";
    105     return false;
    106   }
    107 
    108   // Called by BluetoothDeviceInquiryDelegate.
    109   void DeviceInquiryStarted(IOBluetoothDeviceInquiry* inquiry) {
    110     DCHECK(!inquiry_running_);
    111 
    112     DVLOG(1) << "Device inquiry started!";
    113 
    114     // If discovery was requested to stop in the mean time, stop the inquiry.
    115     if (!should_do_discovery_) {
    116       DVLOG(1) << "Discovery stop was requested earlier. Stopping inquiry";
    117       [inquiry stop];
    118       return;
    119     }
    120 
    121     inquiry_running_ = true;
    122   }
    123 
    124   void DeviceFound(IOBluetoothDeviceInquiry* inquiry,
    125                    IOBluetoothDevice* device) {
    126     DCHECK(observer_);
    127     observer_->DeviceFound(this, device);
    128   }
    129 
    130   void DeviceInquiryComplete(IOBluetoothDeviceInquiry* inquiry,
    131                              IOReturn error,
    132                              bool aborted) {
    133     DCHECK_EQ(inquiry_, inquiry);
    134     DCHECK(observer_);
    135     DVLOG(1) << "Device inquiry complete";
    136     inquiry_running_ = false;
    137 
    138     // If discovery is no longer desired, notify observers that discovery
    139     // has stopped and return.
    140     if (!should_do_discovery_) {
    141       observer_->DiscoveryStopped(this, false /* unexpected */);
    142       return;
    143     }
    144 
    145     // If discovery has stopped due to an unexpected reason, notify the
    146     // observers and return.
    147     if (error != kIOReturnSuccess) {
    148       DVLOG(1) << "Inquiry has stopped with an error: " << error;
    149       should_do_discovery_ = false;
    150       observer_->DiscoveryStopped(this, true /* unexpected */);
    151       return;
    152     }
    153 
    154     DVLOG(1) << "Restarting device inquiry";
    155 
    156     if ([inquiry_ start] == kIOReturnSuccess) {
    157       DVLOG(1) << "Device inquiry restart was successful";
    158       return;
    159     }
    160 
    161     DVLOG(1) << "Failed to restart discovery";
    162     should_do_discovery_ = false;
    163     DCHECK(observer_);
    164     observer_->DiscoveryStopped(this, true /* unexpected */);
    165   }
    166 
    167  private:
    168   // The requested discovery state.
    169   bool should_do_discovery_;
    170 
    171   // The current inquiry state.
    172   bool inquiry_running_;
    173 
    174   // Objective-C objects for running and tracking device inquiry.
    175   base::scoped_nsobject<BluetoothDeviceInquiryDelegate> inquiry_delegate_;
    176   base::scoped_nsobject<IOBluetoothDeviceInquiry> inquiry_;
    177 
    178   DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryManagerMacClassic);
    179 };
    180 
    181 BluetoothDiscoveryManagerMac::BluetoothDiscoveryManagerMac(
    182     Observer* observer) : observer_(observer) {
    183   DCHECK(observer);
    184 }
    185 
    186 BluetoothDiscoveryManagerMac::~BluetoothDiscoveryManagerMac() {
    187 }
    188 
    189 // static
    190 BluetoothDiscoveryManagerMac* BluetoothDiscoveryManagerMac::CreateClassic(
    191     Observer* observer) {
    192   return new BluetoothDiscoveryManagerMacClassic(observer);
    193 }
    194 
    195 }  // namespace device
    196 
    197 @implementation BluetoothDeviceInquiryDelegate
    198 
    199 - (id)initWithManager:
    200           (device::BluetoothDiscoveryManagerMacClassic*)manager {
    201   if ((self = [super init]))
    202     manager_ = manager;
    203 
    204   return self;
    205 }
    206 
    207 - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
    208   manager_->DeviceInquiryStarted(sender);
    209 }
    210 
    211 - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
    212                           device:(IOBluetoothDevice*)device {
    213   manager_->DeviceFound(sender, device);
    214 }
    215 
    216 - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
    217                         error:(IOReturn)error
    218                       aborted:(BOOL)aborted {
    219   manager_->DeviceInquiryComplete(sender, error, aborted);
    220 }
    221 
    222 @end
    223