Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / service_process / service_process_control.cc
blob09d15f9df6b9b6320c575f451f46c72f0dac788f
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 "chrome/browser/service_process/service_process_control.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/metrics/histogram_base.h"
12 #include "base/metrics/histogram_delta_serialization.h"
13 #include "base/process/kill.h"
14 #include "base/process/launch.h"
15 #include "base/stl_util.h"
16 #include "base/threading/thread.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/upgrade_detector.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/service_messages.h"
23 #include "chrome/common/service_process_util.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/common/child_process_host.h"
27 #include "google_apis/gaia/gaia_switches.h"
28 #include "ui/base/ui_base_switches.h"
30 using content::BrowserThread;
31 using content::ChildProcessHost;
33 // ServiceProcessControl implementation.
34 ServiceProcessControl::ServiceProcessControl() {
37 ServiceProcessControl::~ServiceProcessControl() {
40 void ServiceProcessControl::ConnectInternal() {
41 // If the channel has already been established then we run the task
42 // and return.
43 if (channel_.get()) {
44 RunConnectDoneTasks();
45 return;
48 // Actually going to connect.
49 VLOG(1) << "Connecting to Service Process IPC Server";
51 // TODO(hclam): Handle error connecting to channel.
52 const IPC::ChannelHandle channel_id = GetServiceProcessChannel();
53 SetChannel(new IPC::ChannelProxy(
54 channel_id,
55 IPC::Channel::MODE_NAMED_CLIENT,
56 this,
57 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()));
60 void ServiceProcessControl::SetChannel(IPC::ChannelProxy* channel) {
61 channel_.reset(channel);
64 void ServiceProcessControl::RunConnectDoneTasks() {
65 // The tasks executed here may add more tasks to the vector. So copy
66 // them to the stack before executing them. This way recursion is
67 // avoided.
68 TaskList tasks;
70 if (IsConnected()) {
71 tasks.swap(connect_success_tasks_);
72 RunAllTasksHelper(&tasks);
73 DCHECK(tasks.empty());
74 connect_failure_tasks_.clear();
75 } else {
76 tasks.swap(connect_failure_tasks_);
77 RunAllTasksHelper(&tasks);
78 DCHECK(tasks.empty());
79 connect_success_tasks_.clear();
83 // static
84 void ServiceProcessControl::RunAllTasksHelper(TaskList* task_list) {
85 TaskList::iterator index = task_list->begin();
86 while (index != task_list->end()) {
87 (*index).Run();
88 index = task_list->erase(index);
92 bool ServiceProcessControl::IsConnected() const {
93 return channel_ != NULL;
96 void ServiceProcessControl::Launch(const base::Closure& success_task,
97 const base::Closure& failure_task) {
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100 base::Closure failure = failure_task;
101 if (!success_task.is_null())
102 connect_success_tasks_.push_back(success_task);
104 if (!failure.is_null())
105 connect_failure_tasks_.push_back(failure);
107 // If we already in the process of launching, then we are done.
108 if (launcher_.get())
109 return;
111 // If the service process is already running then connects to it.
112 if (CheckServiceProcessReady()) {
113 ConnectInternal();
114 return;
117 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents", SERVICE_EVENT_LAUNCH,
118 SERVICE_EVENT_MAX);
120 // A service process should have a different mechanism for starting, but now
121 // we start it as if it is a child process.
123 #if defined(OS_LINUX)
124 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
125 #else
126 int flags = ChildProcessHost::CHILD_NORMAL;
127 #endif
129 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags);
130 if (exe_path.empty())
131 NOTREACHED() << "Unable to get service process binary name.";
133 CommandLine* cmd_line = new CommandLine(exe_path);
134 cmd_line->AppendSwitchASCII(switches::kProcessType,
135 switches::kServiceProcess);
137 static const char* const kSwitchesToCopy[] = {
138 switches::kCloudPrintServiceURL,
139 switches::kCloudPrintSetupProxy,
140 switches::kEnableLogging,
141 switches::kIgnoreUrlFetcherCertRequests,
142 switches::kLang,
143 switches::kLoggingLevel,
144 switches::kLsoUrl,
145 switches::kNoServiceAutorun,
146 switches::kUserDataDir,
147 switches::kV,
148 switches::kVModule,
149 switches::kWaitForDebugger,
151 cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
152 kSwitchesToCopy,
153 arraysize(kSwitchesToCopy));
155 // And then start the process asynchronously.
156 launcher_ = new Launcher(this, cmd_line);
157 launcher_->Run(base::Bind(&ServiceProcessControl::OnProcessLaunched,
158 base::Unretained(this)));
161 void ServiceProcessControl::Disconnect() {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163 channel_.reset();
166 void ServiceProcessControl::OnProcessLaunched() {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 if (launcher_->launched()) {
169 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
170 SERVICE_EVENT_LAUNCHED, SERVICE_EVENT_MAX);
171 // After we have successfully created the service process we try to connect
172 // to it. The launch task is transfered to a connect task.
173 ConnectInternal();
174 } else {
175 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
176 SERVICE_EVENT_LAUNCH_FAILED, SERVICE_EVENT_MAX);
177 // If we don't have process handle that means launching the service process
178 // has failed.
179 RunConnectDoneTasks();
182 // We don't need the launcher anymore.
183 launcher_ = NULL;
186 bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
187 bool handled = true;
188 IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
189 IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_Info,
190 OnCloudPrintProxyInfo)
191 IPC_MESSAGE_HANDLER(ServiceHostMsg_Histograms, OnHistograms)
192 IPC_MESSAGE_UNHANDLED(handled = false)
193 IPC_END_MESSAGE_MAP()
194 return handled;
197 void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
201 SERVICE_EVENT_CHANNEL_CONNECTED, SERVICE_EVENT_MAX);
203 // We just established a channel with the service process. Notify it if an
204 // upgrade is available.
205 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
206 Send(new ServiceMsg_UpdateAvailable);
207 } else {
208 if (registrar_.IsEmpty())
209 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
210 content::NotificationService::AllSources());
212 RunConnectDoneTasks();
215 void ServiceProcessControl::OnChannelError() {
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
219 SERVICE_EVENT_CHANNEL_ERROR, SERVICE_EVENT_MAX);
221 channel_.reset();
222 RunConnectDoneTasks();
225 bool ServiceProcessControl::Send(IPC::Message* message) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 if (!channel_.get())
228 return false;
229 return channel_->Send(message);
232 // content::NotificationObserver implementation.
233 void ServiceProcessControl::Observe(
234 int type,
235 const content::NotificationSource& source,
236 const content::NotificationDetails& details) {
237 if (type == chrome::NOTIFICATION_UPGRADE_RECOMMENDED) {
238 Send(new ServiceMsg_UpdateAvailable);
242 void ServiceProcessControl::OnCloudPrintProxyInfo(
243 const cloud_print::CloudPrintProxyInfo& proxy_info) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
246 SERVICE_EVENT_INFO_REPLY, SERVICE_EVENT_MAX);
247 if (!cloud_print_info_callback_.is_null()) {
248 cloud_print_info_callback_.Run(proxy_info);
249 cloud_print_info_callback_.Reset();
253 void ServiceProcessControl::OnHistograms(
254 const std::vector<std::string>& pickled_histograms) {
255 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
256 SERVICE_EVENT_HISTOGRAMS_REPLY, SERVICE_EVENT_MAX);
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
258 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
259 pickled_histograms);
260 RunHistogramsCallback();
263 void ServiceProcessControl::RunHistogramsCallback() {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265 if (!histograms_callback_.is_null()) {
266 histograms_callback_.Run();
267 histograms_callback_.Reset();
269 histograms_timeout_callback_.Cancel();
272 bool ServiceProcessControl::GetCloudPrintProxyInfo(
273 const CloudPrintProxyInfoHandler& cloud_print_info_callback) {
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
275 DCHECK(!cloud_print_info_callback.is_null());
276 cloud_print_info_callback_.Reset();
277 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
278 SERVICE_EVENT_INFO_REQUEST, SERVICE_EVENT_MAX);
279 if (!Send(new ServiceMsg_GetCloudPrintProxyInfo()))
280 return false;
281 cloud_print_info_callback_ = cloud_print_info_callback;
282 return true;
285 bool ServiceProcessControl::GetHistograms(
286 const base::Closure& histograms_callback,
287 const base::TimeDelta& timeout) {
288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
289 DCHECK(!histograms_callback.is_null());
290 histograms_callback_.Reset();
292 // If the service process is already running then connect to it.
293 if (!CheckServiceProcessReady())
294 return false;
295 ConnectInternal();
297 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
298 SERVICE_EVENT_HISTOGRAMS_REQUEST,
299 SERVICE_EVENT_MAX);
301 if (!Send(new ServiceMsg_GetHistograms()))
302 return false;
304 // Run timeout task to make sure |histograms_callback| is called.
305 histograms_timeout_callback_.Reset(
306 base::Bind(&ServiceProcessControl::RunHistogramsCallback,
307 base::Unretained(this)));
308 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
309 histograms_timeout_callback_.callback(),
310 timeout);
312 histograms_callback_ = histograms_callback;
313 return true;
316 bool ServiceProcessControl::Shutdown() {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318 bool ret = Send(new ServiceMsg_Shutdown());
319 channel_.reset();
320 return ret;
323 // static
324 ServiceProcessControl* ServiceProcessControl::GetInstance() {
325 return Singleton<ServiceProcessControl>::get();
328 ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process,
329 CommandLine* cmd_line)
330 : process_(process),
331 cmd_line_(cmd_line),
332 launched_(false),
333 retry_count_(0),
334 process_handle_(base::kNullProcessHandle) {
337 // Execute the command line to start the process asynchronously.
338 // After the command is executed, |task| is called with the process handle on
339 // the UI thread.
340 void ServiceProcessControl::Launcher::Run(const base::Closure& task) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
342 notify_task_ = task;
343 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
344 base::Bind(&Launcher::DoRun, this));
347 ServiceProcessControl::Launcher::~Launcher() {
348 CloseProcessHandle();
352 void ServiceProcessControl::Launcher::Notify() {
353 DCHECK(!notify_task_.is_null());
354 notify_task_.Run();
355 notify_task_.Reset();
358 void ServiceProcessControl::Launcher::CloseProcessHandle() {
359 if (process_handle_ != base::kNullProcessHandle) {
360 base::CloseProcessHandle(process_handle_);
361 process_handle_ = base::kNullProcessHandle;
365 #if !defined(OS_MACOSX)
366 void ServiceProcessControl::Launcher::DoDetectLaunched() {
367 DCHECK(!notify_task_.is_null());
369 const uint32 kMaxLaunchDetectRetries = 10;
370 launched_ = CheckServiceProcessReady();
372 int exit_code = 0;
373 if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries) ||
374 base::WaitForExitCodeWithTimeout(process_handle_, &exit_code,
375 base::TimeDelta())) {
376 CloseProcessHandle();
377 BrowserThread::PostTask(
378 BrowserThread::UI, FROM_HERE, base::Bind(&Launcher::Notify, this));
379 return;
381 retry_count_++;
383 // If the service process is not launched yet then check again in 2 seconds.
384 const base::TimeDelta kDetectLaunchRetry = base::TimeDelta::FromSeconds(2);
385 base::MessageLoop::current()->PostDelayedTask(
386 FROM_HERE, base::Bind(&Launcher::DoDetectLaunched, this),
387 kDetectLaunchRetry);
390 void ServiceProcessControl::Launcher::DoRun() {
391 DCHECK(!notify_task_.is_null());
393 base::LaunchOptions options;
394 #if defined(OS_WIN)
395 options.start_hidden = true;
396 #endif
397 if (base::LaunchProcess(*cmd_line_, options, &process_handle_)) {
398 BrowserThread::PostTask(
399 BrowserThread::IO, FROM_HERE,
400 base::Bind(&Launcher::DoDetectLaunched, this));
401 } else {
402 BrowserThread::PostTask(
403 BrowserThread::UI, FROM_HERE, base::Bind(&Launcher::Notify, this));
406 #endif // !OS_MACOSX