blob: e18b70b25702bd1f38f58d9adb46a7d041a0f511 [file]
// Copyright 2013 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/bluez/bluetooth_device_bluez.h"
#include <stdio.h>
#include <algorithm>
#include <memory>
#include <utility>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/notimplemented.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.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_socket.h"
#include "device/bluetooth/bluetooth_socket_thread.h"
#include "device/bluetooth/bluez/bluetooth_adapter_bluez.h"
#include "device/bluetooth/bluez/bluetooth_gatt_connection_bluez.h"
#include "device/bluetooth/bluez/bluetooth_pairing_bluez.h"
#include "device/bluetooth/bluez/bluetooth_remote_gatt_service_bluez.h"
#include "device/bluetooth/bluez/bluetooth_service_record_bluez.h"
#include "device/bluetooth/bluez/bluetooth_socket_bluez.h"
#include "device/bluetooth/bluez/metrics_recorder.h"
#include "device/bluetooth/dbus/bluetooth_adapter_client.h"
#include "device/bluetooth/dbus/bluetooth_device_client.h"
#include "device/bluetooth/dbus/bluetooth_gatt_service_client.h"
#include "device/bluetooth/dbus/bluetooth_input_client.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/public/cpp/bluetooth_address.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "device/bluetooth/chromeos/bluetooth_utils.h"
#endif
using device::BluetoothDevice;
using device::BluetoothRemoteGattService;
using device::BluetoothSocket;
using device::BluetoothUUID;
namespace {
// The unit for connection interval values are in multiples of 1.25ms.
const uint16_t MIN_CONNECTION_INTERVAL_LOW = 6;
const uint16_t MAX_CONNECTION_INTERVAL_LOW = 6;
const uint16_t MIN_CONNECTION_INTERVAL_MEDIUM = 40;
const uint16_t MAX_CONNECTION_INTERVAL_MEDIUM = 56;
const uint16_t MIN_CONNECTION_INTERVAL_HIGH = 80;
const uint16_t MAX_CONNECTION_INTERVAL_HIGH = 100;
// Histogram enumerations for pairing results.
enum UMAPairingResult {
UMA_PAIRING_RESULT_SUCCESS,
UMA_PAIRING_RESULT_INPROGRESS,
UMA_PAIRING_RESULT_FAILED,
UMA_PAIRING_RESULT_AUTH_FAILED,
UMA_PAIRING_RESULT_AUTH_CANCELED,
UMA_PAIRING_RESULT_AUTH_REJECTED,
UMA_PAIRING_RESULT_AUTH_TIMEOUT,
UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE,
UMA_PAIRING_RESULT_UNKNOWN_ERROR,
// NOTE: Add new pairing results immediately above this line. Make sure to
// update the enum list in tools/histogram/histograms.xml accordinly.
UMA_PAIRING_RESULT_COUNT
};
void ParseModalias(const dbus::ObjectPath& object_path,
BluetoothDevice::VendorIDSource* vendor_id_source,
uint16_t* vendor_id,
uint16_t* product_id,
uint16_t* device_id) {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path);
DCHECK(properties);
std::string modalias = properties->modalias.value();
BluetoothDevice::VendorIDSource source_value;
int vendor_value, product_value, device_value;
if (UNSAFE_TODO(sscanf(modalias.c_str(), "bluetooth:v%04xp%04xd%04x",
&vendor_value, &product_value, &device_value)) == 3) {
source_value = BluetoothDevice::VENDOR_ID_BLUETOOTH;
} else if (UNSAFE_TODO(sscanf(modalias.c_str(), "usb:v%04xp%04xd%04x",
&vendor_value, &product_value,
&device_value)) == 3) {
source_value = BluetoothDevice::VENDOR_ID_USB;
} else {
return;
}
if (vendor_id_source != nullptr)
*vendor_id_source = source_value;
if (vendor_id != nullptr)
*vendor_id = vendor_value;
if (product_id != nullptr)
*product_id = product_value;
if (device_id != nullptr)
*device_id = device_value;
}
BluetoothDevice::ConnectErrorCode DBusErrorToConnectError(
const std::string& error_name) {
BluetoothDevice::ConnectErrorCode error_code = BluetoothDevice::ERROR_UNKNOWN;
if (error_name == bluetooth_device::kErrorNotReady) {
error_code = BluetoothDevice::ERROR_DEVICE_NOT_READY;
} else if (error_name == bluetooth_device::kErrorFailed) {
error_code = BluetoothDevice::ERROR_FAILED;
} else if (error_name == bluetooth_device::kErrorInProgress) {
error_code = BluetoothDevice::ERROR_INPROGRESS;
} else if (error_name == bluetooth_device::kErrorAlreadyConnected) {
error_code = BluetoothDevice::ERROR_ALREADY_CONNECTED;
} else if (error_name == bluetooth_device::kErrorAlreadyExists) {
error_code = BluetoothDevice::ERROR_DEVICE_ALREADY_EXISTS;
} else if (error_name == bluetooth_device::kErrorNotConnected) {
error_code = BluetoothDevice::ERROR_DEVICE_UNCONNECTED;
} else if (error_name == bluetooth_device::kErrorDoesNotExist) {
error_code = BluetoothDevice::ERROR_DOES_NOT_EXIST;
} else if (error_name == bluetooth_device::kErrorInvalidArguments) {
error_code = BluetoothDevice::ERROR_INVALID_ARGS;
} else if (error_name == bluetooth_device::kErrorNotSupported) {
error_code = BluetoothDevice::ERROR_UNSUPPORTED_DEVICE;
} else if (error_name == bluetooth_device::kErrorAuthenticationCanceled) {
error_code = BluetoothDevice::ERROR_AUTH_CANCELED;
} else if (error_name == bluetooth_device::kErrorAuthenticationFailed) {
error_code = BluetoothDevice::ERROR_AUTH_FAILED;
} else if (error_name == bluetooth_device::kErrorAuthenticationRejected) {
error_code = BluetoothDevice::ERROR_AUTH_REJECTED;
} else if (error_name == bluetooth_device::kErrorAuthenticationTimeout) {
error_code = BluetoothDevice::ERROR_AUTH_TIMEOUT;
} else if (error_name == bluetooth_device::kErrorConnectionAttemptFailed) {
error_code = BluetoothDevice::ERROR_FAILED;
}
return error_code;
}
void OnForgetSuccess(base::OnceClosure callback) {
#if BUILDFLAG(IS_CHROMEOS)
device::RecordForgetResult(device::ForgetResult::kSuccess);
#endif
std::move(callback).Run();
}
void OnForgetError(dbus::ObjectPath object_path,
bluez::BluetoothDeviceBlueZ::ErrorCallback error_callback,
const std::string& error_name,
const std::string& error_message) {
#if BUILDFLAG(IS_CHROMEOS)
device::RecordForgetResult(device::ForgetResult::kFailure);
#endif
BLUETOOTH_LOG(ERROR) << object_path.value()
<< ": Failed to remove device: " << error_name << ": "
<< error_message;
std::move(error_callback).Run();
}
} // namespace
namespace bluez {
BluetoothDeviceBlueZ::BluetoothDeviceBlueZ(
BluetoothAdapterBlueZ* adapter,
const dbus::ObjectPath& object_path,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<device::BluetoothSocketThread> socket_thread)
: BluetoothDevice(adapter),
object_path_(object_path),
num_connecting_calls_(0),
ui_task_runner_(ui_task_runner),
socket_thread_(socket_thread) {
bluez::BluezDBusManager::Get()->GetBluetoothGattServiceClient()->AddObserver(
this);
// If GATT Services have already been discovered update the list of Gatt
// Services.
if (IsGattServicesDiscoveryComplete()) {
UpdateGattServices(object_path_);
} else {
BLUETOOTH_LOG(DEBUG)
<< "Gatt services have not been fully resolved for device "
<< object_path_.value();
}
// Update all the data that we cache within Chrome and do not pull from
// properties every time. TODO(xiaoyinh): Add a test for this. See
// http://crbug.com/688566.
UpdateServiceData();
UpdateManufacturerData();
UpdateAdvertisingDataFlags();
UpdateServiceUUIDs();
}
BluetoothDeviceBlueZ::~BluetoothDeviceBlueZ() {
bluez::BluezDBusManager::Get()
->GetBluetoothGattServiceClient()
->RemoveObserver(this);
// 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& iter : gatt_services_swapped) {
DCHECK(adapter());
adapter()->NotifyGattServiceRemoved(
static_cast<BluetoothRemoteGattServiceBlueZ*>(iter.second.get()));
}
}
uint32_t BluetoothDeviceBlueZ::GetBluetoothClass() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
return properties->bluetooth_class.value();
}
device::BluetoothTransport BluetoothDeviceBlueZ::GetType() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (!properties->type.is_valid())
return device::BLUETOOTH_TRANSPORT_INVALID;
std::string type = properties->type.value();
if (type == bluez::BluetoothDeviceClient::kTypeBredr) {
return device::BLUETOOTH_TRANSPORT_CLASSIC;
} else if (type == bluez::BluetoothDeviceClient::kTypeLe) {
return device::BLUETOOTH_TRANSPORT_LE;
} else if (type == bluez::BluetoothDeviceClient::kTypeDual) {
return device::BLUETOOTH_TRANSPORT_DUAL;
}
NOTREACHED();
}
void BluetoothDeviceBlueZ::CreateGattConnectionImpl(
std::optional<BluetoothUUID> service_uuid) {
// Once ConnectLE is supported on Linux, this buildflag will not be necessary
// (this bluez code is only run on Chrome OS and Linux).
#if BUILDFLAG(IS_CHROMEOS)
if (num_connecting_calls_++ == 0)
adapter()->NotifyDeviceChanged(this);
auto success_callback = base::BindOnce(
&BluetoothDeviceBlueZ::OnConnect, weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&BluetoothDeviceBlueZ::DidConnectGatt,
weak_ptr_factory_.GetWeakPtr()));
auto error_callback = base::BindOnce(
&BluetoothDeviceBlueZ::OnConnectError, weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&BluetoothDeviceBlueZ::DidConnectGatt,
weak_ptr_factory_.GetWeakPtr()));
// TODO(crbug.com/630586): Until there is a way to create a reference counted
// GATT connection in bluetoothd (i.e., specify a connection to a particular
// GATT service), simply perform a regular LE connect.
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ConnectLE(
object_path_, std::move(success_callback), std::move(error_callback));
#else
Connect(/*pairing_delegate=*/nullptr,
base::BindOnce(&BluetoothDeviceBlueZ::DidConnectGatt,
weak_ptr_factory_.GetWeakPtr()));
#endif
}
void BluetoothDeviceBlueZ::SetGattServicesDiscoveryComplete(bool complete) {
// BlueZ implementation already tracks service discovery state.
NOTIMPLEMENTED();
}
bool BluetoothDeviceBlueZ::IsGattServicesDiscoveryComplete() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
return properties->services_resolved.value();
}
void BluetoothDeviceBlueZ::DisconnectGatt() {
// There isn't currently a good way to manage the ownership of a connection
// between Chrome and bluetoothd plugins/profiles. Until a proper reference
// count is kept in bluetoothd, we might unwittingly kill a connection to a
// device the user is still interested in, e.g. a mouse. A device's paired
// status is usually a good indication that the device is being used by other
// parts of the system and therefore we leak these connections.
// TODO(crbug.com/630586): Call disconnect for all devices.
// IsPaired() returns true if we've connected to the device before. So we
// check the dbus property directly.
// TODO(crbug.com/40486156): Use IsPaired once it returns true only for paired
// devices.
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (properties->paired.value()) {
BLUETOOTH_LOG(ERROR) << "Leaking connection to paired device.";
return;
}
// Once DisconnectLE is supported on Linux, this buildflag will not be necessary
// (this bluez code is only run on Chrome OS and Linux).
#if BUILDFLAG(IS_CHROMEOS)
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->DisconnectLE(
object_path_, base::DoNothing(),
base::BindOnce(&BluetoothDeviceBlueZ::OnDisconnectLEError,
weak_ptr_factory_.GetWeakPtr()));
#else
Disconnect(base::DoNothing(), base::DoNothing());
#endif
}
// Once DisconnectLE is supported on Linux, this buildflag will not be necessary
// (this bluez code is only run on Chrome OS and Linux).
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::OnDisconnectLEError(
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << "DisconnectLE() failed with error name: "
<< error_name << " and error message: " << error_message;
}
#endif
std::string BluetoothDeviceBlueZ::GetIdentifier() const {
// The D-Bus object path is the original Bluetooth device address. If the
// device uses a resolvable address that rotates over time, BlueZ will resolve
// it back to its original address used for the D-Bus object path.
return object_path_.value();
}
std::string BluetoothDeviceBlueZ::GetAddress() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
return device::CanonicalizeBluetoothAddress(properties->address.value());
}
BluetoothDeviceBlueZ::AddressType BluetoothDeviceBlueZ::GetAddressType() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (!properties->address_type.is_valid())
return ADDR_TYPE_UNKNOWN;
if (properties->address_type.value() == bluetooth_device::kAddressTypePublic)
return ADDR_TYPE_PUBLIC;
if (properties->address_type.value() == bluetooth_device::kAddressTypeRandom)
return ADDR_TYPE_RANDOM;
LOG(WARNING) << "Unknown address type: " << properties->address_type.value();
return ADDR_TYPE_UNKNOWN;
}
BluetoothDevice::VendorIDSource BluetoothDeviceBlueZ::GetVendorIDSource()
const {
VendorIDSource vendor_id_source = VENDOR_ID_UNKNOWN;
ParseModalias(object_path_, &vendor_id_source, nullptr, nullptr, nullptr);
return vendor_id_source;
}
uint16_t BluetoothDeviceBlueZ::GetVendorID() const {
uint16_t vendor_id = 0;
ParseModalias(object_path_, nullptr, &vendor_id, nullptr, nullptr);
return vendor_id;
}
uint16_t BluetoothDeviceBlueZ::GetProductID() const {
uint16_t product_id = 0;
ParseModalias(object_path_, nullptr, nullptr, &product_id, nullptr);
return product_id;
}
uint16_t BluetoothDeviceBlueZ::GetDeviceID() const {
uint16_t device_id = 0;
ParseModalias(object_path_, nullptr, nullptr, nullptr, &device_id);
return device_id;
}
uint16_t BluetoothDeviceBlueZ::GetAppearance() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (!properties->appearance.is_valid())
return kAppearanceNotPresent;
return properties->appearance.value();
}
std::optional<std::string> BluetoothDeviceBlueZ::GetName() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (properties->name.is_valid())
return properties->name.value();
else
return std::nullopt;
}
bool BluetoothDeviceBlueZ::IsPaired() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
// The "paired" property reflects the successful pairing for BR/EDR/LE. The
// value of the "paired" property is always false for the devices that don't
// support pairing. Once a device is paired successfully, both the "paired"
// and the "trusted" properties will be set to true.
return properties->paired.value();
}
#if BUILDFLAG(IS_CHROMEOS)
bool BluetoothDeviceBlueZ::IsBonded() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
// The "bonded" property reflects the successful pairing for BR/EDR/LE, where
// the information required to initiate and authenticate connections is stored
// on-device. The value of the "bonded" property is always false for the
// devices that don't support pairing. Once a device is bonded successfully,
// the "bonded", "paired", and "trusted" properties will be set to true.
return properties->bonded.value();
}
#endif
bool BluetoothDeviceBlueZ::IsConnected() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
return properties->connected.value();
}
bool BluetoothDeviceBlueZ::IsGattConnected() const {
// Once the |connected_le| property is supported on Linux, this buildflag will
// not be necessary (this bluez code is only run on Chrome OS and Linux).
#if BUILDFLAG(IS_CHROMEOS)
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
return properties->connected_le.value();
#else
// BlueZ uses the same attribute for GATT Connections and Classic BT
// Connections.
return IsConnected();
#endif
}
bool BluetoothDeviceBlueZ::IsConnectable() const {
bluez::BluetoothInputClient::Properties* input_properties =
bluez::BluezDBusManager::Get()->GetBluetoothInputClient()->GetProperties(
object_path_);
// GetProperties returns nullptr when the device does not implement the given
// interface. Non HID devices are normally connectable.
if (!input_properties)
return true;
return input_properties->reconnect_mode.value() != "device";
}
bool BluetoothDeviceBlueZ::IsConnecting() const {
return num_connecting_calls_ > 0;
}
BluetoothDevice::UUIDSet BluetoothDeviceBlueZ::GetUUIDs() const {
return device_uuids_.GetUUIDs();
}
std::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryRSSI() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (!properties->rssi.is_valid())
return std::nullopt;
// BlueZ uses int16_t because there is no int8_t for DBus, so we should never
// get an int16_t that cannot be represented by an int8_t. But just in case
// clamp the value.
return ClampPower(properties->rssi.value());
}
std::optional<int8_t> BluetoothDeviceBlueZ::GetInquiryTxPower() const {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
DCHECK(properties);
if (!properties->tx_power.is_valid())
return std::nullopt;
// BlueZ uses int16_t because there is no int8_t for DBus, so we should never
// get an int16_t that cannot be represented by an int8_t. But just in case
// clamp the value.
return ClampPower(properties->tx_power.value());
}
bool BluetoothDeviceBlueZ::ExpectingPinCode() const {
return pairing_.get() && pairing_->ExpectingPinCode();
}
bool BluetoothDeviceBlueZ::ExpectingPasskey() const {
return pairing_.get() && pairing_->ExpectingPasskey();
}
bool BluetoothDeviceBlueZ::ExpectingConfirmation() const {
return pairing_.get() && pairing_->ExpectingConfirmation();
}
void BluetoothDeviceBlueZ::GetConnectionInfo(ConnectionInfoCallback callback) {
// DBus method call should gracefully return an error if the device is not
// currently connected.
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetConnInfo(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnGetConnInfo,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnGetConnInfoError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
void BluetoothDeviceBlueZ::SetConnectionLatency(
ConnectionLatency connection_latency,
base::OnceClosure callback,
ErrorCallback error_callback) {
uint16_t min_connection_interval = MIN_CONNECTION_INTERVAL_MEDIUM;
uint16_t max_connection_interval = MAX_CONNECTION_INTERVAL_MEDIUM;
switch (connection_latency) {
case ConnectionLatency::CONNECTION_LATENCY_LOW:
min_connection_interval = MIN_CONNECTION_INTERVAL_LOW;
max_connection_interval = MAX_CONNECTION_INTERVAL_LOW;
break;
case ConnectionLatency::CONNECTION_LATENCY_MEDIUM:
min_connection_interval = MIN_CONNECTION_INTERVAL_MEDIUM;
max_connection_interval = MAX_CONNECTION_INTERVAL_MEDIUM;
break;
case ConnectionLatency::CONNECTION_LATENCY_HIGH:
min_connection_interval = MIN_CONNECTION_INTERVAL_HIGH;
max_connection_interval = MAX_CONNECTION_INTERVAL_HIGH;
break;
default:
NOTREACHED();
}
BLUETOOTH_LOG(EVENT) << "Setting LE connection parameters: min="
<< min_connection_interval
<< ", max=" << max_connection_interval;
bluez::BluetoothDeviceClient::ConnectionParameters connection_parameters;
connection_parameters.min_connection_interval = min_connection_interval;
connection_parameters.max_connection_interval = max_connection_interval;
bluez::BluetoothDeviceClient* client =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient();
client->SetLEConnectionParameters(
object_path_, connection_parameters,
base::BindOnce(&BluetoothDeviceBlueZ::OnSetLEConnectionParameters,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
base::BindOnce(&BluetoothDeviceBlueZ::OnSetLEConnectionParametersError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
void BluetoothDeviceBlueZ::Connect(
BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
if (num_connecting_calls_++ == 0)
adapter()->NotifyDeviceChanged(this);
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Connecting, "
<< num_connecting_calls_ << " in progress";
if (IsPaired() || !pairing_delegate) {
// No need to pair, or unable to, skip straight to connection.
ConnectInternal(std::move(callback));
} else {
// Initiate high-security connection with pairing.
BeginPairing(pairing_delegate);
// This callback is only called once but is passed to two different places.
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->Pair(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnPairDuringConnect,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnPairDuringConnectError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::ConnectClassic(
BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
if (num_connecting_calls_++ == 0)
adapter()->NotifyDeviceChanged(this);
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Connecting with classic, "
<< num_connecting_calls_ << " in progress";
if (IsPaired() || !pairing_delegate) {
// No need to pair, or unable to, skip straight to connection.
ConnectClassicInternal(std::move(callback));
} else {
// Initiate high-security connection with pairing.
BeginPairing(pairing_delegate);
// This callback is only called once but is passed to two different places.
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->Pair(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnPairDuringConnectClassic,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnPairDuringConnectError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::Pair(
BluetoothDevice::PairingDelegate* pairing_delegate,
ConnectCallback callback) {
DCHECK(pairing_delegate);
BeginPairing(pairing_delegate);
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->Pair(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnPair,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnPairError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
void BluetoothDeviceBlueZ::SetPinCode(const std::string& pincode) {
if (!pairing_.get())
return;
pairing_->SetPinCode(pincode);
}
void BluetoothDeviceBlueZ::SetPasskey(uint32_t passkey) {
if (!pairing_.get())
return;
pairing_->SetPasskey(passkey);
}
void BluetoothDeviceBlueZ::ConfirmPairing() {
if (!pairing_.get())
return;
pairing_->ConfirmPairing();
}
void BluetoothDeviceBlueZ::RejectPairing() {
if (!pairing_.get())
return;
pairing_->RejectPairing();
}
void BluetoothDeviceBlueZ::CancelPairing() {
bool canceled = false;
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": CancelPairing";
// If there is a callback in progress that we can reply to then use that
// to cancel the current pairing request.
if (pairing_.get() && pairing_->CancelPairing())
canceled = true;
// If not we have to send an explicit CancelPairing() to the device instead.
if (!canceled) {
BLUETOOTH_LOG(DEBUG) << object_path_.value()
<< ": No pairing context or callback. "
<< "Sending explicit cancel";
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->CancelPairing(
object_path_, base::DoNothing(),
base::BindOnce(&BluetoothDeviceBlueZ::OnCancelPairingError,
weak_ptr_factory_.GetWeakPtr()));
}
// Since there is no callback to this method it's possible that the pairing
// delegate is going to be freed before things complete (indeed it's
// documented that this is the method you should call while freeing the
// pairing delegate), so clear our the context holding on to it.
EndPairing();
}
void BluetoothDeviceBlueZ::Disconnect(base::OnceClosure callback,
ErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Disconnecting";
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->Disconnect(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnDisconnect,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
base::BindOnce(&BluetoothDeviceBlueZ::OnDisconnectError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
void BluetoothDeviceBlueZ::Forget(base::OnceClosure callback,
ErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Removing device";
// |this| can be destroyed during this call, so the callbacks are not class
// methods or else they may never be invoked.
bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->RemoveDevice(
adapter()->object_path(), object_path_,
base::BindOnce(&OnForgetSuccess, std::move(callback)),
base::BindOnce(&OnForgetError, object_path_, std::move(error_callback)));
}
void BluetoothDeviceBlueZ::ConnectToService(
const BluetoothUUID& uuid,
ConnectToServiceCallback callback,
ConnectToServiceErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Connecting to service: " << uuid.canonical_value();
scoped_refptr<BluetoothSocketBlueZ> socket =
BluetoothSocketBlueZ::CreateBluetoothSocket(ui_task_runner_,
socket_thread_);
socket->Connect(this, uuid, BluetoothSocketBlueZ::SECURITY_LEVEL_MEDIUM,
base::BindOnce(std::move(callback), socket),
base::BindOnce(&BluetoothDeviceBlueZ::OnConnectToServiceError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
void BluetoothDeviceBlueZ::ConnectToServiceInsecurely(
const BluetoothUUID& uuid,
ConnectToServiceCallback callback,
ConnectToServiceErrorCallback error_callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Connecting insecurely to service: "
<< uuid.canonical_value();
scoped_refptr<BluetoothSocketBlueZ> socket =
BluetoothSocketBlueZ::CreateBluetoothSocket(ui_task_runner_,
socket_thread_);
socket->Connect(this, uuid, BluetoothSocketBlueZ::SECURITY_LEVEL_LOW,
base::BindOnce(std::move(callback), socket),
base::BindOnce(&BluetoothDeviceBlueZ::OnConnectToServiceError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
std::unique_ptr<device::BluetoothGattConnection>
BluetoothDeviceBlueZ::CreateBluetoothGattConnectionObject() {
return std::make_unique<BluetoothGattConnectionBlueZ>(adapter_, GetAddress(),
object_path_);
}
void BluetoothDeviceBlueZ::GetServiceRecords(
GetServiceRecordsCallback callback,
GetServiceRecordsErrorCallback error_callback) {
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetServiceRecords(
object_path_, std::move(callback),
base::BindOnce(&BluetoothDeviceBlueZ::OnGetServiceRecordsError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::ExecuteWrite(
base::OnceClosure callback,
ExecuteWriteErrorCallback error_callback) {
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ExecuteWrite(
object_path_, std::move(callback),
base::BindOnce(&BluetoothDeviceBlueZ::OnExecuteWriteError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
void BluetoothDeviceBlueZ::AbortWrite(base::OnceClosure callback,
AbortWriteErrorCallback error_callback) {
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->AbortWrite(
object_path_, std::move(callback),
base::BindOnce(&BluetoothDeviceBlueZ::OnAbortWriteError,
weak_ptr_factory_.GetWeakPtr(),
std::move(error_callback)));
}
#endif
void BluetoothDeviceBlueZ::OnConnectToServiceError(
ConnectToServiceErrorCallback error_callback,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to connect to service: " << error_message;
#if BUILDFLAG(IS_CHROMEOS)
bluetooth::ConnectToServiceFailureReason reason =
bluetooth::ExtractFailureReasonFromErrorString(error_message);
bluetooth::RecordConnectToServiceFailureReason(reason);
// If the connection fails when we are supposedly bonded with the remote
// device, record this event specifically. This may indicate that we are in a
// "half paired" (or "half bonded") state as described in b/204274786.
if (IsBonded()) {
bluetooth::RecordBondedConnectToServiceFailureReason(reason);
}
#endif // BUILDFLAG(IS_CHROMEOS)
std::move(error_callback).Run(error_message);
}
void BluetoothDeviceBlueZ::UpdateServiceData() {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
if (!properties || !properties->service_data.is_valid())
return;
service_data_.clear();
for (const auto& pair : properties->service_data.value())
service_data_[BluetoothUUID(pair.first)] = pair.second;
}
void BluetoothDeviceBlueZ::UpdateManufacturerData() {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
if (!properties || !properties->manufacturer_data.is_valid())
return;
manufacturer_data_.clear();
if (properties->manufacturer_data.is_valid()) {
for (const auto& pair : properties->manufacturer_data.value())
manufacturer_data_[pair.first] = pair.second;
}
}
void BluetoothDeviceBlueZ::UpdateAdvertisingDataFlags() {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
if (!properties || !properties->advertising_data_flags.is_valid())
return;
// The advertising data flags property is a vector<uint8> because the
// Supplement to Bluetooth Core Specification Version 6 page 13 said that
// "The Flags field may be zero or more octets long." However, only the first
// byte of that is needed because there is only 5 bits of data defined there.
advertising_data_flags_ = properties->advertising_data_flags.value()[0];
}
void BluetoothDeviceBlueZ::UpdateServiceUUIDs() {
bluez::BluetoothDeviceClient::Properties* properties =
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
object_path_);
if (!properties)
return;
UUIDList uuids;
for (const std::string& dbus_uuid : properties->uuids.value()) {
device::BluetoothUUID uuid(dbus_uuid);
DCHECK(uuid.IsValid());
uuids.push_back(std::move(uuid));
}
device_uuids_.ReplaceServiceUUIDs(uuids);
}
void BluetoothDeviceBlueZ::SetAdvertisedUUIDs(
const BluetoothDevice::UUIDList& uuids) {
device_uuids_.ReplaceAdvertisedUUIDs(uuids);
}
BluetoothPairingBlueZ* BluetoothDeviceBlueZ::BeginPairing(
BluetoothDevice::PairingDelegate* pairing_delegate) {
pairing_ = std::make_unique<BluetoothPairingBlueZ>(this, pairing_delegate);
return pairing_.get();
}
void BluetoothDeviceBlueZ::EndPairing() {
pairing_.reset();
}
BluetoothPairingBlueZ* BluetoothDeviceBlueZ::GetPairing() const {
return pairing_.get();
}
BluetoothAdapterBlueZ* BluetoothDeviceBlueZ::adapter() const {
return static_cast<BluetoothAdapterBlueZ*>(adapter_);
}
void BluetoothDeviceBlueZ::GattServiceAdded(
const dbus::ObjectPath& object_path) {
if (GetGattService(object_path.value())) {
BLUETOOTH_LOG(DEBUG) << "Remote GATT service already exists: "
<< object_path.value();
return;
}
bluez::BluetoothGattServiceClient::Properties* properties =
bluez::BluezDBusManager::Get()
->GetBluetoothGattServiceClient()
->GetProperties(object_path);
DCHECK(properties);
if (properties->device.value() != object_path_) {
BLUETOOTH_LOG(DEBUG)
<< "Remote GATT service does not belong to this device.";
return;
}
BLUETOOTH_LOG(EVENT) << "Adding new remote GATT service for device: "
<< GetAddress();
BluetoothRemoteGattServiceBlueZ* service =
new BluetoothRemoteGattServiceBlueZ(adapter(), this, object_path);
gatt_services_[service->GetIdentifier()] = base::WrapUnique(service);
DCHECK(service->object_path() == object_path);
DCHECK(service->GetUUID().IsValid());
DCHECK(adapter());
adapter()->NotifyGattServiceAdded(service);
}
void BluetoothDeviceBlueZ::GattServiceRemoved(
const dbus::ObjectPath& object_path) {
auto iter = gatt_services_.find(object_path.value());
if (iter == gatt_services_.end()) {
DVLOG(3) << "Unknown GATT service removed: " << object_path.value();
return;
}
BluetoothRemoteGattServiceBlueZ* service =
static_cast<BluetoothRemoteGattServiceBlueZ*>(iter->second.get());
BLUETOOTH_LOG(EVENT) << "Removing remote GATT service with UUID: '"
<< service->GetUUID().canonical_value()
<< "' from device: " << GetAddress();
DCHECK(service->object_path() == object_path);
std::unique_ptr<BluetoothRemoteGattService> scoped_service =
std::move(gatt_services_[object_path.value()]);
gatt_services_.erase(iter);
DCHECK(adapter());
discovery_complete_notified_.erase(service);
adapter()->NotifyGattServiceRemoved(service);
}
void BluetoothDeviceBlueZ::UpdateGattServices(
const dbus::ObjectPath& object_path) {
if (object_path != object_path_) {
// No need to update map if update is for a different device.
return;
}
DCHECK(IsGattServicesDiscoveryComplete());
DVLOG(3) << "Updating the list of GATT services associated with device "
<< object_path_.value();
const std::vector<dbus::ObjectPath> service_paths =
bluez::BluezDBusManager::Get()
->GetBluetoothGattServiceClient()
->GetServices();
for (const auto& service_path : service_paths) {
// Add all previously unknown GATT services associated with the device.
GattServiceAdded(service_path);
// |service_paths| includes all services for all devices, not just this
// device. GetGattService() will return nullptr for services belonging
// to other devices, so we skip those and keep looking for services that
// belong to this device.
BluetoothRemoteGattService* service = GetGattService(service_path.value());
if (service == nullptr) {
continue;
}
// Notify of GATT discovery complete if we haven't before.
auto notified_pair = discovery_complete_notified_.insert(service);
if (notified_pair.second) {
adapter()->NotifyGattDiscoveryComplete(service);
}
}
}
void BluetoothDeviceBlueZ::OnGetConnInfo(ConnectionInfoCallback callback,
int16_t rssi,
int16_t transmit_power,
int16_t max_transmit_power) {
std::move(callback).Run(
ConnectionInfo(rssi, transmit_power, max_transmit_power));
}
void BluetoothDeviceBlueZ::OnGetConnInfoError(
ConnectionInfoCallback callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to get connection info: " << error_name
<< ": " << error_message;
std::move(callback).Run(ConnectionInfo());
}
void BluetoothDeviceBlueZ::OnSetLEConnectionParameters(
base::OnceClosure callback) {
std::move(callback).Run();
}
void BluetoothDeviceBlueZ::OnSetLEConnectionParametersError(
ErrorCallback callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to set connection parameters: "
<< error_name << ": " << error_message;
std::move(callback).Run();
}
void BluetoothDeviceBlueZ::OnGetServiceRecordsError(
GetServiceRecordsErrorCallback error_callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Failed to get service records: " << error_name
<< ": " << error_message;
BluetoothServiceRecordBlueZ::ErrorCode code =
BluetoothServiceRecordBlueZ::ErrorCode::UNKNOWN;
if (error_name == bluetooth_device::kErrorNotConnected) {
code = BluetoothServiceRecordBlueZ::ErrorCode::ERROR_DEVICE_DISCONNECTED;
}
std::move(error_callback).Run(code);
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::OnExecuteWriteError(
ExecuteWriteErrorCallback error_callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Failed to execute write: " << error_name << ": "
<< error_message;
std::move(error_callback)
.Run(BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
}
void BluetoothDeviceBlueZ::OnAbortWriteError(
AbortWriteErrorCallback error_callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Failed to abort write: " << error_name << ": "
<< error_message;
std::move(error_callback)
.Run(BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
}
#endif
void BluetoothDeviceBlueZ::ConnectInternal(ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Connecting";
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->Connect(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnConnect,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnConnectError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::ConnectClassicInternal(ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Connecting with classic";
auto split_callback = base::SplitOnceCallback(std::move(callback));
bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ConnectClassic(
object_path_,
base::BindOnce(&BluetoothDeviceBlueZ::OnConnect,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothDeviceBlueZ::OnConnectError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
#endif // BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::OnConnect(ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Unpausing discovery after connection";
if (--num_connecting_calls_ == 0)
adapter()->NotifyDeviceChanged(this);
DCHECK(num_connecting_calls_ >= 0);
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Connected, "
<< num_connecting_calls_ << " still in progress";
// For CrOS, set trusted upon outgoing connection established.
// No-op for non-CrOS since Chrome is not part of the OS.
#if BUILDFLAG(IS_CHROMEOS)
SetTrusted();
#endif // BUILDFLAG(IS_CHROMEOS)
std::move(callback).Run(/*error_code=*/std::nullopt);
}
void BluetoothDeviceBlueZ::OnConnectError(ConnectCallback callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(EVENT) << object_path_.value()
<< ": Unpausing discovery after failed connection";
if (--num_connecting_calls_ == 0)
adapter()->NotifyDeviceChanged(this);
DCHECK(num_connecting_calls_ >= 0);
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to connect device: " << error_name << ": "
<< error_message;
BLUETOOTH_LOG(DEBUG) << object_path_.value() << ": " << num_connecting_calls_
<< " still in progress";
// Determine the error code from error_name.
ConnectErrorCode error_code = DBusErrorToConnectError(error_name);
std::move(callback).Run(error_code);
}
void BluetoothDeviceBlueZ::OnPairDuringConnect(ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Paired";
EndPairing();
ConnectInternal(std::move(callback));
}
#if BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::OnPairDuringConnectClassic(
ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Paired";
EndPairing();
ConnectClassicInternal(std::move(callback));
}
#endif // BUILDFLAG(IS_CHROMEOS)
void BluetoothDeviceBlueZ::OnPairDuringConnectError(
ConnectCallback callback,
const std::string& error_name,
const std::string& error_message) {
if (--num_connecting_calls_ == 0)
adapter()->NotifyDeviceChanged(this);
DCHECK(num_connecting_calls_ >= 0);
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to pair device: " << error_name << ": "
<< error_message;
BLUETOOTH_LOG(DEBUG) << object_path_.value() << ": " << num_connecting_calls_
<< " still in progress";
EndPairing();
// Determine the error code from error_name.
ConnectErrorCode error_code = DBusErrorToConnectError(error_name);
std::move(callback).Run(error_code);
}
void BluetoothDeviceBlueZ::OnPair(ConnectCallback callback) {
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Paired";
EndPairing();
std::move(callback).Run(/*error_code=*/std::nullopt);
}
void BluetoothDeviceBlueZ::OnPairError(ConnectCallback callback,
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to pair device: " << error_name << ": "
<< error_message;
EndPairing();
ConnectErrorCode error_code = DBusErrorToConnectError(error_name);
std::move(callback).Run(error_code);
}
void BluetoothDeviceBlueZ::OnCancelPairingError(
const std::string& error_name,
const std::string& error_message) {
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to cancel pairing: " << error_name << ": "
<< error_message;
}
void BluetoothDeviceBlueZ::SetTrusted() {
// Unconditionally send the property change, rather than checking the value
// first; there's no harm in doing this and it solves any race conditions
// with the property becoming true or false and this call happening before
// we get the D-Bus signal about the earlier change.
bluez::BluezDBusManager::Get()
->GetBluetoothDeviceClient()
->GetProperties(object_path_)
->trusted.Set(true, base::BindOnce(&BluetoothDeviceBlueZ::OnSetTrusted,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothDeviceBlueZ::OnSetTrusted(bool success) {
device_event_log::LogLevel log_level =
success ? device_event_log::LOG_LEVEL_DEBUG
: device_event_log::LOG_LEVEL_ERROR;
DEVICE_LOG(device_event_log::LOG_TYPE_BLUETOOTH, log_level)
<< object_path_.value() << ": OnSetTrusted: " << success;
}
void BluetoothDeviceBlueZ::OnDisconnect(base::OnceClosure callback) {
#if BUILDFLAG(IS_CHROMEOS)
device::RecordUserInitiatedDisconnectResult(
device::DisconnectResult::kSuccess,
/*transport=*/GetType());
#endif
BLUETOOTH_LOG(EVENT) << object_path_.value() << ": Disconnected";
std::move(callback).Run();
}
void BluetoothDeviceBlueZ::OnDisconnectError(ErrorCallback error_callback,
const std::string& error_name,
const std::string& error_message) {
#if BUILDFLAG(IS_CHROMEOS)
device::RecordUserInitiatedDisconnectResult(
device::DisconnectResult::kFailure,
/*transport=*/GetType());
#endif
BLUETOOTH_LOG(ERROR) << object_path_.value()
<< ": Failed to disconnect device: " << error_name
<< ": " << error_message;
std::move(error_callback).Run();
}
} // namespace bluez