| // 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 |