blob: ae670bc3ebed14c47c4dedd10589c625ad178189 [file] [log] [blame]
// 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_