remote_desktop_portal.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/linux/remote_desktop_portal.h"
  5. #include <glib-object.h>
  6. #include <utility>
  7. #include "base/check.h"
  8. #include "base/rand_util.h"
  9. #include "base/sequence_checker.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "base/time/time.h"
  12. #include "remoting/base/logging.h"
  13. #include "third_party/webrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
  14. namespace remoting {
  15. namespace xdg_portal {
  16. namespace {
  17. constexpr char kRemoteDesktopInterfaceName[] =
  18. "org.freedesktop.portal.RemoteDesktop";
  19. constexpr char kPortalPrefix[] = "remotedesktop";
  20. using webrtc::Scoped;
  21. using webrtc::xdg_portal::kDesktopObjectPath;
  22. using webrtc::xdg_portal::kSessionInterfaceName;
  23. using webrtc::xdg_portal::RequestResponse;
  24. void UnsubscribeSignalHandler(GDBusConnection* connection, guint& signal_id) {
  25. if (signal_id) {
  26. g_dbus_connection_signal_unsubscribe(connection, signal_id);
  27. signal_id = 0;
  28. }
  29. }
  30. } // namespace
  31. RemoteDesktopPortal::RemoteDesktopPortal(
  32. webrtc::ScreenCastPortal::PortalNotifier* notifier)
  33. : notifier_(notifier) {
  34. screencast_portal_ = std::make_unique<webrtc::ScreenCastPortal>(
  35. webrtc::ScreenCastPortal::CaptureSourceType::kAnyScreenContent, this,
  36. OnScreenCastPortalProxyRequested, OnSourcesRequestResponseSignal, this);
  37. DETACH_FROM_SEQUENCE(sequence_checker_);
  38. }
  39. RemoteDesktopPortal::~RemoteDesktopPortal() {
  40. Cleanup();
  41. }
  42. void RemoteDesktopPortal::Cleanup() {
  43. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  44. if (context_) {
  45. g_main_context_pop_thread_default(context_);
  46. g_main_context_unref(context_);
  47. }
  48. if (screencast_portal_)
  49. screencast_portal_.reset();
  50. UnsubscribeSignalHandlers();
  51. webrtc::xdg_portal::TearDownSession(std::move(session_handle_), proxy_,
  52. cancellable_, connection_);
  53. session_handle_.clear();
  54. proxy_ = nullptr;
  55. cancellable_ = nullptr;
  56. connection_ = nullptr;
  57. }
  58. void RemoteDesktopPortal::UnsubscribeSignalHandlers() {
  59. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  60. UnsubscribeSignalHandler(connection_, start_request_signal_id_);
  61. UnsubscribeSignalHandler(connection_, session_request_signal_id_);
  62. UnsubscribeSignalHandler(connection_, devices_request_signal_id_);
  63. UnsubscribeSignalHandler(connection_, session_closed_signal_id_);
  64. }
  65. void RemoteDesktopPortal::Start() {
  66. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  67. // We create and run our own thread-default context so that all the callbacks
  68. // are called on this thread rather than going to the main thread.
  69. context_ = g_main_context_new();
  70. g_main_context_push_thread_default(context_);
  71. HOST_LOG << "Starting screen cast portal";
  72. cancellable_ = g_cancellable_new();
  73. screencast_portal_->SetSessionDetails({.cancellable = cancellable_});
  74. screencast_portal_->Start();
  75. HOST_LOG << "Starting remote desktop portal";
  76. webrtc::xdg_portal::RequestSessionProxy(kRemoteDesktopInterfaceName,
  77. OnProxyRequested, cancellable_, this);
  78. // OpenPipewireRemote (defined on the screencast portal) is the last
  79. // asynchronous call in the combined portal setup so we wait for the
  80. // screencast portal to go into either failed/succeeded state before stopping
  81. // our loop.
  82. while (context_ && screencast_portal_status_ == RequestResponse::kUnknown) {
  83. g_main_context_iteration(context_, /*may_block=*/true);
  84. }
  85. if (context_) {
  86. g_main_context_pop_thread_default(context_);
  87. g_main_context_unref(context_);
  88. context_ = nullptr;
  89. }
  90. HOST_LOG << "Session setup finished";
  91. }
  92. uint32_t RemoteDesktopPortal::pipewire_stream_node_id() {
  93. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  94. DCHECK(screencast_portal_);
  95. return screencast_portal_->pipewire_stream_node_id();
  96. }
  97. // static
  98. void RemoteDesktopPortal::OnProxyRequested(GObject* gobject,
  99. GAsyncResult* result,
  100. gpointer user_data) {
  101. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  102. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  103. DCHECK(that);
  104. Scoped<GError> error;
  105. // We have to acquire the proxy object via "_finish" here since otherwise
  106. // the result will be freed upon return from this callback (and before newly
  107. // posted task on the task runner can start/finish).
  108. GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
  109. if (!proxy) {
  110. if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
  111. return;
  112. LOG(ERROR) << "Failed to get a proxy for the portal: " << error->message;
  113. that->OnPortalDone(RequestResponse::kError);
  114. return;
  115. }
  116. that->RequestSession(proxy);
  117. }
  118. // static
  119. void RemoteDesktopPortal::OnScreenCastPortalProxyRequested(GObject* /*object*/,
  120. GAsyncResult* result,
  121. gpointer user_data) {
  122. webrtc::ScreenCastPortal* that =
  123. static_cast<webrtc::ScreenCastPortal*>(user_data);
  124. DCHECK(that);
  125. Scoped<GError> error;
  126. GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
  127. if (!proxy) {
  128. if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
  129. return;
  130. LOG(ERROR) << "Failed to create a proxy for the screen cast portal: "
  131. << error->message;
  132. that->OnPortalDone(RequestResponse::kError);
  133. return;
  134. }
  135. that->SetSessionDetails({.proxy = proxy});
  136. HOST_LOG << "Successfully created proxy for the screen cast portal.";
  137. }
  138. void RemoteDesktopPortal::RequestSession(GDBusProxy* proxy) {
  139. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  140. DCHECK(!proxy_);
  141. DCHECK(!connection_);
  142. proxy_ = proxy;
  143. connection_ = g_dbus_proxy_get_connection(proxy);
  144. webrtc::xdg_portal::SetupSessionRequestHandlers(
  145. kPortalPrefix, OnSessionRequested, OnSessionRequestResponseSignal,
  146. connection_, proxy_, cancellable_, portal_handle_,
  147. session_request_signal_id_, this);
  148. }
  149. // static
  150. void RemoteDesktopPortal::OnSessionRequested(GDBusProxy* proxy,
  151. GAsyncResult* result,
  152. gpointer user_data) {
  153. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  154. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  155. that->OnSessionRequestResult(proxy, result);
  156. }
  157. // static
  158. void RemoteDesktopPortal::OnDevicesRequested(GDBusProxy* proxy,
  159. GAsyncResult* result,
  160. gpointer user_data) {
  161. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  162. DCHECK(that);
  163. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  164. Scoped<GError> error;
  165. Scoped<GVariant> variant(
  166. g_dbus_proxy_call_finish(proxy, result, error.receive()));
  167. if (!variant) {
  168. LOG(ERROR) << "Failed to select the devices: " << error->message;
  169. return;
  170. }
  171. Scoped<gchar> handle;
  172. g_variant_get_child(variant.get(), 0, "o", handle.receive());
  173. if (!handle) {
  174. LOG(ERROR) << "Failed to initialize the remote desktop session.";
  175. if (that->devices_request_signal_id_) {
  176. g_dbus_connection_signal_unsubscribe(that->connection_,
  177. that->devices_request_signal_id_);
  178. that->devices_request_signal_id_ = 0;
  179. }
  180. return;
  181. }
  182. HOST_LOG << "Subscribed to devices signal.";
  183. }
  184. void RemoteDesktopPortal::RequestSources() {
  185. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  186. screencast_portal_->SourcesRequest();
  187. }
  188. // static
  189. void RemoteDesktopPortal::OnDevicesRequestImpl(GDBusConnection* connection,
  190. const gchar* sender_name,
  191. const gchar* object_path,
  192. const gchar* interface_name,
  193. const gchar* signal_name,
  194. GVariant* parameters,
  195. gpointer user_data) {
  196. HOST_LOG << "Received device selection signal from session.";
  197. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  198. DCHECK(that);
  199. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  200. guint32 portal_response;
  201. g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
  202. if (portal_response) {
  203. LOG(ERROR) << "Failed to select devices for the remote desktop session.";
  204. return;
  205. }
  206. that->RequestSources();
  207. }
  208. void RemoteDesktopPortal::SelectDevices() {
  209. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  210. GVariantBuilder builder;
  211. g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
  212. g_variant_builder_add(&builder, "{sv}", "multiple",
  213. g_variant_new_boolean(false));
  214. auto name = base::StringPrintf(
  215. "%s%d", kPortalPrefix, base::RandInt(0, std::numeric_limits<int>::max()));
  216. g_variant_builder_add(&builder, "{sv}", "handle_token",
  217. g_variant_new_string(name.c_str()));
  218. devices_handle_ =
  219. webrtc::xdg_portal::PrepareSignalHandle(name.c_str(), connection_);
  220. devices_request_signal_id_ = webrtc::xdg_portal::SetupRequestResponseSignal(
  221. devices_handle_.c_str(), OnDevicesRequestImpl, this, connection_);
  222. HOST_LOG << "Selecting devices from the remote desktop session.";
  223. g_dbus_proxy_call(
  224. proxy_, "SelectDevices",
  225. g_variant_new("(oa{sv})", session_handle_.c_str(), &builder),
  226. G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_,
  227. reinterpret_cast<GAsyncReadyCallback>(OnDevicesRequested), this);
  228. }
  229. // static
  230. void RemoteDesktopPortal::OnSessionRequestResponseSignal(
  231. GDBusConnection* connection,
  232. const char* sender_name,
  233. const char* object_path,
  234. const char* interface_name,
  235. const char* signal_name,
  236. GVariant* parameters,
  237. gpointer user_data) {
  238. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  239. DCHECK(that);
  240. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  241. that->RegisterSessionClosedSignalHandler(
  242. OnSessionClosedSignal, parameters, that->connection_,
  243. that->session_handle_, that->session_closed_signal_id_);
  244. that->screencast_portal_->SetSessionDetails(
  245. {.session_handle = that->session_handle_});
  246. that->SelectDevices();
  247. }
  248. // static
  249. void RemoteDesktopPortal::OnSessionClosedSignal(GDBusConnection* connection,
  250. const char* sender_name,
  251. const char* object_path,
  252. const char* interface_name,
  253. const char* signal_name,
  254. GVariant* parameters,
  255. gpointer user_data) {
  256. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  257. DCHECK(that);
  258. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  259. HOST_LOG << "Received closed signal from session.";
  260. // Unsubscribe from the signal and free the session handle to avoid calling
  261. // Session::Close from the destructor since it's already closed
  262. g_dbus_connection_signal_unsubscribe(that->connection_,
  263. that->session_closed_signal_id_);
  264. }
  265. // static
  266. void RemoteDesktopPortal::OnSourcesRequestResponseSignal(
  267. GDBusConnection* connection,
  268. const char* sender_name,
  269. const char* object_path,
  270. const char* interface_name,
  271. const char* signal_name,
  272. GVariant* parameters,
  273. gpointer user_data) {
  274. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  275. DCHECK(that);
  276. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  277. HOST_LOG << "Received sources signal from session.";
  278. uint32_t portal_response;
  279. g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
  280. if (portal_response) {
  281. LOG(ERROR) << "Failed to select sources for the remote desktop session.";
  282. that->OnPortalDone(RequestResponse::kError);
  283. return;
  284. }
  285. that->StartRequest();
  286. }
  287. void RemoteDesktopPortal::StartRequest() {
  288. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  289. webrtc::xdg_portal::StartSessionRequest(
  290. kPortalPrefix, session_handle_, OnStartRequestResponseSignal,
  291. OnStartRequested, proxy_, connection_, cancellable_,
  292. start_request_signal_id_, start_handle_, this);
  293. }
  294. // static
  295. void RemoteDesktopPortal::OnStartRequested(GDBusProxy* proxy,
  296. GAsyncResult* result,
  297. gpointer user_data) {
  298. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  299. DCHECK(that);
  300. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  301. that->OnStartRequestResult(proxy, result);
  302. }
  303. // static
  304. void RemoteDesktopPortal::OnStartRequestResponseSignal(
  305. GDBusConnection* connection,
  306. const char* sender_name,
  307. const char* object_path,
  308. const char* interface_name,
  309. const char* signal_name,
  310. GVariant* parameters,
  311. gpointer user_data) {
  312. RemoteDesktopPortal* that = static_cast<RemoteDesktopPortal*>(user_data);
  313. DCHECK(that);
  314. DCHECK_CALLED_ON_VALID_SEQUENCE(that->sequence_checker_);
  315. HOST_LOG << "Start signal received.";
  316. uint32_t portal_response;
  317. Scoped<GVariant> response_data;
  318. Scoped<GVariantIter> iter;
  319. g_variant_get(parameters, "(u@a{sv})", &portal_response,
  320. response_data.receive());
  321. if (portal_response || !response_data) {
  322. LOG(ERROR) << "Failed to start the remote desktop session.";
  323. return;
  324. }
  325. if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})",
  326. iter.receive())) {
  327. Scoped<GVariant> variant;
  328. while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) {
  329. uint32_t stream_id;
  330. Scoped<GVariant> options;
  331. g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive());
  332. DCHECK(options.get());
  333. that->screencast_portal_->SetSessionDetails(
  334. {.pipewire_stream_node_id = stream_id});
  335. HOST_LOG << "Pipewire stream node id has been set for the portal";
  336. break;
  337. }
  338. }
  339. that->screencast_portal_->OpenPipeWireRemote();
  340. that->OnPortalDone(RequestResponse::kSuccess);
  341. HOST_LOG << "Remote desktop portal start response successful";
  342. }
  343. void RemoteDesktopPortal::OnPortalDone(RequestResponse result) {
  344. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  345. LOG(INFO) << "Remote desktop portal setup is done: "
  346. << webrtc::xdg_portal::RequestResponseToString(result);
  347. if (result != RequestResponse::kSuccess) {
  348. Cleanup();
  349. }
  350. }
  351. webrtc::xdg_portal::SessionDetails RemoteDesktopPortal::GetSessionDetails() {
  352. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  353. HOST_LOG << "Getting session details from the wayland capturer";
  354. return {proxy_.get(), cancellable_.get(), session_handle_,
  355. pipewire_stream_node_id()};
  356. }
  357. void RemoteDesktopPortal::OnScreenCastRequestResult(RequestResponse result,
  358. uint32_t stream_node_id,
  359. int fd) {
  360. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  361. screencast_portal_status_ = result;
  362. notifier_->OnScreenCastRequestResult(result, stream_node_id, fd);
  363. }
  364. void RemoteDesktopPortal::OnScreenCastSessionClosed() {
  365. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  366. notifier_->OnScreenCastSessionClosed();
  367. }
  368. } // namespace xdg_portal
  369. } // namespace remoting