blob: 6bf45f5d039e244ed3c5feb29e93880c3750919c [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/floss/bluetooth_device_floss.h"
#include <memory>
#include "base/bind.h"
#include "base/notreached.h"
#include "components/device_event_log/device_event_log.h"
#include "dbus/bus.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/floss/bluetooth_adapter_floss.h"
#include "device/bluetooth/floss/bluetooth_gatt_connection_floss.h"
#include "device/bluetooth/floss/bluetooth_remote_gatt_service_floss.h"
#include "device/bluetooth/floss/bluetooth_socket_floss.h"
#include "device/bluetooth/floss/floss_dbus_client.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "device/bluetooth/floss/floss_gatt_client.h"
#include "device/bluetooth/floss/floss_socket_manager.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "device/bluetooth/chromeos/bluetooth_utils.h"
#endif
namespace floss {
namespace {
void OnCreateBond(DBusResult<bool> ret) {
if (ret.has_value() && !*ret) {
BLUETOOTH_LOG(ERROR) << "CreateBond returned failure";
}
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "Failed to create bond: " << ret.error();
}
}
void OnRemoveBond(base::OnceClosure callback, DBusResult<bool> ret) {
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "Failed to remove bond: " << ret.error();
} else if (!*ret) {
BLUETOOTH_LOG(ERROR) << "RemoveBond returned failure";
}
#if BUILDFLAG(IS_CHROMEOS)
bool success = ret.has_value() && *ret;
device::RecordForgetResult(success ? device::ForgetResult::kSuccess
: device::ForgetResult::kFailure);
#endif
std::move(callback).Run();
}
} // namespace
using AddressType = device::BluetoothDevice::AddressType;
using VendorIDSource = device::BluetoothDevice::VendorIDSource;
uint32_t BluetoothDeviceFloss::GetBluetoothClass() const {
return cod_;
}
device::BluetoothTransport BluetoothDeviceFloss::GetType() const {
return transport_;
}
std::string BluetoothDeviceFloss::GetAddress() const {
return address_;
}
AddressType BluetoothDeviceFloss::GetAddressType() const {
NOTIMPLEMENTED();
return AddressType::ADDR_TYPE_UNKNOWN;
}
VendorIDSource BluetoothDeviceFloss::GetVendorIDSource() const {
NOTIMPLEMENTED();
return VendorIDSource::VENDOR_ID_UNKNOWN;
}
uint16_t BluetoothDeviceFloss::GetVendorID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceFloss::GetProductID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceFloss::GetDeviceID() const {
NOTIMPLEMENTED();
return 0;
}
uint16_t BluetoothDeviceFloss::GetAppearance() const {
return appearance_;
}
absl::optional<std::string> BluetoothDeviceFloss::GetName() const {
if (name_.length() == 0)
return absl::nullopt;
return name_;
}
bool BluetoothDeviceFloss::IsPaired() const {
return IsBondedImpl() ||
FlossAdapterClient::IsConnectionPaired(connection_state_);
}
#if BUILDFLAG(IS_CHROMEOS)
bool BluetoothDeviceFloss::IsBonded() const {
return IsBondedImpl();
}
#endif // BUILDFLAG(IS_CHROMEOS)
bool BluetoothDeviceFloss::IsConnected() const {
return is_acl_connected_;
}
bool BluetoothDeviceFloss::IsGattConnected() const {
return is_gatt_connected_;
}
bool BluetoothDeviceFloss::IsConnectable() const {
// Mark all devices as connectable for now.
// TODO(b/211126690): Implement based on supported profiles.
return true;
}
bool BluetoothDeviceFloss::IsConnecting() const {
return num_connecting_calls_ > 0;
}
device::BluetoothDevice::UUIDSet BluetoothDeviceFloss::GetUUIDs() const {
return device_uuids_.GetUUIDs();
}
absl::optional<int8_t> BluetoothDeviceFloss::GetInquiryTxPower() const {
NOTIMPLEMENTED();
return absl::nullopt;
}
bool BluetoothDeviceFloss::ExpectingPinCode() const {
if (!pairing_)
return false;
return pairing_->pairing_expectation() ==
BluetoothPairingFloss::PairingExpectation::kPinCode;
}
bool BluetoothDeviceFloss::ExpectingPasskey() const {
if (!pairing_)
return false;
return pairing_->pairing_expectation() ==
BluetoothPairingFloss::PairingExpectation::kPasskey;
}
bool BluetoothDeviceFloss::ExpectingConfirmation() const {
if (!pairing_)
return false;
return pairing_->pairing_expectation() ==
BluetoothPairingFloss::PairingExpectation::kConfirmation;
}
void BluetoothDeviceFloss::GetConnectionInfo(ConnectionInfoCallback callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceFloss::SetConnectionLatency(
ConnectionLatency connection_latency,
base::OnceClosure callback,
ErrorCallback error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceFloss::Connect(
device::BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << "Connecting to " << address_;
if (num_connecting_calls_++ == 0)
adapter_->NotifyDeviceChanged(this);
// To simulate BlueZ API behavior, we don't reply the callback as soon as
// Floss CreateBond API returns, but rather we trigger the callback later
// after pairing is done and profiles are connected.
pending_callback_on_connect_profiles_ = std::move(callback);
if (IsPaired() || !pairing_delegate) {
// No need to pair, or unable to, skip straight to connection.
ConnectAllEnabledProfiles();
} else {
pairing_ = std::make_unique<BluetoothPairingFloss>(pairing_delegate);
FlossDBusManager::Get()->GetAdapterClient()->CreateBond(
base::BindOnce(&OnCreateBond), AsFlossDeviceId(),
FlossAdapterClient::BluetoothTransport::kAuto);
}
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceFloss::ConnectClassic(
device::BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
// TODO(b/215621933): Explicitly create a classic Bluetooth connection.
// Currently Floss doesn't have the BlueZ-equivalent of ConnectClassic() at
// the stack level, so just call the existing Connect().
Connect(pairing_delegate, std::move(callback));
}
#endif // BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceFloss::SetPinCode(const std::string& pincode) {
std::vector<uint8_t> pin(pincode.begin(), pincode.end());
FlossDBusManager::Get()->GetAdapterClient()->SetPin(
base::DoNothing(), AsFlossDeviceId(), /*accept=*/true, pin);
}
void BluetoothDeviceFloss::SetPasskey(uint32_t passkey) {
// No use case in Chrome OS.
NOTIMPLEMENTED();
}
void BluetoothDeviceFloss::ConfirmPairing() {
FlossDBusManager::Get()->GetAdapterClient()->SetPairingConfirmation(
base::DoNothing(), AsFlossDeviceId(), /*accept=*/true);
}
void BluetoothDeviceFloss::RejectPairing() {
FlossDBusManager::Get()->GetAdapterClient()->SetPairingConfirmation(
base::DoNothing(), AsFlossDeviceId(), /*accept=*/false);
}
void BluetoothDeviceFloss::CancelPairing() {
FlossDBusManager::Get()->GetAdapterClient()->CancelBondProcess(
base::DoNothing(), AsFlossDeviceId());
TriggerConnectCallback(BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN);
}
void BluetoothDeviceFloss::Disconnect(base::OnceClosure callback,
ErrorCallback error_callback) {
// TODO (b/223832034): Create API that does hard disconnect of a peer device
FlossDBusManager::Get()->GetAdapterClient()->DisconnectAllEnabledProfiles(
base::BindOnce(&BluetoothDeviceFloss::OnDisconnectAllEnabledProfiles,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(error_callback)),
AsFlossDeviceId());
}
void BluetoothDeviceFloss::Forget(base::OnceClosure callback,
ErrorCallback error_callback) {
FlossDBusManager::Get()->GetAdapterClient()->RemoveBond(
base::BindOnce(&OnRemoveBond, std::move(callback)), AsFlossDeviceId());
}
void BluetoothDeviceFloss::ConnectToService(
const device::BluetoothUUID& uuid,
ConnectToServiceCallback callback,
ConnectToServiceErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << address_
<< ": Connecting to service: " << uuid.canonical_value();
scoped_refptr<BluetoothSocketFloss> socket =
BluetoothSocketFloss::CreateBluetoothSocket(ui_task_runner_,
socket_thread_);
socket->Connect(this, FlossSocketManager::Security::kSecure, uuid,
base::BindOnce(std::move(callback), socket),
base::BindOnce(&BluetoothDeviceFloss::OnConnectToServiceError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
void BluetoothDeviceFloss::ConnectToServiceInsecurely(
const device::BluetoothUUID& uuid,
ConnectToServiceCallback callback,
ConnectToServiceErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << address_
<< ": Connecting to service: " << uuid.canonical_value();
scoped_refptr<BluetoothSocketFloss> socket =
BluetoothSocketFloss::CreateBluetoothSocket(ui_task_runner_,
socket_thread_);
socket->Connect(this, FlossSocketManager::Security::kInsecure, uuid,
base::BindOnce(std::move(callback), socket),
base::BindOnce(&BluetoothDeviceFloss::OnConnectToServiceError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
std::unique_ptr<device::BluetoothGattConnection>
BluetoothDeviceFloss::CreateBluetoothGattConnectionObject() {
return std::make_unique<BluetoothGattConnectionFloss>(adapter_,
AsFlossDeviceId());
}
void BluetoothDeviceFloss::SetGattServicesDiscoveryComplete(bool complete) {
NOTIMPLEMENTED();
// This is not necessary for Floss which already knows of discovery complete.
}
bool BluetoothDeviceFloss::IsGattServicesDiscoveryComplete() const {
// Services are only considered resolved if connection was established without
// a specific search uuid or was subsequently upgraded to full discovery.
return svc_resolved_ && !search_uuid.has_value();
}
void BluetoothDeviceFloss::Pair(
device::BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
NOTIMPLEMENTED();
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceFloss::ExecuteWrite(
base::OnceClosure callback,
ExecuteWriteErrorCallback error_callback) {
NOTIMPLEMENTED();
}
void BluetoothDeviceFloss::AbortWrite(base::OnceClosure callback,
AbortWriteErrorCallback error_callback) {
NOTIMPLEMENTED();
}
#endif // BUILDFLAG(IS_CHROMEOS)
FlossDeviceId BluetoothDeviceFloss::AsFlossDeviceId() const {
return FlossDeviceId{.address = address_, .name = name_};
}
void BluetoothDeviceFloss::SetName(const std::string& name) {
name_ = name;
}
void BluetoothDeviceFloss::SetBondState(
FlossAdapterClient::BondState bond_state) {
bond_state_ = bond_state;
}
void BluetoothDeviceFloss::SetIsConnected(bool is_connected) {
is_acl_connected_ = is_connected;
// Update connection state to "ConnectedOnly" if it was previously
// disconnected and we are now connected. Also, update any connection state
// back to disconnected if acl state disconnects.
if (is_acl_connected_ &&
connection_state_ ==
static_cast<uint32_t>(
FlossAdapterClient::ConnectionState::kDisconnected)) {
connection_state_ = static_cast<uint32_t>(
FlossAdapterClient::ConnectionState::kConnectedOnly);
} else if (!is_acl_connected_) {
connection_state_ = static_cast<uint32_t>(
FlossAdapterClient::ConnectionState::kDisconnected);
}
}
void BluetoothDeviceFloss::SetConnectionState(uint32_t connection_state) {
connection_state_ = connection_state;
}
void BluetoothDeviceFloss::ConnectAllEnabledProfiles() {
FlossDBusManager::Get()->GetAdapterClient()->ConnectAllEnabledProfiles(
base::BindOnce(&BluetoothDeviceFloss::OnConnectAllEnabledProfiles,
weak_ptr_factory_.GetWeakPtr()),
AsFlossDeviceId());
}
void BluetoothDeviceFloss::ResetPairing() {
pairing_.reset();
}
void BluetoothDeviceFloss::CreateGattConnectionImpl(
absl::optional<device::BluetoothUUID> service_uuid) {
if (num_connecting_calls_++ == 0)
adapter()->NotifyDeviceChanged(this);
// Save the service uuid to trigger service discovery later.
search_uuid = service_uuid;
// Gatt connections establish over LE.
FlossDBusManager::Get()->GetGattClient()->Connect(
base::BindOnce(&BluetoothDeviceFloss::OnConnectGatt,
weak_ptr_factory_.GetWeakPtr()),
address_, FlossDBusClient::BluetoothTransport::kLe);
}
void BluetoothDeviceFloss::OnConnectGatt(DBusResult<Void> ret) {
if (!ret.has_value()) {
if (--num_connecting_calls_ == 0)
adapter()->NotifyDeviceChanged(this);
}
}
void BluetoothDeviceFloss::UpgradeToFullDiscovery() {
if (!search_uuid.has_value()) {
LOG(ERROR) << "Attempting to upgrade to full discovery without having "
"searched any uuid.";
return;
}
// Clear previous search uuid.
search_uuid.reset();
svc_resolved_ = false;
FlossDBusManager::Get()->GetGattClient()->DiscoverAllServices(
base::DoNothing(), address_);
}
void BluetoothDeviceFloss::DisconnectGatt() {
if (IsPaired()) {
BLUETOOTH_LOG(ERROR) << "Leaking connection to paired device.";
return;
}
FlossDBusManager::Get()->GetGattClient()->Disconnect(base::DoNothing(),
address_);
}
BluetoothDeviceFloss::BluetoothDeviceFloss(
BluetoothAdapterFloss* adapter,
const FlossDeviceId& device,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<device::BluetoothSocketThread> socket_thread)
: BluetoothDevice(adapter),
address_(device.address),
name_(device.name),
ui_task_runner_(ui_task_runner),
socket_thread_(socket_thread) {
FlossDBusManager::Get()->GetGattClient()->AddObserver(this);
// Enable service specific discovery. This allows gatt connections to
// immediately trigger service discovery for specific uuids without
// requiring full discovery.
supports_service_specific_discovery_ = true;
}
BluetoothDeviceFloss::~BluetoothDeviceFloss() {
FlossDBusManager::Get()->GetGattClient()->RemoveObserver(this);
}
bool BluetoothDeviceFloss::IsBondedImpl() const {
return bond_state_ == FlossAdapterClient::BondState::kBonded;
}
void BluetoothDeviceFloss::OnGetRemoteType(
DBusResult<FlossAdapterClient::BluetoothDeviceType> ret) {
TriggerInitDevicePropertiesCallback();
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "GetRemoteType() failed: " << ret.error();
return;
}
switch (*ret) {
case FlossAdapterClient::BluetoothDeviceType::kBredr:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC;
break;
case FlossAdapterClient::BluetoothDeviceType::kBle:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE;
break;
case FlossAdapterClient::BluetoothDeviceType::kDual:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_DUAL;
break;
default:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_INVALID;
}
}
void BluetoothDeviceFloss::OnGetRemoteClass(DBusResult<uint32_t> ret) {
TriggerInitDevicePropertiesCallback();
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "GetRemoteClass() failed: " << ret.error();
return;
}
cod_ = *ret;
}
void BluetoothDeviceFloss::OnGetRemoteAppearance(DBusResult<uint16_t> ret) {
TriggerInitDevicePropertiesCallback();
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "OnGetRemoteAppearance() failed: " << ret.error();
return;
}
appearance_ = *ret;
}
void BluetoothDeviceFloss::OnGetRemoteUuids(DBusResult<UUIDList> ret) {
TriggerInitDevicePropertiesCallback();
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "GetRemoteUuids() failed: " << ret.error();
return;
}
device_uuids_.ReplaceServiceUUIDs(*ret);
}
void BluetoothDeviceFloss::OnConnectAllEnabledProfiles(DBusResult<Void> ret) {
if (!ret.has_value()) {
BLUETOOTH_LOG(ERROR) << "Failed to connect all enabled profiles: "
<< ret.error();
// TODO(b/202874707): Design a proper new errors for Floss.
if (pending_callback_on_connect_profiles_)
TriggerConnectCallback(BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN);
return;
}
TriggerConnectCallback(absl::nullopt);
}
void BluetoothDeviceFloss::TriggerConnectCallback(
absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
if (num_connecting_calls_ > 0 && --num_connecting_calls_ == 0)
adapter_->NotifyDeviceChanged(this);
if (pending_callback_on_connect_profiles_) {
std::move(*pending_callback_on_connect_profiles_).Run(error_code);
pending_callback_on_connect_profiles_ = absl::nullopt;
}
}
void BluetoothDeviceFloss::OnDisconnectAllEnabledProfiles(
base::OnceClosure callback,
ErrorCallback error_callback,
DBusResult<Void> ret) {
if (!ret.has_value()) {
#if BUILDFLAG(IS_CHROMEOS)
device::RecordUserInitiatedDisconnectResult(
device::DisconnectResult::kFailure,
/*transport=*/GetType());
#endif
BLUETOOTH_LOG(ERROR) << "Failed to discconnect all enabled profiles: "
<< ret.error();
std::move(error_callback).Run();
return;
}
#if BUILDFLAG(IS_CHROMEOS)
device::RecordUserInitiatedDisconnectResult(
device::DisconnectResult::kSuccess,
/*transport=*/GetType());
#endif
std::move(callback).Run();
}
void BluetoothDeviceFloss::OnConnectToServiceError(
ConnectToServiceErrorCallback error_callback,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << address_
<< ": Failed to connect to service: " << error_message;
// TODO - Log service connection failures for metrics.
std::move(error_callback).Run(error_message);
}
void BluetoothDeviceFloss::InitializeDeviceProperties(
base::OnceClosure callback) {
pending_callback_on_init_props_ = std::move(callback);
// This must be incremented when adding more properties below
// and followed up with a TriggerInitDevicePropertiesCallback()
// in the callback.
num_pending_properties_ += 4;
// TODO(b/204708206): Update with property framework when available
FlossDBusManager::Get()->GetAdapterClient()->GetRemoteType(
base::BindOnce(&BluetoothDeviceFloss::OnGetRemoteType,
weak_ptr_factory_.GetWeakPtr()),
AsFlossDeviceId());
FlossDBusManager::Get()->GetAdapterClient()->GetRemoteClass(
base::BindOnce(&BluetoothDeviceFloss::OnGetRemoteClass,
weak_ptr_factory_.GetWeakPtr()),
AsFlossDeviceId());
FlossDBusManager::Get()->GetAdapterClient()->GetRemoteAppearance(
base::BindOnce(&BluetoothDeviceFloss::OnGetRemoteAppearance,
weak_ptr_factory_.GetWeakPtr()),
AsFlossDeviceId());
FlossDBusManager::Get()->GetAdapterClient()->GetRemoteUuids(
base::BindOnce(&BluetoothDeviceFloss::OnGetRemoteUuids,
weak_ptr_factory_.GetWeakPtr()),
AsFlossDeviceId());
}
void BluetoothDeviceFloss::TriggerInitDevicePropertiesCallback() {
if (--num_pending_properties_ == 0 && pending_callback_on_init_props_) {
std::move(*pending_callback_on_init_props_).Run();
pending_callback_on_init_props_ = absl::nullopt;
}
DCHECK(num_pending_properties_ >= 0);
}
void BluetoothDeviceFloss::GattClientConnectionState(GattStatus status,
int32_t client_id,
bool connected,
std::string address) {
// We only care about connections for this device.
if (address != address_)
return;
absl::optional<ConnectErrorCode> err = absl::nullopt;
if (status == GattStatus::kSuccess) {
is_gatt_connected_ = connected;
} else {
// TODO(b/193686094) - Convert GattStatus to other connect error codes.
err = ERROR_UNKNOWN;
}
// If GATT created the connection, we'll also do an ACL connection complete.
// Check that num_connecting is > 0 before decrementing it here.
if (num_connecting_calls_ > 0 && --num_connecting_calls_ == 0)
adapter()->NotifyDeviceChanged(this);
DCHECK(num_connecting_calls_ >= 0);
// Trigger service discovery only when connected.
if (connected) {
if (search_uuid.has_value()) {
FlossDBusManager::Get()->GetGattClient()->DiscoverServiceByUuid(
base::DoNothing(), address_, search_uuid.value());
} else {
FlossDBusManager::Get()->GetGattClient()->DiscoverAllServices(
base::DoNothing(), address_);
}
}
// Complete GATT connection callback.
DidConnectGatt(err);
}
void BluetoothDeviceFloss::GattSearchComplete(
std::string address,
const std::vector<GattService>& services,
GattStatus status) {
if (address != address_)
return;
if (status != GattStatus::kSuccess) {
LOG(ERROR) << "Failed Gatt service discovery with result: "
<< static_cast<uint32_t>(status);
return;
}
svc_resolved_ = true;
for (const auto& service : services) {
BLUETOOTH_LOG(EVENT) << "Adding new remote GATT service for device: "
<< address_;
std::unique_ptr<BluetoothRemoteGattServiceFloss> remote_service =
BluetoothRemoteGattServiceFloss::Create(adapter(), this, service,
/*primary=*/true);
BluetoothRemoteGattServiceFloss* remote_service_ptr = remote_service.get();
gatt_services_[remote_service_ptr->GetIdentifier()] =
std::move(remote_service);
DCHECK(remote_service_ptr->GetUUID().IsValid());
}
adapter()->NotifyGattServicesDiscovered(this);
}
} // namespace floss