#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 {
class GDBusConnectionRef {
public:
using CreateCallback =
base::OnceCallback<void(base::expected<GDBusConnectionRef, Loggable>)>;
template <typename ReturnType>
using CallCallback =
base::OnceCallback<void(base::expected<ReturnType, Loggable>)>;
template <typename ReturnType>
using CallFdCallback = base::OnceCallback<void(
base::expected<std::pair<ReturnType, GDBusFdList>, Loggable>)>;
template <typename ArgType>
using SignalCallback = base::RepeatingCallback<void(ArgType arguments)>;
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)>;
class SignalSubscription;
GDBusConnectionRef();
GDBusConnectionRef(const GDBusConnectionRef& other);
GDBusConnectionRef(GDBusConnectionRef&& other);
GDBusConnectionRef& operator=(const GDBusConnectionRef& other);
GDBusConnectionRef& operator=(GDBusConnectionRef&& other);
~GDBusConnectionRef();
explicit GDBusConnectionRef(ScopedGObject<GDBusConnection> connection);
static void CreateForSessionBus(CreateCallback callback);
static void CreateForSystemBus(CreateCallback callback);
bool is_initialized() const;
inline GDBusConnection* raw() const { return connection_.get(); }
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>();
};
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>();
};
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>();
};
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>();
};
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>(); };
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); };
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>(); };
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>(); };
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>();
};
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>();
};
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>();
};
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>();
};
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>();
});
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); });
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>();
};
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);
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;
template <typename ReturnType>
static CallFdCallback<ReturnType> IgnoreFds(
CallCallback<ReturnType>&& callback);
ScopedGObject<GDBusConnection> connection_;
};
class GDBusConnectionRef::SignalSubscription {
public:
~SignalSubscription();
private:
SignalSubscription(GDBusConnectionRef connection,
const char* sender,
std::optional<gvariant::ObjectPathCStr> object_path,
const char* interface_name,
const char* signal_name,
DetailedSignalCallback<GVariantRef<"r">> 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>();
}
{
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;
}
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>(); }
{
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;
}
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); }
{
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;
}
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>(); }
{
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));
}
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));
}
}
#endif