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 "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
;
39 ChromotingHost
* ChromotingHost::Create(ChromotingHostContext
* context
,
40 MutableHostConfig
* config
,
41 AccessVerifier
* access_verifier
,
43 DesktopEnvironment
* desktop_env
= DesktopEnvironment::Create(context
);
44 return Create(context
, config
, desktop_env
, access_verifier
, logger
);
48 ChromotingHost
* ChromotingHost::Create(ChromotingHostContext
* context
,
49 MutableHostConfig
* config
,
50 DesktopEnvironment
* environment
,
51 AccessVerifier
* access_verifier
,
53 return new ChromotingHost(context
, config
, environment
, access_verifier
,
57 ChromotingHost::ChromotingHost(ChromotingHostContext
* context
,
58 MutableHostConfig
* config
,
59 DesktopEnvironment
* environment
,
60 AccessVerifier
* access_verifier
,
64 desktop_environment_(environment
),
65 access_verifier_(access_verifier
),
68 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
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));
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
)
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.");
108 // Connect to the talk network with a JingleClient.
109 signal_strategy_
.reset(
110 new XmppSignalStrategy(context_
->jingle_thread(), xmpp_login
,
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(
121 base::Bind(&ChromotingHost::Shutdown
, this, shutdown_task
));
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.
131 context_
->main_message_loop()->PostTask(FROM_HERE
, shutdown_task
);
135 shutdown_tasks_
.push_back(shutdown_task
);
136 if (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();
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.
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
));
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
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
;
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
;
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
;
278 for (StatusObserverList::iterator it
= status_observers_
.begin();
279 it
!= status_observers_
.end(); ++it
) {
280 (*it
)->OnAccessDenied();
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 */);
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
;
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(
317 UserAuthenticator::Create(),
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
));
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(
351 NewRunnableMethod(this,
352 &ChromotingHost::PauseSession
,
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
)
372 if (client
== clients_
.end())
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
);
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);
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();
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();
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_
)
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,
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
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(),
498 // Immediately add the connection and start the session.
499 recorder_
->AddConnection(connection
);
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);
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,
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
)
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));
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();
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));
581 if (recorder_
.get()) {
582 recorder_
->Stop(NewRunnableMethod(this, &ChromotingHost::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));
596 base::AutoLock
auto_lock(lock_
);
601 for (StatusObserverList::iterator it
= status_observers_
.begin();
602 it
!= status_observers_
.end(); ++it
) {
606 for (std::vector
<Task
*>::iterator it
= shutdown_tasks_
.begin();
607 it
!= shutdown_tasks_
.end(); ++it
) {
611 shutdown_tasks_
.clear();
614 } // namespace remoting