| // Copyright 2018 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/cast/bluetooth_adapter_cast.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/no_destructor.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "chromecast/device/bluetooth/bluetooth_util.h" |
| #include "chromecast/device/bluetooth/le/gatt_client_manager.h" |
| #include "chromecast/device/bluetooth/le/le_scan_manager.h" |
| #include "chromecast/device/bluetooth/le/remote_characteristic.h" |
| #include "chromecast/device/bluetooth/le/remote_device.h" |
| #include "chromecast/device/bluetooth/le/remote_service.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "device/bluetooth/bluetooth_discovery_session_outcome.h" |
| #include "device/bluetooth/cast/bluetooth_device_cast.h" |
| #include "device/bluetooth/cast/bluetooth_utils.h" |
| |
| namespace device { |
| namespace { |
| |
| BluetoothAdapterCast::FactoryCb& GetFactory() { |
| static base::NoDestructor<BluetoothAdapterCast::FactoryCb> factory_cb; |
| return *factory_cb; |
| } |
| |
| } // namespace |
| |
| BluetoothAdapterCast::DiscoveryParams::DiscoveryParams( |
| device::BluetoothDiscoveryFilter* filter, |
| base::Closure success_callback, |
| DiscoverySessionErrorCallback error_callback) |
| : filter(filter), |
| success_callback(success_callback), |
| error_callback(std::move(error_callback)) {} |
| |
| BluetoothAdapterCast::DiscoveryParams::DiscoveryParams( |
| DiscoveryParams&& params) noexcept = default; |
| BluetoothAdapterCast::DiscoveryParams& BluetoothAdapterCast::DiscoveryParams:: |
| operator=(DiscoveryParams&& params) = default; |
| BluetoothAdapterCast::DiscoveryParams::~DiscoveryParams() = default; |
| |
| BluetoothAdapterCast::BluetoothAdapterCast( |
| chromecast::bluetooth::GattClientManager* gatt_client_manager, |
| chromecast::bluetooth::LeScanManager* le_scan_manager) |
| : gatt_client_manager_(gatt_client_manager), |
| le_scan_manager_(le_scan_manager), |
| weak_factory_(this) { |
| DCHECK(gatt_client_manager_); |
| DCHECK(le_scan_manager_); |
| gatt_client_manager_->AddObserver(this); |
| le_scan_manager_->AddObserver(this); |
| } |
| |
| BluetoothAdapterCast::~BluetoothAdapterCast() { |
| gatt_client_manager_->RemoveObserver(this); |
| le_scan_manager_->RemoveObserver(this); |
| } |
| |
| std::string BluetoothAdapterCast::GetAddress() const { |
| // TODO(slan|bcf): Right now, we aren't surfacing the address of the GATT |
| // client to the caller, because there is no apparent need and this |
| // information is potentially PII. Implement this when it's needed. |
| return std::string(); |
| } |
| |
| std::string BluetoothAdapterCast::GetName() const { |
| return name_; |
| } |
| |
| void BluetoothAdapterCast::SetName(const std::string& name, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| name_ = name; |
| callback.Run(); |
| } |
| |
| bool BluetoothAdapterCast::IsInitialized() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return initialized_; |
| } |
| |
| bool BluetoothAdapterCast::IsPresent() const { |
| // The BluetoothAdapter is always present on Cast devices. |
| return true; |
| } |
| |
| bool BluetoothAdapterCast::IsPowered() const { |
| return powered_; |
| } |
| |
| void BluetoothAdapterCast::SetPowered(bool powered, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| // This class cannot actually change the powered state of the BT stack. |
| // We simulate these changes for the benefit of testing. However, we may |
| // want to actually delegate this call to the bluetooth service, at least |
| // as a signal of the client's intent. |
| // TODO(slan): Determine whether this would be useful. |
| powered_ = powered; |
| NotifyAdapterPoweredChanged(powered_); |
| callback.Run(); |
| } |
| |
| bool BluetoothAdapterCast::IsDiscoverable() const { |
| VLOG(2) << __func__ << " GATT server mode not supported"; |
| return false; |
| } |
| |
| void BluetoothAdapterCast::SetDiscoverable( |
| bool discoverable, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run(); |
| } |
| |
| bool BluetoothAdapterCast::IsDiscovering() const { |
| return num_discovery_sessions_ > 0; |
| } |
| |
| BluetoothAdapter::UUIDList BluetoothAdapterCast::GetUUIDs() const { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| return UUIDList(); |
| } |
| |
| void BluetoothAdapterCast::CreateRfcommService( |
| const BluetoothUUID& uuid, |
| const ServiceOptions& options, |
| const CreateServiceCallback& callback, |
| const CreateServiceErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run("Not Implemented"); |
| } |
| |
| void BluetoothAdapterCast::CreateL2capService( |
| const BluetoothUUID& uuid, |
| const ServiceOptions& options, |
| const CreateServiceCallback& callback, |
| const CreateServiceErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run("Not Implemented"); |
| } |
| |
| void BluetoothAdapterCast::RegisterAdvertisement( |
| std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data, |
| const CreateAdvertisementCallback& callback, |
| const AdvertisementErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM); |
| } |
| |
| void BluetoothAdapterCast::SetAdvertisingInterval( |
| const base::TimeDelta& min, |
| const base::TimeDelta& max, |
| const base::Closure& callback, |
| const AdvertisementErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM); |
| } |
| |
| void BluetoothAdapterCast::ResetAdvertising( |
| const base::Closure& callback, |
| const AdvertisementErrorCallback& error_callback) { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| error_callback.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM); |
| } |
| |
| BluetoothLocalGattService* BluetoothAdapterCast::GetGattService( |
| const std::string& identifier) const { |
| NOTIMPLEMENTED() << __func__ << " GATT server mode not supported"; |
| return nullptr; |
| } |
| |
| bool BluetoothAdapterCast::SetPoweredImpl(bool powered) { |
| NOTREACHED() << "This method is not invoked when SetPowered() is overridden."; |
| return true; |
| } |
| |
| void BluetoothAdapterCast::AddDiscoverySession( |
| BluetoothDiscoveryFilter* discovery_filter, |
| const base::Closure& callback, |
| DiscoverySessionErrorCallback error_callback) { |
| // The discovery filter is unused for now, as the Cast bluetooth stack does |
| // not expose scan filters yet. However, implementation of filtering would |
| // save numerous UI<->IO threadhops by eliminating uneccessary calls to |
| // GetDevice(). |
| // TODO(bcf|slan): Wire this up once scan filters are implemented. |
| (void)discovery_filter; |
| |
| // If the count is greater than 0, increment the count and return success. |
| if (num_discovery_sessions_ > 0) { |
| num_discovery_sessions_++; |
| callback.Run(); |
| return; |
| } |
| |
| // Add this request to the queue. |
| pending_discovery_requests_.emplace(discovery_filter, std::move(callback), |
| std::move(error_callback)); |
| |
| // If the queue length is greater than 1 (i.e. there was a pending request |
| // when this method was called), exit early. This request will be processed |
| // after the pending requests. |
| if (pending_discovery_requests_.size() > 1u) |
| return; |
| |
| // There is no active discovery session, and no pending requests. Enable |
| // scanning. |
| le_scan_manager_->RequestScan(base::BindOnce( |
| &BluetoothAdapterCast::OnScanEnabled, weak_factory_.GetWeakPtr())); |
| } |
| |
| void BluetoothAdapterCast::RemoveDiscoverySession( |
| BluetoothDiscoveryFilter* discovery_filter, |
| const base::Closure& callback, |
| DiscoverySessionErrorCallback error_callback) { |
| // The discovery filter is unused for now, as the Cast bluetooth stack does |
| // not expose scan filters yet. However, implementation of filtering would |
| // save numerous UI<->IO threadhops by eliminating uneccessary calls to |
| // GetDevice(). |
| // TODO(b/77663782): Wire this up once scan filters are implemented. |
| (void)discovery_filter; |
| |
| // If there are pending requests, run the error call immediately. |
| if (pending_discovery_requests_.size() > 0u) { |
| std::move(error_callback) |
| .Run(UMABluetoothDiscoverySessionOutcome::REMOVE_WITH_PENDING_REQUEST); |
| return; |
| } |
| |
| // If the count is greater than 1, decrement the count and return success. |
| if (num_discovery_sessions_ > 1) { |
| num_discovery_sessions_--; |
| callback.Run(); |
| return; |
| } |
| |
| // This was the last active discovery session. Disable scanning. |
| num_discovery_sessions_--; |
| DCHECK(scan_handle_); |
| scan_handle_.reset(); |
| callback.Run(); |
| } |
| |
| void BluetoothAdapterCast::SetDiscoveryFilter( |
| std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter, |
| const base::Closure& callback, |
| DiscoverySessionErrorCallback error_callback) { |
| // The discovery filter is unused for now, as the Cast bluetooth stack does |
| // not expose scan filters yet. However, implementation of filtering would |
| // save numerous UI<->IO threadhops by eliminating unnecessary calls to |
| // GetDevice(). |
| NOTIMPLEMENTED(); |
| callback.Run(); |
| } |
| |
| void BluetoothAdapterCast::RemovePairingDelegateInternal( |
| BluetoothDevice::PairingDelegate* pairing_delegate) { |
| // TODO(slan): Implement this or properly stub. |
| NOTIMPLEMENTED(); |
| } |
| |
| base::WeakPtr<BluetoothAdapterCast> BluetoothAdapterCast::GetWeakPtr() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void BluetoothAdapterCast::OnConnectChanged( |
| scoped_refptr<chromecast::bluetooth::RemoteDevice> device, |
| bool connected) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::string address = GetCanonicalBluetoothAddress(device->addr()); |
| DVLOG(1) << __func__ << " " << address << " connected: " << connected; |
| |
| // This method could be called before this device is detected in a scan and |
| // GetDevice() is called. Add it if needed. |
| if (devices_.find(address) == devices_.end()) |
| AddDevice(std::move(device)); |
| |
| BluetoothDeviceCast* cast_device = GetCastDevice(address); |
| cast_device->SetConnected(connected); |
| } |
| |
| void BluetoothAdapterCast::OnMtuChanged( |
| scoped_refptr<chromecast::bluetooth::RemoteDevice> device, |
| int mtu) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| DVLOG(3) << __func__ << " " << GetCanonicalBluetoothAddress(device->addr()) |
| << " mtu: " << mtu; |
| } |
| |
| void BluetoothAdapterCast::OnCharacteristicNotification( |
| scoped_refptr<chromecast::bluetooth::RemoteDevice> device, |
| scoped_refptr<chromecast::bluetooth::RemoteCharacteristic> characteristic, |
| std::vector<uint8_t> value) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::string address = GetCanonicalBluetoothAddress(device->addr()); |
| BluetoothDeviceCast* cast_device = GetCastDevice(address); |
| if (!cast_device) |
| return; |
| |
| cast_device->UpdateCharacteristicValue( |
| std::move(characteristic), std::move(value), |
| base::BindOnce( |
| &BluetoothAdapterCast::NotifyGattCharacteristicValueChanged, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void BluetoothAdapterCast::OnNewScanResult( |
| chromecast::bluetooth::LeScanResult result) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(3) << __func__; |
| |
| std::string address = GetCanonicalBluetoothAddress(result.addr); |
| |
| // If we haven't created a BluetoothDeviceCast for this address yet, we need |
| // to send an async request to |gatt_client_manager_| for a handle to the |
| // device. |
| if (devices_.find(address) == devices_.end()) { |
| bool first_time_seen = |
| pending_scan_results_.find(address) == pending_scan_results_.end(); |
| // These results will be used to construct the BluetoothDeviceCast. |
| pending_scan_results_[address].push_back(result); |
| |
| // Only send a request if this is the first time we've seen this |address| |
| // in a scan. This may happen if we pick up additional GAP advertisements |
| // while the first request is in-flight. |
| if (first_time_seen) { |
| gatt_client_manager_->GetDevice( |
| result.addr, base::BindOnce(&BluetoothAdapterCast::OnGetDevice, |
| weak_factory_.GetWeakPtr())); |
| } |
| return; |
| } |
| |
| // Update the device with the ScanResult. |
| BluetoothDeviceCast* device = GetCastDevice(address); |
| if (device->UpdateWithScanResult(result)) { |
| for (auto& observer : observers_) |
| observer.DeviceChanged(this, device); |
| } |
| } |
| |
| void BluetoothAdapterCast::OnScanEnableChanged(bool enabled) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // If the scan function has been disabled during discovery, something has |
| // gone wrong. We should consider re-enabling it here. |
| LOG_IF(WARNING, IsDiscovering() && !enabled) |
| << "BLE scan has been disabled during WebBluetooth discovery!"; |
| } |
| |
| BluetoothDeviceCast* BluetoothAdapterCast::GetCastDevice( |
| const std::string& address) { |
| auto it = devices_.find(address); |
| return it == devices_.end() |
| ? nullptr |
| : static_cast<BluetoothDeviceCast*>(it->second.get()); |
| } |
| |
| void BluetoothAdapterCast::AddDevice( |
| scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // This method should not be called if we already have a BluetoothDeviceCast |
| // registered for this device. |
| std::string address = GetCanonicalBluetoothAddress(remote_device->addr()); |
| DCHECK(devices_.find(address) == devices_.end()); |
| |
| devices_[address] = |
| std::make_unique<BluetoothDeviceCast>(this, remote_device); |
| BluetoothDeviceCast* device = GetCastDevice(address); |
| |
| const auto scan_results = std::move(pending_scan_results_[address]); |
| pending_scan_results_.erase(address); |
| |
| // Update the device with the ScanResults. |
| for (const auto& result : scan_results) |
| device->UpdateWithScanResult(result); |
| |
| // Update the observers of the new device. |
| for (auto& observer : observers_) |
| observer.DeviceAdded(this, device); |
| } |
| |
| void BluetoothAdapterCast::OnScanEnabled( |
| std::unique_ptr<chromecast::bluetooth::LeScanManager::ScanHandle> |
| scan_handle) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!scan_handle) { |
| LOG(WARNING) << "Failed to start scan."; |
| |
| // Run the error callback. |
| DCHECK(!pending_discovery_requests_.empty()); |
| std::move(pending_discovery_requests_.front().error_callback) |
| .Run(UMABluetoothDiscoverySessionOutcome::FAILED); |
| pending_discovery_requests_.pop(); |
| |
| // If there is another pending request, try again. |
| if (pending_discovery_requests_.size() > 0u) { |
| le_scan_manager_->RequestScan(base::BindOnce( |
| &BluetoothAdapterCast::OnScanEnabled, weak_factory_.GetWeakPtr())); |
| } |
| return; |
| } |
| |
| scan_handle_ = std::move(scan_handle); |
| |
| // The scan has been successfully enabled. Request the initial scan results |
| // from the scan manager. |
| le_scan_manager_->GetScanResults(base::BindOnce( |
| &BluetoothAdapterCast::OnGetScanResults, weak_factory_.GetWeakPtr())); |
| |
| // For each pending request, increment the count and run the success callback. |
| while (!pending_discovery_requests_.empty()) { |
| num_discovery_sessions_++; |
| pending_discovery_requests_.front().success_callback.Run(); |
| pending_discovery_requests_.pop(); |
| } |
| } |
| |
| void BluetoothAdapterCast::OnGetDevice( |
| scoped_refptr<chromecast::bluetooth::RemoteDevice> remote_device) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::string address = GetCanonicalBluetoothAddress(remote_device->addr()); |
| |
| // This callback could run before or after the device becomes connected and |
| // OnConnectChanged() is called for a particular device. If that happened, |
| // |remote_device| already has a handle. In this case, there should be no |
| // |pending_scan_results_| and we should fast-return. |
| if (devices_.find(address) != devices_.end()) { |
| DCHECK(pending_scan_results_.find(address) == pending_scan_results_.end()); |
| return; |
| } |
| |
| // If there is not a device already, there should be at least one ScanResult |
| // which triggered the GetDevice() call. |
| DCHECK(pending_scan_results_.find(address) != pending_scan_results_.end()); |
| AddDevice(std::move(remote_device)); |
| } |
| |
| void BluetoothAdapterCast::OnGetScanResults( |
| std::vector<chromecast::bluetooth::LeScanResult> results) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| for (auto& result : results) |
| OnNewScanResult(result); |
| } |
| |
| void BluetoothAdapterCast::InitializeAsynchronously(InitCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| initialized_ = true; |
| std::move(callback).Run(); |
| } |
| |
| // static |
| void BluetoothAdapterCast::SetFactory(FactoryCb factory_cb) { |
| FactoryCb& factory = GetFactory(); |
| DCHECK(!factory); |
| factory = std::move(factory_cb); |
| } |
| |
| // static |
| void BluetoothAdapterCast::ResetFactoryForTest() { |
| GetFactory().Reset(); |
| } |
| |
| // static |
| base::WeakPtr<BluetoothAdapter> BluetoothAdapterCast::Create( |
| InitCallback callback) { |
| FactoryCb& factory = GetFactory(); |
| DCHECK(factory) << "SetFactory() must be called before this method!"; |
| base::WeakPtr<BluetoothAdapterCast> weak_ptr = factory.Run(); |
| |
| // BluetoothAdapterFactory assumes that |init_callback| will be called |
| // asynchronously the first time, and that IsInitialized() will return false. |
| // Post init_callback to this sequence so that it gets called back later. |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&BluetoothAdapterCast::InitializeAsynchronously, |
| weak_ptr, std::move(callback))); |
| return weak_ptr; |
| } |
| |
| // static |
| base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter( |
| InitCallback init_callback) { |
| return BluetoothAdapterCast::Create(std::move(init_callback)); |
| } |
| |
| } // namespace device |