blob: d6e379e58d3f6ed24ca1ce33e1e60e74c68989c7 [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.
#ifndef DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_CLIENT_H_
#define DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_CLIENT_H_
#include <map>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/expected.h"
#include "dbus/bus.h"
#include "dbus/exported_object.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_export.h"
#include "device/bluetooth/floss/floss_version.h"
namespace floss {
extern DEVICE_BLUETOOTH_EXPORT int kDBusTimeoutMs;
extern DEVICE_BLUETOOTH_EXPORT int kAdapterEnabledTimeoutMs;
// TODO(b/189499077) - Expose via floss package
inline constexpr char kAdapterService[] = "org.chromium.bluetooth";
inline constexpr char kManagerService[] = "org.chromium.bluetooth.Manager";
inline constexpr char kAdapterLoggingObjectFormat[] =
"/org/chromium/bluetooth/hci%d/logging";
inline constexpr char kAdapterObjectFormat[] =
"/org/chromium/bluetooth/hci%d/adapter";
inline constexpr char kAdminObjectFormat[] =
"/org/chromium/bluetooth/hci%d/admin";
inline constexpr char kBatteryManagerObjectFormat[] =
"/org/chromium/bluetooth/hci%d/battery_manager";
inline constexpr char kBluetoothTelephonyObjectFormat[] =
"/org/chromium/bluetooth/hci%d/telephony";
inline constexpr char kGattObjectFormat[] =
"/org/chromium/bluetooth/hci%d/gatt";
inline constexpr char kManagerObject[] = "/org/chromium/bluetooth/Manager";
inline constexpr char kMediaObjectFormat[] =
"/org/chromium/bluetooth/hci%d/media";
inline constexpr char kAdapterInterface[] = "org.chromium.bluetooth.Bluetooth";
inline constexpr char kAdapterLoggingInterface[] =
"org.chromium.bluetooth.Logging";
inline constexpr char kAdminInterface[] =
"org.chromium.bluetooth.BluetoothAdmin";
inline constexpr char kBatteryManagerInterface[] =
"org.chromium.bluetooth.BatteryManager";
inline constexpr char kBluetoothTelephonyInterface[] =
"org.chromium.bluetooth.BluetoothTelephony";
inline constexpr char kExperimentalInterface[] =
"org.chromium.bluetooth.Experimental";
inline constexpr char kGattInterface[] = "org.chromium.bluetooth.BluetoothGatt";
inline constexpr char kManagerInterface[] = "org.chromium.bluetooth.Manager";
inline constexpr char kSocketManagerInterface[] =
"org.chromium.bluetooth.SocketManager";
namespace adapter {
inline constexpr char kGetAddress[] = "GetAddress";
inline constexpr char kGetName[] = "GetName";
inline constexpr char kSetName[] = "SetName";
inline constexpr char kGetDiscoverable[] = "GetDiscoverable";
inline constexpr char kGetDiscoverableTimeout[] = "GetDiscoverableTimeout";
inline constexpr char kSetDiscoverable[] = "SetDiscoverable";
inline constexpr char kIsLeExtendedAdvertisingSupported[] =
"IsLeExtendedAdvertisingSupported";
inline constexpr char kStartDiscovery[] = "StartDiscovery";
inline constexpr char kCancelDiscovery[] = "CancelDiscovery";
inline constexpr char kCreateBond[] = "CreateBond";
inline constexpr char kCancelBondProcess[] = "CancelBondProcess";
inline constexpr char kRemoveBond[] = "RemoveBond";
inline constexpr char kGetRemoteType[] = "GetRemoteType";
inline constexpr char kGetRemoteClass[] = "GetRemoteClass";
inline constexpr char kGetRemoteAppearance[] = "GetRemoteAppearance";
inline constexpr char kGetRemoteVendorProductInfo[] =
"GetRemoteVendorProductInfo";
inline constexpr char kGetRemoteAddressType[] = "GetRemoteAddressType";
inline constexpr char kGetConnectionState[] = "GetConnectionState";
inline constexpr char kGetRemoteUuids[] = "GetRemoteUuids";
inline constexpr char kFetchRemoteUuids[] = "FetchRemoteUuids";
inline constexpr char kGetBondState[] = "GetBondState";
inline constexpr char kConnectAllEnabledProfiles[] =
"ConnectAllEnabledProfiles";
inline constexpr char kDisconnectAllEnabledProfiles[] =
"DisconnectAllEnabledProfiles";
inline constexpr char kRegisterCallback[] = "RegisterCallback";
inline constexpr char kUnregisterCallback[] = "UnregisterCallback";
inline constexpr char kRegisterConnectionCallback[] =
"RegisterConnectionCallback";
inline constexpr char kUnregisterConnectionCallback[] =
"UnregisterConnectionCallback";
inline constexpr char kRegisterScanner[] = "RegisterScanner";
inline constexpr char kUnregisterScanner[] = "UnregisterScanner";
inline constexpr char kRegisterScannerCallback[] = "RegisterScannerCallback";
inline constexpr char kUnregisterScannerCallback[] =
"UnregisterScannerCallback";
inline constexpr char kStartScan[] = "StartScan";
inline constexpr char kStopScan[] = "StopScan";
inline constexpr char kSetPairingConfirmation[] = "SetPairingConfirmation";
inline constexpr char kSetPin[] = "SetPin";
inline constexpr char kSetPasskey[] = "SetPasskey";
inline constexpr char kGetBondedDevices[] = "GetBondedDevices";
inline constexpr char kGetConnectedDevices[] = "GetConnectedDevices";
inline constexpr char kSdpSearch[] = "SdpSearch";
inline constexpr char kCreateSdpRecord[] = "CreateSdpRecord";
inline constexpr char kRemoveSdpRecord[] = "RemoveSdpRecord";
inline constexpr char kGetSupportedRoles[] = "GetSupportedRoles";
// TODO(abps) - Rename this to AdapterCallback in platform and here
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.BluetoothCallback";
inline constexpr char kConnectionCallbackInterface[] =
"org.chromium.bluetooth.BluetoothConnectionCallback";
inline constexpr char kOnAdapterPropertyChanged[] = "OnAdapterPropertyChanged";
inline constexpr char kOnAddressChanged[] = "OnAddressChanged";
inline constexpr char kOnNameChanged[] = "OnNameChanged";
inline constexpr char kOnDiscoverableChanged[] = "OnDiscoverableChanged";
inline constexpr char kOnDeviceFound[] = "OnDeviceFound";
inline constexpr char kOnDeviceCleared[] = "OnDeviceCleared";
inline constexpr char kOnDeviceKeyMissing[] = "OnDeviceKeyMissing";
inline constexpr char kOnDevicePropertiesChanged[] =
"OnDevicePropertiesChanged";
inline constexpr char kOnDiscoveringChanged[] = "OnDiscoveringChanged";
inline constexpr char kOnSspRequest[] = "OnSspRequest";
inline constexpr char kOnPinDisplay[] = "OnPinDisplay";
inline constexpr char kOnPinRequest[] = "OnPinRequest";
inline constexpr char kOnBondStateChanged[] = "OnBondStateChanged";
inline constexpr char kOnSdpSearchComplete[] = "OnSdpSearchComplete";
inline constexpr char kOnSdpRecordCreated[] = "OnSdpRecordCreated";
inline constexpr char kOnDeviceConnected[] = "OnDeviceConnected";
inline constexpr char kOnDeviceDisconnected[] = "OnDeviceDisconnected";
inline constexpr char kOnDeviceConnectionFailed[] = "OnDeviceConnectionFailed";
inline constexpr char kOnScannerRegistered[] = "OnScannerRegistered";
inline constexpr char kOnScanResult[] = "OnScanResult";
inline constexpr char kOnAdvertisementFound[] = "OnAdvertisementFound";
inline constexpr char kOnAdvertisementLost[] = "OnAdvertisementLost";
} // namespace adapter
namespace manager {
inline constexpr char kStart[] = "Start";
inline constexpr char kStop[] = "Stop";
inline constexpr char kGetAdapterEnabled[] = "GetAdapterEnabled";
inline constexpr char kGetFlossEnabled[] = "GetFlossEnabled";
inline constexpr char kSetFlossEnabled[] = "SetFlossEnabled";
inline constexpr char kGetState[] = "GetState";
inline constexpr char kGetAvailableAdapters[] = "GetAvailableAdapters";
inline constexpr char kGetDefaultAdapter[] = "GetDefaultAdapter";
inline constexpr char kSetDesiredDefaultAdapter[] = "SetDesiredDefaultAdapter";
inline constexpr char kGetFlossApiVersion[] = "GetFlossApiVersion";
inline constexpr char kRegisterCallback[] = "RegisterCallback";
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.ManagerCallback";
inline constexpr char kOnHciDeviceChanged[] = "OnHciDeviceChanged";
inline constexpr char kOnHciEnabledChanged[] = "OnHciEnabledChanged";
inline constexpr char kOnDefaultAdapterChanged[] = "OnDefaultAdapterChanged";
} // namespace manager
namespace socket_manager {
inline constexpr char kRegisterCallback[] = "RegisterCallback";
inline constexpr char kUnregisterCallback[] = "UnregisterCallback";
inline constexpr char kListenUsingInsecureL2capChannel[] =
"ListenUsingInsecureL2capChannel";
inline constexpr char kListenUsingInsecureL2capLeChannel[] =
"ListenUsingInsecureL2capLeChannel";
inline constexpr char kListenUsingInsecureRfcommWithServiceRecord[] =
"ListenUsingInsecureRfcommWithServiceRecord";
inline constexpr char kListenUsingL2capChannel[] = "ListenUsingL2capChannel";
inline constexpr char kListenUsingL2capLeChannel[] =
"ListenUsingL2capLeChannel";
inline constexpr char kListenUsingRfcomm[] = "ListenUsingRfcomm";
inline constexpr char kListenUsingRfcommWithServiceRecord[] =
"ListenUsingRfcommWithServiceRecord";
inline constexpr char kCreateInsecureL2capChannel[] =
"CreateInsecureL2capChannel";
inline constexpr char kCreateInsecureL2capLeChannel[] =
"CreateInsecureL2capLeChannel";
inline constexpr char kCreateInsecureRfcommSocketToServiceRecord[] =
"CreateInsecureRfcommSocketToServiceRecord";
inline constexpr char kCreateL2capChannel[] = "CreateL2capChannel";
inline constexpr char kCreateL2capLeChannel[] = "CreateL2capLeChannel";
inline constexpr char kCreateRfcommSocketToServiceRecord[] =
"CreateRfcommSocketToServiceRecord";
inline constexpr char kAccept[] = "Accept";
inline constexpr char kClose[] = "Close";
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.SocketManagerCallback";
inline constexpr char kOnIncomingSocketReady[] = "OnIncomingSocketReady";
inline constexpr char kOnIncomingSocketClosed[] = "OnIncomingSocketClosed";
inline constexpr char kOnHandleIncomingConnection[] =
"OnHandleIncomingConnection";
inline constexpr char kOnOutgoingConnectionResult[] =
"OnOutgoingConnectionResult";
} // namespace socket_manager
namespace gatt {
inline constexpr char kRegisterClient[] = "RegisterClient";
inline constexpr char kUnregisterClient[] = "UnregisterClient";
inline constexpr char kClientConnect[] = "ClientConnect";
inline constexpr char kClientDisconnect[] = "ClientDisconnect";
inline constexpr char kRefreshDevice[] = "RefreshDevice";
inline constexpr char kDiscoverServices[] = "DiscoverServices";
inline constexpr char kDiscoverServiceByUuid[] = "DiscoverServiceByUuid";
inline constexpr char kReadCharacteristic[] = "ReadCharacteristic";
inline constexpr char kReadUsingCharacteristicUuid[] =
"ReadUsingCharacteristicUuid";
inline constexpr char kWriteCharacteristic[] = "WriteCharacteristic";
inline constexpr char kReadDescriptor[] = "ReadDescriptor";
inline constexpr char kWriteDescriptor[] = "WriteDescriptor";
inline constexpr char kRegisterForNotification[] = "RegisterForNotification";
inline constexpr char kBeginReliableWrite[] = "BeginReliableWrite";
inline constexpr char kEndReliableWrite[] = "EndReliableWrite";
inline constexpr char kReadRemoteRssi[] = "ReadRemoteRssi";
inline constexpr char kConfigureMtu[] = "ConfigureMtu";
inline constexpr char kConnectionParameterUpdate[] =
"ConnectionParameterUpdate";
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.BluetoothGattCallback";
inline constexpr char kServerCallbackInterface[] =
"org.chromium.bluetooth.BluetoothGattServerCallback";
inline constexpr char kOnClientRegistered[] = "OnClientRegistered";
inline constexpr char kOnClientConnectionState[] = "OnClientConnectionState";
inline constexpr char kOnPhyUpdate[] = "OnPhyUpdate";
inline constexpr char kOnPhyRead[] = "OnPhyRead";
inline constexpr char kOnSearchComplete[] = "OnSearchComplete";
inline constexpr char kOnCharacteristicRead[] = "OnCharacteristicRead";
inline constexpr char kOnCharacteristicWrite[] = "OnCharacteristicWrite";
inline constexpr char kOnExecuteWrite[] = "OnExecuteWrite";
inline constexpr char kOnDescriptorRead[] = "OnDescriptorRead";
inline constexpr char kOnDescriptorWrite[] = "OnDescriptorWrite";
inline constexpr char kOnNotify[] = "OnNotify";
inline constexpr char kOnReadRemoteRssi[] = "OnReadRemoteRssi";
inline constexpr char kOnConfigureMtu[] = "OnConfigureMtu";
inline constexpr char kOnConnectionUpdated[] = "OnConnectionUpdated";
inline constexpr char kOnServiceChanged[] = "OnServiceChanged";
inline constexpr char kRegisterServer[] = "RegisterServer";
inline constexpr char kUnregisterServer[] = "UnregisterServer";
inline constexpr char kServerConnect[] = "ServerConnect";
inline constexpr char kServerDisconnect[] = "ServerDisconnect";
inline constexpr char kServerSetPreferredPhy[] = "ServerSetPreferredPhy";
inline constexpr char kServerReadPhy[] = "ServerReadPhy";
inline constexpr char kAddService[] = "AddService";
inline constexpr char kRemoveService[] = "RemoveService";
inline constexpr char kClearServices[] = "ClearServices";
inline constexpr char kSendResponse[] = "SendResponse";
inline constexpr char kServerSendNotification[] = "SendNotification";
inline constexpr char kOnServerRegistered[] = "OnServerRegistered";
inline constexpr char kOnServerConnectionState[] = "OnServerConnectionState";
inline constexpr char kOnServerServiceAdded[] = "OnServiceAdded";
inline constexpr char kOnServerServiceRemoved[] = "OnServiceRemoved";
inline constexpr char kOnServerCharacteristicReadRequest[] =
"OnCharacteristicReadRequest";
inline constexpr char kOnServerDescriptorReadRequest[] =
"OnDescriptorReadRequest";
inline constexpr char kOnServerCharacteristicWriteRequest[] =
"OnCharacteristicWriteRequest";
inline constexpr char kOnServerDescriptorWriteRequest[] =
"OnDescriptorWriteRequest";
inline constexpr char kOnServerNotificationSent[] = "OnNotificationSent";
inline constexpr char kOnServerMtuChanged[] = "OnMtuChanged";
inline constexpr char kOnServerSubrateChange[] = "OnSubrateChange";
} // namespace gatt
namespace advertiser {
inline constexpr char kRegisterCallback[] = "RegisterAdvertiserCallback";
inline constexpr char kUnregisterCallback[] = "UnregisterAdvertiserCallback";
inline constexpr char kStartAdvertisingSet[] = "StartAdvertisingSet";
inline constexpr char kStopAdvertisingSet[] = "StopAdvertisingSet";
inline constexpr char kGetOwnAddress[] = "GetOwnAddress";
inline constexpr char kEnableAdvertisingSet[] = "EnableAdvertisingSet";
inline constexpr char kSetAdvertisingData[] = "SetAdvertisingData";
inline constexpr char kSetScanResponseData[] = "SetScanResponseData";
inline constexpr char kSetAdvertisingParameters[] = "SetAdvertisingParameters";
inline constexpr char kSetPeriodicAdvertisingParameters[] =
"SetPeriodicAdvertisingParameters";
inline constexpr char kSetPeriodicAdvertisingData[] =
"SetPeriodicAdvertisingData";
inline constexpr char kSetPeriodicAdvertisingEnable[] =
"SetPeriodicAdvertisingEnable";
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.AdvertisingSetCallback";
inline constexpr char kOnAdvertisingSetStarted[] = "OnAdvertisingSetStarted";
inline constexpr char kOnOwnAddressRead[] = "OnOwnAddressRead";
inline constexpr char kOnAdvertisingSetStopped[] = "OnAdvertisingSetStopped";
inline constexpr char kOnAdvertisingEnabled[] = "OnAdvertisingEnabled";
inline constexpr char kOnAdvertisingDataSet[] = "OnAdvertisingDataSet";
inline constexpr char kOnScanResponseDataSet[] = "OnScanResponseDataSet";
inline constexpr char kOnAdvertisingParametersUpdated[] =
"OnAdvertisingParametersUpdated";
inline constexpr char kOnPeriodicAdvertisingParametersUpdated[] =
"OnPeriodicAdvertisingParametersUpdated";
inline constexpr char kOnPeriodicAdvertisingDataSet[] =
"OnPeriodicAdvertisingDataSet";
inline constexpr char kOnPeriodicAdvertisingEnabled[] =
"OnPeriodicAdvertisingEnabled";
} // namespace advertiser
namespace battery_manager {
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.BatteryManagerCallback";
inline constexpr char kRegisterBatteryCallback[] = "RegisterBatteryCallback";
inline constexpr char kUnregisterBatteryCallback[] =
"UnregisterBatteryCallback";
inline constexpr char kGetBatteryInformation[] = "GetBatteryInformation";
inline constexpr char kOnBatteryInfoUpdated[] = "OnBatteryInfoUpdated";
} // namespace battery_manager
namespace bluetooth_telephony {
inline constexpr char kSetPhoneOpsEnabled[] = "SetPhoneOpsEnabled";
} // namespace bluetooth_telephony
namespace admin {
inline constexpr char kRegisterCallback[] = "RegisterAdminPolicyCallback";
inline constexpr char kUnregisterCallback[] = "UnregisterAdminPolicyCallback";
inline constexpr char kCallbackInterface[] =
"org.chromium.bluetooth.AdminPolicyCallback";
inline constexpr char kOnServiceAllowlistChanged[] =
"OnServiceAllowlistChanged";
inline constexpr char kOnDevicePolicyEffectChanged[] =
"OnDevicePolicyEffectChanged";
inline constexpr char kSetAllowedServices[] = "SetAllowedServices";
inline constexpr char kGetAllowedServices[] = "GetAllowedServices";
inline constexpr char kGetDevicePolicyEffect[] = "GetDevicePolicyEffect";
inline constexpr char kSetSimpleSecurePairingEnabled[] = "SetAcceptSspRequest";
} // namespace admin
namespace adapter_logging {
inline constexpr char kIsDebugEnabled[] = "IsDebugEnabled";
inline constexpr char kSetDebugLogging[] = "SetDebugLogging";
} // namespace adapter_logging
namespace experimental {
inline constexpr char kSetLLPrivacy[] = "SetLLPrivacy";
} // namespace experimental
// BluetoothDevice structure for DBus apis.
struct DEVICE_BLUETOOTH_EXPORT FlossDeviceId {
std::string address;
std::string name;
inline bool operator==(const FlossDeviceId& rhs) const {
return address == rhs.address && name == rhs.name;
}
friend std::ostream& operator<<(std::ostream& os, const FlossDeviceId& id) {
return os << "FlossDeviceId(" << id.address << ", " << id.name << ")";
}
static const char kDeviceIdNameKey[];
static const char kDeviceIdAddressKey[];
};
// Represents an error sent through DBus.
//
// In a D-Bus message, error contains 2 parts: error name and error message.
// This is a structure to hold these error info and provides a utility for human
// readable representation.
struct DEVICE_BLUETOOTH_EXPORT Error {
Error(const std::string& name, const std::string& message);
// Presents the error as "<error name>: <error message>".
friend std::ostream& operator<<(std::ostream& os, const Error& error);
std::string ToString();
std::string name;
std::string message;
};
// Represents void return type of D-Bus (no return). Needed so that we can use
// "void" as a type in C++ templates.
// Needs to be exported because there are template instantiations using this.
struct DEVICE_BLUETOOTH_EXPORT Void {};
// Represents the result of D-Bus method call. A Floss method call returns
// either a data or a D-Bus error.
template <typename T>
using DBusResult = base::expected<T, Error>;
// A callback of Floss API method call. This encapsulates RPC-level status
// (in Floss case D-Bus status and return data parsing) so that each return can
// be either "ok" (contains T) or "error" (contains error name and message).
template <typename T>
using ResponseCallback = base::OnceCallback<void(DBusResult<T>)>;
// A Weakly Owned base::OnceCallback<void(T)>. The main usecase for this is to
// have a weak pointer available for |PostDelayedTask|, where deleting the main
// object will automatically cancel the posted task.
template <typename T>
class WeaklyOwnedCallback {
public:
explicit WeaklyOwnedCallback(base::OnceCallback<void(T)> cb)
: cb_(std::move(cb)) {}
~WeaklyOwnedCallback() = default;
static std::unique_ptr<WeaklyOwnedCallback> Create(
base::OnceCallback<void(T)> cb) {
return std::make_unique<WeaklyOwnedCallback>(std::move(cb));
}
// Creates a pointer to the callback which will run automatically after
// |timeout_ms| with given return value unless the callback is deleted.
static std::unique_ptr<WeaklyOwnedCallback> CreateWithTimeout(
base::OnceCallback<void(T)> cb,
int timeout_ms,
T error_ret) {
std::unique_ptr<WeaklyOwnedCallback> self = Create(std::move(cb));
self->PostDelayed(timeout_ms, error_ret);
return self;
}
// If the callback hasn't been executed, run it.
void Run(T ret) {
if (cb_) {
std::move(cb_).Run(std::move(ret));
}
}
base::WeakPtr<WeaklyOwnedCallback> GetWeakPtr() const {
return weak_ptr_factory_.GetWeakPtr();
}
private:
void PostDelayed(int timeout_ms, T error_ret) {
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&WeaklyOwnedCallback<T>::Run,
weak_ptr_factory_.GetWeakPtr(), error_ret),
base::Milliseconds(timeout_ms));
}
base::OnceCallback<void(T)> cb_;
base::WeakPtrFactory<WeaklyOwnedCallback> weak_ptr_factory_{this};
};
// A Weakly Owned ResponseCallback<T>.
template <typename T>
using WeaklyOwnedResponseCallback = WeaklyOwnedCallback<DBusResult<T>>;
struct DBusTypeInfo {
const std::string dbus_signature;
const std::string type_name;
};
// To minimize the overhead of constructing a struct, this function returns
// a const reference. Specialization implementations are recommended to return
// a statically allocated DBusTypeInfo for this reason.
template <typename T>
DEVICE_BLUETOOTH_EXPORT const DBusTypeInfo& GetDBusTypeInfo(const T*);
template <typename T>
const DBusTypeInfo& GetDBusTypeInfo(const std::vector<T>*) {
static const base::NoDestructor<DBusTypeInfo> elem_info(
GetDBusTypeInfo(static_cast<T*>(nullptr)));
static const base::NoDestructor<DBusTypeInfo> info{
{base::StrCat({"a", elem_info->dbus_signature}),
base::StrCat({"vector<", elem_info->type_name, ">"})}};
return *info;
}
template <typename T, typename U>
const DBusTypeInfo& GetDBusTypeInfo(const std::map<T, U>*) {
static const base::NoDestructor<DBusTypeInfo> key_info(
GetDBusTypeInfo(static_cast<T*>(nullptr)));
static const base::NoDestructor<DBusTypeInfo> val_info(
GetDBusTypeInfo(static_cast<U*>(nullptr)));
static const base::NoDestructor<DBusTypeInfo> info{
{base::StrCat(
{"a{", key_info->dbus_signature, val_info->dbus_signature, "}"}),
base::StrCat(
{"map<", key_info->type_name, ", ", val_info->type_name, ">"})}};
return *info;
}
template <typename T>
const DBusTypeInfo& GetDBusTypeInfo(const std::optional<T>*) {
static const base::NoDestructor<DBusTypeInfo> elem_info(
GetDBusTypeInfo(static_cast<T*>(nullptr)));
static const base::NoDestructor<DBusTypeInfo> info{
{"a{sv}", base::StrCat({"optional<", elem_info->type_name, ">"})}};
return *info;
}
// Restrict all access to DBus client initialization to FlossDBusManager so we
// can enforce the proper ordering of initialization and shutdowns.
class DEVICE_BLUETOOTH_EXPORT FlossDBusClient {
public:
// Adopted from bt_status_t in system/include/hardware/bluetooth.h
enum class BtifStatus : uint32_t {
kSuccess = 0,
kFail,
kNotReady,
kNomem,
kBusy,
kDone,
kUnsupported,
kParmInvalid,
kUnhandled,
kAuthFailure,
kRmtDevDown,
kAuthRejected,
kJniEnvironmentError,
kJniThreadAttachError,
kWakelockError,
kTimeout,
kDeviceNotFound,
kUnexpectedState,
kSocketError,
};
enum class BluetoothTransport {
kAuto = 0,
kBrEdr = 1,
kLe = 2,
};
// Error: DBus error.
static const char kErrorDBus[];
// Error: No response from bus.
static const char kErrorNoResponse[];
// Error: Invalid parameters.
static const char kErrorInvalidParameters[];
// Error: Invalid return.
static const char kErrorInvalidReturn[];
// Property key for std::optional dbus serialization.
static const char kOptionalValueKey[];
// Error: does not exist.
static const char DEVICE_BLUETOOTH_EXPORT kErrorDoesNotExist[];
// Convert adapter number to adapter object path.
static dbus::ObjectPath GenerateAdapterPath(int adapter_index);
// Convert adapter number to gatt object path.
static dbus::ObjectPath GenerateGattPath(int adapter_index);
// Convert adapter number to battery_manager object path.
static dbus::ObjectPath GenerateBatteryManagerPath(int adapter_index);
// Convert adapter number to bluetooth_telephony object path.
static dbus::ObjectPath GenerateBluetoothTelephonyPath(int adapter_index);
// Convert adapter number to admin object path.
static dbus::ObjectPath GenerateAdminPath(int adapter_index);
// Convert adapter number to logging object path.
static dbus::ObjectPath GenerateLoggingPath(int adapter_index);
// Convert Floss error codes to BluetoothDevice defined error codes.
static device::BluetoothDevice::ConnectErrorCode BtifStatusToConnectErrorCode(
FlossDBusClient::BtifStatus status);
// Generalized DBus serialization (used for generalized method call
// invocation).
template <typename T>
static void WriteDBusParam(dbus::MessageWriter* writer, const T& data);
// Generalized writer for container types using variants (i.e. a{sv}).
template <typename T>
static void WriteDBusParamIntoVariant(dbus::MessageWriter* writer,
const T& data) {
dbus::MessageWriter variant(nullptr);
writer->OpenVariant(GetDBusTypeInfo(&data).dbus_signature, &variant);
WriteDBusParam(&variant, data);
writer->CloseContainer(&variant);
}
// Generalized write for std::vector.
template <typename T>
static void WriteDBusParam(dbus::MessageWriter* writer,
const std::vector<T>& value) {
dbus::MessageWriter array_writer(nullptr);
writer->OpenArray(GetDBusTypeInfo(static_cast<T*>(nullptr)).dbus_signature,
&array_writer);
for (const auto& entry : value) {
WriteDBusParam<>(&array_writer, entry);
}
writer->CloseContainer(&array_writer);
}
// Generalized write for std::map.
template <typename T, typename U>
static void WriteDBusParam(dbus::MessageWriter* writer,
const std::map<T, U>& data) {
std::string signature = base::StrCat(
{"{", GetDBusTypeInfo(static_cast<T*>(nullptr)).dbus_signature,
GetDBusTypeInfo(static_cast<U*>(nullptr)).dbus_signature, "}"});
dbus::MessageWriter array(nullptr);
writer->OpenArray(signature, &array);
for (auto const& [key, val] : data) {
dbus::MessageWriter dict(nullptr);
array.OpenDictEntry(&dict);
WriteDBusParam<>(&dict, key);
WriteDBusParam<>(&dict, val);
array.CloseContainer(&dict);
}
writer->CloseContainer(&array);
}
// Specialized write for base::span<const uint8_t>.
static void WriteDBusParam(dbus::MessageWriter* writer,
base::span<const uint8_t> value) {
writer->AppendArrayOfBytes(value);
}
// Optional container type needs to be explicitly listed here.
template <typename T>
static void WriteDBusParam(dbus::MessageWriter* writer,
const std::optional<T>& data) {
dbus::MessageWriter array(nullptr);
dbus::MessageWriter dict(nullptr);
writer->OpenArray("{sv}", &array);
// Only serialize optional value if it exists.
if (data) {
array.OpenDictEntry(&dict);
dict.AppendString(kOptionalValueKey);
WriteDBusParamIntoVariant<T>(&dict, *data);
array.CloseContainer(&dict);
}
writer->CloseContainer(&array);
}
template <typename T>
static void WriteDBusParamIntoVariant(dbus::MessageWriter* writer,
const std::optional<T>& data) {
dbus::MessageWriter variant(nullptr);
writer->OpenVariant("a{sv}", &variant);
WriteDBusParam(&variant, data);
writer->CloseContainer(&variant);
}
// Base case for variadic write.
static void WriteAllDBusParams(dbus::MessageWriter* writer) {}
// Variadic write method that expands to multiple WriteDBusParam calls.
template <typename T, typename... Args>
static void WriteAllDBusParams(dbus::MessageWriter* writer,
const T& first,
const Args&... args) {
WriteDBusParam(writer, first);
WriteAllDBusParams(writer, args...);
}
template <typename T>
static void WriteDictEntry(dbus::MessageWriter* writer,
const std::string& key,
const T& value) {
dbus::MessageWriter dict(nullptr);
writer->OpenDictEntry(&dict);
dict.AppendString(key);
WriteDBusParamIntoVariant(&dict, value);
writer->CloseContainer(&dict);
}
// Generalized DBus deserialization (used for generalized method call returns
// and can be used for exported methods as well). Implement for each type that
// you want deserialized.
template <typename T>
static bool ReadDBusParam(dbus::MessageReader* reader, T* value);
// Generalized reader for container types using variants (i.e. a{sv}).
template <typename T>
static bool ReadDBusParamFromVariant(dbus::MessageReader* reader, T* value) {
dbus::MessageReader variant_reader(nullptr);
if (!reader->PopVariant(&variant_reader)) {
return false;
}
return ReadDBusParam(&variant_reader, value);
}
// Specialization for vector of anything.
template <typename T>
static bool ReadDBusParam(dbus::MessageReader* reader,
std::vector<T>* value) {
dbus::MessageReader subreader(nullptr);
if (!reader->PopArray(&subreader))
return false;
while (subreader.HasMoreData()) {
T element;
if (!ReadDBusParam<>(&subreader, &element))
return false;
value->emplace_back(std::move(element));
}
return true;
}
// Specialization for std::map.
template <typename T, typename U>
static bool ReadDBusParam(dbus::MessageReader* reader, std::map<T, U>* data) {
dbus::MessageReader array_reader(nullptr);
if (!reader->PopArray(&array_reader))
return false;
while (array_reader.HasMoreData()) {
dbus::MessageReader dict_entry_reader(nullptr);
if (!array_reader.PopDictEntry(&dict_entry_reader))
return false;
T key;
U value;
if (!ReadDBusParam<>(&dict_entry_reader, &key) ||
!ReadDBusParam<>(&dict_entry_reader, &value))
return false;
data->insert({key, value});
}
return true;
}
// Optional container type needs to be explicitly implemented here.
template <typename T>
static bool ReadDBusParam(dbus::MessageReader* reader,
std::optional<T>* value) {
dbus::MessageReader array(nullptr);
dbus::MessageReader dict(nullptr);
T inner;
if (!reader->PopArray(&array)) {
return false;
}
while (array.PopDictEntry(&dict)) {
std::string key;
dict.PopString(&key);
if (key == kOptionalValueKey) {
if (!ReadDBusParamFromVariant<T>(&dict, &inner)) {
return false;
}
*value = std::move(std::optional<T>(std::move(inner)));
}
}
return true;
}
// Base case for variadic read.
static bool ReadAllDBusParams(dbus::MessageReader* reader) { return true; }
// Variadic read method that expands to multiple ReadDBusParam calls.
// Individual calls to |ReadDBusParam| must succeed before the next call is
// done.
template <typename T, typename... Args>
static bool ReadAllDBusParams(dbus::MessageReader* reader,
T* first,
Args*... args) {
return ReadDBusParam(reader, first) && ReadAllDBusParams(reader, args...);
}
template <typename T>
using FieldReader = std::function<bool(dbus::MessageReader*, T* data)>;
// Useful to generate D-Bus reader of a struct. Example usage:
//
// template <>
// bool FlossDBusClient::ReadDBusParam(dbus::MessageReader* reader,
// ScanResult* scan_result) {
// static StructReader<ScanResult> struct_reader({
// {"address", CreateFieldReader(&ScanResult::address)},
// {"addr_type", CreateFieldReader(&ScanResult::addr_type)},
// <just define more fields here>
// });
// return struct_reader.ReadDBusParam(reader, scan_result);
// }
template <typename T>
class StructReader {
private:
std::unordered_map<std::string, FieldReader<T>> fields_;
public:
explicit StructReader(
std::vector<std::pair<std::string, FieldReader<T>>> fields) {
for (auto const& kv : fields) {
fields_.insert(kv);
}
}
bool ReadDBusParam(dbus::MessageReader* reader, T* data) {
// Keep track of parsed fields to detect missing and duplicate fields.
std::unordered_set<std::string> parsed_fields;
dbus::MessageReader array_reader(nullptr);
if (!reader->PopArray(&array_reader))
return false;
// For each dictionary entry
while (array_reader.HasMoreData()) {
dbus::MessageReader entry_reader(nullptr);
if (!array_reader.PopDictEntry(&entry_reader))
return false;
std::string key;
if (!entry_reader.PopString(&key))
return false;
if (base::Contains(fields_, key)) {
dbus::MessageReader variant_reader(nullptr);
entry_reader.PopVariant(&variant_reader);
if (!fields_[key](&variant_reader, data))
return false;
if (base::Contains(parsed_fields, key))
return false;
parsed_fields.insert(key);
} else {
DBusTypeInfo type_info = GetDBusTypeInfo(data);
VLOG(3) << "Does not know how to read field " << type_info.type_name
<< "." << key;
}
}
// All defined fields are required.
return parsed_fields.size() == fields_.size();
}
};
// S is the type of the container struct.
// T is the type of the field.
template <typename S, typename T>
static FieldReader<S> CreateFieldReader(T S::*field) {
return [field](dbus::MessageReader* reader, S* container) -> bool {
return FlossDBusClient::ReadDBusParam(reader, &(container->*field));
};
}
template <typename R, typename... Args>
void CallMethod(ResponseCallback<R> callback,
dbus::Bus* bus,
const std::string& service_name,
const std::string& interface_name,
const dbus::ObjectPath& object_path,
const char* method_name,
Args... args) {
if (bus == nullptr) {
LOG(ERROR) << "D-Bus is not initialized, cannot call method "
<< method_name << " on " << object_path.value();
std::move(callback).Run(base::unexpected(
Error(std::string(kErrorDBus), "DBus not initialized")));
return;
}
dbus::ObjectProxy* object_proxy =
bus->GetObjectProxy(service_name, object_path);
if (!object_proxy) {
VLOG(1) << "Object proxy does not exist when trying to call "
<< method_name;
std::move(callback).Run(base::unexpected(
Error(std::string(kErrorDBus), "Invalid object proxy")));
return;
}
dbus::MethodCall method_call(interface_name, method_name);
dbus::MessageWriter writer(&method_call);
FlossDBusClient::WriteAllDBusParams(&writer, args...);
object_proxy->CallMethodWithErrorResponse(
&method_call, kDBusTimeoutMs,
base::BindOnce(&FlossDBusClient::DefaultResponseWithCallback<R>,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
FlossDBusClient(const FlossDBusClient&) = delete;
FlossDBusClient& operator=(const FlossDBusClient&) = delete;
// Common init signature for all clients. |on_ready| should be called once the
// client is ready to be used.
virtual void Init(dbus::Bus* bus,
const std::string& bluetooth_service_name,
const int adapter_index,
base::Version version,
base::OnceClosure on_ready) = 0;
protected:
// Convert a dbus::ErrorResponse into a floss::Error struct.
static Error ErrorResponseToError(const std::string& default_name,
const std::string& default_message,
dbus::ErrorResponse* error);
FlossDBusClient();
virtual ~FlossDBusClient();
// Log a dbus::ErrorResponse.
void LogErrorResponse(const std::string& message, dbus::ErrorResponse* error);
// Default handler that runs |callback| with the callback with an optional
// return and optional error.
template <typename T>
void DefaultResponseWithCallback(ResponseCallback<T> callback,
dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (response) {
T ret;
dbus::MessageReader reader(response);
if (!FlossDBusClient::ReadAllDBusParams<T>(&reader, &ret)) {
LOG(ERROR) << "Failed reading return from response";
std::move(callback).Run(
base::unexpected(Error(kErrorInvalidReturn, "")));
return;
}
std::move(callback).Run(ret);
return;
}
std::move(callback).Run(base::unexpected(ErrorResponseToError(
kErrorNoResponse, /*default_message=*/std::string(), error_response)));
}
// Default handler for a response. It will either log the error response or
// print |caller| to VLOG. |caller| should be the name of the DBus method that
// is being called.
void DefaultResponse(const std::string& caller,
dbus::Response* response,
dbus::ErrorResponse* error_response);
// API version.
base::Version version_;
private:
base::WeakPtrFactory<FlossDBusClient> weak_ptr_factory_{this};
};
// Utility to keep a property that takes care of getting the initial value,
// monitoring for updates, and notifying when value is updated.
//
// In Floss API, it is a pattern to abstract a value as a "property". These
// values always have:
// * A getter: A method exposed by Floss daemon to get current value.
// * An update callback: A method to be called by Floss daemon to client when
// the value is updated.
//
// To simplify repetitive code performing common operations above, use this
// utility by just specifying the property getter, update method, and the
// interface names.
//
// |T| is the type of the property.
template <typename T>
class FlossProperty {
public:
// Instantiates a property, given:
// |interface| - The D-Bus interface of the getter.
// |callback_interface| - The D-Bus interface of the value update method.
// |getter| - The method name of the getter.
// |on_update| - The method name of the value update.
FlossProperty(const char* interface,
const char* callback_interface,
const char* getter,
const char* on_update)
: interface_(interface),
callback_interface_(callback_interface),
getter_(getter),
on_update_(on_update) {}
// Initializes the property. Once Init-ed, it takes care of getting the
// initial value, keeping it updated, notifying when there is an update.
// |bus| - D-Bus connection.
// |service_name| - Floss daemon D-Bus name.
// |path| - Object path where the getter is available.
// |callback_path| - Object path where the daemon calls back on value updates.
// |update_callback| - Caller can provide this to be notified when there are
// updates.
void Init(FlossDBusClient* client,
dbus::Bus* bus,
const std::string& service_name,
const dbus::ObjectPath& path,
const dbus::ObjectPath& callback_path,
base::RepeatingCallback<void(const T&)> update_callback) {
update_callback_ = update_callback;
// Get the initial value.
client->CallMethod(base::BindOnce(&FlossProperty::OnGetInitialValue,
weak_ptr_factory_.GetWeakPtr()),
bus, service_name, interface_, path, getter_);
if (!on_update_) {
return;
}
// Listen for property updates.
dbus::ExportedObject* exported_object =
bus->GetExportedObject(callback_path);
if (!exported_object) {
LOG(ERROR) << "Could not export callback to listen for property updates"
<< callback_path.value();
return;
}
exported_object->ExportMethod(
callback_interface_, on_update_,
base::BindRepeating(&FlossProperty::OnValueUpdated,
weak_ptr_factory_.GetWeakPtr()),
base::DoNothing());
}
// Returns the current value of this property.
const T& Get() const { return value_; }
private:
void OnGetInitialValue(DBusResult<T> ret) {
if (!ret.has_value()) {
LOG(ERROR) << "Error getting initial value";
return;
}
UpdateValue(std::move(*ret));
}
void OnValueUpdated(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
T data;
dbus::MessageReader reader(method_call);
if (!FlossDBusClient::ReadDBusParam(&reader, &data)) {
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, floss::FlossDBusClient::kErrorInvalidParameters,
"Error parsing property value"));
return;
}
UpdateValue(std::move(data));
std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
}
void UpdateValue(T val) {
value_ = std::move(val);
update_callback_.Run(value_);
}
// Interfaces.
const char* interface_;
const char* callback_interface_;
// Method name to call to Floss daemon to get property value.
const char* getter_;
// Method name called by Floss daemon to us to notify updates.
// Null means the property value never changes.
const char* on_update_;
// Keeps the property value.
T value_ = T();
// To update caller when property is updated.
base::RepeatingCallback<void(const T&)> update_callback_;
// WeakPtrFactory must be last.
base::WeakPtrFactory<FlossProperty> weak_ptr_factory_{this};
};
} // namespace floss
#endif // DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_CLIENT_H_