| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/bluetooth/bluetooth_adapter_factory_wrapper.h" |
| |
| #include <stddef.h> |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/containers/contains.h" |
| #include "base/location.h" |
| #include "base/no_destructor.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/browser/bluetooth/web_bluetooth_service_impl.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| |
| namespace content { |
| |
| namespace { |
| using ::device::BluetoothAdapter; |
| using ::device::BluetoothAdapterFactory; |
| } // namespace |
| |
| BluetoothAdapterFactoryWrapper::BluetoothAdapterFactoryWrapper() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| BluetoothAdapterFactoryWrapper::~BluetoothAdapterFactoryWrapper() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| // All observers should have been removed already. |
| DCHECK(adapter_observers_.empty()); |
| // Clear adapter. |
| set_adapter(nullptr); |
| } |
| |
| // static |
| BluetoothAdapterFactoryWrapper& BluetoothAdapterFactoryWrapper::Get() { |
| static base::NoDestructor<BluetoothAdapterFactoryWrapper> singleton; |
| return *singleton; |
| } |
| |
| bool BluetoothAdapterFactoryWrapper::IsLowEnergySupported() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (adapter_ || test_adapter_) |
| return true; |
| return BluetoothAdapterFactory::Get()->IsLowEnergySupported(); |
| } |
| |
| void BluetoothAdapterFactoryWrapper::AcquireAdapter( |
| WebBluetoothServiceImpl* service, |
| AcquireAdapterCallback callback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!GetAdapter(service)); |
| |
| MaybeAddAdapterObserver(service); |
| if (adapter_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), adapter_)); |
| return; |
| } |
| |
| // Simulate the normally asynchronous process of acquiring the adapter in |
| // tests. |
| if (test_adapter_) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&BluetoothAdapterFactoryWrapper::OnGetAdapter, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(callback), test_adapter_)); |
| return; |
| } |
| |
| DCHECK(BluetoothAdapterFactory::Get()->IsLowEnergySupported()); |
| BluetoothAdapterFactory::Get()->GetAdapter( |
| base::BindOnce(&BluetoothAdapterFactoryWrapper::OnGetAdapter, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void BluetoothAdapterFactoryWrapper::ReleaseAdapter( |
| WebBluetoothServiceImpl* service) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (!HasAdapter(service)) { |
| return; |
| } |
| RemoveAdapterObserver(service); |
| if (adapter_observers_.empty()) |
| set_adapter(nullptr); |
| } |
| |
| BluetoothAdapter* BluetoothAdapterFactoryWrapper::GetAdapter( |
| WebBluetoothServiceImpl* service) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (HasAdapter(service)) { |
| return adapter_.get(); |
| } |
| return nullptr; |
| } |
| |
| void BluetoothAdapterFactoryWrapper::SetBluetoothAdapterForTesting( |
| scoped_refptr<BluetoothAdapter> test_adapter) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // If an adapter has already been acquired allow the adapter to be swapped out |
| // synchronously. |
| if (adapter_) { |
| set_adapter(std::move(test_adapter)); |
| return; |
| } |
| |
| test_adapter_ = std::move(test_adapter); |
| } |
| |
| void BluetoothAdapterFactoryWrapper::OnGetAdapter( |
| AcquireAdapterCallback continuation, |
| scoped_refptr<BluetoothAdapter> adapter) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // Clear the adapter configured for testing now that it has been acquired. |
| test_adapter_.reset(); |
| |
| set_adapter(adapter); |
| std::move(continuation).Run(adapter_); |
| } |
| |
| bool BluetoothAdapterFactoryWrapper::HasAdapter( |
| WebBluetoothServiceImpl* service) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| return base::Contains(adapter_observers_, service); |
| } |
| |
| void BluetoothAdapterFactoryWrapper::MaybeAddAdapterObserver( |
| WebBluetoothServiceImpl* service) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // The same WebBluetoothServiceImpl might acquire the adapter multiple times |
| // if it gets multiple requests in parallel before the adapter is ready but is |
| // guaranteed to only call ReleaseAdapter() once on destruction. |
| auto [it, inserted] = adapter_observers_.insert(service); |
| if (inserted && adapter_) { |
| adapter_->AddObserver(service); |
| } |
| } |
| |
| void BluetoothAdapterFactoryWrapper::RemoveAdapterObserver( |
| WebBluetoothServiceImpl* service) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| size_t removed = adapter_observers_.erase(service); |
| DCHECK(removed); |
| if (adapter_) { |
| adapter_->RemoveObserver(service); |
| } |
| } |
| |
| void BluetoothAdapterFactoryWrapper::set_adapter( |
| scoped_refptr<BluetoothAdapter> adapter) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| // There might be extra unnecessary calls to this method if multiple requests |
| // to acquire an adapter were in flight at once. |
| if (adapter.get() == adapter_.get()) |
| return; |
| |
| if (adapter_.get()) { |
| for (WebBluetoothServiceImpl* service : adapter_observers_) { |
| adapter_->RemoveObserver(service); |
| } |
| } |
| adapter_ = std::move(adapter); |
| if (adapter_.get()) { |
| for (WebBluetoothServiceImpl* service : adapter_observers_) { |
| adapter_->AddObserver(service); |
| } |
| } |
| } |
| |
| } // namespace content |