// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "device/bluetooth/bluetooth_discovery_manager_mac.h" #import #import #include "base/mac/scoped_nsobject.h" #include "base/mac/sdk_forward_declarations.h" #include "base/logging.h" namespace device { class BluetoothDiscoveryManagerMacClassic; } // namespace device // IOBluetoothDeviceInquiryDelegate implementation. @interface BluetoothDeviceInquiryDelegate : NSObject { @private device::BluetoothDiscoveryManagerMacClassic* manager_; // weak } - (id)initWithManager:(device::BluetoothDiscoveryManagerMacClassic*)manager; @end namespace device { // ementation of BluetoothDiscoveryManagerMac for Bluetooth classic device // discovery, using the IOBluetooth framework. class BluetoothDiscoveryManagerMacClassic : public BluetoothDiscoveryManagerMac { public: explicit BluetoothDiscoveryManagerMacClassic(Observer* observer) : BluetoothDiscoveryManagerMac(observer), should_do_discovery_(false), inquiry_running_(false), inquiry_delegate_( [[BluetoothDeviceInquiryDelegate alloc] initWithManager:this]), inquiry_([[IOBluetoothDeviceInquiry alloc] initWithDelegate:inquiry_delegate_]) {} ~BluetoothDiscoveryManagerMacClassic() override {} // BluetoothDiscoveryManagerMac override. bool IsDiscovering() const override { return should_do_discovery_; } // BluetoothDiscoveryManagerMac override. bool StartDiscovery() override { DVLOG(1) << "Bluetooth Classic: StartDiscovery"; DCHECK(!should_do_discovery_); DVLOG(1) << "Discovery requested"; should_do_discovery_ = true; if (inquiry_running_) { DVLOG(1) << "Device inquiry already running"; return true; } DVLOG(1) << "Requesting to start device inquiry"; if ([inquiry_ start] != kIOReturnSuccess) { DVLOG(1) << "Failed to start device inquiry"; // Set |should_do_discovery_| to false here. Since we're reporting an // error, we're indicating that the adapter call StartDiscovery again // if needed. should_do_discovery_ = false; return false; } DVLOG(1) << "Device inquiry start was successful"; return true; } // BluetoothDiscoveryManagerMac override. bool StopDiscovery() override { DVLOG(1) << "Bluetooth Classic: StopDiscovery"; DCHECK(should_do_discovery_); should_do_discovery_ = false; if (!inquiry_running_) { DVLOG(1) << "No device inquiry running; discovery stopped"; return true; } DVLOG(1) << "Requesting to stop device inquiry"; IOReturn status = [inquiry_ stop]; if (status == kIOReturnSuccess) { DVLOG(1) << "Device inquiry stop was successful"; return true; } if (status == kIOReturnNotPermitted) { DVLOG(1) << "Device inquiry was already stopped"; return true; } LOG(WARNING) << "Failed to stop device inquiry"; return false; } // Called by BluetoothDeviceInquiryDelegate. void DeviceInquiryStarted(IOBluetoothDeviceInquiry* inquiry) { DCHECK(!inquiry_running_); DVLOG(1) << "Device inquiry started!"; // If discovery was requested to stop in the mean time, stop the inquiry. if (!should_do_discovery_) { DVLOG(1) << "Discovery stop was requested earlier. Stopping inquiry"; [inquiry stop]; return; } inquiry_running_ = true; } void DeviceFound(IOBluetoothDeviceInquiry* inquiry, IOBluetoothDevice* device) { DCHECK(observer_); observer_->DeviceFound(device); } void DeviceInquiryComplete(IOBluetoothDeviceInquiry* inquiry, IOReturn error, bool aborted) { DCHECK_EQ(inquiry_, inquiry); DCHECK(observer_); DVLOG(1) << "Device inquiry complete"; inquiry_running_ = false; // If discovery is no longer desired, notify observers that discovery // has stopped and return. if (!should_do_discovery_) { observer_->DiscoveryStopped(false /* unexpected */); return; } // If discovery has stopped due to an unexpected reason, notify the // observers and return. if (error != kIOReturnSuccess) { DVLOG(1) << "Inquiry has stopped with an error: " << error; should_do_discovery_ = false; observer_->DiscoveryStopped(true /* unexpected */); return; } DVLOG(1) << "Restarting device inquiry"; if ([inquiry_ start] == kIOReturnSuccess) { DVLOG(1) << "Device inquiry restart was successful"; return; } DVLOG(1) << "Failed to restart discovery"; should_do_discovery_ = false; DCHECK(observer_); observer_->DiscoveryStopped(true /* unexpected */); } private: // The requested discovery state. bool should_do_discovery_; // The current inquiry state. bool inquiry_running_; // Objective-C objects for running and tracking device inquiry. base::scoped_nsobject inquiry_delegate_; base::scoped_nsobject inquiry_; DISALLOW_COPY_AND_ASSIGN(BluetoothDiscoveryManagerMacClassic); }; BluetoothDiscoveryManagerMac::BluetoothDiscoveryManagerMac( Observer* observer) : observer_(observer) { DCHECK(observer); } BluetoothDiscoveryManagerMac::~BluetoothDiscoveryManagerMac() { } // static BluetoothDiscoveryManagerMac* BluetoothDiscoveryManagerMac::CreateClassic( Observer* observer) { return new BluetoothDiscoveryManagerMacClassic(observer); } } // namespace device @implementation BluetoothDeviceInquiryDelegate - (id)initWithManager: (device::BluetoothDiscoveryManagerMacClassic*)manager { if ((self = [super init])) manager_ = manager; return self; } - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender { manager_->DeviceInquiryStarted(sender); } - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender device:(IOBluetoothDevice*)device { manager_->DeviceFound(sender, device); } - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender error:(IOReturn)error aborted:(BOOL)aborted { manager_->DeviceInquiryComplete(sender, error, aborted); } @end