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"
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
;
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
);
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
)
58 desktop_environment_(environment
),
59 access_verifier_(access_verifier
),
61 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
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));
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
)
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.";
99 // Connect to the talk network with a JingleClient.
100 signal_strategy_
.reset(
101 new XmppSignalStrategy(context_
->jingle_thread(), xmpp_login
,
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(
112 base::Bind(&ChromotingHost::Shutdown
, this, shutdown_task
));
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.
122 context_
->main_message_loop()->PostTask(FROM_HERE
, shutdown_task
);
126 shutdown_tasks_
.push_back(shutdown_task
);
127 if (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();
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.
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
));
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
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
;
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
;
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
;
269 for (StatusObserverList::iterator it
= status_observers_
.begin();
270 it
!= status_observers_
.end(); ++it
) {
271 (*it
)->OnAccessDenied();
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 */);
285 LOG(WARNING
) << "Rejecting connection from " << session
->jid()
286 << " because no compatible configuration has been found.";
287 *response
= protocol::SessionManager::INCOMPATIBLE
;
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(
306 UserAuthenticator::Create(),
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
));
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(
340 NewRunnableMethod(this,
341 &ChromotingHost::PauseSession
,
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
)
361 if (client
== clients_
.end())
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
);
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);
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();
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();
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_
)
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,
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
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(),
487 // Immediately add the connection and start the session.
488 recorder_
->AddConnection(connection
);
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);
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,
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
)
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));
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();
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));
570 if (recorder_
.get()) {
571 recorder_
->Stop(NewRunnableMethod(this, &ChromotingHost::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));
585 base::AutoLock
auto_lock(lock_
);
590 for (StatusObserverList::iterator it
= status_observers_
.begin();
591 it
!= status_observers_
.end(); ++it
) {
595 for (std::vector
<Task
*>::iterator it
= shutdown_tasks_
.begin();
596 it
!= shutdown_tasks_
.end(); ++it
) {
600 shutdown_tasks_
.clear();
603 } // namespace remoting