// 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" #include "device/bluetooth/bluetooth_socket_mac.h" #include "device/bluetooth/bluetooth_uuid.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::CreateRfcommService( const BluetoothUUID& uuid, int channel, bool insecure, const CreateServiceCallback& callback, const CreateServiceErrorCallback& error_callback) { // TODO(keybuk): implement. NOTIMPLEMENTED(); } void BluetoothAdapterMac::CreateL2capService( const BluetoothUUID& uuid, int psm, const CreateServiceCallback& callback, const CreateServiceErrorCallback& error_callback) { // TODO(keybuk): implement. NOTIMPLEMENTED(); } 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 = BluetoothDevice::CanonicalizeAddress( 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