Fix mouse warp with 2x displays
[chromium-blink-merge.git] / remoting / host / daemon_process.cc
blobe08677a0ec16961ecb1aaece0b919221b2f389ec
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.
5 #include "remoting/host/daemon_process.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback_helpers.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/location.h"
17 #include "base/single_thread_task_runner.h"
18 #include "net/base/net_util.h"
19 #include "remoting/base/auto_thread_task_runner.h"
20 #include "remoting/host/branding.h"
21 #include "remoting/host/chromoting_messages.h"
22 #include "remoting/host/config_file_watcher.h"
23 #include "remoting/host/desktop_session.h"
24 #include "remoting/host/host_event_logger.h"
25 #include "remoting/host/host_exit_codes.h"
26 #include "remoting/host/host_status_observer.h"
27 #include "remoting/host/screen_resolution.h"
28 #include "remoting/protocol/transport.h"
30 namespace remoting {
32 namespace {
34 // This is used for tagging system event logs.
35 const char kApplicationName[] = "chromoting";
37 std::ostream& operator<<(std::ostream& os, const ScreenResolution& resolution) {
38 return os << resolution.dimensions().width() << "x"
39 << resolution.dimensions().height() << " at "
40 << resolution.dpi().x() << "x" << resolution.dpi().y() << " DPI";
43 } // namespace
45 DaemonProcess::~DaemonProcess() {
46 DCHECK(caller_task_runner()->BelongsToCurrentThread());
48 host_event_logger_.reset();
49 weak_factory_.InvalidateWeakPtrs();
51 config_watcher_.reset();
52 DeleteAllDesktopSessions();
55 void DaemonProcess::OnConfigUpdated(const std::string& serialized_config) {
56 DCHECK(caller_task_runner()->BelongsToCurrentThread());
58 if (serialized_config_ != serialized_config) {
59 serialized_config_ = serialized_config;
60 SendToNetwork(
61 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_));
65 void DaemonProcess::OnConfigWatcherError() {
66 DCHECK(caller_task_runner()->BelongsToCurrentThread());
68 Stop();
71 void DaemonProcess::AddStatusObserver(HostStatusObserver* observer) {
72 DCHECK(caller_task_runner()->BelongsToCurrentThread());
74 status_observers_.AddObserver(observer);
77 void DaemonProcess::RemoveStatusObserver(HostStatusObserver* observer) {
78 DCHECK(caller_task_runner()->BelongsToCurrentThread());
80 status_observers_.RemoveObserver(observer);
83 void DaemonProcess::OnChannelConnected(int32 peer_pid) {
84 DCHECK(caller_task_runner()->BelongsToCurrentThread());
86 VLOG(1) << "IPC: daemon <- network (" << peer_pid << ")";
88 DeleteAllDesktopSessions();
90 // Reset the last known terminal ID because no IDs have been allocated
91 // by the the newly started process yet.
92 next_terminal_id_ = 0;
94 // Send the configuration to the network process.
95 SendToNetwork(
96 new ChromotingDaemonNetworkMsg_Configuration(serialized_config_));
99 bool DaemonProcess::OnMessageReceived(const IPC::Message& message) {
100 DCHECK(caller_task_runner()->BelongsToCurrentThread());
102 bool handled = true;
103 IPC_BEGIN_MESSAGE_MAP(DaemonProcess, message)
104 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_ConnectTerminal,
105 CreateDesktopSession)
106 IPC_MESSAGE_HANDLER(ChromotingNetworkHostMsg_DisconnectTerminal,
107 CloseDesktopSession)
108 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_SetScreenResolution,
109 SetScreenResolution)
110 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_AccessDenied,
111 OnAccessDenied)
112 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientAuthenticated,
113 OnClientAuthenticated)
114 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientConnected,
115 OnClientConnected)
116 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientDisconnected,
117 OnClientDisconnected)
118 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_ClientRouteChange,
119 OnClientRouteChange)
120 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostStarted,
121 OnHostStarted)
122 IPC_MESSAGE_HANDLER(ChromotingNetworkDaemonMsg_HostShutdown,
123 OnHostShutdown)
124 IPC_MESSAGE_UNHANDLED(handled = false)
125 IPC_END_MESSAGE_MAP()
127 if (!handled) {
128 LOG(ERROR) << "Received unexpected IPC type: " << message.type();
129 CrashNetworkProcess(FROM_HERE);
132 return handled;
135 void DaemonProcess::OnPermanentError(int exit_code) {
136 DCHECK(caller_task_runner()->BelongsToCurrentThread());
137 DCHECK(kMinPermanentErrorExitCode <= exit_code &&
138 exit_code <= kMaxPermanentErrorExitCode);
140 Stop();
143 void DaemonProcess::CloseDesktopSession(int terminal_id) {
144 DCHECK(caller_task_runner()->BelongsToCurrentThread());
146 // Validate the supplied terminal ID. An attempt to use a desktop session ID
147 // that couldn't possibly have been allocated is considered a protocol error
148 // and the network process will be restarted.
149 if (!WasTerminalIdAllocated(terminal_id)) {
150 LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
151 CrashNetworkProcess(FROM_HERE);
152 return;
155 DesktopSessionList::iterator i;
156 for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) {
157 if ((*i)->id() == terminal_id) {
158 break;
162 // It is OK if the terminal ID wasn't found. There is a race between
163 // the network and daemon processes. Each frees its own recources first and
164 // notifies the other party if there was something to clean up.
165 if (i == desktop_sessions_.end())
166 return;
168 delete *i;
169 desktop_sessions_.erase(i);
171 VLOG(1) << "Daemon: closed desktop session " << terminal_id;
172 SendToNetwork(
173 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id));
176 DaemonProcess::DaemonProcess(
177 scoped_refptr<AutoThreadTaskRunner> caller_task_runner,
178 scoped_refptr<AutoThreadTaskRunner> io_task_runner,
179 const base::Closure& stopped_callback)
180 : caller_task_runner_(caller_task_runner),
181 io_task_runner_(io_task_runner),
182 next_terminal_id_(0),
183 stopped_callback_(stopped_callback),
184 weak_factory_(this) {
185 DCHECK(caller_task_runner->BelongsToCurrentThread());
188 void DaemonProcess::CreateDesktopSession(int terminal_id,
189 const ScreenResolution& resolution,
190 bool virtual_terminal) {
191 DCHECK(caller_task_runner()->BelongsToCurrentThread());
193 // Validate the supplied terminal ID. An attempt to create a desktop session
194 // with an ID that could possibly have been allocated already is considered
195 // a protocol error and the network process will be restarted.
196 if (WasTerminalIdAllocated(terminal_id)) {
197 LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
198 CrashNetworkProcess(FROM_HERE);
199 return;
202 // Terminal IDs cannot be reused. Update the expected next terminal ID.
203 next_terminal_id_ = std::max(next_terminal_id_, terminal_id + 1);
205 // Create the desktop session.
206 scoped_ptr<DesktopSession> session = DoCreateDesktopSession(
207 terminal_id, resolution, virtual_terminal);
208 if (!session) {
209 LOG(ERROR) << "Failed to create a desktop session.";
210 SendToNetwork(
211 new ChromotingDaemonNetworkMsg_TerminalDisconnected(terminal_id));
212 return;
215 VLOG(1) << "Daemon: opened desktop session " << terminal_id;
216 desktop_sessions_.push_back(session.release());
219 void DaemonProcess::SetScreenResolution(int terminal_id,
220 const ScreenResolution& resolution) {
221 DCHECK(caller_task_runner()->BelongsToCurrentThread());
223 // Validate the supplied terminal ID. An attempt to use a desktop session ID
224 // that couldn't possibly have been allocated is considered a protocol error
225 // and the network process will be restarted.
226 if (!WasTerminalIdAllocated(terminal_id)) {
227 LOG(ERROR) << "Invalid terminal ID: " << terminal_id;
228 CrashNetworkProcess(FROM_HERE);
229 return;
232 // Validate |resolution| and restart the sender if it is not valid.
233 if (resolution.IsEmpty()) {
234 LOG(ERROR) << "Invalid resolution specified: " << resolution;
235 CrashNetworkProcess(FROM_HERE);
236 return;
239 DesktopSessionList::iterator i;
240 for (i = desktop_sessions_.begin(); i != desktop_sessions_.end(); ++i) {
241 if ((*i)->id() == terminal_id) {
242 break;
246 // It is OK if the terminal ID wasn't found. There is a race between
247 // the network and daemon processes. Each frees its own resources first and
248 // notifies the other party if there was something to clean up.
249 if (i == desktop_sessions_.end())
250 return;
252 (*i)->SetScreenResolution(resolution);
255 void DaemonProcess::CrashNetworkProcess(
256 const tracked_objects::Location& location) {
257 DCHECK(caller_task_runner()->BelongsToCurrentThread());
259 DoCrashNetworkProcess(location);
260 DeleteAllDesktopSessions();
263 void DaemonProcess::Initialize() {
264 DCHECK(caller_task_runner()->BelongsToCurrentThread());
266 const base::CommandLine* command_line =
267 base::CommandLine::ForCurrentProcess();
268 // Get the name of the host configuration file.
269 base::FilePath default_config_dir = remoting::GetConfigDir();
270 base::FilePath config_path = default_config_dir.Append(
271 kDefaultHostConfigFile);
272 if (command_line->HasSwitch(kHostConfigSwitchName)) {
273 config_path = command_line->GetSwitchValuePath(kHostConfigSwitchName);
275 config_watcher_.reset(new ConfigFileWatcher(
276 caller_task_runner(), io_task_runner(), config_path));
277 config_watcher_->Watch(this);
278 host_event_logger_ =
279 HostEventLogger::Create(weak_factory_.GetWeakPtr(), kApplicationName);
281 // Launch the process.
282 LaunchNetworkProcess();
285 void DaemonProcess::Stop() {
286 DCHECK(caller_task_runner()->BelongsToCurrentThread());
288 if (!stopped_callback_.is_null()) {
289 base::ResetAndReturn(&stopped_callback_).Run();
293 bool DaemonProcess::WasTerminalIdAllocated(int terminal_id) {
294 return terminal_id < next_terminal_id_;
297 void DaemonProcess::OnAccessDenied(const std::string& jid) {
298 DCHECK(caller_task_runner()->BelongsToCurrentThread());
300 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnAccessDenied(jid));
303 void DaemonProcess::OnClientAuthenticated(const std::string& jid) {
304 DCHECK(caller_task_runner()->BelongsToCurrentThread());
306 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
307 OnClientAuthenticated(jid));
310 void DaemonProcess::OnClientConnected(const std::string& jid) {
311 DCHECK(caller_task_runner()->BelongsToCurrentThread());
313 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
314 OnClientConnected(jid));
317 void DaemonProcess::OnClientDisconnected(const std::string& jid) {
318 DCHECK(caller_task_runner()->BelongsToCurrentThread());
320 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
321 OnClientDisconnected(jid));
324 void DaemonProcess::OnClientRouteChange(const std::string& jid,
325 const std::string& channel_name,
326 const SerializedTransportRoute& route) {
327 DCHECK(caller_task_runner()->BelongsToCurrentThread());
329 // Validate |route|.
330 if (route.type != protocol::TransportRoute::DIRECT &&
331 route.type != protocol::TransportRoute::STUN &&
332 route.type != protocol::TransportRoute::RELAY) {
333 LOG(ERROR) << "An invalid RouteType " << route.type << " passed.";
334 CrashNetworkProcess(FROM_HERE);
335 return;
337 if (route.remote_address.size() != net::kIPv4AddressSize &&
338 route.remote_address.size() != net::kIPv6AddressSize) {
339 LOG(ERROR) << "An invalid net::IPAddressNumber size "
340 << route.remote_address.size() << " passed.";
341 CrashNetworkProcess(FROM_HERE);
342 return;
344 if (route.local_address.size() != net::kIPv4AddressSize &&
345 route.local_address.size() != net::kIPv6AddressSize) {
346 LOG(ERROR) << "An invalid net::IPAddressNumber size "
347 << route.local_address.size() << " passed.";
348 CrashNetworkProcess(FROM_HERE);
349 return;
352 protocol::TransportRoute parsed_route;
353 parsed_route.type =
354 static_cast<protocol::TransportRoute::RouteType>(route.type);
355 parsed_route.remote_address =
356 net::IPEndPoint(route.remote_address, route.remote_port);
357 parsed_route.local_address =
358 net::IPEndPoint(route.local_address, route.local_port);
359 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_,
360 OnClientRouteChange(jid, channel_name, parsed_route));
363 void DaemonProcess::OnHostStarted(const std::string& xmpp_login) {
364 DCHECK(caller_task_runner()->BelongsToCurrentThread());
366 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnStart(xmpp_login));
369 void DaemonProcess::OnHostShutdown() {
370 DCHECK(caller_task_runner()->BelongsToCurrentThread());
372 FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnShutdown());
375 void DaemonProcess::DeleteAllDesktopSessions() {
376 while (!desktop_sessions_.empty()) {
377 delete desktop_sessions_.front();
378 desktop_sessions_.pop_front();
382 } // namespace remoting