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 |