desktop_session_proxy.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. // Copyright (c) 2012 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/desktop_session_proxy.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/compiler_specific.h"
  10. #include "base/location.h"
  11. #include "base/logging.h"
  12. #include "base/memory/ptr_util.h"
  13. #include "base/notreached.h"
  14. #include "base/process/process_handle.h"
  15. #include "base/task/single_thread_task_runner.h"
  16. #include "build/build_config.h"
  17. #include "ipc/ipc_channel_proxy.h"
  18. #include "remoting/base/capabilities.h"
  19. #include "remoting/host/client_session.h"
  20. #include "remoting/host/client_session_control.h"
  21. #include "remoting/host/crash_process.h"
  22. #include "remoting/host/desktop_session_connector.h"
  23. #include "remoting/host/ipc_action_executor.h"
  24. #include "remoting/host/ipc_audio_capturer.h"
  25. #include "remoting/host/ipc_input_injector.h"
  26. #include "remoting/host/ipc_keyboard_layout_monitor.h"
  27. #include "remoting/host/ipc_mouse_cursor_monitor.h"
  28. #include "remoting/host/ipc_screen_controls.h"
  29. #include "remoting/host/ipc_url_forwarder_configurator.h"
  30. #include "remoting/host/ipc_video_frame_capturer.h"
  31. #include "remoting/host/mojom/desktop_session.mojom.h"
  32. #include "remoting/host/remote_open_url/remote_open_url_util.h"
  33. #include "remoting/host/webauthn/remote_webauthn_delegated_state_change_notifier.h"
  34. #include "remoting/proto/audio.pb.h"
  35. #include "remoting/proto/control.pb.h"
  36. #include "remoting/proto/event.pb.h"
  37. #include "remoting/protocol/capability_names.h"
  38. #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
  39. #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
  40. #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
  41. #include "third_party/webrtc/modules/desktop_capture/shared_memory.h"
  42. #if BUILDFLAG(IS_WIN)
  43. #include "base/win/scoped_handle.h"
  44. #endif // BUILDFLAG(IS_WIN)
  45. namespace remoting {
  46. using SetUpUrlForwarderResponse =
  47. protocol::UrlForwarderControl::SetUpUrlForwarderResponse;
  48. class DesktopSessionProxy::IpcSharedBufferCore
  49. : public base::RefCountedThreadSafe<IpcSharedBufferCore> {
  50. public:
  51. IpcSharedBufferCore(int id, base::ReadOnlySharedMemoryRegion region)
  52. : id_(id) {
  53. mapping_ = region.Map();
  54. if (!mapping_.IsValid()) {
  55. LOG(ERROR) << "Failed to map a shared buffer: id=" << id
  56. << ", size=" << region.GetSize();
  57. }
  58. // After being mapped, |region| is no longer needed and can be discarded.
  59. }
  60. IpcSharedBufferCore(const IpcSharedBufferCore&) = delete;
  61. IpcSharedBufferCore& operator=(const IpcSharedBufferCore&) = delete;
  62. int id() const { return id_; }
  63. size_t size() const { return mapping_.size(); }
  64. const void* memory() const { return mapping_.memory(); }
  65. private:
  66. virtual ~IpcSharedBufferCore() = default;
  67. friend class base::RefCountedThreadSafe<IpcSharedBufferCore>;
  68. int id_;
  69. base::ReadOnlySharedMemoryMapping mapping_;
  70. };
  71. class DesktopSessionProxy::IpcSharedBuffer : public webrtc::SharedMemory {
  72. public:
  73. // Note that the webrtc::SharedMemory class is used for both read-only and
  74. // writable shared memory, necessitating the ugly const_cast here.
  75. IpcSharedBuffer(scoped_refptr<IpcSharedBufferCore> core)
  76. : SharedMemory(const_cast<void*>(core->memory()),
  77. core->size(),
  78. 0,
  79. core->id()),
  80. core_(core) {}
  81. IpcSharedBuffer(const IpcSharedBuffer&) = delete;
  82. IpcSharedBuffer& operator=(const IpcSharedBuffer&) = delete;
  83. private:
  84. scoped_refptr<IpcSharedBufferCore> core_;
  85. };
  86. DesktopSessionProxy::DesktopSessionProxy(
  87. scoped_refptr<base::SingleThreadTaskRunner> audio_capture_task_runner,
  88. scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
  89. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
  90. base::WeakPtr<ClientSessionControl> client_session_control,
  91. base::WeakPtr<ClientSessionEvents> client_session_events,
  92. base::WeakPtr<DesktopSessionConnector> desktop_session_connector,
  93. const DesktopEnvironmentOptions& options)
  94. : audio_capture_task_runner_(audio_capture_task_runner),
  95. caller_task_runner_(caller_task_runner),
  96. io_task_runner_(io_task_runner),
  97. client_session_control_(client_session_control),
  98. client_session_events_(client_session_events),
  99. desktop_session_connector_(desktop_session_connector),
  100. ipc_file_operations_factory_(this),
  101. pending_capture_frame_requests_(0),
  102. is_desktop_session_connected_(false),
  103. options_(options) {
  104. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  105. }
  106. std::unique_ptr<ActionExecutor> DesktopSessionProxy::CreateActionExecutor() {
  107. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  108. return std::make_unique<IpcActionExecutor>(this);
  109. }
  110. std::unique_ptr<AudioCapturer> DesktopSessionProxy::CreateAudioCapturer() {
  111. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  112. return std::make_unique<IpcAudioCapturer>(this);
  113. }
  114. std::unique_ptr<InputInjector> DesktopSessionProxy::CreateInputInjector() {
  115. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  116. return std::make_unique<IpcInputInjector>(this);
  117. }
  118. std::unique_ptr<ScreenControls> DesktopSessionProxy::CreateScreenControls() {
  119. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  120. return std::make_unique<IpcScreenControls>(this);
  121. }
  122. std::unique_ptr<DesktopCapturer> DesktopSessionProxy::CreateVideoCapturer() {
  123. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  124. return std::make_unique<IpcVideoFrameCapturer>(this);
  125. }
  126. std::unique_ptr<webrtc::MouseCursorMonitor>
  127. DesktopSessionProxy::CreateMouseCursorMonitor() {
  128. return std::make_unique<IpcMouseCursorMonitor>(this);
  129. }
  130. std::unique_ptr<KeyboardLayoutMonitor>
  131. DesktopSessionProxy::CreateKeyboardLayoutMonitor(
  132. base::RepeatingCallback<void(const protocol::KeyboardLayout&)> callback) {
  133. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  134. return std::make_unique<IpcKeyboardLayoutMonitor>(std::move(callback), this);
  135. }
  136. std::unique_ptr<FileOperations> DesktopSessionProxy::CreateFileOperations() {
  137. return ipc_file_operations_factory_.CreateFileOperations();
  138. }
  139. std::unique_ptr<UrlForwarderConfigurator>
  140. DesktopSessionProxy::CreateUrlForwarderConfigurator() {
  141. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  142. return std::make_unique<IpcUrlForwarderConfigurator>(this);
  143. }
  144. std::unique_ptr<RemoteWebAuthnStateChangeNotifier>
  145. DesktopSessionProxy::CreateRemoteWebAuthnStateChangeNotifier() {
  146. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  147. return std::make_unique<RemoteWebAuthnDelegatedStateChangeNotifier>(
  148. base::BindRepeating(&DesktopSessionProxy::SignalWebAuthnExtension, this));
  149. }
  150. std::string DesktopSessionProxy::GetCapabilities() const {
  151. std::string result = protocol::kRateLimitResizeRequests;
  152. // Ask the client to send its resolution unconditionally.
  153. if (options_.enable_curtaining()) {
  154. result += " ";
  155. result += protocol::kSendInitialResolution;
  156. }
  157. if (InputInjector::SupportsTouchEvents()) {
  158. result += " ";
  159. result += protocol::kTouchEventsCapability;
  160. }
  161. if (options_.enable_file_transfer()) {
  162. result += " ";
  163. result += protocol::kFileTransferCapability;
  164. }
  165. if (options_.enable_remote_open_url() && IsRemoteOpenUrlSupported()) {
  166. result += " ";
  167. result += protocol::kRemoteOpenUrlCapability;
  168. }
  169. if (options_.enable_remote_webauthn()) {
  170. result += " ";
  171. result += protocol::kRemoteWebAuthnCapability;
  172. }
  173. return result;
  174. }
  175. void DesktopSessionProxy::SetCapabilities(const std::string& capabilities) {
  176. // Delay creation of the desktop session until the client screen resolution is
  177. // received if the desktop session requires the initial screen resolution
  178. // (when enable_curtaining() is true) and the client is expected to
  179. // sent its screen resolution (the 'sendInitialResolution' capability is
  180. // supported).
  181. if (options_.enable_curtaining() &&
  182. HasCapability(capabilities, protocol::kSendInitialResolution)) {
  183. VLOG(1) << "Waiting for the client screen resolution.";
  184. return;
  185. }
  186. // Connect to the desktop session.
  187. if (!is_desktop_session_connected_) {
  188. is_desktop_session_connected_ = true;
  189. if (desktop_session_connector_.get()) {
  190. desktop_session_connector_->ConnectTerminal(this, screen_resolution_,
  191. options_.enable_curtaining());
  192. }
  193. }
  194. }
  195. bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) {
  196. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  197. NOTREACHED() << "Received unexpected IPC type: " << message.type();
  198. return false;
  199. }
  200. void DesktopSessionProxy::OnChannelConnected(int32_t peer_pid) {
  201. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  202. VLOG(1) << "IPC: network <- desktop (" << peer_pid << ")";
  203. desktop_session_agent_->Start(
  204. client_session_control_->client_jid(), screen_resolution_, options_,
  205. base::BindOnce(&DesktopSessionProxy::OnDesktopSessionAgentStarted,
  206. base::Unretained(this)));
  207. }
  208. void DesktopSessionProxy::OnChannelError() {
  209. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  210. DetachFromDesktop();
  211. }
  212. void DesktopSessionProxy::OnAssociatedInterfaceRequest(
  213. const std::string& interface_name,
  214. mojo::ScopedInterfaceEndpointHandle handle) {
  215. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  216. if (interface_name == mojom::DesktopSessionEventHandler::Name_) {
  217. if (desktop_session_event_handler_.is_bound()) {
  218. LOG(ERROR) << "Receiver already bound for associated interface: "
  219. << mojom::DesktopSessionEventHandler::Name_;
  220. CrashProcess(base::Location::Current());
  221. }
  222. mojo::PendingAssociatedReceiver<mojom::DesktopSessionEventHandler>
  223. pending_receiver(std::move(handle));
  224. desktop_session_event_handler_.Bind(std::move(pending_receiver));
  225. } else if (interface_name == mojom::DesktopSessionStateHandler::Name_) {
  226. if (desktop_session_state_handler_.is_bound()) {
  227. LOG(ERROR) << "Receiver already bound for associated interface: "
  228. << mojom::DesktopSessionStateHandler::Name_;
  229. CrashProcess(base::Location::Current());
  230. }
  231. mojo::PendingAssociatedReceiver<mojom::DesktopSessionStateHandler>
  232. pending_receiver(std::move(handle));
  233. desktop_session_state_handler_.Bind(std::move(pending_receiver));
  234. } else {
  235. LOG(ERROR) << "Unknown associated interface requested: " << interface_name
  236. << ", crashing this process";
  237. CrashProcess(base::Location::Current());
  238. }
  239. }
  240. bool DesktopSessionProxy::AttachToDesktop(
  241. mojo::ScopedMessagePipeHandle desktop_pipe,
  242. int session_id) {
  243. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  244. DCHECK(!desktop_channel_);
  245. // Ignore the attach event if the client session has already disconnected.
  246. if (!client_session_control_.get())
  247. return false;
  248. // Connect to the desktop process.
  249. desktop_channel_ = IPC::ChannelProxy::Create(
  250. desktop_pipe.release(), IPC::Channel::MODE_CLIENT, this,
  251. io_task_runner_.get(), base::ThreadTaskRunnerHandle::Get());
  252. // Reset the associated remote to allow us to connect to the new desktop
  253. // process. This is needed as the desktop may crash and the daemon process
  254. // will restart it however the remote will still be bound to the previous
  255. // process since DetachFromDesktop() will not be called.
  256. desktop_session_agent_.reset();
  257. desktop_channel_->GetRemoteAssociatedInterface(&desktop_session_agent_);
  258. desktop_session_id_ = session_id;
  259. return true;
  260. }
  261. void DesktopSessionProxy::DetachFromDesktop() {
  262. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  263. desktop_channel_.reset();
  264. desktop_session_agent_.reset();
  265. desktop_session_control_.reset();
  266. desktop_session_event_handler_.reset();
  267. desktop_session_state_handler_.reset();
  268. desktop_session_id_ = UINT32_MAX;
  269. current_url_forwarder_state_ = mojom::UrlForwarderState::kUnknown;
  270. // We don't reset |is_url_forwarder_set_up_callback_| here since the request
  271. // can come in before the DetachFromDesktop-AttachToDesktop sequence.
  272. shared_buffers_.clear();
  273. // Notify interested folks that the IPC has been disconnected.
  274. disconnect_handlers_.Notify();
  275. // Generate fake responses to keep the video capturer in sync.
  276. while (pending_capture_frame_requests_) {
  277. OnCaptureResult(mojom::CaptureResult::NewCaptureError(
  278. webrtc::DesktopCapturer::Result::ERROR_TEMPORARY));
  279. }
  280. if (client_session_events_) {
  281. client_session_events_->OnDesktopDetached();
  282. }
  283. }
  284. void DesktopSessionProxy::OnDesktopSessionAgentStarted(
  285. mojo::PendingAssociatedRemote<mojom::DesktopSessionControl>
  286. pending_remote) {
  287. // Reset the associated remote to allow us to connect to the new desktop
  288. // process. This is needed as the desktop may crash and the daemon process
  289. // will restart it however the remote will still be bound to the previous
  290. // process since DetachFromDesktop() will not be called.
  291. desktop_session_control_.reset();
  292. desktop_session_control_.Bind(std::move(pending_remote));
  293. if (client_session_events_) {
  294. client_session_events_->OnDesktopAttached(desktop_session_id_);
  295. }
  296. }
  297. void DesktopSessionProxy::SetAudioCapturer(
  298. const base::WeakPtr<IpcAudioCapturer>& audio_capturer) {
  299. DCHECK(audio_capture_task_runner_->BelongsToCurrentThread());
  300. audio_capturer_ = audio_capturer;
  301. }
  302. void DesktopSessionProxy::CaptureFrame() {
  303. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  304. if (desktop_session_control_) {
  305. ++pending_capture_frame_requests_;
  306. desktop_session_control_->CaptureFrame();
  307. } else {
  308. video_capturer_->OnCaptureResult(
  309. webrtc::DesktopCapturer::Result::ERROR_TEMPORARY, nullptr);
  310. }
  311. }
  312. bool DesktopSessionProxy::SelectSource(webrtc::DesktopCapturer::SourceId id) {
  313. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  314. if (desktop_session_control_) {
  315. desktop_session_control_->SelectSource(id);
  316. }
  317. return true;
  318. }
  319. void DesktopSessionProxy::SetVideoCapturer(
  320. const base::WeakPtr<IpcVideoFrameCapturer> video_capturer) {
  321. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  322. video_capturer_ = video_capturer;
  323. }
  324. void DesktopSessionProxy::SetMouseCursorMonitor(
  325. const base::WeakPtr<IpcMouseCursorMonitor>& mouse_cursor_monitor) {
  326. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  327. mouse_cursor_monitor_ = mouse_cursor_monitor;
  328. }
  329. void DesktopSessionProxy::SetKeyboardLayoutMonitor(
  330. const base::WeakPtr<IpcKeyboardLayoutMonitor>& keyboard_layout_monitor) {
  331. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  332. keyboard_layout_monitor_ = std::move(keyboard_layout_monitor);
  333. }
  334. const absl::optional<protocol::KeyboardLayout>&
  335. DesktopSessionProxy::GetKeyboardCurrentLayout() const {
  336. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  337. return keyboard_layout_;
  338. }
  339. void DesktopSessionProxy::DisconnectSession(protocol::ErrorCode error) {
  340. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  341. // Disconnect the client session if it hasn't been disconnected yet.
  342. if (client_session_control_.get())
  343. client_session_control_->DisconnectSession(error);
  344. }
  345. void DesktopSessionProxy::InjectClipboardEvent(
  346. const protocol::ClipboardEvent& event) {
  347. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  348. if (desktop_session_control_) {
  349. desktop_session_control_->InjectClipboardEvent(event);
  350. }
  351. }
  352. void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) {
  353. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  354. if (desktop_session_control_) {
  355. desktop_session_control_->InjectKeyEvent(event);
  356. }
  357. }
  358. void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) {
  359. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  360. if (desktop_session_control_) {
  361. desktop_session_control_->InjectTextEvent(event);
  362. }
  363. }
  364. void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) {
  365. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  366. if (desktop_session_control_) {
  367. desktop_session_control_->InjectMouseEvent(event);
  368. }
  369. }
  370. void DesktopSessionProxy::InjectTouchEvent(const protocol::TouchEvent& event) {
  371. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  372. if (desktop_session_control_) {
  373. desktop_session_control_->InjectTouchEvent(event);
  374. }
  375. }
  376. void DesktopSessionProxy::StartInputInjector(
  377. std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
  378. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  379. client_clipboard_ = std::move(client_clipboard);
  380. }
  381. void DesktopSessionProxy::SetScreenResolution(
  382. const ScreenResolution& resolution) {
  383. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  384. screen_resolution_ = resolution;
  385. // Connect to the desktop session if it is not done yet.
  386. if (!is_desktop_session_connected_) {
  387. is_desktop_session_connected_ = true;
  388. if (desktop_session_connector_.get()) {
  389. desktop_session_connector_->ConnectTerminal(this, screen_resolution_,
  390. options_.enable_curtaining());
  391. }
  392. return;
  393. }
  394. // Pass the client's resolution to both daemon and desktop session agent.
  395. // Depending on the session kind the screen resolution can be set by either
  396. // the daemon (for example RDP sessions on Windows) or by the desktop session
  397. // agent (when sharing the physical console).
  398. // Desktop-size-restore functionality (via an empty resolution param) does not
  399. // exist for the Daemon process. Passing an empty resolution object is
  400. // treated as a critical error so we want to prevent that here.
  401. if (desktop_session_connector_.get() && !screen_resolution_.IsEmpty())
  402. desktop_session_connector_->SetScreenResolution(this, screen_resolution_);
  403. // Passing an empty |screen_resolution_| value to the desktop process
  404. // indicates that the original resolution, if one exists, should be restored.
  405. if (desktop_session_control_) {
  406. desktop_session_control_->SetScreenResolution(screen_resolution_);
  407. }
  408. }
  409. void DesktopSessionProxy::ExecuteAction(
  410. const protocol::ActionRequest& request) {
  411. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  412. if (!desktop_session_control_) {
  413. return;
  414. }
  415. switch (request.action()) {
  416. case protocol::ActionRequest::LOCK_WORKSTATION:
  417. desktop_session_control_->LockWorkstation();
  418. break;
  419. case protocol::ActionRequest::SEND_ATTENTION_SEQUENCE:
  420. desktop_session_control_->InjectSendAttentionSequence();
  421. break;
  422. default:
  423. LOG(WARNING) << "Unknown action requested: " << request.action();
  424. }
  425. }
  426. void DesktopSessionProxy::BeginFileRead(
  427. IpcFileOperations::BeginFileReadCallback callback,
  428. base::OnceClosure on_disconnect) {
  429. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  430. if (!desktop_session_control_) {
  431. std::move(callback).Run(
  432. mojom::BeginFileReadResult::NewError(protocol::MakeFileTransferError(
  433. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR)));
  434. return;
  435. }
  436. auto disconnect_handler_subscription =
  437. disconnect_handlers_.Add(std::move(on_disconnect));
  438. // Unretained is sound as DesktopSessionProxy owns |desktop_session_control_|
  439. // and the callback won't be invoked after the remote is destroyed.
  440. desktop_session_control_->BeginFileRead(base::BindOnce(
  441. &DesktopSessionProxy::OnBeginFileReadResult, base::Unretained(this),
  442. std::move(callback), std::move(disconnect_handler_subscription)));
  443. }
  444. void DesktopSessionProxy::BeginFileWrite(
  445. const base::FilePath& file_path,
  446. IpcFileOperations::BeginFileWriteCallback callback,
  447. base::OnceClosure on_disconnect) {
  448. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  449. if (!desktop_session_control_) {
  450. std::move(callback).Run(
  451. mojom::BeginFileWriteResult::NewError(protocol::MakeFileTransferError(
  452. FROM_HERE, protocol::FileTransfer_Error_Type_UNEXPECTED_ERROR)));
  453. return;
  454. }
  455. auto disconnect_handler_subscription =
  456. disconnect_handlers_.Add(std::move(on_disconnect));
  457. // Unretained is sound as DesktopSessionProxy owns |desktop_session_control_|
  458. // and the callback won't be invoked after the remote is destroyed.
  459. desktop_session_control_->BeginFileWrite(
  460. file_path, base::BindOnce(&DesktopSessionProxy::OnBeginFileWriteResult,
  461. base::Unretained(this), std::move(callback),
  462. std::move(disconnect_handler_subscription)));
  463. }
  464. void DesktopSessionProxy::IsUrlForwarderSetUp(
  465. UrlForwarderConfigurator::IsUrlForwarderSetUpCallback callback) {
  466. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  467. switch (current_url_forwarder_state_) {
  468. case mojom::UrlForwarderState::kUnknown:
  469. // State is not known yet. Wait for OnUrlForwarderStateChange() to be
  470. // called.
  471. DCHECK(!is_url_forwarder_set_up_callback_);
  472. is_url_forwarder_set_up_callback_ = std::move(callback);
  473. break;
  474. case mojom::UrlForwarderState::kSetUp:
  475. std::move(callback).Run(true);
  476. break;
  477. default:
  478. std::move(callback).Run(false);
  479. }
  480. }
  481. void DesktopSessionProxy::SetUpUrlForwarder(
  482. const UrlForwarderConfigurator::SetUpUrlForwarderCallback& callback) {
  483. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  484. DCHECK(!set_up_url_forwarder_callback_);
  485. if (!desktop_session_control_) {
  486. LOG(ERROR) << "The UrlForwarderConfigurator remote is not connected. Setup "
  487. << "request ignored.";
  488. callback.Run(SetUpUrlForwarderResponse::FAILED);
  489. return;
  490. }
  491. set_up_url_forwarder_callback_ = callback;
  492. desktop_session_control_->SetUpUrlForwarder();
  493. }
  494. void DesktopSessionProxy::OnUrlForwarderStateChange(
  495. mojom::UrlForwarderState state) {
  496. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  497. current_url_forwarder_state_ = state;
  498. if (is_url_forwarder_set_up_callback_) {
  499. std::move(is_url_forwarder_set_up_callback_)
  500. .Run(state == mojom::UrlForwarderState::kSetUp);
  501. }
  502. if (set_up_url_forwarder_callback_) {
  503. switch (state) {
  504. case mojom::UrlForwarderState::kSetUp:
  505. set_up_url_forwarder_callback_.Run(SetUpUrlForwarderResponse::COMPLETE);
  506. // Cleanup callback due to terminating state.
  507. set_up_url_forwarder_callback_.Reset();
  508. break;
  509. case mojom::UrlForwarderState::kNotSetUp:
  510. // The desktop session agent during the setup process will only report
  511. // SET_UP or FAILED. NOT_SET_UP must come from a freshly started agent.
  512. LOG(WARNING) << "Setup process failed because the previous desktop "
  513. << "session agent has exited";
  514. [[fallthrough]];
  515. case mojom::UrlForwarderState::kFailed:
  516. set_up_url_forwarder_callback_.Run(SetUpUrlForwarderResponse::FAILED);
  517. // Cleanup callback due to terminating state.
  518. set_up_url_forwarder_callback_.Reset();
  519. break;
  520. case mojom::UrlForwarderState::kSetupPendingUserIntervention:
  521. set_up_url_forwarder_callback_.Run(
  522. SetUpUrlForwarderResponse::USER_INTERVENTION_REQUIRED);
  523. break;
  524. default:
  525. LOG(ERROR) << "Received unexpected state: " << state;
  526. }
  527. }
  528. }
  529. DesktopSessionProxy::~DesktopSessionProxy() {
  530. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  531. if (desktop_session_connector_.get() && is_desktop_session_connected_)
  532. desktop_session_connector_->DisconnectTerminal(this);
  533. }
  534. scoped_refptr<DesktopSessionProxy::IpcSharedBufferCore>
  535. DesktopSessionProxy::GetSharedBufferCore(int id) {
  536. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  537. SharedBuffers::const_iterator i = shared_buffers_.find(id);
  538. if (i != shared_buffers_.end()) {
  539. return i->second;
  540. } else {
  541. LOG(ERROR) << "Failed to find the shared buffer " << id;
  542. return nullptr;
  543. }
  544. }
  545. void DesktopSessionProxy::OnAudioPacket(
  546. std::unique_ptr<AudioPacket> audio_packet) {
  547. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  548. // Pass the captured audio packet to |audio_capturer_|.
  549. audio_capture_task_runner_->PostTask(
  550. FROM_HERE, base::BindOnce(&IpcAudioCapturer::OnAudioPacket,
  551. audio_capturer_, std::move(audio_packet)));
  552. }
  553. void DesktopSessionProxy::OnSharedMemoryRegionCreated(
  554. int id,
  555. base::ReadOnlySharedMemoryRegion region,
  556. uint32_t size) {
  557. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  558. scoped_refptr<IpcSharedBufferCore> shared_buffer =
  559. new IpcSharedBufferCore(id, std::move(region));
  560. if (shared_buffer->memory() != nullptr &&
  561. !shared_buffers_.insert(std::make_pair(id, shared_buffer)).second) {
  562. LOG(ERROR) << "Duplicate shared buffer id " << id << " encountered";
  563. }
  564. }
  565. void DesktopSessionProxy::OnSharedMemoryRegionReleased(int id) {
  566. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  567. // Drop the cached reference to the buffer.
  568. shared_buffers_.erase(id);
  569. }
  570. void DesktopSessionProxy::OnDesktopDisplayChanged(
  571. const protocol::VideoLayout& displays) {
  572. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  573. LOG(INFO) << "DSP::OnDesktopDisplayChanged";
  574. for (int display_id = 0; display_id < displays.video_track_size();
  575. display_id++) {
  576. protocol::VideoTrackLayout track = displays.video_track(display_id);
  577. LOG(INFO) << " #" << display_id << " : "
  578. << " [" << track.x_dpi() << "," << track.y_dpi() << "]";
  579. }
  580. if (client_session_control_) {
  581. auto layout = std::make_unique<protocol::VideoLayout>();
  582. layout->CopyFrom(displays);
  583. client_session_control_->OnDesktopDisplayChanged(std::move(layout));
  584. }
  585. }
  586. void DesktopSessionProxy::OnCaptureResult(mojom::CaptureResultPtr result) {
  587. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  588. --pending_capture_frame_requests_;
  589. if (!video_capturer_) {
  590. return;
  591. }
  592. if (result->is_capture_error()) {
  593. video_capturer_->OnCaptureResult(result->get_capture_error(), nullptr);
  594. return;
  595. }
  596. // Assume that |desktop_frame| is well-formed because it was received from a
  597. // more privileged process.
  598. mojom::DesktopFramePtr& desktop_frame = result->get_desktop_frame();
  599. scoped_refptr<IpcSharedBufferCore> shared_buffer_core =
  600. GetSharedBufferCore(desktop_frame->shared_buffer_id);
  601. CHECK(shared_buffer_core.get());
  602. std::unique_ptr<webrtc::DesktopFrame> frame =
  603. std::make_unique<webrtc::SharedMemoryDesktopFrame>(
  604. desktop_frame->size, desktop_frame->stride,
  605. new IpcSharedBuffer(shared_buffer_core));
  606. frame->set_capture_time_ms(desktop_frame->capture_time_ms);
  607. frame->set_dpi(desktop_frame->dpi);
  608. frame->set_capturer_id(desktop_frame->capturer_id);
  609. for (const auto& rect : desktop_frame->dirty_region) {
  610. frame->mutable_updated_region()->AddRect(rect);
  611. }
  612. video_capturer_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
  613. std::move(frame));
  614. }
  615. void DesktopSessionProxy::OnBeginFileReadResult(
  616. IpcFileOperations::BeginFileReadCallback callback,
  617. base::CallbackListSubscription disconnect_handler_subscription,
  618. mojom::BeginFileReadResultPtr result) {
  619. // This handler is only needed until the pending_remote is returned from the
  620. // DesktopSessionAgent as the IpcFileReader will then use the new channel and
  621. // hook into its disconnect handler.
  622. disconnect_handler_subscription = {};
  623. std::move(callback).Run(std::move(result));
  624. }
  625. void DesktopSessionProxy::OnBeginFileWriteResult(
  626. IpcFileOperations::BeginFileWriteCallback callback,
  627. base::CallbackListSubscription disconnect_handler_subscription,
  628. mojom::BeginFileWriteResultPtr result) {
  629. // This handler is only needed until the pending_remote is returned from the
  630. // DesktopSessionAgent as the IpcFileWriter will then use the new channel and
  631. // hook into its disconnect handler.
  632. disconnect_handler_subscription = {};
  633. std::move(callback).Run(std::move(result));
  634. }
  635. void DesktopSessionProxy::OnMouseCursorChanged(
  636. const webrtc::MouseCursor& mouse_cursor) {
  637. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  638. if (mouse_cursor_monitor_) {
  639. mouse_cursor_monitor_->OnMouseCursor(
  640. base::WrapUnique(webrtc::MouseCursor::CopyOf(mouse_cursor)));
  641. }
  642. }
  643. void DesktopSessionProxy::OnKeyboardLayoutChanged(
  644. const protocol::KeyboardLayout& layout) {
  645. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  646. keyboard_layout_ = layout;
  647. if (keyboard_layout_monitor_) {
  648. keyboard_layout_monitor_->OnKeyboardChanged(layout);
  649. }
  650. }
  651. void DesktopSessionProxy::OnClipboardEvent(
  652. const protocol::ClipboardEvent& event) {
  653. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  654. if (client_clipboard_) {
  655. client_clipboard_->InjectClipboardEvent(event);
  656. }
  657. }
  658. void DesktopSessionProxy::SignalWebAuthnExtension() {
  659. DCHECK(caller_task_runner_->BelongsToCurrentThread());
  660. if (desktop_session_control_) {
  661. desktop_session_control_->SignalWebAuthnExtension();
  662. }
  663. }
  664. // static
  665. void DesktopSessionProxyTraits::Destruct(
  666. const DesktopSessionProxy* desktop_session_proxy) {
  667. desktop_session_proxy->caller_task_runner_->DeleteSoon(FROM_HERE,
  668. desktop_session_proxy);
  669. }
  670. } // namespace remoting