| // Copyright 2021 The Chromium Authors. All rights reserved. |
| // 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 <ostream> |
| #include <string> |
| |
| #include "base/callback.h" |
| #include "base/logging.h" |
| #include "base/types/expected.h" |
| #include "dbus/bus.h" |
| #include "dbus/message.h" |
| #include "dbus/object_proxy.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "device/bluetooth/bluetooth_export.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace floss { |
| |
| extern DEVICE_BLUETOOTH_EXPORT int kDBusTimeoutMs; |
| |
| // TODO(b/189499077) - Expose via floss package |
| extern DEVICE_BLUETOOTH_EXPORT const char kAdapterService[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kManagerService[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kAdapterInterface[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kManagerInterface[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kManagerObject[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kAdapterObjectFormat[]; |
| |
| namespace adapter { |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetAddress[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetName[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetName[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetDiscoverable[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetDiscoverable[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kStartDiscovery[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kCancelDiscovery[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kCreateBond[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kCancelBondProcess[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kRemoveBond[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetRemoteType[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetRemoteClass[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetConnectionState[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetRemoteUuids[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetBondState[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kConnectAllEnabledProfiles[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kDisconnectAllEnabledProfiles[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kRegisterCallback[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kRegisterConnectionCallback[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kCallbackInterface[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kConnectionCallbackInterface[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetPairingConfirmation[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetPin[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetPasskey[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetBondedDevices[]; |
| |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnAdapterPropertyChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnAddressChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnNameChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDiscoverableChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceFound[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceCleared[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDiscoveringChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnSspRequest[]; |
| |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnBondStateChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceConnected[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceDisconnected[]; |
| } // namespace adapter |
| |
| namespace manager { |
| extern DEVICE_BLUETOOTH_EXPORT const char kStart[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kStop[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetFlossEnabled[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kSetFlossEnabled[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetState[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kGetAvailableAdapters[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kRegisterCallback[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kCallbackInterface[]; |
| |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnHciDeviceChanged[]; |
| extern DEVICE_BLUETOOTH_EXPORT const char kOnHciEnabledChanged[]; |
| } // namespace manager |
| |
| // 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 ResponseCallback<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(ResponseCallback<T> cb) : cb_(std::move(cb)) {} |
| ~WeaklyOwnedCallback() = default; |
| |
| static std::unique_ptr<WeaklyOwnedCallback> Create(ResponseCallback<T> cb) { |
| return std::make_unique<WeaklyOwnedCallback>(std::move(cb)); |
| } |
| |
| // If the callback hasn't been executed, run it and return true. Otherwise |
| // false. |
| bool Run(DBusResult<T> ret) { |
| if (cb_) { |
| std::move(cb_).Run(std::move(ret)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| base::WeakPtr<WeaklyOwnedCallback> GetWeakPtr() const { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| private: |
| ResponseCallback<T> cb_; |
| base::WeakPtrFactory<WeaklyOwnedCallback> weak_ptr_factory_{this}; |
| }; |
| |
| // Restrict all access to DBus client initialization to FlossDBusManager so we |
| // can enforce the proper ordering of initialization and shutdowns. |
| class FlossDBusClient { |
| public: |
| // Error: DBus error. |
| static const char DEVICE_BLUETOOTH_EXPORT kErrorDBus[]; |
| |
| // Error: No response from bus. |
| static const char DEVICE_BLUETOOTH_EXPORT kErrorNoResponse[]; |
| |
| // Error: Invalid parameters. |
| static const char DEVICE_BLUETOOTH_EXPORT kErrorInvalidParameters[]; |
| |
| // Error: Invalid return. |
| static const char DEVICE_BLUETOOTH_EXPORT kErrorInvalidReturn[]; |
| |
| // Error: does not exist. |
| static const char DEVICE_BLUETOOTH_EXPORT kErrorDoesNotExist[]; |
| |
| // Generalized DBus serialization (used for generalized method call |
| // invocation). |
| template <typename T> |
| static void DEVICE_BLUETOOTH_EXPORT |
| WriteDBusParam(dbus::MessageWriter* writer, const T& data); |
| |
| // Base case for variadic write. |
| static void DEVICE_BLUETOOTH_EXPORT |
| WriteAllDBusParams(dbus::MessageWriter* writer) {} |
| |
| // Variadic write method that expands to multiple WriteDBusParam calls. |
| template <typename T, typename... Args> |
| static void DEVICE_BLUETOOTH_EXPORT |
| WriteAllDBusParams(dbus::MessageWriter* writer, |
| const T& first, |
| const Args&... args) { |
| WriteDBusParam(writer, first); |
| WriteAllDBusParams(writer, args...); |
| } |
| |
| // 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 DEVICE_BLUETOOTH_EXPORT ReadDBusParam(dbus::MessageReader* reader, |
| T* value); |
| |
| // Container type needs to be explicitly listed here. |
| template <typename T> |
| static bool DEVICE_BLUETOOTH_EXPORT ReadDBusParam(dbus::MessageReader* reader, |
| std::vector<T>* value); |
| |
| // Base case for variadic read. |
| static bool DEVICE_BLUETOOTH_EXPORT |
| 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 DEVICE_BLUETOOTH_EXPORT |
| ReadAllDBusParams(dbus::MessageReader* reader, T* first, Args*... args) { |
| return ReadDBusParam(reader, first) && ReadAllDBusParams(reader, args...); |
| } |
| |
| 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) { |
| LOG(ERROR) << "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>, |
| base::Unretained(this), std::move(callback))); |
| } |
| |
| FlossDBusClient(const FlossDBusClient&) = delete; |
| FlossDBusClient& operator=(const FlossDBusClient&) = delete; |
| |
| // Common init signature for all clients. |
| virtual void Init(dbus::Bus* bus, |
| const std::string& bluetooth_service_name, |
| const std::string& bluetooth_adapter_path) = 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 DEVICE_BLUETOOTH_EXPORT |
| DefaultResponseWithCallback(ResponseCallback<T> callback, |
| dbus::Response* response, |
| dbus::ErrorResponse* 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); |
| }; |
| |
| } // namespace floss |
| |
| #endif // DEVICE_BLUETOOTH_FLOSS_FLOSS_DBUS_CLIENT_H_ |