blob: fafa93b8bd1d837fa3f49bdc7ac3931639991627 [file] [log] [blame]
// 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/callback_helpers.h"
#include "base/location.h"
#include "base/logging.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_filter.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(
std::unique_ptr<device::BluetoothDiscoveryFilter> filter,
base::OnceClosure success_callback,
DiscoverySessionErrorCallback error_callback)
: filter(std::move(filter)),
success_callback(std::move(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);
}
void BluetoothAdapterCast::Initialize(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
initialized_ = true;
std::move(callback).Run();
}
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,
base::OnceClosure callback,
ErrorCallback error_callback) {
name_ = name;
std::move(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,
base::OnceClosure callback,
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_);
std::move(callback).Run();
}
bool BluetoothAdapterCast::IsDiscoverable() const {
DVLOG(2) << __func__ << " GATT server mode not supported";
return false;
}
void BluetoothAdapterCast::SetDiscoverable(bool discoverable,
base::OnceClosure callback,
ErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(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,
CreateServiceCallback callback,
CreateServiceErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback).Run("Not Implemented");
}
void BluetoothAdapterCast::CreateL2capService(
const BluetoothUUID& uuid,
const ServiceOptions& options,
CreateServiceCallback callback,
CreateServiceErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback).Run("Not Implemented");
}
void BluetoothAdapterCast::RegisterAdvertisement(
std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
CreateAdvertisementCallback callback,
AdvertisementErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback)
.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
}
void BluetoothAdapterCast::SetAdvertisingInterval(
const base::TimeDelta& min,
const base::TimeDelta& max,
base::OnceClosure callback,
AdvertisementErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback)
.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
}
void BluetoothAdapterCast::ConnectDevice(
const std::string& address,
const base::Optional<BluetoothDevice::AddressType>& address_type,
ConnectDeviceCallback callback,
ErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback).Run();
}
void BluetoothAdapterCast::ResetAdvertising(
base::OnceClosure callback,
AdvertisementErrorCallback error_callback) {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
std::move(error_callback)
.Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
}
BluetoothLocalGattService* BluetoothAdapterCast::GetGattService(
const std::string& identifier) const {
NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
return nullptr;
}
base::WeakPtr<BluetoothAdapter> BluetoothAdapterCast::GetWeakPtr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return weak_factory_.GetWeakPtr();
}
base::WeakPtr<BluetoothAdapterCast> BluetoothAdapterCast::GetCastWeakPtr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return weak_factory_.GetWeakPtr();
}
bool BluetoothAdapterCast::SetPoweredImpl(bool powered) {
NOTREACHED() << "This method is not invoked when SetPowered() is overridden.";
return true;
}
void BluetoothAdapterCast::StartScanWithFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
DiscoverySessionResultCallback 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().
// TODO(bcf|slan): Wire this up once scan filters are implemented.
(void)discovery_filter;
auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
// Add this request to the queue.
pending_discovery_requests_.emplace(BluetoothAdapterCast::DiscoveryParams(
std::move(discovery_filter),
base::BindOnce(copyable_callback, /*is_error=*/false,
UMABluetoothDiscoverySessionOutcome::SUCCESS),
base::BindOnce(copyable_callback, /*is_error=*/true)));
// 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::UpdateFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
DiscoverySessionResultCallback 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().
// TODO(bcf|slan): Wire this up once scan filters are implemented.
// If calling update then there must be other sessions actively scanning
// besides this one.
DCHECK_GT(NumDiscoverySessions(), 1);
// Since filters are not used simply return success.
std::move(callback).Run(/*is_error=*/false,
UMABluetoothDiscoverySessionOutcome::SUCCESS);
}
void BluetoothAdapterCast::StopScan(DiscoverySessionResultCallback callback) {
DCHECK(scan_handle_);
scan_handle_.reset();
std::move(callback).Run(/*is_error*/ false,
UMABluetoothDiscoverySessionOutcome::SUCCESS);
}
void BluetoothAdapterCast::RemovePairingDelegateInternal(
BluetoothDevice::PairingDelegate* pairing_delegate) {
// TODO(slan): Implement this or properly stub.
NOTIMPLEMENTED();
}
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_++;
std::move(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);
}
// static
void BluetoothAdapterCast::SetFactory(FactoryCb factory_cb) {
FactoryCb& factory = GetFactory();
DCHECK(!factory);
factory = std::move(factory_cb);
}
// static
void BluetoothAdapterCast::ResetFactoryForTest() {
GetFactory().Reset();
}
// static
scoped_refptr<BluetoothAdapter> BluetoothAdapterCast::Create() {
FactoryCb& factory = GetFactory();
DCHECK(factory) << "SetFactory() must be called before this method!";
return factory.Run();
}
// static
scoped_refptr<BluetoothAdapter> BluetoothAdapter::CreateAdapter() {
return BluetoothAdapterCast::Create();
}
} // namespace device