Conenct Chromoting plugin debug log to JS UI.
[chromium-blink-merge.git] / remoting / host / chromoting_host.cc
blob49c315bdb3d42e570f8e2888287e51012a87a19a
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 "base/logging.h"
10 #include "build/build_config.h"
11 #include "remoting/base/constants.h"
12 #include "remoting/base/encoder.h"
13 #include "remoting/base/encoder_row_based.h"
14 #include "remoting/base/encoder_vp8.h"
15 #include "remoting/base/logger.h"
16 #include "remoting/host/chromoting_host_context.h"
17 #include "remoting/host/curtain.h"
18 #include "remoting/host/desktop_environment.h"
19 #include "remoting/host/event_executor.h"
20 #include "remoting/host/host_config.h"
21 #include "remoting/host/host_key_pair.h"
22 #include "remoting/host/screen_recorder.h"
23 #include "remoting/host/user_authenticator.h"
24 #include "remoting/jingle_glue/xmpp_signal_strategy.h"
25 #include "remoting/proto/auth.pb.h"
26 #include "remoting/protocol/connection_to_client.h"
27 #include "remoting/protocol/client_stub.h"
28 #include "remoting/protocol/host_stub.h"
29 #include "remoting/protocol/input_stub.h"
30 #include "remoting/protocol/jingle_session_manager.h"
31 #include "remoting/protocol/session_config.h"
33 using remoting::protocol::ConnectionToClient;
34 using remoting::protocol::InputStub;
36 namespace remoting {
38 // static
39 ChromotingHost* ChromotingHost::Create(ChromotingHostContext* context,
40 MutableHostConfig* config,
41 AccessVerifier* access_verifier,
42 Logger* logger) {
43 DesktopEnvironment* desktop_env = DesktopEnvironment::Create(context);
44 return Create(context, config, desktop_env, access_verifier, logger);
47 // static
48 ChromotingHost* ChromotingHost::Create(ChromotingHostContext* context,
49 MutableHostConfig* config,
50 DesktopEnvironment* environment,
51 AccessVerifier* access_verifier,
52 Logger* logger) {
53 return new ChromotingHost(context, config, environment, access_verifier,
54 logger);
57 ChromotingHost::ChromotingHost(ChromotingHostContext* context,
58 MutableHostConfig* config,
59 DesktopEnvironment* environment,
60 AccessVerifier* access_verifier,
61 Logger* logger)
62 : context_(context),
63 config_(config),
64 desktop_environment_(environment),
65 access_verifier_(access_verifier),
66 logger_(logger),
67 state_(kInitial),
68 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
69 is_curtained_(false),
70 is_it2me_(false) {
71 DCHECK(desktop_environment_.get());
72 desktop_environment_->set_host(this);
75 ChromotingHost::~ChromotingHost() {
78 void ChromotingHost::Start() {
79 if (MessageLoop::current() != context_->network_message_loop()) {
80 context_->network_message_loop()->PostTask(
81 FROM_HERE, base::Bind(&ChromotingHost::Start, this));
82 return;
85 logger_->Log(logging::LOG_INFO, "Starting host");
86 DCHECK(!signal_strategy_.get());
87 DCHECK(access_verifier_.get());
89 // Make sure this object is not started.
91 base::AutoLock auto_lock(lock_);
92 if (state_ != kInitial)
93 return;
94 state_ = kStarted;
97 std::string xmpp_login;
98 std::string xmpp_auth_token;
99 std::string xmpp_auth_service;
100 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) ||
101 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token) ||
102 !config_->GetString(kXmppAuthServiceConfigPath, &xmpp_auth_service)) {
103 logger_->Log(logging::LOG_ERROR,
104 "XMPP credentials are not defined in the config.");
105 return;
108 // Connect to the talk network with a JingleClient.
109 signal_strategy_.reset(
110 new XmppSignalStrategy(context_->jingle_thread(), xmpp_login,
111 xmpp_auth_token,
112 xmpp_auth_service));
113 signal_strategy_->Init(this);
116 // This method is called when we need to destroy the host process.
117 void ChromotingHost::Shutdown(Task* shutdown_task) {
118 if (MessageLoop::current() != context_->main_message_loop()) {
119 context_->main_message_loop()->PostTask(
120 FROM_HERE,
121 base::Bind(&ChromotingHost::Shutdown, this, shutdown_task));
122 return;
125 // No-op if this object is not started yet.
127 base::AutoLock auto_lock(lock_);
128 if (state_ == kInitial || state_ == kStopped) {
129 // Nothing to do if we are not started.
130 state_ = kStopped;
131 context_->main_message_loop()->PostTask(FROM_HERE, shutdown_task);
132 return;
134 if (shutdown_task)
135 shutdown_tasks_.push_back(shutdown_task);
136 if (state_ == kStopping)
137 return;
138 state_ = kStopping;
141 // Make sure ScreenRecorder doesn't write to the connection.
142 if (recorder_.get()) {
143 recorder_->RemoveAllConnections();
146 // Stop all desktop interaction.
147 desktop_environment_->OnLastDisconnect();
149 // Disconnect the clients.
150 for (size_t i = 0; i < clients_.size(); i++) {
151 clients_[i]->Disconnect();
153 clients_.clear();
155 ShutdownNetwork();
158 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
159 DCHECK_EQ(state_, kInitial);
160 status_observers_.push_back(observer);
163 ////////////////////////////////////////////////////////////////////////////
164 // protocol::ConnectionToClient::EventHandler implementations
165 void ChromotingHost::OnConnectionOpened(ConnectionToClient* connection) {
166 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
167 logger_->VLog(1, "Connection to client established.");
168 // TODO(wez): ChromotingHost shouldn't need to know about Me2Mom.
169 if (is_it2me_) {
170 context_->main_message_loop()->PostTask(
171 FROM_HERE, base::Bind(&ChromotingHost::ProcessPreAuthentication, this,
172 make_scoped_refptr(connection)));
176 void ChromotingHost::OnConnectionClosed(ConnectionToClient* connection) {
177 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
179 logger_->VLog(1, "Connection to client closed.");
180 context_->main_message_loop()->PostTask(
181 FROM_HERE, base::Bind(&ChromotingHost::OnClientDisconnected, this,
182 make_scoped_refptr(connection)));
185 void ChromotingHost::OnConnectionFailed(ConnectionToClient* connection) {
186 DCHECK_EQ(context_->network_message_loop(), MessageLoop::current());
188 logger_->Log(logging::LOG_ERROR, "Connection failed unexpectedly.");
189 context_->main_message_loop()->PostTask(
190 FROM_HERE, base::Bind(&ChromotingHost::OnClientDisconnected, this,
191 make_scoped_refptr(connection)));
194 void ChromotingHost::OnSequenceNumberUpdated(ConnectionToClient* connection,
195 int64 sequence_number) {
196 // Update the sequence number in ScreenRecorder.
197 if (MessageLoop::current() != context_->main_message_loop()) {
198 context_->main_message_loop()->PostTask(
199 FROM_HERE, base::Bind(&ChromotingHost::OnSequenceNumberUpdated, this,
200 make_scoped_refptr(connection), sequence_number));
201 return;
204 if (recorder_.get())
205 recorder_->UpdateSequenceNumber(sequence_number);
208 ////////////////////////////////////////////////////////////////////////////
209 // JingleClient::Callback implementations
210 void ChromotingHost::OnStateChange(
211 SignalStrategy::StatusObserver::State state) {
212 DCHECK_EQ(MessageLoop::current(), context_->network_message_loop());
214 if (state == SignalStrategy::StatusObserver::CONNECTED) {
215 logger_->Log(logging::LOG_INFO, "Host connected as %s", local_jid_.c_str());
217 // Create and start session manager.
218 protocol::JingleSessionManager* server =
219 protocol::JingleSessionManager::CreateNotSandboxed();
220 // TODO(ajwong): Make this a command switch when we're more stable.
221 server->set_allow_local_ips(true);
223 // Assign key and certificate to server.
224 HostKeyPair key_pair;
225 CHECK(key_pair.Load(config_))
226 << "Failed to load server authentication data";
228 server->Init(local_jid_, signal_strategy_.get(),
229 NewCallback(this, &ChromotingHost::OnNewClientSession),
230 key_pair.CopyPrivateKey(), key_pair.GenerateCertificate());
232 session_manager_.reset(server);
234 for (StatusObserverList::iterator it = status_observers_.begin();
235 it != status_observers_.end(); ++it) {
236 (*it)->OnSignallingConnected(signal_strategy_.get(), local_jid_);
238 } else if (state == SignalStrategy::StatusObserver::CLOSED) {
239 logger_->Log(logging::LOG_INFO, "Host disconnected from talk network.");
240 for (StatusObserverList::iterator it = status_observers_.begin();
241 it != status_observers_.end(); ++it) {
242 (*it)->OnSignallingDisconnected();
244 // TODO(sergeyu): Don't shutdown the host and let the upper level
245 // decide what needs to be done when signalling channel is
246 // disconnected.
247 Shutdown(NULL);
251 void ChromotingHost::OnJidChange(const std::string& full_jid) {
252 DCHECK_EQ(MessageLoop::current(), context_->network_message_loop());
253 local_jid_ = full_jid;
256 void ChromotingHost::OnNewClientSession(
257 protocol::Session* session,
258 protocol::SessionManager::IncomingSessionResponse* response) {
259 base::AutoLock auto_lock(lock_);
260 if (state_ != kStarted) {
261 *response = protocol::SessionManager::DECLINE;
262 return;
265 // If we are running Me2Mom and already have an authenticated client then
266 // reject the connection immediately.
267 if (is_it2me_ && AuthenticatedClientsCount() > 0) {
268 *response = protocol::SessionManager::DECLINE;
269 return;
272 // Check that the client has access to the host.
273 if (!access_verifier_->VerifyPermissions(session->jid(),
274 session->initiator_token())) {
275 *response = protocol::SessionManager::DECLINE;
277 // Notify observers.
278 for (StatusObserverList::iterator it = status_observers_.begin();
279 it != status_observers_.end(); ++it) {
280 (*it)->OnAccessDenied();
282 return;
285 // TODO(simonmorris): The resolution is set in the video stream now,
286 // so it doesn't need to be set here.
287 *protocol_config_->mutable_initial_resolution() =
288 protocol::ScreenResolution(2048, 2048);
289 // TODO(sergeyu): Respect resolution requested by the client if supported.
290 protocol::SessionConfig* config = protocol_config_->Select(
291 session->candidate_config(), true /* force_host_resolution */);
293 if (!config) {
294 logger_->Log(logging::LOG_WARNING,
295 "Rejecting connection from %s because no compatible"
296 " configuration has been found.", session->jid().c_str());
297 *response = protocol::SessionManager::INCOMPATIBLE;
298 return;
301 session->set_config(config);
302 session->set_receiver_token(
303 GenerateHostAuthToken(session->initiator_token()));
305 *response = protocol::SessionManager::ACCEPT;
307 logger_->Log(logging::LOG_INFO, "Client connected: %s",
308 session->jid().c_str());
310 // We accept the connection, so create a connection object.
311 ConnectionToClient* connection = new ConnectionToClient(
312 context_->network_message_loop(), this);
314 // Create a client object.
315 ClientSession* client = new ClientSession(
316 this,
317 UserAuthenticator::Create(),
318 connection,
319 desktop_environment_->event_executor());
320 connection->set_host_stub(client);
321 connection->set_input_stub(client);
323 connection->Init(session);
325 clients_.push_back(client);
328 void ChromotingHost::set_protocol_config(
329 protocol::CandidateSessionConfig* config) {
330 DCHECK(config_.get());
331 DCHECK_EQ(state_, kInitial);
332 protocol_config_.reset(config);
335 void ChromotingHost::LocalMouseMoved(const gfx::Point& new_pos) {
336 if (MessageLoop::current() != context_->network_message_loop()) {
337 context_->network_message_loop()->PostTask(
338 FROM_HERE, base::Bind(&ChromotingHost::LocalMouseMoved, this, new_pos));
339 return;
341 ClientList::iterator client;
342 for (client = clients_.begin(); client != clients_.end(); ++client) {
343 client->get()->LocalMouseMoved(new_pos);
347 void ChromotingHost::PauseSession(bool pause) {
348 if (context_->main_message_loop() != MessageLoop::current()) {
349 context_->main_message_loop()->PostTask(
350 FROM_HERE,
351 NewRunnableMethod(this,
352 &ChromotingHost::PauseSession,
353 pause));
354 return;
356 ClientList::iterator client;
357 for (client = clients_.begin(); client != clients_.end(); ++client) {
358 client->get()->set_awaiting_continue_approval(pause);
360 desktop_environment_->OnPause(!pause);
363 void ChromotingHost::OnClientDisconnected(ConnectionToClient* connection) {
364 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
366 // Find the client session corresponding to the given connection.
367 ClientList::iterator client;
368 for (client = clients_.begin(); client != clients_.end(); ++client) {
369 if (client->get()->connection() == connection)
370 break;
372 if (client == clients_.end())
373 return;
375 // Remove the connection from the session manager and stop the session.
376 // TODO(hclam): Stop only if the last connection disconnected.
377 if (recorder_.get()) {
378 recorder_->RemoveConnection(connection);
379 // The recorder only exists to serve the unique authenticated client.
380 // If that client has disconnected, then we can kill the recorder.
381 if (client->get()->authenticated()) {
382 recorder_->Stop(NULL);
383 recorder_ = NULL;
387 // Close the connection to connection just to be safe.
388 connection->Disconnect();
390 // Also remove reference to ConnectionToClient from this object.
391 int old_authenticated_clients = AuthenticatedClientsCount();
392 clients_.erase(client);
394 // Notify the observers of the change, if any.
395 int authenticated_clients = AuthenticatedClientsCount();
396 if (old_authenticated_clients != authenticated_clients) {
397 for (StatusObserverList::iterator it = status_observers_.begin();
398 it != status_observers_.end(); ++it) {
399 (*it)->OnAuthenticatedClientsChanged(authenticated_clients);
403 // Disable the "curtain" if there are no more active clients.
404 if (AuthenticatedClientsCount() == 0) {
405 EnableCurtainMode(false);
406 if (is_it2me_) {
407 desktop_environment_->OnLastDisconnect();
412 // TODO(sergeyu): Move this to SessionManager?
413 Encoder* ChromotingHost::CreateEncoder(const protocol::SessionConfig* config) {
414 const protocol::ChannelConfig& video_config = config->video_config();
416 if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) {
417 return EncoderRowBased::CreateVerbatimEncoder();
418 } else if (video_config.codec == protocol::ChannelConfig::CODEC_ZIP) {
419 return EncoderRowBased::CreateZlibEncoder();
421 // TODO(sergeyu): Enable VP8 on ARM builds.
422 #if !defined(ARCH_CPU_ARM_FAMILY)
423 else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) {
424 return new remoting::EncoderVp8();
426 #endif
428 return NULL;
431 std::string ChromotingHost::GenerateHostAuthToken(
432 const std::string& encoded_client_token) {
433 // TODO(ajwong): Return the signature of this instead.
434 return encoded_client_token;
437 int ChromotingHost::AuthenticatedClientsCount() const {
438 int authenticated_clients = 0;
439 for (ClientList::const_iterator it = clients_.begin(); it != clients_.end();
440 ++it) {
441 if (it->get()->authenticated())
442 ++authenticated_clients;
444 return authenticated_clients;
447 void ChromotingHost::EnableCurtainMode(bool enable) {
448 // TODO(jamiewalch): This will need to be more sophisticated when we think
449 // about proper crash recovery and daemon mode.
450 // TODO(wez): CurtainMode shouldn't be driven directly by ChromotingHost.
451 if (is_it2me_ || enable == is_curtained_)
452 return;
453 desktop_environment_->curtain()->EnableCurtainMode(enable);
454 is_curtained_ = enable;
457 void ChromotingHost::LocalLoginSucceeded(
458 scoped_refptr<ConnectionToClient> connection) {
459 if (MessageLoop::current() != context_->main_message_loop()) {
460 context_->main_message_loop()->PostTask(
461 FROM_HERE, base::Bind(&ChromotingHost::LocalLoginSucceeded, this,
462 connection));
463 return;
466 protocol::LocalLoginStatus* status = new protocol::LocalLoginStatus();
467 status->set_success(true);
468 connection->client_stub()->BeginSessionResponse(
469 status, new DeleteTask<protocol::LocalLoginStatus>(status));
471 // Disconnect all other clients.
472 // Iterate over a copy of the list of clients, to avoid mutating the list
473 // while iterating over it.
474 ClientList clients_copy(clients_);
475 for (ClientList::const_iterator client = clients_copy.begin();
476 client != clients_copy.end(); client++) {
477 ConnectionToClient* connection_other = client->get()->connection();
478 if (connection_other != connection) {
479 OnClientDisconnected(connection_other);
482 // Those disconnections should have killed the screen recorder.
483 CHECK(recorder_.get() == NULL);
485 // Create a new RecordSession if there was none.
486 if (!recorder_.get()) {
487 // Then we create a ScreenRecorder passing the message loops that
488 // it should run on.
489 Encoder* encoder = CreateEncoder(connection->session()->config());
491 recorder_ = new ScreenRecorder(context_->main_message_loop(),
492 context_->encode_message_loop(),
493 context_->network_message_loop(),
494 desktop_environment_->capturer(),
495 encoder);
498 // Immediately add the connection and start the session.
499 recorder_->AddConnection(connection);
500 recorder_->Start();
501 // TODO(jamiewalch): Tidy up actions to be taken on connect/disconnect,
502 // including closing the connection on failure of a critical operation.
503 EnableCurtainMode(true);
504 if (is_it2me_) {
505 std::string username = connection->session()->jid();
506 size_t pos = username.find('/');
507 if (pos != std::string::npos)
508 username.replace(pos, std::string::npos, "");
509 desktop_environment_->OnConnect(username);
512 // Notify observers that there is at least one authenticated client.
513 for (StatusObserverList::iterator it = status_observers_.begin();
514 it != status_observers_.end(); ++it) {
515 (*it)->OnAuthenticatedClientsChanged(AuthenticatedClientsCount());
519 void ChromotingHost::LocalLoginFailed(
520 scoped_refptr<ConnectionToClient> connection) {
521 if (MessageLoop::current() != context_->main_message_loop()) {
522 context_->main_message_loop()->PostTask(
523 FROM_HERE, base::Bind(&ChromotingHost::LocalLoginFailed, this,
524 connection));
525 return;
528 protocol::LocalLoginStatus* status = new protocol::LocalLoginStatus();
529 status->set_success(false);
530 connection->client_stub()->BeginSessionResponse(
531 status, new DeleteTask<protocol::LocalLoginStatus>(status));
534 void ChromotingHost::ProcessPreAuthentication(
535 const scoped_refptr<ConnectionToClient>& connection) {
536 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current());
537 // Find the client session corresponding to the given connection.
538 ClientList::iterator client;
539 for (client = clients_.begin(); client != clients_.end(); ++client) {
540 if (client->get()->connection() == connection)
541 break;
543 CHECK(client != clients_.end());
544 client->get()->OnAuthorizationComplete(true);
547 void ChromotingHost::ShutdownNetwork() {
548 if (MessageLoop::current() != context_->network_message_loop()) {
549 context_->network_message_loop()->PostTask(
550 FROM_HERE, base::Bind(&ChromotingHost::ShutdownNetwork, this));
551 return;
554 // Stop chromotocol session manager.
555 if (session_manager_.get()) {
556 session_manager_->Close();
557 session_manager_.reset();
560 // Stop XMPP connection.
561 if (signal_strategy_.get()) {
562 signal_strategy_->Close();
563 signal_strategy_.reset();
565 for (StatusObserverList::iterator it = status_observers_.begin();
566 it != status_observers_.end(); ++it) {
567 (*it)->OnSignallingDisconnected();
571 ShutdownRecorder();
574 void ChromotingHost::ShutdownRecorder() {
575 if (MessageLoop::current() != context_->main_message_loop()) {
576 context_->main_message_loop()->PostTask(
577 FROM_HERE, base::Bind(&ChromotingHost::ShutdownRecorder, this));
578 return;
581 if (recorder_.get()) {
582 recorder_->Stop(NewRunnableMethod(this, &ChromotingHost::ShutdownFinish));
583 } else {
584 ShutdownFinish();
588 void ChromotingHost::ShutdownFinish() {
589 if (MessageLoop::current() != context_->main_message_loop()) {
590 context_->main_message_loop()->PostTask(
591 FROM_HERE, base::Bind(&ChromotingHost::ShutdownFinish, this));
592 return;
596 base::AutoLock auto_lock(lock_);
597 state_ = kStopped;
600 // Notify observers.
601 for (StatusObserverList::iterator it = status_observers_.begin();
602 it != status_observers_.end(); ++it) {
603 (*it)->OnShutdown();
606 for (std::vector<Task*>::iterator it = shutdown_tasks_.begin();
607 it != shutdown_tasks_.end(); ++it) {
608 (*it)->Run();
609 delete *it;
611 shutdown_tasks_.clear();
614 } // namespace remoting