blob: 82c1b6b5001388636cbe3f490b60969a13ebca95 [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/functional/bind.h"
#include "base/notreached.h"
#include "base/task/sequenced_task_runner.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_manager_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 {
// Connection intervals for LE connections.
// The unit for connection interval values are in multiples of 1.25ms.
const int32_t kMinConnectionIntervalLow = 6;
const int32_t kMaxConnectionIntervalLow = 6;
const int32_t kMinConnectionIntervalMedium = 40;
const int32_t kMaxConnectionIntervalMedium = 56;
const int32_t kMinConnectionIntervalHigh = 80;
const int32_t kMaxConnectionIntervalHigh = 100;
// Default connection latency for LE connections.
const int32_t kDefaultConnectionLatency = 0;
// Link supervision timeout for LE connections.
const int32_t kDefaultConnectionTimeout = 2000;
// Maximum MTU size that can be requested by Android.
const int32_t kMaxMtuSize = 517;
// Timeout for connection response after Connect() method is called.
constexpr base::TimeDelta kDefaultConnectTimeout = base::Seconds(10);
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 gatt_connecting_state_ == GattConnectingState::kGattConnected;
}
bool BluetoothDeviceFloss::IsConnectable() const {
// Mimic current BlueZ behavior that Non-HID is connectable
switch (GetDeviceType()) {
case device::BluetoothDeviceType::PERIPHERAL:
case device::BluetoothDeviceType::JOYSTICK:
case device::BluetoothDeviceType::KEYBOARD:
case device::BluetoothDeviceType::MOUSE:
case device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO:
return false;
default:
return true;
}
}
bool BluetoothDeviceFloss::IsConnecting() const {
return (connecting_state_ == ConnectingState::kACLConnecting) ||
(connecting_state_ == ConnectingState::kProfilesConnecting) ||
(gatt_connecting_state_ == GattConnectingState::kGattConnecting);
}
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) {
// TODO(b/255650738): Floss doesn't currently provide max_transmit_power.
std::move(callback).Run(ConnectionInfo(inquiry_rssi_.value_or(0),
inquiry_tx_power_.value_or(0),
/*max_transmit_power=*/0));
}
void BluetoothDeviceFloss::SetConnectionLatency(
ConnectionLatency connection_latency,
base::OnceClosure callback,
ErrorCallback error_callback) {
int32_t min_connection_interval = kMinConnectionIntervalMedium;
int32_t max_connection_interval = kMaxConnectionIntervalMedium;
switch (connection_latency) {
case ConnectionLatency::CONNECTION_LATENCY_LOW:
min_connection_interval = kMinConnectionIntervalLow;
max_connection_interval = kMaxConnectionIntervalLow;
break;
case ConnectionLatency::CONNECTION_LATENCY_MEDIUM:
min_connection_interval = kMinConnectionIntervalMedium;
max_connection_interval = kMaxConnectionIntervalMedium;
break;
case ConnectionLatency::CONNECTION_LATENCY_HIGH:
min_connection_interval = kMinConnectionIntervalHigh;
max_connection_interval = kMaxConnectionIntervalHigh;
break;
default:
NOTREACHED();
break;
}
BLUETOOTH_LOG(EVENT) << "Setting LE connection parameters: min="
<< min_connection_interval
<< ", max=" << max_connection_interval;
FlossDBusManager::Get()->GetGattManagerClient()->UpdateConnectionParameters(
base::BindOnce(&BluetoothDeviceFloss::OnSetConnectionLatency,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(error_callback)),
GetAddress(), min_connection_interval, max_connection_interval,
kDefaultConnectionLatency, kDefaultConnectionTimeout,
/*min_ce_len=*/min_connection_interval * 2,
/*max_ce_len=*/max_connection_interval * 2);
}
void BluetoothDeviceFloss::OnSetConnectionLatency(base::OnceClosure callback,
ErrorCallback error_callback,
DBusResult<Void> ret) {
if (!ret.has_value()) {
std::move(error_callback).Run();
return;
}
// If we already had a pending call, fail it.
if (pending_set_connection_latency_.has_value()) {
auto& [pending_cb, pending_error_cb] =
pending_set_connection_latency_.value();
std::move(pending_error_cb).Run();
pending_set_connection_latency_ = absl::nullopt;
}
pending_set_connection_latency_ =
std::make_pair(std::move(callback), std::move(error_callback));
}
void BluetoothDeviceFloss::Connect(
device::BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << "Connecting to " << address_;
if ((connecting_state_ == ConnectingState::kACLConnecting) ||
(connecting_state_ == ConnectingState::kProfilesConnecting)) {
std::move(callback).Run(
BluetoothDevice::ConnectErrorCode::ERROR_INPROGRESS);
return;
} else if (connecting_state_ == ConnectingState::kProfilesConnected) {
std::move(callback).Run(
BluetoothDevice::ConnectErrorCode::ERROR_ALREADY_CONNECTED);
return;
}
// 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);
}
}
void BluetoothDeviceFloss::ConnectionIncomplete() {
UpdateConnectingState(
ConnectingState::kIdle,
BluetoothDevice::ConnectErrorCode::ERROR_DEVICE_NOT_READY);
}
#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());
}
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(), socket,
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(), socket,
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) {
// Pair is the same as Connect due to influence from BlueZ.
// TODO(b/269516642): We should make distinction between them in the future.
Connect(pairing_delegate, std::move(callback));
}
BluetoothPairingFloss* BluetoothDeviceFloss::BeginPairing(
BluetoothDevice::PairingDelegate* pairing_delegate) {
pairing_ = std::make_unique<BluetoothPairingFloss>(pairing_delegate);
return pairing_.get();
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceFloss::OnExecuteWrite(
base::OnceClosure callback,
ExecuteWriteErrorCallback error_callback,
DBusResult<Void> ret) {
if (!ret.has_value()) {
std::move(error_callback)
.Run(device::BluetoothGattService::GattErrorCode::kFailed);
return;
}
pending_execute_write_ =
std::make_pair(std::move(callback), std::move(error_callback));
}
void BluetoothDeviceFloss::BeginReliableWrite() {
DCHECK(!using_reliable_write_);
if (!using_reliable_write_) {
using_reliable_write_ = true;
FlossDBusManager::Get()->GetGattManagerClient()->BeginReliableWrite(
base::DoNothing(), address_);
}
}
void BluetoothDeviceFloss::ExecuteWrite(
base::OnceClosure callback,
ExecuteWriteErrorCallback error_callback) {
// Only one pending execute allowed at a time.
if (pending_execute_write_) {
std::move(error_callback)
.Run(device::BluetoothGattService::GattErrorCode::kInProgress);
return;
}
if (!using_reliable_write_) {
std::move(error_callback)
.Run(device::BluetoothGattService::GattErrorCode::kFailed);
return;
}
FlossDBusManager::Get()->GetGattManagerClient()->EndReliableWrite(
base::BindOnce(&BluetoothDeviceFloss::OnExecuteWrite,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(error_callback)),
address_, /*execute=*/true);
}
void BluetoothDeviceFloss::AbortWrite(base::OnceClosure callback,
AbortWriteErrorCallback error_callback) {
// Only one pending execute allowed at a time.
if (pending_execute_write_) {
std::move(error_callback)
.Run(device::BluetoothGattService::GattErrorCode::kInProgress);
return;
}
if (!using_reliable_write_) {
std::move(error_callback)
.Run(device::BluetoothGattService::GattErrorCode::kFailed);
return;
}
FlossDBusManager::Get()->GetGattManagerClient()->EndReliableWrite(
base::BindOnce(&BluetoothDeviceFloss::OnExecuteWrite,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
std::move(error_callback)),
address_, /*execute=*/false);
}
void BluetoothDeviceFloss::GattExecuteWrite(std::string address,
GattStatus status) {
if (address != address_) {
return;
}
if (!pending_execute_write_) {
return;
}
if (status != GattStatus::kSuccess) {
std::move(pending_execute_write_->second)
.Run(
floss::BluetoothGattServiceFloss::GattStatusToServiceError(status));
} else {
std::move(pending_execute_write_->first).Run();
}
pending_execute_write_ = absl::nullopt;
}
#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,
absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
bond_state_ = bond_state;
switch (bond_state_) {
case FlossAdapterClient::BondState::kNotBonded:
UpdateConnectingState(ConnectingState::kIdle, error_code);
break;
case FlossAdapterClient::BondState::kBondingInProgress:
UpdateConnectingState(ConnectingState::kACLConnecting, absl::nullopt);
break;
case FlossAdapterClient::BondState::kBonded:
if (connecting_state_ == ConnectingState::kACLConnecting) {
ConnectAllEnabledProfiles();
}
break;
default:
NOTREACHED();
break;
}
}
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);
}
if (!is_connected) {
UpdateConnectingState(
ConnectingState::kIdle,
BluetoothDevice::ConnectErrorCode::ERROR_DEVICE_UNCONNECTED);
} else if (is_connected &&
connecting_state_ == ConnectingState::kProfilesConnecting) {
UpdateConnectingState(ConnectingState::kProfilesConnected, absl::nullopt);
}
}
void BluetoothDeviceFloss::SetConnectionState(uint32_t connection_state) {
connection_state_ = connection_state;
}
void BluetoothDeviceFloss::ConnectAllEnabledProfiles() {
UpdateConnectingState(ConnectingState::kProfilesConnecting, absl::nullopt);
connection_incomplete_timer_.Start(
FROM_HERE, kDefaultConnectTimeout,
base::BindOnce(&BluetoothDeviceFloss::ConnectionIncomplete,
weak_ptr_factory_.GetWeakPtr()));
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) {
// Generally, the first ever connection to a device should be direct and
// subsequent connections to known devices should be invoked with is_direct =
// false. Refer to |autoConnect| on BluetoothGatt.java.
bool is_direct = !IsBondedImpl();
UpdateGattConnectingState(GattConnectingState::kGattConnecting);
// Save the service uuid to trigger service discovery later.
search_uuid = service_uuid;
// Gatt connections establish over LE.
FlossDBusManager::Get()->GetGattManagerClient()->Connect(
base::BindOnce(&BluetoothDeviceFloss::OnConnectGatt,
weak_ptr_factory_.GetWeakPtr()),
address_, FlossDBusClient::BluetoothTransport::kLe, is_direct);
}
void BluetoothDeviceFloss::OnConnectGatt(DBusResult<Void> ret) {
if (!ret.has_value()) {
UpdateGattConnectingState(GattConnectingState::kGattDisconnected);
}
}
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()->GetGattManagerClient()->DiscoverAllServices(
base::DoNothing(), address_);
}
void BluetoothDeviceFloss::DisconnectGatt() {
svc_resolved_ = false;
FlossDBusManager::Get()->GetGattManagerClient()->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()->GetGattManagerClient()->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()->GetGattManagerClient()->RemoveObserver(this);
}
bool BluetoothDeviceFloss::IsBondedImpl() const {
return bond_state_ == FlossAdapterClient::BondState::kBonded;
}
void BluetoothDeviceFloss::OnGetRemoteType(
DBusResult<FlossAdapterClient::BluetoothDeviceType> ret) {
if (ret.has_value()) {
switch (*ret) {
case FlossAdapterClient::BluetoothDeviceType::kBle:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_LE;
break;
case FlossAdapterClient::BluetoothDeviceType::kDual:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_DUAL;
break;
// Default to BrEdr. ARC++ for example doesn't know how to translate the
// Invalid state.
case FlossAdapterClient::BluetoothDeviceType::kBredr:
[[fallthrough]];
default:
transport_ = device::BluetoothTransport::BLUETOOTH_TRANSPORT_CLASSIC;
break;
}
} else {
BLUETOOTH_LOG(ERROR) << "GetRemoteType() failed: " << ret.error();
}
TriggerInitDevicePropertiesCallback();
}
void BluetoothDeviceFloss::OnGetRemoteClass(DBusResult<uint32_t> ret) {
if (ret.has_value()) {
cod_ = *ret;
} else {
BLUETOOTH_LOG(ERROR) << "GetRemoteClass() failed: " << ret.error();
}
TriggerInitDevicePropertiesCallback();
}
void BluetoothDeviceFloss::OnGetRemoteAppearance(DBusResult<uint16_t> ret) {
if (ret.has_value()) {
appearance_ = *ret;
} else {
BLUETOOTH_LOG(ERROR) << "OnGetRemoteAppearance() failed: " << ret.error();
}
TriggerInitDevicePropertiesCallback();
}
void BluetoothDeviceFloss::OnGetRemoteUuids(DBusResult<UUIDList> ret) {
if (ret.has_value()) {
device_uuids_.ReplaceServiceUUIDs(*ret);
} else {
BLUETOOTH_LOG(ERROR) << "GetRemoteUuids() failed: " << ret.error();
}
TriggerInitDevicePropertiesCallback();
}
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.
UpdateConnectingState(ConnectingState::kIdle,
BluetoothDevice::ConnectErrorCode::ERROR_UNKNOWN);
return;
}
// Floss does not send any notifications that profiles have successfully
// connected if we are already ACL connected.
if (is_acl_connected_) {
UpdateConnectingState(ConnectingState::kProfilesConnected, absl::nullopt);
}
}
void BluetoothDeviceFloss::UpdateConnectingState(
ConnectingState state,
absl::optional<BluetoothDevice::ConnectErrorCode> error) {
if ((state == ConnectingState::kIdle) &&
((connecting_state_ == ConnectingState::kACLConnecting) ||
(connecting_state_ == ConnectingState::kProfilesConnecting))) {
// Something went wrong during connecting
TriggerConnectCallback(error);
} else if ((state == ConnectingState::kProfilesConnected) &&
(connecting_state_ == ConnectingState::kProfilesConnecting)) {
// Successful profile connection
TriggerConnectCallback(absl::nullopt);
}
if (connecting_state_ != state) {
connecting_state_ = state;
adapter_->NotifyDeviceChanged(this);
}
}
void BluetoothDeviceFloss::UpdateGattConnectingState(
GattConnectingState state) {
if (gatt_connecting_state_ != state) {
gatt_connecting_state_ = state;
adapter_->NotifyDeviceChanged(this);
}
}
void BluetoothDeviceFloss::TriggerConnectCallback(
absl::optional<BluetoothDevice::ConnectErrorCode> error_code) {
connection_incomplete_timer_.Stop();
if (pending_callback_on_connect_profiles_) {
// We need to move it first and set pending_callback_on_connect_profiles_
// to nullopt before Run-ing the callback, because this may trigger arriving
// at this same location.
auto callback = std::move(*pending_callback_on_connect_profiles_);
pending_callback_on_connect_profiles_ = absl::nullopt;
std::move(callback).Run(error_code);
}
}
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(
scoped_refptr<BluetoothSocketFloss> socket,
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) {
// If a property read is already active, don't re-run it.
if (property_reads_triggered_) {
return;
}
property_reads_triggered_ = true;
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_) {
property_reads_completed_ = true;
property_reads_triggered_ = false;
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) {
// TODO(b/193686094) - Convert GattStatus to other connect error codes.
err = ERROR_UNKNOWN;
}
if (connected) {
UpdateGattConnectingState(GattConnectingState::kGattConnected);
// Request for maximum MTU only when connected.
FlossDBusManager::Get()->GetGattManagerClient()->ConfigureMTU(
base::DoNothing(), address_, kMaxMtuSize);
return;
}
UpdateGattConnectingState(GattConnectingState::kGattDisconnected);
// 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;
// Copy the GATT services list here and clear the original so that when we
// send GattServiceRemoved(), GetGattServices() returns no services.
GattServiceMap gatt_services_swapped;
gatt_services_swapped.swap(gatt_services_);
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);
BluetoothRemoteGattServiceFloss* remote_service_ptr = remote_service.get();
gatt_services_[remote_service_ptr->GetIdentifier()] =
std::move(remote_service);
DCHECK(remote_service_ptr->GetUUID().IsValid());
// GattDiscoveryCompleteForService is deprecated. Currently only Fast Pair
// needs to listen to this.
//
// TODO(b/269478974): We should remove this once all callers migrate to
// GattServicesDiscovered.
adapter()->NotifyGattDiscoveryComplete(remote_service_ptr);
}
adapter()->NotifyGattServicesDiscovered(this);
}
void BluetoothDeviceFloss::GattConnectionUpdated(std::string address,
int32_t interval,
int32_t latency,
int32_t timeout,
GattStatus status) {
if (address != GetAddress())
return;
VLOG(1) << "Gatt connection updated on " << GetAddress()
<< " with status=" << static_cast<uint32_t>(status);
if (pending_set_connection_latency_.has_value()) {
auto& [pending_cb, pending_error_cb] =
pending_set_connection_latency_.value();
if (status == GattStatus::kSuccess) {
std::move(pending_cb).Run();
} else {
std::move(pending_error_cb).Run();
}
pending_set_connection_latency_ = absl::nullopt;
}
}
void BluetoothDeviceFloss::GattConfigureMtu(std::string address,
int32_t mtu,
GattStatus status) {
if (address != GetAddress())
return;
VLOG(1) << "GattConfigureMtu on " << GetAddress() << "; mtu=" << mtu
<< "; status=" << static_cast<uint32_t>(status);
// Discover services after configuring MTU
// This can be done even if configuring MTU failed.
if (search_uuid.has_value()) {
FlossDBusManager::Get()->GetGattManagerClient()->DiscoverServiceByUuid(
base::DoNothing(), address_, search_uuid.value());
} else if (!IsGattServicesDiscoveryComplete()) {
FlossDBusManager::Get()->GetGattManagerClient()->DiscoverAllServices(
base::DoNothing(), address_);
}
DidConnectGatt(absl::nullopt);
}
} // namespace floss