910e62b5创建于 1月15日历史提交
// 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.

#include "remoting/host/linux/gdbus_connection_ref.h"

#include <gio/gio.h>
#include <glib-object.h>
#include <glib.h>

#include <optional>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/strings/strcat.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected.h"
#include "remoting/host/base/loggable.h"
#include "remoting/host/linux/gdbus_fd_list.h"
#include "remoting/host/linux/gvariant_ref.h"
#include "ui/base/glib/scoped_gobject.h"

namespace remoting {

GDBusConnectionRef::GDBusConnectionRef() = default;
GDBusConnectionRef::GDBusConnectionRef(const GDBusConnectionRef& other) =
    default;
GDBusConnectionRef::GDBusConnectionRef(GDBusConnectionRef&& other) = default;
GDBusConnectionRef& GDBusConnectionRef::operator=(
    const GDBusConnectionRef& other) = default;
GDBusConnectionRef& GDBusConnectionRef::operator=(GDBusConnectionRef&& other) =
    default;
GDBusConnectionRef::~GDBusConnectionRef() = default;

GDBusConnectionRef::GDBusConnectionRef(
    ScopedGObject<GDBusConnection> connection)
    : connection_(connection) {}

bool GDBusConnectionRef::is_initialized() const {
  return connection_;
}

// static
void GDBusConnectionRef::CreateForSessionBus(CreateCallback callback) {
  return CreateForBus(G_BUS_TYPE_SESSION, std::move(callback));
}

// static
void GDBusConnectionRef::CreateForSystemBus(CreateCallback callback) {
  return CreateForBus(G_BUS_TYPE_SYSTEM, std::move(callback));
}

// static
void GDBusConnectionRef::CreateForBus(GBusType bus, CreateCallback callback) {
  auto* bound_callback = new CreateCallback(base::BindPostTask(
      base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)));

  // May run on a different sequence.
  auto on_complete = [](GObject* source, GAsyncResult* result,
                        gpointer user_data) {
    auto callback =
        base::WrapUnique(static_cast<decltype(bound_callback)>(user_data));
    GError* error = nullptr;
    GDBusConnection* connection = g_bus_get_finish(result, &error);
    // Will post back to the proper sequence thanks to BindPostTask above.
    if (connection) {
      std::move(*callback).Run(
          base::ok(GDBusConnectionRef(TakeGObject(connection))));
    } else {
      std::move(*callback).Run(base::unexpected(Loggable(
          FROM_HERE,
          base::StrCat({"Failed to connect to bus: ", error->message}))));
      g_error_free(error);
    }
  };

  g_bus_get(bus, nullptr, on_complete, bound_callback);
}

void GDBusConnectionRef::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 {
  auto* bound_callback =
      new CallFdCallback<GVariantRef<"r">>(base::BindPostTask(
          base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)));

  // May run on a different sequence.
  auto on_complete = [](GObject* source, GAsyncResult* result,
                        gpointer user_data) {
    auto callback =
        base::WrapUnique(static_cast<decltype(bound_callback)>(user_data));
    GUnixFDList* fd_list = nullptr;
    GError* error = nullptr;
    GVariant* variant = g_dbus_connection_call_with_unix_fd_list_finish(
        G_DBUS_CONNECTION(source), &fd_list, result, &error);
    GDBusFdList out_fds;
    if (fd_list != nullptr) {
      out_fds = GDBusFdList::StealFromGUnixFDList(fd_list);
      g_object_unref(fd_list);
    }
    // Will post back to the proper sequence thanks to BindPostTask above.
    if (variant != nullptr) {
      std::move(*callback).Run(std::pair(
          GVariantRef<"r">::TakeUnchecked(variant), std::move(out_fds)));
    } else {
      std::move(*callback).Run(base::unexpected(
          Loggable(FROM_HERE,
                   base::StrCat({"Error invoking method: ", error->message}))));
      g_error_free(error);
    }
  };

  g_dbus_connection_call_with_unix_fd_list(
      connection_, bus_name, object_path.c_str(), interface_name, method_name,
      arguments.raw(), G_VARIANT_TYPE_TUPLE, flags, timeout_msec,
      std::move(fds).IntoGUnixFDList(), nullptr, on_complete, bound_callback);
}

GDBusConnectionRef::SignalSubscription::~SignalSubscription() {
  g_dbus_connection_signal_unsubscribe(connection_.raw(), subscription_id_);
}

GDBusConnectionRef::SignalSubscription::SignalSubscription(
    GDBusConnectionRef connection,
    const char* sender,
    std::optional<gvariant::ObjectPathCStr> object_path,
    const char* interface_name,
    const char* signal_name,
    DetailedSignalCallback<GVariantRef<"r">> callback)
    : connection_(std::move(connection)),
      callback_(std::move(callback)),
      weak_factory_(this) {
  auto* bound_callback = new DetailedSignalCallback<GVariantRef<"r">>(
      base::BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(),
                         base::BindRepeating(&SignalSubscription::OnSignal,
                                             weak_factory_.GetWeakPtr())));

  // May run on a different sequence.
  auto on_signal = [](GDBusConnection* connection, const gchar* sender_name,
                      const gchar* object_path, const gchar* interface_name,
                      const gchar* signal_name, GVariant* arguments,
                      gpointer user_data) {
    auto* callback = static_cast<decltype(bound_callback)>(user_data);
    // Will post back to the proper sequence thanks to BindPostTask above.
    callback->Run(sender_name ? std::string(sender_name) : std::string(),
                  gvariant::ObjectPath::TryFrom(object_path).value(),
                  interface_name, signal_name,
                  GVariantRef<"r">::RefSinkUnchecked(arguments));
  };

  auto free_func = [](gpointer data) {
    delete static_cast<decltype(bound_callback)>(data);
  };

  subscription_id_ = g_dbus_connection_signal_subscribe(
      connection_.raw(), sender, interface_name, signal_name,
      object_path ? object_path->c_str() : nullptr, nullptr,
      G_DBUS_SIGNAL_FLAGS_NONE, on_signal, bound_callback, free_func);
}

void GDBusConnectionRef::SignalSubscription::OnSignal(
    std::string sender,
    gvariant::ObjectPath object_path,
    std::string interface_name,
    std::string signal_name,
    GVariantRef<"r"> arguments) {
  callback_.Run(std::move(sender), std::move(object_path),
                std::move(interface_name), std::move(signal_name),
                std::move(arguments));
}

}  // namespace remoting