OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/chromoting_host.h" | 5 #include "remoting/host/chromoting_host.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
11 #include "build/build_config.h" | 11 #include "build/build_config.h" |
12 #include "remoting/base/constants.h" | 12 #include "remoting/base/constants.h" |
13 #include "remoting/codec/audio_encoder.h" | 13 #include "remoting/codec/audio_encoder.h" |
14 #include "remoting/codec/audio_encoder_speex.h" | 14 #include "remoting/codec/audio_encoder_speex.h" |
15 #include "remoting/codec/audio_encoder_verbatim.h" | 15 #include "remoting/codec/audio_encoder_verbatim.h" |
16 #include "remoting/codec/video_encoder.h" | 16 #include "remoting/codec/video_encoder.h" |
17 #include "remoting/codec/video_encoder_row_based.h" | 17 #include "remoting/codec/video_encoder_row_based.h" |
18 #include "remoting/codec/video_encoder_vp8.h" | 18 #include "remoting/codec/video_encoder_vp8.h" |
19 #include "remoting/host/audio_capturer.h" | 19 #include "remoting/host/audio_capturer.h" |
Sergey Ulanov
2012/09/11 22:30:16
Please remove this icnlude.
alexeypa (please no reviews)
2012/09/11 22:56:51
Done.
| |
20 #include "remoting/host/audio_scheduler.h" | 20 #include "remoting/host/audio_scheduler.h" |
21 #include "remoting/host/chromoting_host_context.h" | 21 #include "remoting/host/chromoting_host_context.h" |
22 #include "remoting/host/desktop_environment.h" | 22 #include "remoting/host/desktop_environment.h" |
23 #include "remoting/host/desktop_environment_factory.h" | |
23 #include "remoting/host/event_executor.h" | 24 #include "remoting/host/event_executor.h" |
24 #include "remoting/host/host_config.h" | 25 #include "remoting/host/host_config.h" |
25 #include "remoting/host/screen_recorder.h" | |
26 #include "remoting/protocol/connection_to_client.h" | 26 #include "remoting/protocol/connection_to_client.h" |
27 #include "remoting/protocol/client_stub.h" | 27 #include "remoting/protocol/client_stub.h" |
28 #include "remoting/protocol/host_stub.h" | 28 #include "remoting/protocol/host_stub.h" |
29 #include "remoting/protocol/input_stub.h" | 29 #include "remoting/protocol/input_stub.h" |
30 #include "remoting/protocol/session_config.h" | 30 #include "remoting/protocol/session_config.h" |
31 | 31 |
32 using remoting::protocol::ConnectionToClient; | 32 using remoting::protocol::ConnectionToClient; |
33 using remoting::protocol::InputStub; | 33 using remoting::protocol::InputStub; |
34 | 34 |
35 namespace remoting { | 35 namespace remoting { |
(...skipping 24 matching lines...) Expand all Loading... | |
60 | 60 |
61 // Don't use initial delay unless the last request was an error. | 61 // Don't use initial delay unless the last request was an error. |
62 false, | 62 false, |
63 }; | 63 }; |
64 | 64 |
65 } // namespace | 65 } // namespace |
66 | 66 |
67 ChromotingHost::ChromotingHost( | 67 ChromotingHost::ChromotingHost( |
68 ChromotingHostContext* context, | 68 ChromotingHostContext* context, |
69 SignalStrategy* signal_strategy, | 69 SignalStrategy* signal_strategy, |
70 DesktopEnvironment* environment, | 70 DesktopEnvironmentFactory* desktop_environment_factory, |
71 scoped_ptr<protocol::SessionManager> session_manager) | 71 scoped_ptr<protocol::SessionManager> session_manager) |
72 : context_(context), | 72 : context_(context), |
73 desktop_environment_(environment), | 73 desktop_environment_factory_(desktop_environment_factory), |
74 session_manager_(session_manager.Pass()), | 74 session_manager_(session_manager.Pass()), |
75 signal_strategy_(signal_strategy), | 75 signal_strategy_(signal_strategy), |
76 stopping_recorders_(0), | 76 clients_count_(0), |
77 state_(kInitial), | 77 state_(kInitial), |
78 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), | 78 protocol_config_(protocol::CandidateSessionConfig::CreateDefault()), |
79 login_backoff_(&kDefaultBackoffPolicy), | 79 login_backoff_(&kDefaultBackoffPolicy), |
80 authenticating_client_(false), | 80 authenticating_client_(false), |
81 reject_authenticating_client_(false) { | 81 reject_authenticating_client_(false) { |
82 DCHECK(context_); | 82 DCHECK(context_); |
83 DCHECK(signal_strategy); | 83 DCHECK(signal_strategy); |
84 DCHECK(desktop_environment_); | |
85 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 84 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
86 | 85 |
87 if (!AudioCapturer::IsSupported()) { | 86 if (!desktop_environment_factory_->SupportsAudioCapture()) { |
88 // Disable audio by replacing our list of supported audio configurations | 87 // Disable audio by replacing our list of supported audio configurations |
89 // with the NONE config. | 88 // with the NONE config. |
90 protocol_config_->mutable_audio_configs()->clear(); | 89 protocol_config_->mutable_audio_configs()->clear(); |
91 protocol_config_->mutable_audio_configs()->push_back( | 90 protocol_config_->mutable_audio_configs()->push_back( |
92 protocol::ChannelConfig()); | 91 protocol::ChannelConfig()); |
93 } | 92 } |
94 } | 93 } |
95 | 94 |
96 ChromotingHost::~ChromotingHost() { | 95 ChromotingHost::~ChromotingHost() { |
97 DCHECK(clients_.empty()); | 96 DCHECK(clients_.empty()); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
135 // We are already stopping. Just save the task. | 134 // We are already stopping. Just save the task. |
136 if (!shutdown_task.is_null()) | 135 if (!shutdown_task.is_null()) |
137 shutdown_tasks_.push_back(shutdown_task); | 136 shutdown_tasks_.push_back(shutdown_task); |
138 break; | 137 break; |
139 | 138 |
140 case kStarted: | 139 case kStarted: |
141 if (!shutdown_task.is_null()) | 140 if (!shutdown_task.is_null()) |
142 shutdown_tasks_.push_back(shutdown_task); | 141 shutdown_tasks_.push_back(shutdown_task); |
143 state_ = kStopping; | 142 state_ = kStopping; |
144 | 143 |
145 // Disconnect all of the clients, implicitly stopping the ScreenRecorder. | 144 // Disconnect all of the clients. |
146 while (!clients_.empty()) { | 145 while (!clients_.empty()) { |
147 clients_.front()->Disconnect(); | 146 clients_.front()->Disconnect(); |
148 } | 147 } |
149 DCHECK(!recorder_.get()); | |
150 DCHECK(!audio_scheduler_.get()); | |
151 | 148 |
152 // Destroy session manager. | 149 // Run the remaining shutdown tasks. |
153 session_manager_.reset(); | 150 if (state_ == kStopping && !clients_count_) |
151 ShutdownFinish(); | |
154 | 152 |
155 if (!stopping_recorders_) | |
156 ShutdownFinish(); | |
157 break; | 153 break; |
158 } | 154 } |
159 } | 155 } |
160 | 156 |
161 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { | 157 void ChromotingHost::AddStatusObserver(HostStatusObserver* observer) { |
162 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 158 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
163 status_observers_.AddObserver(observer); | 159 status_observers_.AddObserver(observer); |
164 } | 160 } |
165 | 161 |
166 void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { | 162 void ChromotingHost::RemoveStatusObserver(HostStatusObserver* observer) { |
(...skipping 23 matching lines...) Expand all Loading... | |
190 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 186 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
191 | 187 |
192 login_backoff_.Reset(); | 188 login_backoff_.Reset(); |
193 | 189 |
194 // Disconnect all other clients. | 190 // Disconnect all other clients. |
195 // Iterate over a copy of the list of clients, to avoid mutating the list | 191 // Iterate over a copy of the list of clients, to avoid mutating the list |
196 // while iterating over it. | 192 // while iterating over it. |
197 ClientList clients_copy(clients_); | 193 ClientList clients_copy(clients_); |
198 for (ClientList::const_iterator other_client = clients_copy.begin(); | 194 for (ClientList::const_iterator other_client = clients_copy.begin(); |
199 other_client != clients_copy.end(); ++other_client) { | 195 other_client != clients_copy.end(); ++other_client) { |
200 if ((*other_client) != client) { | 196 if (other_client->get() != client) { |
201 (*other_client)->Disconnect(); | 197 (*other_client)->Disconnect(); |
202 } | 198 } |
203 } | 199 } |
204 | 200 |
205 // Disconnects above must have destroyed all other clients and |recorder_|. | 201 // Disconnects above must have destroyed all other clients and |recorder_|. |
206 DCHECK_EQ(clients_.size(), 1U); | 202 DCHECK_EQ(clients_.size(), 1U); |
207 DCHECK(!recorder_.get()); | |
208 DCHECK(!audio_scheduler_.get()); | |
209 | 203 |
210 // Notify observers that there is at least one authenticated client. | 204 // Notify observers that there is at least one authenticated client. |
211 const std::string& jid = client->client_jid(); | 205 const std::string& jid = client->client_jid(); |
212 | 206 |
213 reject_authenticating_client_ = false; | 207 reject_authenticating_client_ = false; |
214 | 208 |
215 authenticating_client_ = true; | 209 authenticating_client_ = true; |
216 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 210 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
217 OnClientAuthenticated(jid)); | 211 OnClientAuthenticated(jid)); |
218 authenticating_client_ = false; | 212 authenticating_client_ = false; |
219 | 213 |
220 if (reject_authenticating_client_) { | 214 if (reject_authenticating_client_) { |
221 client->Disconnect(); | 215 client->Disconnect(); |
222 } | 216 } |
223 } | 217 } |
224 | 218 |
225 void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { | 219 void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { |
226 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 220 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
227 | 221 |
228 // Then we create a ScreenRecorder passing the message loops that | 222 // Notify observers. |
229 // it should run on. | |
230 VideoEncoder* video_encoder = | |
231 CreateVideoEncoder(client->connection()->session()->config()); | |
232 | |
233 recorder_ = new ScreenRecorder(context_->capture_task_runner(), | |
234 context_->encode_task_runner(), | |
235 context_->network_task_runner(), | |
236 desktop_environment_->capturer(), | |
237 video_encoder); | |
238 if (client->connection()->session()->config().is_audio_enabled()) { | |
239 scoped_ptr<AudioEncoder> audio_encoder = | |
240 CreateAudioEncoder(client->connection()->session()->config()); | |
241 audio_scheduler_ = new AudioScheduler( | |
242 context_->audio_task_runner(), | |
243 context_->network_task_runner(), | |
244 desktop_environment_->audio_capturer(), | |
245 audio_encoder.Pass(), | |
246 client->connection()->audio_stub()); | |
247 } | |
248 | |
249 // Immediately add the connection and start the session. | |
250 recorder_->AddConnection(client->connection()); | |
251 recorder_->Start(); | |
252 desktop_environment_->OnSessionStarted(client->CreateClipboardProxy()); | |
253 | |
254 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 223 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
255 OnClientConnected(client->client_jid())); | 224 OnClientConnected(client->client_jid())); |
256 } | 225 } |
257 | 226 |
258 void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { | 227 void ChromotingHost::OnSessionAuthenticationFailed(ClientSession* client) { |
259 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 228 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
260 | 229 |
261 // Notify observers. | 230 // Notify observers. |
262 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 231 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
263 OnAccessDenied(client->client_jid())); | 232 OnAccessDenied(client->client_jid())); |
264 } | 233 } |
265 | 234 |
266 void ChromotingHost::OnSessionClosed(ClientSession* client) { | 235 void ChromotingHost::OnSessionClosed(ClientSession* client) { |
267 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 236 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
268 | 237 |
269 scoped_ptr<ClientSession> client_destroyer(client); | 238 for (ClientList::iterator it = clients_.begin(); it != clients_.end(); ++it) { |
239 if (it->get() == client) { | |
240 if (client->is_authenticated()) { | |
Sergey Ulanov
2012/09/11 22:30:16
It's better to move this if and the Stop() call ou
alexeypa (please no reviews)
2012/09/11 22:56:51
Done.
| |
241 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | |
242 OnClientDisconnected(client->client_jid())); | |
243 } | |
270 | 244 |
271 ClientList::iterator it = std::find(clients_.begin(), clients_.end(), client); | 245 client->Stop(base::Bind(&ChromotingHost::OnClientStopped, this)); |
272 CHECK(it != clients_.end()); | 246 clients_.erase(it); |
273 clients_.erase(it); | 247 return; |
274 | 248 } |
275 if (recorder_.get()) { | |
276 recorder_->RemoveConnection(client->connection()); | |
277 } | 249 } |
278 | 250 |
279 if (audio_scheduler_.get()) { | 251 CHECK(!"Could not find the client session."); |
Sergey Ulanov
2012/09/11 22:30:16
NOTREACHED()?
Or if you need a message
NOTREACH
alexeypa (please no reviews)
2012/09/11 22:56:51
I restored the old CHECK after moving the logic ou
| |
280 audio_scheduler_->OnClientDisconnected(); | |
281 StopAudioScheduler(); | |
282 } | |
283 | |
284 if (client->is_authenticated()) { | |
285 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | |
286 OnClientDisconnected(client->client_jid())); | |
287 | |
288 // TODO(sergeyu): This teardown logic belongs to ClientSession | |
289 // class. It should start/stop screen recorder or tell the host | |
290 // when to do it. | |
291 if (recorder_.get()) { | |
292 // Currently we don't allow more than one simultaneous connection, | |
293 // so we need to shutdown recorder when a client disconnects. | |
294 StopScreenRecorder(); | |
295 } | |
296 desktop_environment_->OnSessionFinished(); | |
297 } | |
298 } | 252 } |
299 | 253 |
300 void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, | 254 void ChromotingHost::OnSessionSequenceNumber(ClientSession* session, |
301 int64 sequence_number) { | 255 int64 sequence_number) { |
302 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 256 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
303 if (recorder_.get()) | |
304 recorder_->UpdateSequenceNumber(sequence_number); | |
305 } | 257 } |
306 | 258 |
307 void ChromotingHost::OnSessionRouteChange( | 259 void ChromotingHost::OnSessionRouteChange( |
308 ClientSession* session, | 260 ClientSession* session, |
309 const std::string& channel_name, | 261 const std::string& channel_name, |
310 const protocol::TransportRoute& route) { | 262 const protocol::TransportRoute& route) { |
311 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 263 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
312 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 264 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
313 OnClientRouteChange(session->client_jid(), channel_name, | 265 OnClientRouteChange(session->client_jid(), channel_name, |
314 route)); | 266 route)); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
348 *response = protocol::SessionManager::INCOMPATIBLE; | 300 *response = protocol::SessionManager::INCOMPATIBLE; |
349 return; | 301 return; |
350 } | 302 } |
351 | 303 |
352 session->set_config(config); | 304 session->set_config(config); |
353 | 305 |
354 *response = protocol::SessionManager::ACCEPT; | 306 *response = protocol::SessionManager::ACCEPT; |
355 | 307 |
356 LOG(INFO) << "Client connected: " << session->jid(); | 308 LOG(INFO) << "Client connected: " << session->jid(); |
357 | 309 |
310 // Create the desktop integration implementation for the client to use. | |
311 scoped_ptr<DesktopEnvironment> desktop_environment = | |
312 desktop_environment_factory_->Create(context_); | |
313 | |
358 // Create a client object. | 314 // Create a client object. |
359 scoped_ptr<protocol::ConnectionToClient> connection( | 315 scoped_ptr<protocol::ConnectionToClient> connection( |
360 new protocol::ConnectionToClient(session)); | 316 new protocol::ConnectionToClient(session)); |
361 ClientSession* client = new ClientSession( | 317 scoped_refptr<ClientSession> client = new ClientSession( |
362 this, | 318 this, |
319 context_->capture_task_runner(), | |
320 context_->encode_task_runner(), | |
321 context_->network_task_runner(), | |
363 connection.Pass(), | 322 connection.Pass(), |
364 desktop_environment_->event_executor(), | 323 desktop_environment.Pass(), |
365 desktop_environment_->event_executor(), | |
366 desktop_environment_->capturer(), | |
367 max_session_duration_); | 324 max_session_duration_); |
368 clients_.push_back(client); | 325 clients_.push_back(client); |
326 clients_count_++; | |
369 } | 327 } |
370 | 328 |
371 void ChromotingHost::set_protocol_config( | 329 void ChromotingHost::set_protocol_config( |
372 protocol::CandidateSessionConfig* config) { | 330 protocol::CandidateSessionConfig* config) { |
373 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 331 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
374 DCHECK(config); | 332 DCHECK(config); |
375 DCHECK_EQ(state_, kInitial); | 333 DCHECK_EQ(state_, kInitial); |
376 protocol_config_.reset(config); | 334 protocol_config_.reset(config); |
377 } | 335 } |
378 | 336 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
417 } | 375 } |
418 } | 376 } |
419 | 377 |
420 void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) { | 378 void ChromotingHost::SetUiStrings(const UiStrings& ui_strings) { |
421 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 379 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
422 DCHECK_EQ(state_, kInitial); | 380 DCHECK_EQ(state_, kInitial); |
423 | 381 |
424 ui_strings_ = ui_strings; | 382 ui_strings_ = ui_strings; |
425 } | 383 } |
426 | 384 |
427 // TODO(sergeyu): Move this to SessionManager? | 385 void ChromotingHost::OnClientStopped() { |
428 // static | 386 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
429 VideoEncoder* ChromotingHost::CreateVideoEncoder( | |
430 const protocol::SessionConfig& config) { | |
431 const protocol::ChannelConfig& video_config = config.video_config(); | |
432 | 387 |
433 if (video_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { | 388 --clients_count_; |
434 return VideoEncoderRowBased::CreateVerbatimEncoder(); | 389 if (state_ == kStopping && !clients_count_) |
435 } else if (video_config.codec == protocol::ChannelConfig::CODEC_ZIP) { | |
436 return VideoEncoderRowBased::CreateZlibEncoder(); | |
437 } else if (video_config.codec == protocol::ChannelConfig::CODEC_VP8) { | |
438 return new remoting::VideoEncoderVp8(); | |
439 } | |
440 | |
441 return NULL; | |
442 } | |
443 | |
444 // static | |
445 scoped_ptr<AudioEncoder> ChromotingHost::CreateAudioEncoder( | |
446 const protocol::SessionConfig& config) { | |
447 const protocol::ChannelConfig& audio_config = config.audio_config(); | |
448 | |
449 if (audio_config.codec == protocol::ChannelConfig::CODEC_VERBATIM) { | |
450 return scoped_ptr<AudioEncoder>(new AudioEncoderVerbatim()); | |
451 } else if (audio_config.codec == protocol::ChannelConfig::CODEC_SPEEX) { | |
452 return scoped_ptr<AudioEncoder>(new AudioEncoderSpeex()); | |
453 } | |
454 | |
455 NOTIMPLEMENTED(); | |
456 return scoped_ptr<AudioEncoder>(NULL); | |
457 } | |
458 | |
459 void ChromotingHost::StopScreenRecorder() { | |
460 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | |
461 DCHECK(recorder_.get()); | |
462 | |
463 ++stopping_recorders_; | |
464 scoped_refptr<ScreenRecorder> recorder = recorder_; | |
465 recorder_ = NULL; | |
466 recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
467 } | |
468 | |
469 void ChromotingHost::StopAudioScheduler() { | |
470 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | |
471 DCHECK(audio_scheduler_.get()); | |
472 | |
473 ++stopping_recorders_; | |
474 scoped_refptr<AudioScheduler> recorder = audio_scheduler_; | |
475 audio_scheduler_ = NULL; | |
476 recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
477 } | |
478 | |
479 void ChromotingHost::OnRecorderStopped() { | |
480 if (!context_->network_task_runner()->BelongsToCurrentThread()) { | |
481 context_->network_task_runner()->PostTask( | |
482 FROM_HERE, base::Bind(&ChromotingHost::OnRecorderStopped, this)); | |
483 return; | |
484 } | |
485 | |
486 --stopping_recorders_; | |
487 DCHECK_GE(stopping_recorders_, 0); | |
488 | |
489 if (!stopping_recorders_ && state_ == kStopping) | |
490 ShutdownFinish(); | 390 ShutdownFinish(); |
491 } | 391 } |
492 | 392 |
493 void ChromotingHost::ShutdownFinish() { | 393 void ChromotingHost::ShutdownFinish() { |
494 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 394 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
495 DCHECK(!stopping_recorders_); | 395 DCHECK_EQ(state_, kStopping); |
396 | |
397 // Destroy session manager. | |
398 session_manager_.reset(); | |
496 | 399 |
497 state_ = kStopped; | 400 state_ = kStopped; |
498 | 401 |
499 // Keep reference to |this|, so that we don't get destroyed while | 402 // Keep reference to |this|, so that we don't get destroyed while |
500 // sending notifications. | 403 // sending notifications. |
501 scoped_refptr<ChromotingHost> self(this); | 404 scoped_refptr<ChromotingHost> self(this); |
502 | 405 |
503 // Notify observers. | 406 // Notify observers. |
504 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, | 407 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, |
505 OnShutdown()); | 408 OnShutdown()); |
506 | 409 |
507 for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin(); | 410 for (std::vector<base::Closure>::iterator it = shutdown_tasks_.begin(); |
508 it != shutdown_tasks_.end(); ++it) { | 411 it != shutdown_tasks_.end(); ++it) { |
509 it->Run(); | 412 it->Run(); |
510 } | 413 } |
511 shutdown_tasks_.clear(); | 414 shutdown_tasks_.clear(); |
512 } | 415 } |
513 | 416 |
514 } // namespace remoting | 417 } // namespace remoting |
OLD | NEW |