blob: fe5f88824d1eb54fbcf9b1ba43559521cf67846b [file] [log] [blame]
// Copyright 2013 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_adapter_mac.h"
#import <IOBluetooth/objc/IOBluetoothDevice.h>
#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
#import <IOBluetooth/objc/IOBluetoothHostController.h>
#include <string>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/containers/hash_tables.h"
#include "base/location.h"
#include "base/memory/scoped_ptr.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "device/bluetooth/bluetooth_device_mac.h"
// Replicate specific 10.7 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
@interface IOBluetoothHostController (LionSDKDeclarations)
- (NSString*)nameAsString;
- (BluetoothHCIPowerState)powerState;
@end
@protocol IOBluetoothDeviceInquiryDelegate
- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
device:(IOBluetoothDevice*)device;
- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
error:(IOReturn)error
aborted:(BOOL)aborted;
@end
#endif // MAC_OS_X_VERSION_10_7
@interface BluetoothAdapterMacDelegate
: NSObject <IOBluetoothDeviceInquiryDelegate> {
@private
device::BluetoothAdapterMac* adapter_; // weak
}
- (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter;
@end
@implementation BluetoothAdapterMacDelegate
- (id)initWithAdapter:(device::BluetoothAdapterMac*)adapter {
if ((self = [super init]))
adapter_ = adapter;
return self;
}
- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender {
adapter_->DeviceInquiryStarted(sender);
}
- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
device:(IOBluetoothDevice*)device {
adapter_->DeviceFound(sender, device);
}
- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
error:(IOReturn)error
aborted:(BOOL)aborted {
adapter_->DeviceInquiryComplete(sender, error, aborted);
}
@end
namespace {
const int kPollIntervalMs = 500;
} // namespace
namespace device {
// static
base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
const InitCallback& init_callback) {
return BluetoothAdapterMac::CreateAdapter();
}
// static
base::WeakPtr<BluetoothAdapter> BluetoothAdapterMac::CreateAdapter() {
BluetoothAdapterMac* adapter = new BluetoothAdapterMac();
adapter->Init();
return adapter->weak_ptr_factory_.GetWeakPtr();
}
BluetoothAdapterMac::BluetoothAdapterMac()
: BluetoothAdapter(),
powered_(false),
discovery_status_(NOT_DISCOVERING),
adapter_delegate_(
[[BluetoothAdapterMacDelegate alloc] initWithAdapter:this]),
device_inquiry_(
[[IOBluetoothDeviceInquiry
inquiryWithDelegate:adapter_delegate_] retain]),
weak_ptr_factory_(this) {
}
BluetoothAdapterMac::~BluetoothAdapterMac() {
}
void BluetoothAdapterMac::AddObserver(BluetoothAdapter::Observer* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void BluetoothAdapterMac::RemoveObserver(BluetoothAdapter::Observer* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
std::string BluetoothAdapterMac::GetAddress() const {
return address_;
}
std::string BluetoothAdapterMac::GetName() const {
return name_;
}
void BluetoothAdapterMac::SetName(const std::string& name,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterMac::IsInitialized() const {
return true;
}
bool BluetoothAdapterMac::IsPresent() const {
return !address_.empty();
}
bool BluetoothAdapterMac::IsPowered() const {
return powered_;
}
void BluetoothAdapterMac::SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterMac::IsDiscoverable() const {
NOTIMPLEMENTED();
return false;
}
void BluetoothAdapterMac::SetDiscoverable(
bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTIMPLEMENTED();
}
bool BluetoothAdapterMac::IsDiscovering() const {
return discovery_status_ == DISCOVERING ||
discovery_status_ == DISCOVERY_STOPPING;
}
void BluetoothAdapterMac::ReadLocalOutOfBandPairingData(
const BluetoothOutOfBandPairingDataCallback& callback,
const ErrorCallback& error_callback) {
}
void BluetoothAdapterMac::AddDiscoverySession(
const base::Closure& callback,
const ErrorCallback& error_callback) {
if (discovery_status_ == DISCOVERING) {
num_discovery_listeners_++;
callback.Run();
return;
}
on_start_discovery_callbacks_.push_back(
std::make_pair(callback, error_callback));
MaybeStartDeviceInquiry();
}
void BluetoothAdapterMac::RemoveDiscoverySession(
const base::Closure& callback,
const ErrorCallback& error_callback) {
if (discovery_status_ == NOT_DISCOVERING) {
error_callback.Run();
return;
}
on_stop_discovery_callbacks_.push_back(
std::make_pair(callback, error_callback));
MaybeStopDeviceInquiry();
}
void BluetoothAdapterMac::RemovePairingDelegateInternal(
BluetoothDevice::PairingDelegate* pairing_delegate) {
}
void BluetoothAdapterMac::Init() {
ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
PollAdapter();
}
void BluetoothAdapterMac::InitForTest(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner) {
ui_task_runner_ = ui_task_runner;
PollAdapter();
}
void BluetoothAdapterMac::PollAdapter() {
bool was_present = IsPresent();
std::string name;
std::string address;
bool powered = false;
IOBluetoothHostController* controller =
[IOBluetoothHostController defaultController];
if (controller != nil) {
name = base::SysNSStringToUTF8([controller nameAsString]);
address = BluetoothDeviceMac::NormalizeAddress(
base::SysNSStringToUTF8([controller addressAsString]));
powered = ([controller powerState] == kBluetoothHCIPowerStateON);
}
bool is_present = !address.empty();
name_ = name;
address_ = address;
if (was_present != is_present) {
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterPresentChanged(this, is_present));
}
if (powered_ != powered) {
powered_ = powered;
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterPoweredChanged(this, powered_));
}
IOBluetoothDevice* recent_device =
[[IOBluetoothDevice recentDevices:1] lastObject];
NSDate* access_timestamp = [recent_device recentAccessDate];
if (recently_accessed_device_timestamp_ == nil ||
access_timestamp == nil ||
[recently_accessed_device_timestamp_ compare:access_timestamp] ==
NSOrderedAscending) {
UpdateDevices([IOBluetoothDevice pairedDevices]);
recently_accessed_device_timestamp_.reset([access_timestamp copy]);
}
ui_task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&BluetoothAdapterMac::PollAdapter,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kPollIntervalMs));
}
void BluetoothAdapterMac::UpdateDevices(NSArray* devices) {
STLDeleteValues(&devices_);
for (IOBluetoothDevice* device in devices) {
std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
devices_[device_address] = new BluetoothDeviceMac(device);
}
}
void BluetoothAdapterMac::DeviceInquiryStarted(
IOBluetoothDeviceInquiry* inquiry) {
DCHECK_EQ(device_inquiry_, inquiry);
if (discovery_status_ == DISCOVERING)
return;
discovery_status_ = DISCOVERING;
RunCallbacks(on_start_discovery_callbacks_, true);
num_discovery_listeners_ = on_start_discovery_callbacks_.size();
on_start_discovery_callbacks_.clear();
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterDiscoveringChanged(this, true));
MaybeStopDeviceInquiry();
}
void BluetoothAdapterMac::DeviceFound(IOBluetoothDeviceInquiry* inquiry,
IOBluetoothDevice* device) {
DCHECK_EQ(device_inquiry_, inquiry);
std::string device_address = BluetoothDeviceMac::GetDeviceAddress(device);
if (discovered_devices_.find(device_address) == discovered_devices_.end()) {
BluetoothDeviceMac device_mac(device);
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
DeviceAdded(this, &device_mac));
discovered_devices_.insert(device_address);
}
}
void BluetoothAdapterMac::DeviceInquiryComplete(
IOBluetoothDeviceInquiry* inquiry,
IOReturn error,
bool aborted) {
DCHECK_EQ(device_inquiry_, inquiry);
if (discovery_status_ == DISCOVERING &&
[device_inquiry_ start] == kIOReturnSuccess) {
return;
}
// Device discovery is done.
discovered_devices_.clear();
discovery_status_ = NOT_DISCOVERING;
RunCallbacks(on_stop_discovery_callbacks_, error == kIOReturnSuccess);
num_discovery_listeners_ = 0;
on_stop_discovery_callbacks_.clear();
FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
AdapterDiscoveringChanged(this, false));
MaybeStartDeviceInquiry();
}
void BluetoothAdapterMac::MaybeStartDeviceInquiry() {
if (discovery_status_ == NOT_DISCOVERING &&
!on_start_discovery_callbacks_.empty()) {
discovery_status_ = DISCOVERY_STARTING;
if ([device_inquiry_ start] != kIOReturnSuccess) {
discovery_status_ = NOT_DISCOVERING;
RunCallbacks(on_start_discovery_callbacks_, false);
on_start_discovery_callbacks_.clear();
}
}
}
void BluetoothAdapterMac::MaybeStopDeviceInquiry() {
if (discovery_status_ != DISCOVERING)
return;
if (on_stop_discovery_callbacks_.size() < num_discovery_listeners_) {
RunCallbacks(on_stop_discovery_callbacks_, true);
num_discovery_listeners_ -= on_stop_discovery_callbacks_.size();
on_stop_discovery_callbacks_.clear();
return;
}
discovery_status_ = DISCOVERY_STOPPING;
if ([device_inquiry_ stop] != kIOReturnSuccess) {
RunCallbacks(on_stop_discovery_callbacks_, false);
on_stop_discovery_callbacks_.clear();
}
}
void BluetoothAdapterMac::RunCallbacks(
const DiscoveryCallbackList& callback_list, bool success) const {
for (DiscoveryCallbackList::const_iterator iter = callback_list.begin();
iter != callback_list.end();
++iter) {
if (success)
ui_task_runner_->PostTask(FROM_HERE, iter->first);
else
ui_task_runner_->PostTask(FROM_HERE, iter->second);
}
}
} // namespace device