chromoting_host.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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
5#include "remoting/host/chromoting_host.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/logging.h"
10#include "base/message_loop_proxy.h"
11#include "build/build_config.h"
12#include "remoting/base/constants.h"
13#include "remoting/host/chromoting_host_context.h"
14#include "remoting/host/desktop_environment_factory.h"
15#include "remoting/host/event_executor.h"
16#include "remoting/host/host_config.h"
17#include "remoting/protocol/connection_to_client.h"
18#include "remoting/protocol/client_stub.h"
19#include "remoting/protocol/host_stub.h"
20#include "remoting/protocol/input_stub.h"
21#include "remoting/protocol/session_config.h"
22
23using remoting::protocol::ConnectionToClient;
24using remoting::protocol::InputStub;
25
26namespace remoting {
27
28namespace {
29
30const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
31  // Number of initial errors (in sequence) to ignore before applying
32  // exponential back-off rules.
33  0,
34
35  // Initial delay for exponential back-off in ms.
36  2000,
37
38  // Factor by which the waiting time will be multiplied.
39  2,
40
41  // Fuzzing percentage. ex: 10% will spread requests randomly
42  // between 90%-100% of the calculated time.
43  0,
44
45  // Maximum amount of time we are willing to delay our request in ms.
46  -1,
47
48  // Time to keep an entry from being discarded even when it
49  // has no significant state, -1 to never discard.
50  -1,
51
52  // Don't use initial delay unless the last request was an error.
53  false,
54};
55
56}  // namespace
57
58ChromotingHost::ChromotingHost(
59    SignalStrategy* signal_strategy,
60    DesktopEnvironmentFactory* desktop_environment_factory,
61    scoped_ptr<protocol::SessionManager> session_manager,
62    scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
63    scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner,
64    scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner,
65    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
66    : desktop_environment_factory_(desktop_environment_factory),
67      session_manager_(session_manager.Pass()),
68      audio_task_runner_(audio_task_runner),
69      capture_task_runner_(capture_task_runner),
70      encode_task_runner_(encode_task_runner),
71      network_task_runner_(network_task_runner),
72      signal_strategy_(signal_strategy),
73      clients_count_(0),
74      state_(kInitial),
75      protocol_config_(protocol::CandidateSessionConfig::CreateDefault()),
76      login_backoff_(&kDefaultBackoffPolicy),
77      authenticating_client_(false),
78      reject_authenticating_client_(false) {
79  DCHECK(signal_strategy);
80  DCHECK(network_task_runner_->BelongsToCurrentThread());
81
82  if (!desktop_environment_factory_->SupportsAudioCapture()) {
83    protocol::CandidateSessionConfig::DisableAudioChannel(
84        protocol_config_.get());
85  }
86}
87
88ChromotingHost::~ChromotingHost() {
89  DCHECK(clients_.empty());
90}
91
92void ChromotingHost::Start(const std::string& xmpp_login) {
93  DCHECK(network_task_runner_->BelongsToCurrentThread());
94
95  LOG(INFO) << "Starting host";
96
97  // Make sure this object is not started.
98  if (state_ != kInitial)
99    return;
100  state_ = kStarted;
101
102  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
103                    OnStart(xmpp_login));
104
105  // Start the SessionManager, supplying this ChromotingHost as the listener.
106  session_manager_->Init(signal_strategy_, this);
107}
108
109// This method is called when we need to destroy the host process.
110void ChromotingHost::Shutdown(const base::Closure& shutdown_task) {
111  if (!network_task_runner_->BelongsToCurrentThread()) {
112    network_task_runner_->PostTask(
113        FROM_HERE, base::Bind(&ChromotingHost::Shutdown, this, shutdown_task));
114    return;
115  }
116
117  switch (state_) {
118    case kInitial:
119    case kStopped:
120      // Nothing to do if we are not started.
121      state_ = kStopped;
122      if (!shutdown_task.is_null())
123        network_task_runner_->PostTask(FROM_HERE, shutdown_task);
124      break;
125
126    case kStopping:
127      // We are already stopping. Just save the task.
128      if (!shutdown_task.is_null())
129        shutdown_tasks_.push_back(shutdown_task);
130      break;
131
132    case kStarted:
133      if (!shutdown_task.is_null())
134        shutdown_tasks_.push_back(shutdown_task);
135      state_ = kStopping;
136
137      // Disconnect all of the clients.
138      while (!clients_.empty()) {
139        clients_.front()->Disconnect();
140      }
141
142      // Run the remaining shutdown tasks.
143      if (state_ == kStopping && !clients_count_)
144        ShutdownFinish();
145
146      break;
147  }
148}
149
150void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) {
151  DCHECK(network_task_runner_->BelongsToCurrentThread());
152  status_observers_.AddObserver(observer);
153}
154
155void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) {
156  DCHECK(network_task_runner_->BelongsToCurrentThread());
157  status_observers_.RemoveObserver(observer);
158}
159
160void ChromotingHost::RejectAuthenticatingClient() {
161  DCHECK(authenticating_client_);
162  reject_authenticating_client_ = true;
163}
164
165void ChromotingHost::SetAuthenticatorFactory(
166    scoped_ptr<protocol::AuthenticatorFactory> authenticator_factory) {
167  DCHECK(network_task_runner_->BelongsToCurrentThread());
168  session_manager_->set_authenticator_factory(authenticator_factory.Pass());
169}
170
171void ChromotingHost::SetMaximumSessionDuration(
172    const base::TimeDelta& max_session_duration) {
173  max_session_duration_ = max_session_duration;
174}
175
176////////////////////////////////////////////////////////////////////////////
177// protocol::ClientSession::EventHandler implementation.
178void ChromotingHost::OnSessionAuthenticated(ClientSession* client) {
179  DCHECK(network_task_runner_->BelongsToCurrentThread());
180
181  login_backoff_.Reset();
182
183  // Disconnect all other clients.
184  // Iterate over a copy of the list of clients, to avoid mutating the list
185  // while iterating over it.
186  ClientList clients_copy(clients_);
187  for (ClientList::const_iterator other_client = clients_copy.begin();
188       other_client != clients_copy.end(); ++other_client) {
189    if (other_client->get() != client) {
190      (*other_client)->Disconnect();
191    }
192  }
193
194  // Disconnects above must have destroyed all other clients and |recorder_|.
195  DCHECK_EQ(clients_.size(), 1U);
196
197  // Notify observers that there is at least one authenticated client.
198  const std::string& jid = client->client_jid();
199
200  reject_authenticating_client_ = false;
201
202  authenticating_client_ = true;
203  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
204                    OnClientAuthenticated(jid));
205  authenticating_client_ = false;
206
207  if (reject_authenticating_client_) {
208    client->Disconnect();
209  }
210}
211
212void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) {
213  DCHECK(network_task_runner_->BelongsToCurrentThread());
214
215  // Notify observers.
216  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
217                    OnClientConnected(client->client_jid()));
218}
219
220void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) {
221  DCHECK(network_task_runner_->BelongsToCurrentThread());
222
223  // Notify observers.
224  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
225                    OnAccessDenied(client->client_jid()));
226}
227
228void ChromotingHost::OnSessionClosed(ClientSession* client) {
229  DCHECK(network_task_runner_->BelongsToCurrentThread());
230
231  ClientList::iterator it = clients_.begin();
232  for (; it != clients_.end(); ++it) {
233    if (it->get() == client) {
234      break;
235    }
236  }
237  CHECK(it != clients_.end());
238
239  if (client->is_authenticated()) {
240    FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
241                      OnClientDisconnected(client->client_jid()));
242  }
243
244  client->Stop(base::Bind(&ChromotingHost::OnClientStopped, this));
245  clients_.erase(it);
246}
247
248void ChromotingHost::OnSessionSequenceNumber(ClientSession* session,
249                                             int64 sequence_number) {
250  DCHECK(network_task_runner_->BelongsToCurrentThread());
251}
252
253void ChromotingHost::OnSessionRouteChange(
254    ClientSession* session,
255    const std::string& channel_name,
256    const protocol::TransportRoute& route) {
257  DCHECK(network_task_runner_->BelongsToCurrentThread());
258  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
259                    OnClientRouteChange(session->client_jid(), channel_name,
260                                        route));
261}
262
263void ChromotingHost::OnClientDimensionsChanged(ClientSession* session,
264                                               const SkISize& size) {
265  DCHECK(network_task_runner_->BelongsToCurrentThread());
266  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
267                    OnClientDimensionsChanged(session->client_jid(), size));
268}
269
270void ChromotingHost::OnSessionManagerReady() {
271  DCHECK(network_task_runner_->BelongsToCurrentThread());
272  // Don't need to do anything here, just wait for incoming
273  // connections.
274}
275
276void ChromotingHost::OnIncomingSession(
277      protocol::Session* session,
278      protocol::SessionManager::IncomingSessionResponse* response) {
279  DCHECK(network_task_runner_->BelongsToCurrentThread());
280
281  if (state_ != kStarted) {
282    *response = protocol::SessionManager::DECLINE;
283    return;
284  }
285
286  if (login_backoff_.ShouldRejectRequest()) {
287    *response = protocol::SessionManager::OVERLOAD;
288    return;
289  }
290
291  // We treat each incoming connection as a failure to authenticate,
292  // and clear the backoff when a connection successfully
293  // authenticates. This allows the backoff to protect from parallel
294  // connection attempts as well as sequential ones.
295  login_backoff_.InformOfRequest(false);
296
297  protocol::SessionConfig config;
298  if (!protocol_config_->Select(session->candidate_config(), &config)) {
299    LOG(WARNING) << "Rejecting connection from " << session->jid()
300                 << " because no compatible configuration has been found.";
301    *response = protocol::SessionManager::INCOMPATIBLE;
302    return;
303  }
304
305  session->set_config(config);
306
307  *response = protocol::SessionManager::ACCEPT;
308
309  LOG(INFO) << "Client connected: " << session->jid();
310
311  // Create a client object.
312  scoped_ptr<protocol::ConnectionToClient> connection(
313      new protocol::ConnectionToClient(session));
314  scoped_refptr<ClientSession> client = new ClientSession(
315      this,
316      audio_task_runner_,
317      capture_task_runner_,
318      encode_task_runner_,
319      network_task_runner_,
320      connection.Pass(),
321      desktop_environment_factory_,
322      max_session_duration_);
323  clients_.push_back(client);
324  clients_count_++;
325}
326
327void ChromotingHost::set_protocol_config(
328    scoped_ptr<protocol::CandidateSessionConfig> config) {
329  DCHECK(network_task_runner_->BelongsToCurrentThread());
330  DCHECK(config.get());
331  DCHECK_EQ(state_, kInitial);
332  protocol_config_ = config.Pass();
333}
334
335void ChromotingHost::OnLocalMouseMoved(const SkIPoint& new_pos) {
336  if (!network_task_runner_->BelongsToCurrentThread()) {
337    network_task_runner_->PostTask(
338        FROM_HERE, base::Bind(&ChromotingHost::OnLocalMouseMoved,
339                              this, new_pos));
340    return;
341  }
342
343  ClientList::iterator client;
344  for (client = clients_.begin(); client != clients_.end(); ++client) {
345    (*client)->LocalMouseMoved(new_pos);
346  }
347}
348
349void ChromotingHost::PauseSession(bool pause) {
350  if (!network_task_runner_->BelongsToCurrentThread()) {
351    network_task_runner_->PostTask(
352        FROM_HERE, base::Bind(&ChromotingHost::PauseSession, this, pause));
353    return;
354  }
355
356  ClientList::iterator client;
357  for (client = clients_.begin(); client != clients_.end(); ++client) {
358    (*client)->SetDisableInputs(pause);
359  }
360}
361
362void ChromotingHost::DisconnectAllClients() {
363  if (!network_task_runner_->BelongsToCurrentThread()) {
364    network_task_runner_->PostTask(
365        FROM_HERE, base::Bind(&ChromotingHost::DisconnectAllClients, this));
366    return;
367  }
368
369  while (!clients_.empty()) {
370    size_t size = clients_.size();
371    clients_.front()->Disconnect();
372    CHECK_EQ(clients_.size(), size - 1);
373  }
374}
375
376void ChromotingHost::DisconnectClient(DesktopEnvironment* desktop_environment) {
377  DCHECK(network_task_runner_->BelongsToCurrentThread());
378
379  for (ClientList::iterator i = clients_.begin(); i != clients_.end(); ++i) {
380    if ((*i)->desktop_environment() == desktop_environment) {
381      (*i)->Disconnect();
382      break;
383    }
384  }
385}
386
387void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) {
388  DCHECK(network_task_runner_->BelongsToCurrentThread());
389  DCHECK_EQ(state_, kInitial);
390
391  ui_strings_ = ui_strings;
392}
393
394void ChromotingHost::OnClientStopped() {
395  DCHECK(network_task_runner_->BelongsToCurrentThread());
396
397  --clients_count_;
398  if (state_ == kStopping && !clients_count_)
399    ShutdownFinish();
400}
401
402void ChromotingHost::ShutdownFinish() {
403  DCHECK(network_task_runner_->BelongsToCurrentThread());
404  DCHECK_EQ(state_, kStopping);
405
406  state_ = kStopped;
407
408  // Destroy session manager.
409  session_manager_.reset();
410
411  // Clear |desktop_environment_factory_| and |signal_strategy_| to
412  // ensure we don't try to touch them after running shutdown tasks
413  desktop_environment_factory_ = NULL;
414  signal_strategy_ = NULL;
415
416  // Keep reference to |this|, so that we don't get destroyed while
417  // sending notifications.
418  scoped_refptr<ChromotingHost> self(this);
419
420  // Notify observers.
421  FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
422                    OnShutdown());
423
424  for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin();
425       it != shutdown_tasks_.end(); ++it) {
426    it->Run();
427  }
428  shutdown_tasks_.clear();
429}
430
431}  // namespace remoting
432