| // 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 <IOBluetooth/objc/IOBluetoothDevice.h> |
| #import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h> |
| |
| #include "base/logging.h" |
| #include "base/mac/scoped_nsobject.h" |
| #include "base/mac/sdk_forward_declarations.h" |
| #include "base/macros.h" |
| |
| namespace device { |
| |
| class BluetoothDiscoveryManagerMacClassic; |
| |
| } // namespace device |
| |
| // IOBluetoothDeviceInquiryDelegate implementation. |
| @interface BluetoothDeviceInquiryDelegate |
| : NSObject<IOBluetoothDeviceInquiryDelegate> { |
| @private |
| device::BluetoothDiscoveryManagerMacClassic* manager_; // weak |
| } |
| |
| - (id)initWithManager:(device::BluetoothDiscoveryManagerMacClassic*)manager; |
| |
| @end |
| |
| namespace device { |
| |
| // Implementation 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; |
| |
| // Clean the cache so that new discovery sessions find previously |
| // discovered devices as well. |
| [inquiry_ clearFoundDevices]; |
| |
| 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_->ClassicDeviceFound(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_->ClassicDiscoveryStopped(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_->ClassicDiscoveryStopped(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_->ClassicDiscoveryStopped(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<BluetoothDeviceInquiryDelegate> inquiry_delegate_; |
| base::scoped_nsobject<IOBluetoothDeviceInquiry> 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 |