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