| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/stl_util-inl.h" | 7 #include "base/stl_util-inl.h" |
| 8 #include "base/waitable_event.h" | 8 #include "base/task.h" |
| 9 #include "build/build_config.h" | 9 #include "build/build_config.h" |
| 10 #include "remoting/base/constants.h" | 10 #include "remoting/base/constants.h" |
| 11 #include "remoting/base/protocol_decoder.h" | 11 #include "remoting/base/protocol_decoder.h" |
| 12 #include "remoting/host/chromoting_host_context.h" |
| 12 #include "remoting/host/host_config.h" | 13 #include "remoting/host/host_config.h" |
| 13 #include "remoting/host/session_manager.h" | 14 #include "remoting/host/session_manager.h" |
| 14 #include "remoting/jingle_glue/jingle_channel.h" | 15 #include "remoting/jingle_glue/jingle_channel.h" |
| 15 | 16 |
| 16 namespace remoting { | 17 namespace remoting { |
| 17 | 18 |
| 18 ChromotingHost::ChromotingHost(MutableHostConfig* config, | 19 ChromotingHost::ChromotingHost(ChromotingHostContext* context, |
| 20 MutableHostConfig* config, |
| 19 Capturer* capturer, | 21 Capturer* capturer, |
| 20 Encoder* encoder, | 22 Encoder* encoder, |
| 21 EventExecutor* executor, | 23 EventExecutor* executor) |
| 22 base::WaitableEvent* host_done) | 24 : context_(context), |
| 23 : main_thread_("MainThread"), | 25 config_(config), |
| 24 capture_thread_("CaptureThread"), | 26 capturer_(capturer), |
| 25 encode_thread_("EncodeThread"), | 27 encoder_(encoder), |
| 26 config_(config), | 28 executor_(executor), |
| 27 capturer_(capturer), | 29 state_(kInitial) { |
| 28 encoder_(encoder), | |
| 29 executor_(executor), | |
| 30 host_done_(host_done) { | |
| 31 // TODO(ajwong): The thread injection and object ownership is odd here. | |
| 32 // Fix so we do not start this thread in the constructor, so we only | |
| 33 // take in a session manager, don't let session manager own the | |
| 34 // capturer/encoder, and then associate the capturer and encoder threads with | |
| 35 // the capturer and encoder objects directly. This will require a | |
| 36 // non-refcounted NewRunnableMethod. | |
| 37 main_thread_.StartWithOptions( | |
| 38 base::Thread::Options(MessageLoop::TYPE_UI, 0)); | |
| 39 network_thread_.Start(); | |
| 40 } | 30 } |
| 41 | 31 |
| 42 ChromotingHost::~ChromotingHost() { | 32 ChromotingHost::~ChromotingHost() { |
| 43 // TODO(ajwong): We really need to inject these threads and get rid of these | |
| 44 // start/stops. | |
| 45 main_thread_.Stop(); | |
| 46 network_thread_.Stop(); | |
| 47 DCHECK(!encode_thread_.IsRunning()); | |
| 48 DCHECK(!capture_thread_.IsRunning()); | |
| 49 } | 33 } |
| 50 | 34 |
| 51 void ChromotingHost::Run() { | 35 void ChromotingHost::Start(Task* shutdown_task) { |
| 52 // Submit a task to perform host registration. We'll also start | 36 // Submit a task to perform host registration. We'll also start |
| 53 // listening to connection if registration is done. | 37 // listening to connection if registration is done. |
| 54 message_loop()->PostTask( | 38 context_->main_message_loop()->PostTask( |
| 55 FROM_HERE, | 39 FROM_HERE, |
| 56 NewRunnableMethod(this, &ChromotingHost::RegisterHost)); | 40 NewRunnableMethod(this, &ChromotingHost::DoStart, shutdown_task)); |
| 57 } | 41 } |
| 58 | 42 |
| 59 // This method is called when we need to destroy the host process. | 43 // This method is called when we need to destroy the host process. |
| 60 void ChromotingHost::DestroySession() { | 44 void ChromotingHost::Shutdown() { |
| 61 DCHECK_EQ(message_loop(), MessageLoop::current()); | 45 context_->main_message_loop()->PostTask( |
| 62 | 46 FROM_HERE, |
| 63 // First we tell the session to pause and then we wait until all | 47 NewRunnableMethod(this, &ChromotingHost::DoShutdown)); |
| 64 // the tasks are done. | |
| 65 if (session_.get()) { | |
| 66 session_->Pause(); | |
| 67 | |
| 68 // TODO(hclam): Revise the order. | |
| 69 DCHECK(encode_thread_.IsRunning()); | |
| 70 encode_thread_.Stop(); | |
| 71 | |
| 72 DCHECK(capture_thread_.IsRunning()); | |
| 73 capture_thread_.Stop(); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 // This method talks to the cloud to register the host process. If | |
| 78 // successful we will start listening to network requests. | |
| 79 void ChromotingHost::RegisterHost() { | |
| 80 DCHECK_EQ(message_loop(), MessageLoop::current()); | |
| 81 DCHECK(!jingle_client_); | |
| 82 | |
| 83 std::string xmpp_login; | |
| 84 std::string xmpp_auth_token; | |
| 85 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) || | |
| 86 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) { | |
| 87 LOG(ERROR) << "XMMP credentials are not defined in config."; | |
| 88 return; | |
| 89 } | |
| 90 | |
| 91 // Connect to the talk network with a JingleClient. | |
| 92 jingle_client_ = new JingleClient(&network_thread_); | |
| 93 jingle_client_->Init(xmpp_login, xmpp_auth_token, | |
| 94 kChromotingTokenServiceName, this); | |
| 95 } | 48 } |
| 96 | 49 |
| 97 // This method is called if a client is connected to this object. | 50 // This method is called if a client is connected to this object. |
| 98 void ChromotingHost::OnClientConnected(ClientConnection* client) { | 51 void ChromotingHost::OnClientConnected(ClientConnection* client) { |
| 99 DCHECK_EQ(message_loop(), MessageLoop::current()); | 52 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 100 | 53 |
| 101 // Create a new RecordSession if there was none. | 54 // Create a new RecordSession if there was none. |
| 102 if (!session_.get()) { | 55 if (!session_.get()) { |
| 103 // The first we need to make sure capture and encode thread are | |
| 104 // running. | |
| 105 capture_thread_.Start(); | |
| 106 encode_thread_.Start(); | |
| 107 | |
| 108 // Then we create a SessionManager passing the message loops that | 56 // Then we create a SessionManager passing the message loops that |
| 109 // it should run on. | 57 // it should run on. |
| 110 // Note that we pass the ownership of the capturer and encoder to | |
| 111 // the session manager. | |
| 112 DCHECK(capturer_.get()); | 58 DCHECK(capturer_.get()); |
| 113 DCHECK(encoder_.get()); | 59 DCHECK(encoder_.get()); |
| 114 session_ = new SessionManager(capture_thread_.message_loop(), | 60 session_ = new SessionManager(context_->capture_message_loop(), |
| 115 encode_thread_.message_loop(), | 61 context_->encode_message_loop(), |
| 116 message_loop(), | 62 context_->main_message_loop(), |
| 117 capturer_.release(), | 63 capturer_.get(), |
| 118 encoder_.release()); | 64 encoder_.get()); |
| 119 | 65 |
| 120 // Immediately add the client and start the session. | 66 // Immediately add the client and start the session. |
| 121 session_->AddClient(client); | 67 session_->AddClient(client); |
| 122 session_->Start(); | 68 session_->Start(); |
| 123 LOG(INFO) << "Session manager started"; | 69 LOG(INFO) << "Session manager started"; |
| 124 } else { | 70 } else { |
| 125 // If a session manager already exists we simply add the new client. | 71 // If a session manager already exists we simply add the new client. |
| 126 session_->AddClient(client); | 72 session_->AddClient(client); |
| 127 } | 73 } |
| 128 } | 74 } |
| 129 | 75 |
| 130 void ChromotingHost::OnClientDisconnected(ClientConnection* client) { | 76 void ChromotingHost::OnClientDisconnected(ClientConnection* client) { |
| 131 DCHECK_EQ(message_loop(), MessageLoop::current()); | 77 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 132 | 78 |
| 133 // Remove the client from the session manager. | 79 // Remove the client from the session manager and pause the session. |
| 134 if (session_.get()) | 80 // TODO(hclam): Pause only if the last client disconnected. |
| 81 if (session_.get()) { |
| 135 session_->RemoveClient(client); | 82 session_->RemoveClient(client); |
| 83 session_->Pause(); |
| 84 } |
| 136 | 85 |
| 137 // Also remove reference to ClientConnection from this object. | 86 // Also remove reference to ClientConnection from this object. |
| 138 client_ = NULL; | 87 client_ = NULL; |
| 139 | |
| 140 // TODO(hclam): If the last client has disconnected we need to destroy | |
| 141 // the session manager and shutdown the capture and encode threads. | |
| 142 // Right now we assume that there's only one client. | |
| 143 DestroySession(); | |
| 144 } | 88 } |
| 145 | 89 |
| 146 //////////////////////////////////////////////////////////////////////////// | 90 //////////////////////////////////////////////////////////////////////////// |
| 147 // ClientConnection::EventHandler implementations | 91 // ClientConnection::EventHandler implementations |
| 148 void ChromotingHost::HandleMessages(ClientConnection* client, | 92 void ChromotingHost::HandleMessages(ClientConnection* client, |
| 149 ClientMessageList* messages) { | 93 ClientMessageList* messages) { |
| 150 DCHECK_EQ(message_loop(), MessageLoop::current()); | 94 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 151 | 95 |
| 152 // Delegate the messages to EventExecutor and delete the unhandled | 96 // Delegate the messages to EventExecutor and delete the unhandled |
| 153 // messages. | 97 // messages. |
| 154 DCHECK(executor_.get()); | 98 DCHECK(executor_.get()); |
| 155 executor_->HandleInputEvents(messages); | 99 executor_->HandleInputEvents(messages); |
| 156 STLDeleteElements<ClientMessageList>(messages); | 100 STLDeleteElements<ClientMessageList>(messages); |
| 157 } | 101 } |
| 158 | 102 |
| 159 void ChromotingHost::OnConnectionOpened(ClientConnection* client) { | 103 void ChromotingHost::OnConnectionOpened(ClientConnection* client) { |
| 160 DCHECK_EQ(message_loop(), MessageLoop::current()); | 104 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 161 | 105 |
| 162 // Completes the client connection. | 106 // Completes the client connection. |
| 163 LOG(INFO) << "Connection to client established."; | 107 LOG(INFO) << "Connection to client established."; |
| 164 OnClientConnected(client_.get()); | 108 OnClientConnected(client_.get()); |
| 165 } | 109 } |
| 166 | 110 |
| 167 void ChromotingHost::OnConnectionClosed(ClientConnection* client) { | 111 void ChromotingHost::OnConnectionClosed(ClientConnection* client) { |
| 168 DCHECK_EQ(message_loop(), MessageLoop::current()); | 112 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 169 | 113 |
| 170 // Completes the client connection. | 114 // Completes the client connection. |
| 171 LOG(INFO) << "Connection to client closed."; | 115 LOG(INFO) << "Connection to client closed."; |
| 172 OnClientDisconnected(client_.get()); | 116 OnClientDisconnected(client_.get()); |
| 173 } | 117 } |
| 174 | 118 |
| 175 void ChromotingHost::OnConnectionFailed(ClientConnection* client) { | 119 void ChromotingHost::OnConnectionFailed(ClientConnection* client) { |
| 176 DCHECK_EQ(message_loop(), MessageLoop::current()); | 120 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 177 | 121 |
| 178 // The client has disconnected. | 122 // The client has disconnected. |
| 179 LOG(ERROR) << "Connection failed unexpectedly."; | 123 LOG(ERROR) << "Connection failed unexpectedly."; |
| 180 OnClientDisconnected(client_.get()); | 124 OnClientDisconnected(client_.get()); |
| 181 } | 125 } |
| 182 | 126 |
| 183 //////////////////////////////////////////////////////////////////////////// | 127 //////////////////////////////////////////////////////////////////////////// |
| 184 // JingleClient::Callback implementations | 128 // JingleClient::Callback implementations |
| 185 void ChromotingHost::OnStateChange(JingleClient* jingle_client, | 129 void ChromotingHost::OnStateChange(JingleClient* jingle_client, |
| 186 JingleClient::State state) { | 130 JingleClient::State state) { |
| 187 DCHECK_EQ(jingle_client_.get(), jingle_client); | |
| 188 | |
| 189 if (state == JingleClient::CONNECTED) { | 131 if (state == JingleClient::CONNECTED) { |
| 132 DCHECK_EQ(jingle_client_.get(), jingle_client); |
| 190 LOG(INFO) << "Host connected as " | 133 LOG(INFO) << "Host connected as " |
| 191 << jingle_client->GetFullJid() << "." << std::endl; | 134 << jingle_client->GetFullJid() << "." << std::endl; |
| 192 | 135 |
| 193 // Start heartbeating after we connected | 136 // Start heartbeating after we connected |
| 194 heartbeat_sender_ = new HeartbeatSender(); | 137 heartbeat_sender_ = new HeartbeatSender(); |
| 195 heartbeat_sender_->Start(config_, jingle_client_.get()); | 138 heartbeat_sender_->Start(config_, jingle_client_.get()); |
| 196 } else if (state == JingleClient::CLOSED) { | 139 } else if (state == JingleClient::CLOSED) { |
| 197 LOG(INFO) << "Host disconnected from talk network." << std::endl; | 140 LOG(INFO) << "Host disconnected from talk network." << std::endl; |
| 198 heartbeat_sender_ = NULL; | 141 heartbeat_sender_ = NULL; |
| 199 | 142 |
| 200 // Quit the message loop if disconected. | |
| 201 // TODO(sergeyu): We should try reconnecting here instead of terminating | 143 // TODO(sergeyu): We should try reconnecting here instead of terminating |
| 202 // the host. | 144 // the host. |
| 203 message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); | 145 // Post a shutdown task to properly shutdown the chromoting host. |
| 204 host_done_->Signal(); | 146 context_->main_message_loop()->PostTask( |
| 147 FROM_HERE, NewRunnableMethod(this, &ChromotingHost::DoShutdown)); |
| 205 } | 148 } |
| 206 } | 149 } |
| 207 | 150 |
| 208 bool ChromotingHost::OnAcceptConnection( | 151 bool ChromotingHost::OnAcceptConnection( |
| 209 JingleClient* jingle_client, const std::string& jid, | 152 JingleClient* jingle_client, const std::string& jid, |
| 210 JingleChannel::Callback** channel_callback) { | 153 JingleChannel::Callback** channel_callback) { |
| 154 AutoLock auto_lock(lock_); |
| 155 if (state_ != kStarted) |
| 156 return false; |
| 157 |
| 211 DCHECK_EQ(jingle_client_.get(), jingle_client); | 158 DCHECK_EQ(jingle_client_.get(), jingle_client); |
| 212 | 159 |
| 213 // TODO(hclam): Allow multiple clients to connect to the host. | 160 // TODO(hclam): Allow multiple clients to connect to the host. |
| 214 if (client_.get()) | 161 if (client_.get()) |
| 215 return false; | 162 return false; |
| 216 | 163 |
| 217 LOG(INFO) << "Client connected: " << jid << std::endl; | 164 LOG(INFO) << "Client connected: " << jid << std::endl; |
| 218 | 165 |
| 219 // If we accept the connected then create a client object and set the | 166 // If we accept the connected then create a client object and set the |
| 220 // callback. | 167 // callback. |
| 221 client_ = new ClientConnection(message_loop(), new ProtocolDecoder(), this); | 168 client_ = new ClientConnection(context_->main_message_loop(), |
| 169 new ProtocolDecoder(), this); |
| 222 *channel_callback = client_.get(); | 170 *channel_callback = client_.get(); |
| 223 return true; | 171 return true; |
| 224 } | 172 } |
| 225 | 173 |
| 226 void ChromotingHost::OnNewConnection(JingleClient* jingle_client, | 174 void ChromotingHost::OnNewConnection(JingleClient* jingle_client, |
| 227 scoped_refptr<JingleChannel> channel) { | 175 scoped_refptr<JingleChannel> channel) { |
| 176 AutoLock auto_lock(lock_); |
| 177 if (state_ != kStarted) |
| 178 return; |
| 179 |
| 228 DCHECK_EQ(jingle_client_.get(), jingle_client); | 180 DCHECK_EQ(jingle_client_.get(), jingle_client); |
| 229 | 181 |
| 230 // Since the session manager has not started, it is still safe to access | 182 // Since the session manager has not started, it is still safe to access |
| 231 // the client directly. Note that we give the ownership of the channel | 183 // the client directly. Note that we give the ownership of the channel |
| 232 // to the client. | 184 // to the client. |
| 233 client_->set_jingle_channel(channel); | 185 client_->set_jingle_channel(channel); |
| 234 } | 186 } |
| 235 | 187 |
| 236 MessageLoop* ChromotingHost::message_loop() { | 188 void ChromotingHost::DoStart(Task* shutdown_task) { |
| 237 return main_thread_.message_loop(); | 189 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 190 DCHECK(!jingle_client_); |
| 191 DCHECK(shutdown_task); |
| 192 |
| 193 // Make sure this object is not started. |
| 194 { |
| 195 AutoLock auto_lock(lock_); |
| 196 if (state_ != kInitial) |
| 197 return; |
| 198 state_ = kStarted; |
| 199 } |
| 200 |
| 201 // Save the shutdown task. |
| 202 shutdown_task_.reset(shutdown_task); |
| 203 |
| 204 std::string xmpp_login; |
| 205 std::string xmpp_auth_token; |
| 206 if (!config_->GetString(kXmppLoginConfigPath, &xmpp_login) || |
| 207 !config_->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token)) { |
| 208 LOG(ERROR) << "XMMP credentials are not defined in config."; |
| 209 return; |
| 210 } |
| 211 |
| 212 // Connect to the talk network with a JingleClient. |
| 213 jingle_client_ = new JingleClient(context_->jingle_thread()); |
| 214 jingle_client_->Init(xmpp_login, xmpp_auth_token, |
| 215 kChromotingTokenServiceName, this); |
| 216 } |
| 217 |
| 218 void ChromotingHost::DoShutdown() { |
| 219 DCHECK_EQ(context_->main_message_loop(), MessageLoop::current()); |
| 220 |
| 221 // No-op if this object is not started yet. |
| 222 { |
| 223 AutoLock auto_lock(lock_); |
| 224 if (state_ != kStarted) |
| 225 return; |
| 226 state_ = kStopped; |
| 227 } |
| 228 |
| 229 // Tell the session to pause and then disconnect all clients. |
| 230 if (session_.get()) { |
| 231 session_->Pause(); |
| 232 session_->RemoveAllClients(); |
| 233 } |
| 234 |
| 235 // Disconnect all clients. |
| 236 if (client_) { |
| 237 client_->Disconnect(); |
| 238 } |
| 239 |
| 240 // Disconnect from the talk network. |
| 241 if (jingle_client_) { |
| 242 jingle_client_->Close(); |
| 243 } |
| 244 |
| 245 // Lastly call the shutdown task. |
| 246 if (shutdown_task_.get()) { |
| 247 shutdown_task_->Run(); |
| 248 } |
| 238 } | 249 } |
| 239 | 250 |
| 240 } // namespace remoting | 251 } // namespace remoting |
| OLD | NEW |