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 14namespace device { 15 16class 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 31namespace device { 32 33// ementation of BluetoothDiscoveryManagerMac for Bluetooth classic device 34// discovery, using the IOBluetooth framework. 35class 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 181BluetoothDiscoveryManagerMac::BluetoothDiscoveryManagerMac( 182 Observer* observer) : observer_(observer) { 183 DCHECK(observer); 184} 185 186BluetoothDiscoveryManagerMac::~BluetoothDiscoveryManagerMac() { 187} 188 189// static 190BluetoothDiscoveryManagerMac* 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