| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef REMOTING_HOST_LINUX_GDBUS_CONNECTION_REF_H_ |
| #define REMOTING_HOST_LINUX_GDBUS_CONNECTION_REF_H_ |
| |
| #include <gio/gio.h> |
| #include <glib-object.h> |
| #include <glib.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/types/expected.h" |
| #include "remoting/host/base/loggable.h" |
| #include "remoting/host/linux/dbus_interfaces/org_freedesktop_DBus_Properties.h" |
| #include "remoting/host/linux/gdbus_fd_list.h" |
| #include "remoting/host/linux/gvariant_ref.h" |
| #include "remoting/host/linux/gvariant_type.h" |
| #include "ui/base/glib/scoped_gobject.h" |
| |
| namespace remoting { |
| |
| // A wrapper around a GDBusConnection providing Chromium-style async callbacks |
| // using GVariantRef for type-safe GVariant handling. |
| // |
| // Like with GDBusConnection, calls can be made from any thread. The reply |
| // callback will be invoke via the default task runner of the caller's virtual |
| // thread, as obtained by base::SequencedTaskRunner::GetCurrentDefault(). |
| class GDBusConnectionRef { |
| public: |
| // Callback to receive the connection created by one of the Create* methods. |
| using CreateCallback = |
| base::OnceCallback<void(base::expected<GDBusConnectionRef, Loggable>)>; |
| // Callback to receive the result of a method call. |
| template <typename ReturnType> |
| using CallCallback = |
| base::OnceCallback<void(base::expected<ReturnType, Loggable>)>; |
| // Callback to receive the result of a method call that returns one or more |
| // file descriptors. |
| template <typename ReturnType> |
| using CallFdCallback = base::OnceCallback<void( |
| base::expected<std::pair<ReturnType, GDBusFdList>, Loggable>)>; |
| // Callback to receive subscribed signal messages. |
| template <typename ArgType> |
| using SignalCallback = base::RepeatingCallback<void(ArgType arguments)>; |
| // Can be passed in lieu of SignalCallback when details of the source of the |
| // signal are needed (e.g., when registering the same callback for multiple |
| // signals). |
| template <typename ArgType> |
| using DetailedSignalCallback = |
| base::RepeatingCallback<void(std::string sender, |
| gvariant::ObjectPath object_path, |
| std::string interface_name, |
| std::string signal_name, |
| ArgType arguments)>; |
| |
| // Returned from SignalSubscribe. Dropping will free the callback and cancel |
| // the underlying subscription. Must be dropped on the sequence where it was |
| // created. |
| class SignalSubscription; |
| |
| // The only valid operation for a default-constructed GDBusConnectionRef is |
| // assigning a connection to it. |
| GDBusConnectionRef(); |
| GDBusConnectionRef(const GDBusConnectionRef& other); |
| GDBusConnectionRef(GDBusConnectionRef&& other); |
| GDBusConnectionRef& operator=(const GDBusConnectionRef& other); |
| GDBusConnectionRef& operator=(GDBusConnectionRef&& other); |
| ~GDBusConnectionRef(); |
| |
| // Create from an existing connection. |
| explicit GDBusConnectionRef(ScopedGObject<GDBusConnection> connection); |
| |
| // Asynchronously tries to create an instance for the session bus and invokes |
| // callback with the result. |
| static void CreateForSessionBus(CreateCallback callback); |
| |
| // Asynchronously tries to create an instance for the system bus and invokes |
| // callback with the result. |
| static void CreateForSystemBus(CreateCallback callback); |
| |
| // Returns whether this instance is initialized for use (not default |
| // constructed or moved from). |
| bool is_initialized() const; |
| |
| // Obtains the underlying GDBusConnection pointer. |
| inline GDBusConnection* raw() const { return connection_.get(); } |
| |
| // Dynamically-checked overloads. Type mismatches reported at run time. |
| |
| // Asynchronously invoke the provided method, checking types at run time. |
| // |
| // bus_name - The owner of the object on which to call the method. May be a |
| // unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object on which to call the method. |
| // interface_name - The interface the method is a part of. |
| // method_name - The name of the method to call. |
| // arguments - The arguments to pass to the method. The method call will fail |
| // if the arguments don't match the actual parameters expected by the |
| // method. |
| // callback - A callback to invoke with the result of the method call, or an |
| // error if something goes wrong. If the actual return type can't be |
| // converted to ReturnType, an error will be returned (but note that in |
| // this case, the method was still executed.) A ReturnType of |
| // GVariantRef<"r"> can accept any return value. |
| // flags - See https://docs.gtk.org/gio/flags.DBusCallFlags.html |
| // timeout_msec - Timeout for the call in milliseconds. Pass -1 to use the |
| // default, or G_MAXINT for no timeout. |
| template <typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| }; |
| |
| // Variant of dynamically-checked Call that can return one or more file |
| // descriptors in the response. |
| template <typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| }; |
| |
| // Variant of dynamically-checked Call that can accept one or more file |
| // descriptors to be sent along with the call. |
| template <typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| }; |
| |
| // Variant of dynamically-checked Call that can accept one or more file |
| // descriptors to be sent along with the call and can return one or more file |
| // descriptors in the response. |
| template <typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| }; |
| |
| // Asynchronously retrieve the specified property value. |
| // |
| // bus_name - The owner of the object from which to retrieve the property. May |
| // be a unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object from which to retrieve the property. |
| // interface_name - The interface the property is a part of. |
| // property_name - The name of the property to retrieve. |
| // callback - A callback to invoke with the retrieved property value, or an |
| // error if something goes wrong. If the property value cannot be |
| // converted to ValueType, an error will be returned. Accept a |
| // GVariantRef<> to handle any value type. |
| template <typename ValueType> |
| void GetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* property_name, |
| CallCallback<ValueType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<> variant) { variant.TryInto<ValueType>(); }; |
| |
| // Asynchronously set the specified property value. |
| // |
| // bus_name - The owner of the object on which to set the property. May be a |
| // unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object on which to set the property. |
| // interface_name - The interface the property is a part of. |
| // property_name - The name of the property to set. |
| // value - The new property value. An error will be returned if the value is |
| // incompatible with the property. |
| // callback - A callback to invoke when the property has been set, or an error |
| // if something goes wrong. |
| template <typename ValueType> |
| void SetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* property_name, |
| const ValueType& value, |
| CallCallback<void> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires() { GVariantRef<>::TryFrom(value); }; |
| |
| // Subscribe to matching signals from the sender. |
| // |
| // bus_name - The sender from which to receive signals. May be a unique or |
| // well-known bus name. If this is a direct peer connection rather than a |
| // bus connection, pass nullptr. |
| // object_path - The remote object from which to receive signals. May be |
| // std::nullopt to receive signals from all objects owned by the sender. |
| // interface_name - The interface from which to receive signals. May be |
| // nullptr to receive signals from all interfaces. |
| // signal_name - The name of the signals to receive. May be nullptr to |
| // receive signals with any name. |
| // callback - The callback to invoke when a signal is received. Only signals |
| // convertible to ArgType will be delivered. Accept a GVariantRef<"r"> to |
| // receive signals of any type. |
| // |
| // Returns a subscription object, which must be dropped on the same sequence. |
| // Dropping the returned object will unsubscribe from the signal and no more |
| // signals will be sent. |
| template <typename ArgType> |
| std::unique_ptr<SignalSubscription> SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| const char* interface_name, |
| const char* signal_name, |
| SignalCallback<ArgType> callback) |
| requires requires(GVariantRef<"r"> variant) { variant.TryInto<ArgType>(); }; |
| |
| // Variant of subscribe that provides sender information for the signal. In |
| // addition to the signal data, provides the callback with the following |
| // information: |
| // |
| // sender - The unique bus name of the sender of the signal. Note that this |
| // will be the unique name of the sender even if the subscription was |
| // created using the well-known name. If this is a direct peer connection, |
| // sender will be an empty string. |
| // object_path - The remote object that was the source of the signal. |
| // interface_name - The interface that was the source of the signal. |
| // signal_name - The name of the signal. |
| // |
| // This additional information is mostly useful when the same callback is used |
| // for multiple signals. |
| template <typename ArgType> |
| std::unique_ptr<SignalSubscription> SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| const char* interface_name, |
| const char* signal_name, |
| DetailedSignalCallback<ArgType> callback) |
| requires requires(GVariantRef<"r"> variant) { variant.TryInto<ArgType>(); }; |
| |
| // Statically-checked overloads. Types checked against provided spec at |
| // compile time. (The call may still fail for a variety of other reasons, |
| // including the spec not matching the actual implementation on the bus.) |
| |
| // Asynchronously invoke the method declared by the provided MethodSpec. |
| // |
| // bus_name - The owner of the object on which to call the method. May be a |
| // unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object on which to call the method. |
| // arguments - The arguments to pass to the method. Must be infallibly |
| // convertible to the input type declared by MethodSpec. |
| // callback - A callback to invoke with the result of the method call, or an |
| // error if something goes wrong. The output type declared by MethodSpec |
| // must be infallibly convertible to ReturnType. |
| // flags - See https://docs.gtk.org/gio/flags.DBusCallFlags.html |
| // timeout_msec - Timeout for the call in milliseconds. Pass -1 to use the |
| // default, or G_MAXINT for no timeout. |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| }; |
| |
| // Variant of statically-checked Call that can return one or more file |
| // descriptors in the response. |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| }; |
| |
| // Variant of statically-checked Call that can accept one or more file |
| // descriptors to be sent along with the call. |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| }; |
| |
| // Variant of statically-checked Call that can accept one or more file |
| // descriptors to be sent along with the call and can return one or more file |
| // descriptors in the response. |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| }; |
| |
| // Asynchronously retrieve the property declared by the provided PropertySpec. |
| // |
| // bus_name - The owner of the object from which to retrieve the property. May |
| // be a unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object from which to retrieve the property. |
| // callback - A callback to invoke with the retrieved property value, or an |
| // error if something goes wrong. The property type declared by |
| // PropertySpec must be infallibly convertible to ValueType. |
| template <typename PropertySpec, typename ValueType> |
| void GetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| CallCallback<ValueType> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires(PropertySpec::kReadable && |
| requires(GVariantRef<PropertySpec::kType> variant) { |
| variant.template Into<ValueType>(); |
| }); |
| |
| // Asynchronously set the property declared by the provided PropertySpec. |
| // |
| // bus_name - The owner of the object on which to set the property. May be a |
| // unique or well-known bus name. If this is a direct peer connection |
| // rather than a bus connection, pass nullptr. |
| // object_path - The remote object on which to set the property. |
| // interface_name - The interface the property is a part of. |
| // property_name - The name of the property to set. |
| // value - The new property value. Must be infallibly convertible to the type |
| // declared by PropertySpec. |
| // callback - A callback to invoke when the property has been set, or an error |
| // if something goes wrong. |
| template <typename PropertySpec, typename ValueType> |
| void SetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ValueType& value, |
| CallCallback<void> callback, |
| GDBusCallFlags flags = G_DBUS_CALL_FLAGS_NONE, |
| gint timeout_msec = -1) const |
| requires(PropertySpec::kWritable && |
| requires() { GVariantRef<PropertySpec::kType>::From(value); }); |
| |
| // Subscribe to signals matching the provided SignalSpec. |
| // |
| // bus_name - The sender from which to receive signals. May be a unique or |
| // well-known bus name. If this is a direct peer connection rather than a |
| // bus connection, pass nullptr. |
| // object_path - The remote object from which to receive signals. May be |
| // nullptr to receive signals from all objects owned by the sender. |
| // callback - The callback to invoke when a signal is received. The signal |
| // type declared by SignalSpec must be infallibly convertible to ArgType. |
| // |
| // Returns a subscription object, which must be dropped on the same sequence. |
| // Dropping the returned object will unsubscribe from the signal and no more |
| // signals will be sent. |
| template <typename SignalSpec, typename ArgType> |
| std::unique_ptr<SignalSubscription> SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| SignalCallback<ArgType> callback) |
| requires requires(GVariantRef<SignalSpec::kType> variant) { |
| variant.template Into<ArgType>(); |
| }; |
| |
| // Variant of subscribe that provides sender information for the signal. In |
| // addition to the signal data, provides the callback with the following |
| // information: |
| // |
| // sender - The unique bus name of the sender of the signal. Note that this |
| // will be the unique name of the sender even if the subscription was |
| // created using the well-known name. If this is a direct peer connection, |
| // sender will be an empty string. |
| // object_path - The remote object that was the source of the signal. |
| // interface_name - The interface that was the source of the signal. |
| // signal_name - The name of the signal. |
| // |
| // This additional information is mostly useful when the same callback is used |
| // for multiple signals. |
| template <typename SignalSpec, typename ArgType> |
| std::unique_ptr<SignalSubscription> SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| DetailedSignalCallback<ArgType> callback) |
| requires requires(GVariantRef<SignalSpec::kType> variant) { |
| variant.template Into<ArgType>(); |
| }; |
| |
| private: |
| static void CreateForBus(GBusType bus, CreateCallback callback); |
| |
| // Common logic for all Calls. |
| void CallInternal(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const GVariantRef<"r">& arguments, |
| GDBusFdList fds, |
| CallFdCallback<GVariantRef<"r">> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const; |
| |
| // Convert CallCallback to a CallFdCallback that discards (and thus closes) |
| // any returned file descriptors. |
| template <typename ReturnType> |
| static CallFdCallback<ReturnType> IgnoreFds( |
| CallCallback<ReturnType>&& callback); |
| |
| ScopedGObject<GDBusConnection> connection_; |
| }; |
| |
| // Represents an active signal subscription. |
| class GDBusConnectionRef::SignalSubscription { |
| public: |
| // Unsubscribes from the signal. |
| ~SignalSubscription(); |
| |
| private: |
| // Subscribes to the signal with the given callback. |
| SignalSubscription(GDBusConnectionRef connection, |
| const char* sender, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| const char* interface_name, |
| const char* signal_name, |
| DetailedSignalCallback<GVariantRef<"r">> callback); |
| |
| // Called when a signal arrives. Invokes the provided callback. |
| void OnSignal(std::string sender, |
| gvariant::ObjectPath object_path, |
| std::string interface_name, |
| std::string signal_name, |
| GVariantRef<"r"> arguments); |
| |
| GDBusConnectionRef connection_; |
| DetailedSignalCallback<GVariantRef<"r">> callback_; |
| guint subscription_id_; |
| base::WeakPtrFactory<SignalSubscription> weak_factory_; |
| |
| friend class GDBusConnectionRef; |
| }; |
| |
| template <typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, interface_name, method_name, arguments, |
| GDBusFdList(), IgnoreFds(std::move(callback)), flags, timeout_msec); |
| } |
| |
| template <typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, interface_name, method_name, arguments, |
| GDBusFdList(), std::move(callback), flags, timeout_msec); |
| } |
| |
| template <typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, interface_name, method_name, arguments, |
| std::move(fds), IgnoreFds(std::move(callback)), flags, timeout_msec); |
| } |
| |
| template <typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* method_name, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<"r"> variant) { |
| GVariantRef<"r">::TryFrom(arguments); |
| variant.TryInto<ReturnType>(); |
| } |
| { |
| // First check that the provided arguments can be converted to a GVariant |
| // tuple. |
| auto arg_variant = GVariantRef<"r">::TryFrom(arguments); |
| if (!arg_variant.has_value()) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| std::move(callback), |
| std::move(arg_variant) |
| .error() |
| .UnexpectedWithContext( |
| FROM_HERE, "While converting D-Bus call arguments"))); |
| return; |
| } |
| |
| // Attempt to convert return value into the target type. |
| auto convert_result = base::BindOnce( |
| [](base::expected<std::pair<GVariantRef<"r">, GDBusFdList>, Loggable> |
| result) { |
| return std::move(result).and_then([](auto&& inner) { |
| return inner.first.template TryInto<ReturnType>() |
| .transform([&](ReturnType&& value) { |
| return std::pair(std::move(value), std::move(inner.second)); |
| }) |
| .transform_error([](Loggable&& loggable) { |
| loggable.AddContext(FROM_HERE, |
| "While converting D-Bus call return value"); |
| return std::move(loggable); |
| }); |
| }); |
| }); |
| |
| CallInternal(bus_name, object_path, interface_name, method_name, |
| arg_variant.value(), std::move(fds), |
| std::move(convert_result).Then(std::move(callback)), flags, |
| timeout_msec); |
| } |
| |
| template <typename ValueType> |
| void GDBusConnectionRef::GetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* property_name, |
| CallCallback<ValueType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<> variant) { variant.TryInto<ValueType>(); } |
| { |
| // Ensure interface and property names are valid UTF-8. |
| auto args = |
| GVariantRef<"(ss)">::TryFrom(std::tuple(interface_name, property_name)); |
| if (!args.has_value()) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| std::move(callback), |
| std::move(args).error().UnexpectedWithContext( |
| FROM_HERE, "While checking D-Bus get-property args"))); |
| return; |
| } |
| |
| // Unboxes the returned "v" value and attempts to convert it to the expected |
| // type. |
| auto convert_result = |
| base::BindOnce([](base::expected<GVariantRef<"(v)">, Loggable> result) { |
| return std::move(result) |
| .transform_error([](Loggable&& loggable) { |
| loggable.AddContext(FROM_HERE, |
| "While getting D-Bus property value"); |
| return std::move(loggable); |
| }) |
| .and_then([](GVariantRef<"(v)"> variant) { |
| return variant.get<0>() |
| .get<0>() |
| .TryInto<ValueType>() |
| .transform_error([](Loggable&& loggable) { |
| loggable.AddContext( |
| FROM_HERE, "While converting D-Bus property value"); |
| return std::move(loggable); |
| }); |
| }); |
| }); |
| |
| Call<remoting::org_freedesktop_DBus_Properties::Get>( |
| bus_name, object_path, args.value(), |
| std::move(convert_result).Then(std::move(callback)), flags, timeout_msec); |
| } |
| |
| template <typename ValueType> |
| void GDBusConnectionRef::SetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const char* interface_name, |
| const char* property_name, |
| const ValueType& value, |
| CallCallback<void> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires() { GVariantRef<>::TryFrom(value); } |
| { |
| // Ensure interface and property names are valid UTF-8 and the value can be |
| // converted to a GVariant. |
| auto args = GVariantRef<"(ssv)">::TryFrom(std::tuple( |
| interface_name, property_name, gvariant::Boxed<const ValueType&>{value})); |
| if (!args.has_value()) { |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| std::move(callback), |
| std::move(args).error().UnexpectedWithContext( |
| FROM_HERE, "While checking D-Bus set-property args"))); |
| return; |
| } |
| |
| // Ignores the returned std::tuple() and returns void instead. |
| auto convert_result = |
| base::BindOnce([](base::expected<std::tuple<>, Loggable> result) { |
| return std::move(result) |
| .transform([](std::tuple<>) {}) |
| .transform_error([](Loggable&& loggable) { |
| loggable.AddContext(FROM_HERE, |
| "While setting D-Bus property value"); |
| return std::move(loggable); |
| }); |
| }); |
| |
| Call<remoting::org_freedesktop_DBus_Properties::Set>( |
| bus_name, object_path, args.value(), |
| std::move(convert_result).Then(std::move(callback)), flags, timeout_msec); |
| } |
| |
| template <typename ArgType> |
| std::unique_ptr<GDBusConnectionRef::SignalSubscription> |
| GDBusConnectionRef::SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| const char* interface_name, |
| const char* signal_name, |
| SignalCallback<ArgType> callback) |
| requires requires(GVariantRef<"r"> variant) { variant.TryInto<ArgType>(); } |
| { |
| return SignalSubscribe( |
| bus_name, object_path, interface_name, signal_name, |
| base::IgnoreArgs<std::string, gvariant::ObjectPath, std::string, |
| std::string>(std::move(callback))); |
| } |
| |
| template <typename ArgType> |
| std::unique_ptr<GDBusConnectionRef::SignalSubscription> |
| GDBusConnectionRef::SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| const char* interface_name, |
| const char* signal_name, |
| DetailedSignalCallback<ArgType> callback) |
| requires requires(GVariantRef<"r"> variant) { variant.TryInto<ArgType>(); } |
| { |
| // Attempts to convert return value into the target type and invokes the |
| // provided callback if it matches. If the signal is of the wrong type, it is |
| // ignored. |
| auto callback_wrapper = base::BindRepeating( |
| [](const DetailedSignalCallback<ArgType>& callback, std::string sender, |
| gvariant::ObjectPath object_path, std::string interface_name, |
| std::string signal_name, GVariantRef<"r"> arguments) { |
| base::expected<ArgType, Loggable> try_result = |
| arguments.TryInto<ArgType>(); |
| if (try_result.has_value()) { |
| callback.Run(std::move(sender), std::move(object_path), |
| std::move(interface_name), std::move(signal_name), |
| std::move(try_result).value()); |
| } |
| }, |
| std::move(callback)); |
| |
| return std::unique_ptr<SignalSubscription>( |
| new SignalSubscription(*this, bus_name, object_path, interface_name, |
| signal_name, std::move(callback_wrapper))); |
| } |
| |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, MethodSpec::kInterfaceName, |
| MethodSpec::kMethodName, arguments, std::move(callback), flags, |
| timeout_msec); |
| } |
| |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, MethodSpec::kInterfaceName, |
| MethodSpec::kMethodName, arguments, std::move(callback), flags, |
| timeout_msec); |
| } |
| |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, MethodSpec::kInterfaceName, |
| MethodSpec::kMethodName, arguments, std::move(fds), std::move(callback), |
| flags, timeout_msec); |
| } |
| |
| template <typename MethodSpec, typename ArgType, typename ReturnType> |
| void GDBusConnectionRef::Call(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ArgType& arguments, |
| GDBusFdList fds, |
| CallFdCallback<ReturnType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires requires(GVariantRef<MethodSpec::kOutType> variant) { |
| GVariantRef<MethodSpec::kInType>::From(arguments); |
| variant.template Into<ReturnType>(); |
| } |
| { |
| Call(bus_name, object_path, MethodSpec::kInterfaceName, |
| MethodSpec::kMethodName, arguments, std::move(fds), std::move(callback), |
| flags, timeout_msec); |
| } |
| |
| template <typename PropertySpec, typename ValueType> |
| void GDBusConnectionRef::GetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| CallCallback<ValueType> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires(PropertySpec::kReadable && |
| requires(GVariantRef<PropertySpec::kType> variant) { |
| variant.template Into<ValueType>(); |
| }) |
| { |
| GetProperty(bus_name, object_path, PropertySpec::kInterfaceName, |
| PropertySpec::kPropertyName, std::move(callback), flags, |
| timeout_msec); |
| } |
| |
| template <typename PropertySpec, typename ValueType> |
| void GDBusConnectionRef::SetProperty(const char* bus_name, |
| gvariant::ObjectPathCStr object_path, |
| const ValueType& value, |
| CallCallback<void> callback, |
| GDBusCallFlags flags, |
| gint timeout_msec) const |
| requires(PropertySpec::kWritable && |
| requires() { GVariantRef<PropertySpec::kType>::From(value); }) |
| { |
| SetProperty(bus_name, object_path, PropertySpec::kInterfaceName, |
| PropertySpec::kPropertyName, value, std::move(callback), flags, |
| timeout_msec); |
| } |
| |
| template <typename SignalSpec, typename ArgType> |
| std::unique_ptr<GDBusConnectionRef::SignalSubscription> |
| GDBusConnectionRef::SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| SignalCallback<ArgType> callback) |
| requires requires(GVariantRef<SignalSpec::kType> variant) { |
| variant.template Into<ArgType>(); |
| } |
| { |
| return SignalSubscribe(bus_name, object_path, SignalSpec::kInterfaceName, |
| SignalSpec::kSignalName, std::move(callback)); |
| } |
| |
| template <typename SignalSpec, typename ArgType> |
| std::unique_ptr<GDBusConnectionRef::SignalSubscription> |
| GDBusConnectionRef::SignalSubscribe( |
| const char* bus_name, |
| std::optional<gvariant::ObjectPathCStr> object_path, |
| DetailedSignalCallback<ArgType> callback) |
| requires requires(GVariantRef<SignalSpec::kType> variant) { |
| variant.template Into<ArgType>(); |
| } |
| { |
| return SignalSubscribe(bus_name, object_path, SignalSpec::kInterfaceName, |
| SignalSpec::kSignalName, std::move(callback)); |
| } |
| |
| // static |
| template <typename ReturnType> |
| GDBusConnectionRef::CallFdCallback<ReturnType> GDBusConnectionRef::IgnoreFds( |
| CallCallback<ReturnType>&& callback) { |
| auto drop_fds = base::BindOnce( |
| [](base::expected<std::pair<ReturnType, GDBusFdList>, Loggable> result) { |
| return result.transform( |
| [](auto&& inner) { return std::move(inner).first; }); |
| }); |
| return std::move(drop_fds).Then(std::move(callback)); |
| } |
| |
| } // namespace remoting |
| |
| #endif // REMOTING_HOST_LINUX_GDBUS_CONNECTION_REF_H_ |