Chromoting: Move host input and window mgmt into DesktopEnvironment
[chromium-blink-merge.git] / remoting / host / chromoting_host.cc
blob87902727b59c98ff924c2684d353c4b397f997f9
1 // Copyright (c) 2011 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.
5 #include "remoting/host/chromoting_host.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "build/build_config.h"
10 #include "remoting/base/constants.h"
11 #include "remoting/base/encoder.h"
12 #include "remoting/base/encoder_row_based.h"
13 #include "remoting/base/encoder_vp8.h"
14 #include "remoting/host/chromoting_host_context.h"
15 #include "remoting/host/curtain.h"
16 #include "remoting/host/desktop_environment.h"
17 #include "remoting/host/event_executor.h"
18 #include "remoting/host/host_config.h"
19 #include "remoting/host/host_key_pair.h"
20 #include "remoting/host/screen_recorder.h"
21 #include "remoting/host/user_authenticator.h"
22 #include "remoting/jingle_glue/xmpp_signal_strategy.h"
23 #include "remoting/proto/auth.pb.h"
24 #include "remoting/protocol/connection_to_client.h"
25 #include "remoting/protocol/client_stub.h"
26 #include "remoting/protocol/host_stub.h"
27 #include "remoting/protocol/input_stub.h"
28 #include "remoting/protocol/jingle_session_manager.h"
29 #include "remoting/protocol/session_config.h"
31 using remoting::protocol::ConnectionToClient;
32 using remoting::protocol::InputStub;
34 namespace remoting {
36 // static
37 ChromotingHost* ChromotingHost::Create(ChromotingHostContext* context,
38 MutableHostConfig* config,
39 AccessVerifier* access_verifier) {
40 DesktopEnvironment* desktop_env = DesktopEnvironment::Create(context);
41 return Create(context, config, desktop_env, access_verifier);
44 // static
45 ChromotingHost* ChromotingHost::Create(ChromotingHostContext* context,
46 MutableHostConfig* config,
47 DesktopEnvironment* environment,
48 AccessVerifier* access_verifier) {
49 return new ChromotingHost(context, config, environment, access_verifier);
52 ChromotingHost::ChromotingHost(ChromotingHostContext* context,
53 MutableHostConfig* config,
54 DesktopEnvironment* environment,
55 AccessVerifier* access_verifier)
56 : context_(context),
57 config_(config),
58 desktop_environment_(environment),
59 access_verifier_(access_verifier),
60 state_(kInitial),
61 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
62 is_curtained_(false),
63 is_it2me_(false) {
64 DCHECK(desktop_environment_.get());
65 desktop_environment_->set_host(this);
68 ChromotingHost::~ChromotingHost() {
71 void ChromotingHost::Start() {
72 if (MessageLoop::current() != context_->network_message_loop()) {
73 context_->network_message_loop()->PostTask(
74 FROM_HERE, base::Bind(&ChromotingHost::Start, this));
75 return;
78 DCHECK(!signal_strategy_.get());
79 DCHECK(access_verifier_.get());
81 // Make sure this object is not started.
83 base::AutoLock auto_lock(lock_);
84 if (state_ != kInitial)
85 return;
86 state_ = kStarted;
89 std::string xmpp_login;
90 std::string xmpp_auth_token;
91 std::string xmpp_auth_service;
92 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
93 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token) ||
94 !config_->GetString(kXmppAuthServiceConfigPath, &xmpp_auth_service)) {
95 LOG(ERROR) << "XMPP credentials are not defined in the config.";
96 return;
99 // Connect to the talk network with a JingleClient.
100 signal_strategy_.reset(
101 new XmppSignalStrategy(context_->jingle_thread(), xmpp_login,
102 xmpp_auth_token,
103 xmpp_auth_service));
104 signal_strategy_->Init(this);
107 // This method is called when we need to destroy the host process.
108 void ChromotingHost::Shutdown(Task* shutdown_task) {
109 if (MessageLoop::current() != context_->main_message_loop()) {
110 context_->main_message_loop()->PostTask(
111 FROM_HERE,
112 base::Bind(&ChromotingHost::Shutdown, this, shutdown_task));
113 return;
116 // No-op if this object is not started yet.
118 base::AutoLock auto_lock(lock_);
119 if (state_ == kInitial || state_ == kStopped) {
120 // Nothing to do if we are not started.
121 state_ = kStopped;
122 context_->main_message_loop()->PostTask(FROM_HERE, shutdown_task);
123 return;
125 if (shutdown_task)
126 shutdown_tasks_.push_back(shutdown_task);
127 if (state_ == kStopping)
128 return;
129 state_ = kStopping;
132 // Make sure ScreenRecorder doesn't write to the connection.
133 if (recorder_.get()) {
134 recorder_->RemoveAllConnections();
137 // Stop all desktop interaction.
138 desktop_environment_->OnLastDisconnect();
140 // Disconnect the clients.
141 for (size_t i = 0; i < clients_.size(); i++) {
142 clients_[i]->Disconnect();
144 clients_.clear();
146 ShutdownNetwork();
149 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
150 DCHECK_EQ(state_, kInitial);
151 status_observers_.push_back(observer);
154 ////////////////////////////////////////////////////////////////////////////
155 // protocol::ConnectionToClient::EventHandler implementations
156 void ChromotingHost::OnConnectionOpened(ConnectionToClient* connection) {
157 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
158 VLOG(1) << "Connection to client established.";
159 // TODO(wez): ChromotingHost shouldn't need to know about Me2Mom.
160 if (is_it2me_) {
161 context_->main_message_loop()->PostTask(
162 FROM_HERE, base::Bind(&ChromotingHost::ProcessPreAuthentication, this,
163 make_scoped_refptr(connection)));
167 void ChromotingHost::OnConnectionClosed(ConnectionToClient* connection) {
168 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
170 VLOG(1) << "Connection to client closed.";
171 context_->main_message_loop()->PostTask(
172 FROM_HERE, base::Bind(&ChromotingHost::OnClientDisconnected, this,
173 make_scoped_refptr(connection)));
176 void ChromotingHost::OnConnectionFailed(ConnectionToClient* connection) {
177 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
179 LOG(ERROR) << "Connection failed unexpectedly.";
180 context_->main_message_loop()->PostTask(
181 FROM_HERE, base::Bind(&ChromotingHost::OnClientDisconnected, this,
182 make_scoped_refptr(connection)));
185 void ChromotingHost::OnSequenceNumberUpdated(ConnectionToClient* connection,
186 int64 sequence_number) {
187 // Update the sequence number in ScreenRecorder.
188 if (MessageLoop::current() != context_->main_message_loop()) {
189 context_->main_message_loop()->PostTask(
190 FROM_HERE, base::Bind(&ChromotingHost::OnSequenceNumberUpdated, this,
191 make_scoped_refptr(connection), sequence_number));
192 return;
195 if (recorder_.get())
196 recorder_->UpdateSequenceNumber(sequence_number);
199 ////////////////////////////////////////////////////////////////////////////
200 // JingleClient::Callback implementations
201 void ChromotingHost::OnStateChange(
202 SignalStrategy::StatusObserver::State state) {
203 DCHECK_EQ(MessageLoop::current(), context_->network_message_loop());
205 if (state == SignalStrategy::StatusObserver::CONNECTED) {
206 VLOG(1) << "Host connected as " << local_jid_;
208 // Create and start session manager.
209 protocol::JingleSessionManager* server =
210 protocol::JingleSessionManager::CreateNotSandboxed();
211 // TODO(ajwong): Make this a command switch when we're more stable.
212 server->set_allow_local_ips(true);
214 // Assign key and certificate to server.
215 HostKeyPair key_pair;
216 CHECK(key_pair.Load(config_))
217 << "Failed to load server authentication data";
219 server->Init(local_jid_, signal_strategy_.get(),
220 NewCallback(this, &ChromotingHost::OnNewClientSession),
221 key_pair.CopyPrivateKey(), key_pair.GenerateCertificate());
223 session_manager_.reset(server);
225 for (StatusObserverList::iterator it = status_observers_.begin();
226 it != status_observers_.end(); ++it) {
227 (*it)->OnSignallingConnected(signal_strategy_.get(), local_jid_);
229 } else if (state == SignalStrategy::StatusObserver::CLOSED) {
230 VLOG(1) << "Host disconnected from talk network.";
231 for (StatusObserverList::iterator it = status_observers_.begin();
232 it != status_observers_.end(); ++it) {
233 (*it)->OnSignallingDisconnected();
235 // TODO(sergeyu): Don't shutdown the host and let the upper level
236 // decide what needs to be done when signalling channel is
237 // disconnected.
238 Shutdown(NULL);
242 void ChromotingHost::OnJidChange(const std::string& full_jid) {
243 DCHECK_EQ(MessageLoop::current(), context_->network_message_loop());
244 local_jid_ = full_jid;
247 void ChromotingHost::OnNewClientSession(
248 protocol::Session* session,
249 protocol::SessionManager::IncomingSessionResponse* response) {
250 base::AutoLock auto_lock(lock_);
251 if (state_ != kStarted) {
252 *response = protocol::SessionManager::DECLINE;
253 return;
256 // If we are running Me2Mom and already have an authenticated client then
257 // reject the connection immediately.
258 if (is_it2me_ && AuthenticatedClientsCount() > 0) {
259 *response = protocol::SessionManager::DECLINE;
260 return;
263 // Check that the client has access to the host.
264 if (!access_verifier_->VerifyPermissions(session->jid(),
265 session->initiator_token())) {
266 *response = protocol::SessionManager::DECLINE;
268 // Notify observers.
269 for (StatusObserverList::iterator it = status_observers_.begin();
270 it != status_observers_.end(); ++it) {
271 (*it)->OnAccessDenied();
273 return;
276 // TODO(simonmorris): The resolution is set in the video stream now,
277 // so it doesn't need to be set here.
278 *protocol_config_->mutable_initial_resolution() =
279 protocol::ScreenResolution(2048, 2048);
280 // TODO(sergeyu): Respect resolution requested by the client if supported.
281 protocol::SessionConfig* config = protocol_config_->Select(
282 session->candidate_config(), true /* force_host_resolution */);
284 if (!config) {
285 LOG(WARNING) << "Rejecting connection from " << session->jid()
286 << " because no compatible configuration has been found.";
287 *response = protocol::SessionManager::INCOMPATIBLE;
288 return;
291 session->set_config(config);
292 session->set_receiver_token(
293 GenerateHostAuthToken(session->initiator_token()));
295 *response = protocol::SessionManager::ACCEPT;
297 VLOG(1) << "Client connected: " << session->jid();
299 // We accept the connection, so create a connection object.
300 ConnectionToClient* connection = new ConnectionToClient(
301 context_->network_message_loop(), this);
303 // Create a client object.
304 ClientSession* client = new ClientSession(
305 this,
306 UserAuthenticator::Create(),
307 connection,
308 desktop_environment_->event_executor());
309 connection->set_host_stub(client);
310 connection->set_input_stub(client);
312 connection->Init(session);
314 clients_.push_back(client);
317 void ChromotingHost::set_protocol_config(
318 protocol::CandidateSessionConfig* config) {
319 DCHECK(config_.get());
320 DCHECK_EQ(state_, kInitial);
321 protocol_config_.reset(config);
324 void ChromotingHost::LocalMouseMoved(const gfx::Point& new_pos) {
325 if (MessageLoop::current() != context_->network_message_loop()) {
326 context_->network_message_loop()->PostTask(
327 FROM_HERE, base::Bind(&ChromotingHost::LocalMouseMoved, this, new_pos));
328 return;
330 ClientList::iterator client;
331 for (client = clients_.begin(); client != clients_.end(); ++client) {
332 client->get()->LocalMouseMoved(new_pos);
336 void ChromotingHost::PauseSession(bool pause) {
337 if (context_->main_message_loop() != MessageLoop::current()) {
338 context_->main_message_loop()->PostTask(
339 FROM_HERE,
340 NewRunnableMethod(this,
341 &ChromotingHost::PauseSession,
342 pause));
343 return;
345 ClientList::iterator client;
346 for (client = clients_.begin(); client != clients_.end(); ++client) {
347 client->get()->set_awaiting_continue_approval(pause);
349 desktop_environment_->OnPause(!pause);
352 void ChromotingHost::OnClientDisconnected(ConnectionToClient* connection) {
353 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
355 // Find the client session corresponding to the given connection.
356 ClientList::iterator client;
357 for (client = clients_.begin(); client != clients_.end(); ++client) {
358 if (client->get()->connection() == connection)
359 break;
361 if (client == clients_.end())
362 return;
364 // Remove the connection from the session manager and stop the session.
365 // TODO(hclam): Stop only if the last connection disconnected.
366 if (recorder_.get()) {
367 recorder_->RemoveConnection(connection);
368 // The recorder only exists to serve the unique authenticated client.
369 // If that client has disconnected, then we can kill the recorder.
370 if (client->get()->authenticated()) {
371 recorder_->Stop(NULL);
372 recorder_ = NULL;
376 // Close the connection to connection just to be safe.
377 connection->Disconnect();
379 // Also remove reference to ConnectionToClient from this object.
380 int old_authenticated_clients = AuthenticatedClientsCount();
381 clients_.erase(client);
383 // Notify the observers of the change, if any.
384 int authenticated_clients = AuthenticatedClientsCount();
385 if (old_authenticated_clients != authenticated_clients) {
386 for (StatusObserverList::iterator it = status_observers_.begin();
387 it != status_observers_.end(); ++it) {
388 (*it)->OnAuthenticatedClientsChanged(authenticated_clients);
392 // Disable the "curtain" if there are no more active clients.
393 if (AuthenticatedClientsCount() == 0) {
394 EnableCurtainMode(false);
395 if (is_it2me_) {
396 desktop_environment_->OnLastDisconnect();
401 // TODO(sergeyu): Move this to SessionManager?
402 Encoder* ChromotingHost::CreateEncoder(const protocol::SessionConfig* config) {
403 const protocol::ChannelConfig& video_config = config->video_config();
405 if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) {
406 return EncoderRowBased::CreateVerbatimEncoder();
407 } else if (video_config.codec == protocol::ChannelConfig::CODEC_ZIP) {
408 return EncoderRowBased::CreateZlibEncoder();
410 // TODO(sergeyu): Enable VP8 on ARM builds.
411 #if !defined(ARCH_CPU_ARM_FAMILY)
412 else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) {
413 return new remoting::EncoderVp8();
415 #endif
417 return NULL;
420 std::string ChromotingHost::GenerateHostAuthToken(
421 const std::string& encoded_client_token) {
422 // TODO(ajwong): Return the signature of this instead.
423 return encoded_client_token;
426 int ChromotingHost::AuthenticatedClientsCount() const {
427 int authenticated_clients = 0;
428 for (ClientList::const_iterator it = clients_.begin(); it != clients_.end();
429 ++it) {
430 if (it->get()->authenticated())
431 ++authenticated_clients;
433 return authenticated_clients;
436 void ChromotingHost::EnableCurtainMode(bool enable) {
437 // TODO(jamiewalch): This will need to be more sophisticated when we think
438 // about proper crash recovery and daemon mode.
439 // TODO(wez): CurtainMode shouldn't be driven directly by ChromotingHost.
440 if (is_it2me_ || enable == is_curtained_)
441 return;
442 desktop_environment_->curtain()->EnableCurtainMode(enable);
443 is_curtained_ = enable;
446 void ChromotingHost::LocalLoginSucceeded(
447 scoped_refptr<ConnectionToClient> connection) {
448 if (MessageLoop::current() != context_->main_message_loop()) {
449 context_->main_message_loop()->PostTask(
450 FROM_HERE, base::Bind(&ChromotingHost::LocalLoginSucceeded, this,
451 connection));
452 return;
455 protocol::LocalLoginStatus* status = new protocol::LocalLoginStatus();
456 status->set_success(true);
457 connection->client_stub()->BeginSessionResponse(
458 status, new DeleteTask<protocol::LocalLoginStatus>(status));
460 // Disconnect all other clients.
461 // Iterate over a copy of the list of clients, to avoid mutating the list
462 // while iterating over it.
463 ClientList clients_copy(clients_);
464 for (ClientList::const_iterator client = clients_copy.begin();
465 client != clients_copy.end(); client++) {
466 ConnectionToClient* connection_other = client->get()->connection();
467 if (connection_other != connection) {
468 OnClientDisconnected(connection_other);
471 // Those disconnections should have killed the screen recorder.
472 CHECK(recorder_.get() == NULL);
474 // Create a new RecordSession if there was none.
475 if (!recorder_.get()) {
476 // Then we create a ScreenRecorder passing the message loops that
477 // it should run on.
478 Encoder* encoder = CreateEncoder(connection->session()->config());
480 recorder_ = new ScreenRecorder(context_->main_message_loop(),
481 context_->encode_message_loop(),
482 context_->network_message_loop(),
483 desktop_environment_->capturer(),
484 encoder);
487 // Immediately add the connection and start the session.
488 recorder_->AddConnection(connection);
489 recorder_->Start();
490 // TODO(jamiewalch): Tidy up actions to be taken on connect/disconnect,
491 // including closing the connection on failure of a critical operation.
492 EnableCurtainMode(true);
493 if (is_it2me_) {
494 std::string username = connection->session()->jid();
495 size_t pos = username.find('/');
496 if (pos != std::string::npos)
497 username.replace(pos, std::string::npos, "");
498 desktop_environment_->OnConnect(username);
501 // Notify observers that there is at least one authenticated client.
502 for (StatusObserverList::iterator it = status_observers_.begin();
503 it != status_observers_.end(); ++it) {
504 (*it)->OnAuthenticatedClientsChanged(AuthenticatedClientsCount());
508 void ChromotingHost::LocalLoginFailed(
509 scoped_refptr<ConnectionToClient> connection) {
510 if (MessageLoop::current() != context_->main_message_loop()) {
511 context_->main_message_loop()->PostTask(
512 FROM_HERE, base::Bind(&ChromotingHost::LocalLoginFailed, this,
513 connection));
514 return;
517 protocol::LocalLoginStatus* status = new protocol::LocalLoginStatus();
518 status->set_success(false);
519 connection->client_stub()->BeginSessionResponse(
520 status, new DeleteTask<protocol::LocalLoginStatus>(status));
523 void ChromotingHost::ProcessPreAuthentication(
524 const scoped_refptr<ConnectionToClient>& connection) {
525 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
526 // Find the client session corresponding to the given connection.
527 ClientList::iterator client;
528 for (client = clients_.begin(); client != clients_.end(); ++client) {
529 if (client->get()->connection() == connection)
530 break;
532 CHECK(client != clients_.end());
533 client->get()->OnAuthorizationComplete(true);
536 void ChromotingHost::ShutdownNetwork() {
537 if (MessageLoop::current() != context_->network_message_loop()) {
538 context_->network_message_loop()->PostTask(
539 FROM_HERE, base::Bind(&ChromotingHost::ShutdownNetwork, this));
540 return;
543 // Stop chromotocol session manager.
544 if (session_manager_.get()) {
545 session_manager_->Close();
546 session_manager_.reset();
549 // Stop XMPP connection.
550 if (signal_strategy_.get()) {
551 signal_strategy_->Close();
552 signal_strategy_.reset();
554 for (StatusObserverList::iterator it = status_observers_.begin();
555 it != status_observers_.end(); ++it) {
556 (*it)->OnSignallingDisconnected();
560 ShutdownRecorder();
563 void ChromotingHost::ShutdownRecorder() {
564 if (MessageLoop::current() != context_->main_message_loop()) {
565 context_->main_message_loop()->PostTask(
566 FROM_HERE, base::Bind(&ChromotingHost::ShutdownRecorder, this));
567 return;
570 if (recorder_.get()) {
571 recorder_->Stop(NewRunnableMethod(this, &ChromotingHost::ShutdownFinish));
572 } else {
573 ShutdownFinish();
577 void ChromotingHost::ShutdownFinish() {
578 if (MessageLoop::current() != context_->main_message_loop()) {
579 context_->main_message_loop()->PostTask(
580 FROM_HERE, base::Bind(&ChromotingHost::ShutdownFinish, this));
581 return;
585 base::AutoLock auto_lock(lock_);
586 state_ = kStopped;
589 // Notify observers.
590 for (StatusObserverList::iterator it = status_observers_.begin();
591 it != status_observers_.end(); ++it) {
592 (*it)->OnShutdown();
595 for (std::vector<Task*>::iterator it = shutdown_tasks_.begin();
596 it != shutdown_tasks_.end(); ++it) {
597 (*it)->Run();
598 delete *it;
600 shutdown_tasks_.clear();
603 } // namespace remoting