blob: ebeb01dc0904dccea9e4880e3d5def3eaa0ac8c1 [file] [log] [blame]
// 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