// Copyright (c) 2012 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.h" #include #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "build/build_config.h" #include "device/bluetooth/bluetooth_common.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_discovery_session.h" #include "device/bluetooth/bluetooth_discovery_session_outcome.h" #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h" #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h" #include "device/bluetooth/bluetooth_remote_gatt_service.h" namespace device { BluetoothAdapter::ServiceOptions::ServiceOptions() = default; BluetoothAdapter::ServiceOptions::~ServiceOptions() = default; #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) && !defined(OS_MACOSX) && \ !defined(OS_WIN) && !defined(OS_LINUX) // static scoped_refptr BluetoothAdapter::CreateAdapter() { return nullptr; } #endif // Not supported platforms. base::WeakPtr BluetoothAdapter::GetWeakPtrForTesting() { return GetWeakPtr(); } #if defined(OS_LINUX) void BluetoothAdapter::Shutdown() { NOTIMPLEMENTED(); } #endif void BluetoothAdapter::AddObserver(BluetoothAdapter::Observer* observer) { DCHECK(observer); observers_.AddObserver(observer); } void BluetoothAdapter::RemoveObserver(BluetoothAdapter::Observer* observer) { DCHECK(observer); observers_.RemoveObserver(observer); } std::string BluetoothAdapter::GetSystemName() const { NOTIMPLEMENTED(); return std::string(); } bool BluetoothAdapter::HasObserver(BluetoothAdapter::Observer* observer) { DCHECK(observer); return observers_.HasObserver(observer); } bool BluetoothAdapter::CanPower() const { return IsPresent(); } void BluetoothAdapter::SetPowered(bool powered, const base::Closure& callback, const ErrorCallback& error_callback) { if (set_powered_callbacks_) { // Only allow one pending callback at a time. ui_task_runner_->PostTask(FROM_HERE, error_callback); return; } if (powered == IsPowered()) { // Return early in case no change of power state is needed. ui_task_runner_->PostTask(FROM_HERE, callback); return; } if (!SetPoweredImpl(powered)) { ui_task_runner_->PostTask(FROM_HERE, error_callback); return; } set_powered_callbacks_ = std::make_unique(); set_powered_callbacks_->powered = powered; set_powered_callbacks_->callback = callback; set_powered_callbacks_->error_callback = error_callback; } bool BluetoothAdapter::IsPeripheralRoleSupported() const { // TODO(crbug/1071595): Implement this for more platforms. return true; } std::unordered_map BluetoothAdapter::RetrieveGattConnectedDevicesWithDiscoveryFilter( const BluetoothDiscoveryFilter& discovery_filter) { return std::unordered_map(); } void BluetoothAdapter::StartDiscoverySession(DiscoverySessionCallback callback, ErrorOnceCallback error_callback) { StartDiscoverySessionWithFilter(nullptr, std::move(callback), std::move(error_callback)); } void BluetoothAdapter::StartDiscoverySessionWithFilter( std::unique_ptr discovery_filter, DiscoverySessionCallback callback, ErrorOnceCallback error_callback) { std::unique_ptr new_session( new BluetoothDiscoverySession(this, std::move(discovery_filter))); discovery_sessions_.insert(new_session.get()); auto new_session_callbacks = std::make_unique( base::BindOnce(std::move(callback), std::move(new_session)), std::move(error_callback)); // Queue up the callbacks to be handled when we process the discovery queue. discovery_callback_queue_.push(std::move(new_session_callbacks)); // If OS is already working on a discovery request we must wait to process the // queue until that request is complete. if (discovery_request_pending_) { return; } // The OS is ready to start a request so process the queue now. ProcessDiscoveryQueue(); } void BluetoothAdapter::MaybeUpdateFilter( std::unique_ptr discovery_filter, DiscoverySessionResultCallback callback) { if (discovery_filter->Equals(current_discovery_filter_)) { std::move(callback).Run(/*is_error=*/false, UMABluetoothDiscoverySessionOutcome::SUCCESS); return; } UpdateFilter(std::move(discovery_filter), std::move(callback)); } void BluetoothAdapter::RemoveDiscoverySession( BluetoothDiscoverySession* discovery_session, base::OnceClosure callback, DiscoverySessionErrorCallback error_callback) { size_t erased = discovery_sessions_.erase(discovery_session); DCHECK_EQ(1u, erased); auto removal_callbacks = std::make_unique( std::move(callback), std::move(error_callback)); // Queue up the callbacks to be handled when we process the discovery queue. discovery_callback_queue_.push(std::move(removal_callbacks)); // If OS is already working on a discovery request we must wait to process the // queue until that request is complete. if (discovery_request_pending_) { return; } // The OS is ready to start a request so process the queue now. ProcessDiscoveryQueue(); } std::unique_ptr BluetoothAdapter::GetMergedDiscoveryFilter() const { auto result = std::make_unique(BLUETOOTH_TRANSPORT_DUAL); bool first_merge = true; for (auto* iter : discovery_sessions_) { if (!iter->IsActive()) continue; const BluetoothDiscoveryFilter* curr_filter = iter->GetDiscoveryFilter(); if (first_merge) { first_merge = false; if (curr_filter) { result->CopyFrom(*curr_filter); } continue; } result = BluetoothDiscoveryFilter::Merge(result.get(), curr_filter); } return result; } BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() { ConstDeviceList const_devices = const_cast(this)->GetDevices(); DeviceList devices; for (ConstDeviceList::const_iterator i = const_devices.begin(); i != const_devices.end(); ++i) devices.push_back(const_cast(*i)); return devices; } BluetoothAdapter::ConstDeviceList BluetoothAdapter::GetDevices() const { ConstDeviceList devices; for (const auto& device : devices_) devices.push_back(device.second.get()); return devices; } BluetoothDevice* BluetoothAdapter::GetDevice(const std::string& address) { return const_cast( const_cast(this)->GetDevice(address)); } const BluetoothDevice* BluetoothAdapter::GetDevice( const std::string& address) const { std::string canonicalized_address = BluetoothDevice::CanonicalizeAddress(address); if (canonicalized_address.empty()) return nullptr; auto iter = devices_.find(canonicalized_address); if (iter != devices_.end()) return iter->second.get(); return nullptr; } void BluetoothAdapter::AddPairingDelegate( BluetoothDevice::PairingDelegate* pairing_delegate, PairingDelegatePriority priority) { // Remove the delegate, if it already exists, before inserting to allow a // change of priority. RemovePairingDelegate(pairing_delegate); // Find the first point with a lower priority, or the end of the list. auto iter = pairing_delegates_.begin(); while (iter != pairing_delegates_.end() && iter->second >= priority) ++iter; pairing_delegates_.insert(iter, std::make_pair(pairing_delegate, priority)); } void BluetoothAdapter::RemovePairingDelegate( BluetoothDevice::PairingDelegate* pairing_delegate) { for (auto iter = pairing_delegates_.begin(); iter != pairing_delegates_.end(); ++iter) { if (iter->first == pairing_delegate) { RemovePairingDelegateInternal(pairing_delegate); pairing_delegates_.erase(iter); return; } } } BluetoothDevice::PairingDelegate* BluetoothAdapter::DefaultPairingDelegate() { if (pairing_delegates_.empty()) return NULL; return pairing_delegates_.front().first; } std::vector BluetoothAdapter::GetPendingAdvertisementsForTesting() const { return {}; } void BluetoothAdapter::NotifyAdapterPresentChanged(bool present) { for (auto& observer : observers_) observer.AdapterPresentChanged(this, present); } void BluetoothAdapter::NotifyAdapterPoweredChanged(bool powered) { for (auto& observer : observers_) observer.AdapterPoweredChanged(this, powered); } void BluetoothAdapter::NotifyDeviceChanged(BluetoothDevice* device) { DCHECK(device); DCHECK_EQ(device->GetAdapter(), this); for (auto& observer : observers_) observer.DeviceChanged(this, device); } void BluetoothAdapter::NotifyAdapterDiscoveryChangeCompletedForTesting() { for (auto& observer : observers_) observer.DiscoveryChangeCompletedForTesting(); } #if defined(OS_CHROMEOS) || defined(OS_LINUX) void BluetoothAdapter::NotifyDevicePairedChanged(BluetoothDevice* device, bool new_paired_status) { for (auto& observer : observers_) observer.DevicePairedChanged(this, device, new_paired_status); } #endif #if defined(OS_CHROMEOS) void BluetoothAdapter::NotifyDeviceBatteryChanged(BluetoothDevice* device) { DCHECK_EQ(device->GetAdapter(), this); for (auto& observer : observers_) { observer.DeviceBatteryChanged(this, device, device->battery_percentage()); } } #endif void BluetoothAdapter::NotifyGattServiceAdded( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattServiceAdded(this, service->GetDevice(), service); } void BluetoothAdapter::NotifyGattServiceRemoved( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattServiceRemoved(this, service->GetDevice(), service); } void BluetoothAdapter::NotifyGattServiceChanged( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattServiceChanged(this, service); } int BluetoothAdapter::NumDiscoverySessions() const { return discovery_sessions_.size(); } int BluetoothAdapter::NumScanningDiscoverySessions() const { int count = 0; for (auto* session : discovery_sessions_) { if (session->status() == BluetoothDiscoverySession::SessionStatus::SCANNING) { ++count; } } return count; } void BluetoothAdapter::NotifyGattServicesDiscovered(BluetoothDevice* device) { DCHECK(device->GetAdapter() == this); for (auto& observer : observers_) observer.GattServicesDiscovered(this, device); } void BluetoothAdapter::NotifyGattDiscoveryComplete( BluetoothRemoteGattService* service) { DCHECK_EQ(service->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattDiscoveryCompleteForService(this, service); } void BluetoothAdapter::NotifyGattCharacteristicAdded( BluetoothRemoteGattCharacteristic* characteristic) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattCharacteristicAdded(this, characteristic); } void BluetoothAdapter::NotifyGattCharacteristicRemoved( BluetoothRemoteGattCharacteristic* characteristic) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattCharacteristicRemoved(this, characteristic); } void BluetoothAdapter::NotifyGattDescriptorAdded( BluetoothRemoteGattDescriptor* descriptor) { DCHECK_EQ( descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattDescriptorAdded(this, descriptor); } void BluetoothAdapter::NotifyGattDescriptorRemoved( BluetoothRemoteGattDescriptor* descriptor) { DCHECK_EQ( descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattDescriptorRemoved(this, descriptor); } void BluetoothAdapter::NotifyGattCharacteristicValueChanged( BluetoothRemoteGattCharacteristic* characteristic, const std::vector& value) { DCHECK_EQ(characteristic->GetService()->GetDevice()->GetAdapter(), this); base::WeakPtr weak_characteristic = characteristic->GetWeakPtr(); for (auto& observer : observers_) { if (!weak_characteristic) break; observer.GattCharacteristicValueChanged(this, characteristic, value); } } void BluetoothAdapter::NotifyGattDescriptorValueChanged( BluetoothRemoteGattDescriptor* descriptor, const std::vector& value) { DCHECK_EQ( descriptor->GetCharacteristic()->GetService()->GetDevice()->GetAdapter(), this); for (auto& observer : observers_) observer.GattDescriptorValueChanged(this, descriptor, value); } BluetoothAdapter::SetPoweredCallbacks::SetPoweredCallbacks() = default; BluetoothAdapter::SetPoweredCallbacks::~SetPoweredCallbacks() = default; BluetoothAdapter::StartOrStopDiscoveryCallback::StartOrStopDiscoveryCallback( base::OnceClosure start_callback, ErrorOnceCallback start_error_callback) { this->start_callback = std::move(start_callback); this->start_error_callback = std::move(start_error_callback); } BluetoothAdapter::StartOrStopDiscoveryCallback::StartOrStopDiscoveryCallback( base::OnceClosure stop_callback, DiscoverySessionErrorCallback stop_error_callback) { this->stop_callback = std::move(stop_callback); this->stop_error_callback = std::move(stop_error_callback); } BluetoothAdapter::StartOrStopDiscoveryCallback:: ~StartOrStopDiscoveryCallback() = default; BluetoothAdapter::BluetoothAdapter() {} BluetoothAdapter::~BluetoothAdapter() { // If there's a pending powered request, run its error callback. if (set_powered_callbacks_) set_powered_callbacks_->error_callback.Run(); } void BluetoothAdapter::RunPendingPowerCallbacks() { if (set_powered_callbacks_) { // Move into a local variable to clear out both callbacks at the end of the // scope and to allow scheduling another SetPowered() call in either of the // callbacks. auto callbacks = std::move(set_powered_callbacks_); callbacks->powered == IsPowered() ? std::move(callbacks->callback).Run() : callbacks->error_callback.Run(); } } void BluetoothAdapter::OnDiscoveryChangeComplete( bool is_error, UMABluetoothDiscoverySessionOutcome outcome) { UpdateDiscoveryState(is_error); // Take a weak reference to |this| in case a callback frees the adapter. base::WeakPtr self = GetWeakPtr(); if (is_error) { NotifyDiscoveryError(std::move(callbacks_awaiting_response_)); if (!self) return; discovery_request_pending_ = false; NotifyAdapterDiscoveryChangeCompletedForTesting(); ProcessDiscoveryQueue(); return; } // Inform BluetoothDiscoverySession that updates being processed have // completed. for (auto* session : discovery_sessions_) session->StartingSessionsScanning(); current_discovery_filter_.CopyFrom(filter_being_set_); auto callbacks_awaiting_response = std::move(callbacks_awaiting_response_); while (!callbacks_awaiting_response.empty()) { std::unique_ptr callbacks = std::move(callbacks_awaiting_response.front()); callbacks_awaiting_response.pop(); if (callbacks->start_callback) std::move(callbacks->start_callback).Run(); if (callbacks->stop_callback) std::move(callbacks->stop_callback).Run(); } if (!self) return; discovery_request_pending_ = false; NotifyAdapterDiscoveryChangeCompletedForTesting(); ProcessDiscoveryQueue(); } void BluetoothAdapter::UpdateDiscoveryState(bool is_error) { if (is_error) { if (internal_discovery_state_ == DiscoveryState::kStarting) internal_discovery_state_ = DiscoveryState::kIdle; // If there was an error stopping we still assume it worked as there is not // much we can do about the device messing up. if (internal_discovery_state_ == DiscoveryState::kStopping) internal_discovery_state_ = DiscoveryState::kIdle; return; } if (internal_discovery_state_ == DiscoveryState::kStarting) internal_discovery_state_ = DiscoveryState::kDiscovering; if (internal_discovery_state_ == DiscoveryState::kStopping) internal_discovery_state_ = DiscoveryState::kIdle; } void BluetoothAdapter::ProcessDiscoveryQueue() { if (discovery_callback_queue_.empty()) return; DCHECK(callbacks_awaiting_response_.empty()); callbacks_awaiting_response_.swap(discovery_callback_queue_); if (NumDiscoverySessions() == 0) { if (internal_discovery_state_ == DiscoveryState::kIdle) { OnDiscoveryChangeComplete(false, UMABluetoothDiscoverySessionOutcome::SUCCESS); return; } internal_discovery_state_ = DiscoveryState::kStopping; discovery_request_pending_ = true; StopScan(base::BindOnce(&BluetoothAdapter::OnDiscoveryChangeComplete, GetWeakPtr())); return; } // Inform BluetoothDiscoverySession that any updates they have made are being // processed. for (auto* session : discovery_sessions_) session->PendingSessionsStarting(); auto result_callback = base::BindOnce( &BluetoothAdapter::OnDiscoveryChangeComplete, GetWeakPtr()); auto new_desired_filter = GetMergedDiscoveryFilter(); discovery_request_pending_ = true; filter_being_set_.CopyFrom(*new_desired_filter.get()); if (internal_discovery_state_ == DiscoveryState::kDiscovering) { MaybeUpdateFilter(std::move(new_desired_filter), std::move(result_callback)); return; } internal_discovery_state_ = DiscoveryState::kStarting; StartScanWithFilter(std::move(new_desired_filter), std::move(result_callback)); } void BluetoothAdapter::NotifyDiscoveryError(CallbackQueue callback_queue) { while (!callback_queue.empty()) { std::unique_ptr callbacks = std::move(callback_queue.front()); callback_queue.pop(); if (callbacks->start_error_callback) std::move(callbacks->start_error_callback).Run(); // We never return error when stopping. If the physical adapter is messing // up and not stopping we are still just going to continue like it did stop. if (callbacks->stop_callback) std::move(callbacks->stop_callback).Run(); } } void BluetoothAdapter::MarkDiscoverySessionsAsInactive() { // All sessions are becoming inactive so any pending requests should now fail if (!discovery_callback_queue_.empty()) NotifyDiscoveryError(std::move(discovery_callback_queue_)); // As sessions are marked as inactive they will notify the adapter that they // have become inactive, upon which the adapter will remove them from // |discovery_sessions_|. To avoid invalidating the iterator, make a copy // here. std::set temp(discovery_sessions_); for (auto iter = temp.begin(); iter != temp.end(); ++iter) { (*iter)->MarkAsInactive(); RemoveDiscoverySession(*iter, base::DoNothing(), base::DoNothing()); } } void BluetoothAdapter::DeleteDeviceForTesting(const std::string& address) { devices_.erase(address); } void BluetoothAdapter::RemoveTimedOutDevices() { for (auto it = devices_.begin(); it != devices_.end();) { BluetoothDevice* device = it->second.get(); if (device->IsPaired() || device->IsConnected() || device->IsGattConnected()) { ++it; continue; } base::Time last_update_time = device->GetLastUpdateTime(); bool device_expired = (base::Time::NowFromSystemTime() - last_update_time) > timeoutSec; DVLOG(3) << "device: " << device->GetAddress() << ", last_update: " << last_update_time << ", exp: " << device_expired; if (!device_expired) { ++it; continue; } DVLOG(1) << "Removing device: " << device->GetAddress(); auto next = it; next++; std::unique_ptr removed_device = std::move(it->second); devices_.erase(it); it = next; for (auto& observer : observers_) observer.DeviceRemoved(this, removed_device.get()); } } // static void BluetoothAdapter::RecordBluetoothDiscoverySessionStartOutcome( UMABluetoothDiscoverySessionOutcome outcome) { UMA_HISTOGRAM_ENUMERATION( "Bluetooth.DiscoverySession.Start.Outcome", static_cast(outcome), static_cast(UMABluetoothDiscoverySessionOutcome::COUNT)); } // static void BluetoothAdapter::RecordBluetoothDiscoverySessionStopOutcome( UMABluetoothDiscoverySessionOutcome outcome) { UMA_HISTOGRAM_ENUMERATION( "Bluetooth.DiscoverySession.Stop.Outcome", static_cast(outcome), static_cast(UMABluetoothDiscoverySessionOutcome::COUNT)); } // static constexpr base::TimeDelta BluetoothAdapter::timeoutSec = base::TimeDelta::FromSeconds(180); } // namespace device