xref: /aosp_15_r20/external/webrtc/modules/portal/xdg_desktop_portal_utils.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright 2022 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #include "modules/portal/xdg_desktop_portal_utils.h"
11 
12 #include <string>
13 
14 #include "absl/strings/string_view.h"
15 #include "modules/portal/scoped_glib.h"
16 #include "rtc_base/logging.h"
17 
18 namespace webrtc {
19 namespace xdg_portal {
20 
RequestResponseToString(RequestResponse request)21 std::string RequestResponseToString(RequestResponse request) {
22   switch (request) {
23     case RequestResponse::kUnknown:
24       return "kUnknown";
25     case RequestResponse::kSuccess:
26       return "kSuccess";
27     case RequestResponse::kUserCancelled:
28       return "kUserCancelled";
29     case RequestResponse::kError:
30       return "kError";
31     default:
32       return "Uknown";
33   }
34 }
35 
RequestResponseFromPortalResponse(uint32_t portal_response)36 RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response) {
37   // See:
38   //  https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Request.Response
39   switch (portal_response) {
40     case 0:
41       return RequestResponse::kSuccess;
42     case 1:
43       return RequestResponse::kUserCancelled;
44     case 2:
45       return RequestResponse::kError;
46     default:
47       return RequestResponse::kUnknown;
48   }
49 }
50 
PrepareSignalHandle(absl::string_view token,GDBusConnection * connection)51 std::string PrepareSignalHandle(absl::string_view token,
52                                 GDBusConnection* connection) {
53   Scoped<char> sender(
54       g_strdup(g_dbus_connection_get_unique_name(connection) + 1));
55   for (int i = 0; sender.get()[i]; ++i) {
56     if (sender.get()[i] == '.') {
57       sender.get()[i] = '_';
58     }
59   }
60   const char* handle =
61       g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), "/",
62                   std::string(token).c_str(), /*end of varargs*/ nullptr);
63   return handle;
64 }
65 
SetupRequestResponseSignal(absl::string_view object_path,const GDBusSignalCallback callback,gpointer user_data,GDBusConnection * connection)66 uint32_t SetupRequestResponseSignal(absl::string_view object_path,
67                                     const GDBusSignalCallback callback,
68                                     gpointer user_data,
69                                     GDBusConnection* connection) {
70   return g_dbus_connection_signal_subscribe(
71       connection, kDesktopBusName, kRequestInterfaceName, "Response",
72       std::string(object_path).c_str(), /*arg0=*/nullptr,
73       G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, user_data,
74       /*user_data_free_func=*/nullptr);
75 }
76 
RequestSessionProxy(absl::string_view interface_name,const ProxyRequestCallback proxy_request_callback,GCancellable * cancellable,gpointer user_data)77 void RequestSessionProxy(absl::string_view interface_name,
78                          const ProxyRequestCallback proxy_request_callback,
79                          GCancellable* cancellable,
80                          gpointer user_data) {
81   g_dbus_proxy_new_for_bus(
82       G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr,
83       kDesktopBusName, kDesktopObjectPath, std::string(interface_name).c_str(),
84       cancellable,
85       reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data);
86 }
87 
SetupSessionRequestHandlers(absl::string_view portal_prefix,const SessionRequestCallback session_request_callback,const SessionRequestResponseSignalHandler request_response_signale_handler,GDBusConnection * connection,GDBusProxy * proxy,GCancellable * cancellable,std::string & portal_handle,guint & session_request_signal_id,gpointer user_data)88 void SetupSessionRequestHandlers(
89     absl::string_view portal_prefix,
90     const SessionRequestCallback session_request_callback,
91     const SessionRequestResponseSignalHandler request_response_signale_handler,
92     GDBusConnection* connection,
93     GDBusProxy* proxy,
94     GCancellable* cancellable,
95     std::string& portal_handle,
96     guint& session_request_signal_id,
97     gpointer user_data) {
98   GVariantBuilder builder;
99   Scoped<char> variant_string;
100 
101   g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
102   variant_string =
103       g_strdup_printf("%.*s_session%d", static_cast<int>(portal_prefix.size()),
104                       portal_prefix.data(), g_random_int_range(0, G_MAXINT));
105   g_variant_builder_add(&builder, "{sv}", "session_handle_token",
106                         g_variant_new_string(variant_string.get()));
107 
108   variant_string =
109       g_strdup_printf("%.*s_%d", static_cast<int>(portal_prefix.size()),
110                       portal_prefix.data(), g_random_int_range(0, G_MAXINT));
111   g_variant_builder_add(&builder, "{sv}", "handle_token",
112                         g_variant_new_string(variant_string.get()));
113 
114   portal_handle = PrepareSignalHandle(variant_string.get(), connection);
115   session_request_signal_id = SetupRequestResponseSignal(
116       portal_handle.c_str(), request_response_signale_handler, user_data,
117       connection);
118 
119   RTC_LOG(LS_INFO) << "Desktop session requested.";
120   g_dbus_proxy_call(
121       proxy, "CreateSession", g_variant_new("(a{sv})", &builder),
122       G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
123       reinterpret_cast<GAsyncReadyCallback>(session_request_callback),
124       user_data);
125 }
126 
StartSessionRequest(absl::string_view prefix,absl::string_view session_handle,const StartRequestResponseSignalHandler signal_handler,const SessionStartRequestedHandler session_started_handler,GDBusProxy * proxy,GDBusConnection * connection,GCancellable * cancellable,guint & start_request_signal_id,std::string & start_handle,gpointer user_data)127 void StartSessionRequest(
128     absl::string_view prefix,
129     absl::string_view session_handle,
130     const StartRequestResponseSignalHandler signal_handler,
131     const SessionStartRequestedHandler session_started_handler,
132     GDBusProxy* proxy,
133     GDBusConnection* connection,
134     GCancellable* cancellable,
135     guint& start_request_signal_id,
136     std::string& start_handle,
137     gpointer user_data) {
138   GVariantBuilder builder;
139   Scoped<char> variant_string;
140 
141   g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
142   variant_string =
143       g_strdup_printf("%.*s%d", static_cast<int>(prefix.size()), prefix.data(),
144                       g_random_int_range(0, G_MAXINT));
145   g_variant_builder_add(&builder, "{sv}", "handle_token",
146                         g_variant_new_string(variant_string.get()));
147 
148   start_handle = PrepareSignalHandle(variant_string.get(), connection);
149   start_request_signal_id = SetupRequestResponseSignal(
150       start_handle.c_str(), signal_handler, user_data, connection);
151 
152   // "Identifier for the application window", this is Wayland, so not "x11:...".
153   const char parent_window[] = "";
154 
155   RTC_LOG(LS_INFO) << "Starting the portal session.";
156   g_dbus_proxy_call(
157       proxy, "Start",
158       g_variant_new("(osa{sv})", std::string(session_handle).c_str(),
159                     parent_window, &builder),
160       G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
161       reinterpret_cast<GAsyncReadyCallback>(session_started_handler),
162       user_data);
163 }
164 
TearDownSession(absl::string_view session_handle,GDBusProxy * proxy,GCancellable * cancellable,GDBusConnection * connection)165 void TearDownSession(absl::string_view session_handle,
166                      GDBusProxy* proxy,
167                      GCancellable* cancellable,
168                      GDBusConnection* connection) {
169   if (!session_handle.empty()) {
170     Scoped<GDBusMessage> message(g_dbus_message_new_method_call(
171         kDesktopBusName, std::string(session_handle).c_str(),
172         kSessionInterfaceName, "Close"));
173     if (message.get()) {
174       Scoped<GError> error;
175       g_dbus_connection_send_message(connection, message.get(),
176                                      G_DBUS_SEND_MESSAGE_FLAGS_NONE,
177                                      /*out_serial=*/nullptr, error.receive());
178       if (error.get()) {
179         RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message;
180       }
181     }
182   }
183 
184   if (cancellable) {
185     g_cancellable_cancel(cancellable);
186     g_object_unref(cancellable);
187   }
188 
189   if (proxy) {
190     g_object_unref(proxy);
191   }
192 }
193 
194 }  // namespace xdg_portal
195 }  // namespace webrtc
196