Enable CDD in cloud print connector on windows by default.
[chromium-blink-merge.git] / chrome / browser / service_process / service_process_control.cc
blob15a902e136d6ec05bad574aefabd3dc378391afd
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 #if defined(OS_WIN)
141 switches::kEnableCloudPrintXps,
142 #endif
143 switches::kEnableLogging,
144 switches::kIgnoreUrlFetcherCertRequests,
145 switches::kLang,
146 switches::kLoggingLevel,
147 switches::kLsoUrl,
148 switches::kNoServiceAutorun,
149 switches::kUserDataDir,
150 switches::kV,
151 switches::kVModule,
152 switches::kWaitForDebugger,
154 cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
155 kSwitchesToCopy,
156 arraysize(kSwitchesToCopy));
158 // And then start the process asynchronously.
159 launcher_ = new Launcher(this, cmd_line);
160 launcher_->Run(base::Bind(&ServiceProcessControl::OnProcessLaunched,
161 base::Unretained(this)));
164 void ServiceProcessControl::Disconnect() {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
166 channel_.reset();
169 void ServiceProcessControl::OnProcessLaunched() {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
171 if (launcher_->launched()) {
172 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
173 SERVICE_EVENT_LAUNCHED, SERVICE_EVENT_MAX);
174 // After we have successfully created the service process we try to connect
175 // to it. The launch task is transfered to a connect task.
176 ConnectInternal();
177 } else {
178 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
179 SERVICE_EVENT_LAUNCH_FAILED, SERVICE_EVENT_MAX);
180 // If we don't have process handle that means launching the service process
181 // has failed.
182 RunConnectDoneTasks();
185 // We don't need the launcher anymore.
186 launcher_ = NULL;
189 bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
190 bool handled = true;
191 IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
192 IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_Info,
193 OnCloudPrintProxyInfo)
194 IPC_MESSAGE_HANDLER(ServiceHostMsg_Histograms, OnHistograms)
195 IPC_MESSAGE_UNHANDLED(handled = false)
196 IPC_END_MESSAGE_MAP()
197 return handled;
200 void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
204 SERVICE_EVENT_CHANNEL_CONNECTED, SERVICE_EVENT_MAX);
206 // We just established a channel with the service process. Notify it if an
207 // upgrade is available.
208 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
209 Send(new ServiceMsg_UpdateAvailable);
210 } else {
211 if (registrar_.IsEmpty())
212 registrar_.Add(this, chrome::NOTIFICATION_UPGRADE_RECOMMENDED,
213 content::NotificationService::AllSources());
215 RunConnectDoneTasks();
218 void ServiceProcessControl::OnChannelError() {
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
222 SERVICE_EVENT_CHANNEL_ERROR, SERVICE_EVENT_MAX);
224 channel_.reset();
225 RunConnectDoneTasks();
228 bool ServiceProcessControl::Send(IPC::Message* message) {
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
230 if (!channel_.get())
231 return false;
232 return channel_->Send(message);
235 // content::NotificationObserver implementation.
236 void ServiceProcessControl::Observe(
237 int type,
238 const content::NotificationSource& source,
239 const content::NotificationDetails& details) {
240 if (type == chrome::NOTIFICATION_UPGRADE_RECOMMENDED) {
241 Send(new ServiceMsg_UpdateAvailable);
245 void ServiceProcessControl::OnCloudPrintProxyInfo(
246 const cloud_print::CloudPrintProxyInfo& proxy_info) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
248 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
249 SERVICE_EVENT_INFO_REPLY, SERVICE_EVENT_MAX);
250 if (!cloud_print_info_callback_.is_null()) {
251 cloud_print_info_callback_.Run(proxy_info);
252 cloud_print_info_callback_.Reset();
256 void ServiceProcessControl::OnHistograms(
257 const std::vector<std::string>& pickled_histograms) {
258 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
259 SERVICE_EVENT_HISTOGRAMS_REPLY, SERVICE_EVENT_MAX);
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
262 pickled_histograms);
263 RunHistogramsCallback();
266 void ServiceProcessControl::RunHistogramsCallback() {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
268 if (!histograms_callback_.is_null()) {
269 histograms_callback_.Run();
270 histograms_callback_.Reset();
272 histograms_timeout_callback_.Cancel();
275 bool ServiceProcessControl::GetCloudPrintProxyInfo(
276 const CloudPrintProxyInfoHandler& cloud_print_info_callback) {
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278 DCHECK(!cloud_print_info_callback.is_null());
279 cloud_print_info_callback_.Reset();
280 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
281 SERVICE_EVENT_INFO_REQUEST, SERVICE_EVENT_MAX);
282 if (!Send(new ServiceMsg_GetCloudPrintProxyInfo()))
283 return false;
284 cloud_print_info_callback_ = cloud_print_info_callback;
285 return true;
288 bool ServiceProcessControl::GetHistograms(
289 const base::Closure& histograms_callback,
290 const base::TimeDelta& timeout) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 DCHECK(!histograms_callback.is_null());
293 histograms_callback_.Reset();
295 // If the service process is already running then connect to it.
296 if (!CheckServiceProcessReady())
297 return false;
298 ConnectInternal();
300 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceEvents",
301 SERVICE_EVENT_HISTOGRAMS_REQUEST,
302 SERVICE_EVENT_MAX);
304 if (!Send(new ServiceMsg_GetHistograms()))
305 return false;
307 // Run timeout task to make sure |histograms_callback| is called.
308 histograms_timeout_callback_.Reset(
309 base::Bind(&ServiceProcessControl::RunHistogramsCallback,
310 base::Unretained(this)));
311 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
312 histograms_timeout_callback_.callback(),
313 timeout);
315 histograms_callback_ = histograms_callback;
316 return true;
319 bool ServiceProcessControl::Shutdown() {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321 bool ret = Send(new ServiceMsg_Shutdown());
322 channel_.reset();
323 return ret;
326 // static
327 ServiceProcessControl* ServiceProcessControl::GetInstance() {
328 return Singleton<ServiceProcessControl>::get();
331 ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process,
332 CommandLine* cmd_line)
333 : process_(process),
334 cmd_line_(cmd_line),
335 launched_(false),
336 retry_count_(0),
337 process_handle_(base::kNullProcessHandle) {
340 // Execute the command line to start the process asynchronously.
341 // After the command is executed, |task| is called with the process handle on
342 // the UI thread.
343 void ServiceProcessControl::Launcher::Run(const base::Closure& task) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345 notify_task_ = task;
346 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
347 base::Bind(&Launcher::DoRun, this));
350 ServiceProcessControl::Launcher::~Launcher() {
351 CloseProcessHandle();
355 void ServiceProcessControl::Launcher::Notify() {
356 DCHECK(!notify_task_.is_null());
357 notify_task_.Run();
358 notify_task_.Reset();
361 void ServiceProcessControl::Launcher::CloseProcessHandle() {
362 if (process_handle_ != base::kNullProcessHandle) {
363 base::CloseProcessHandle(process_handle_);
364 process_handle_ = base::kNullProcessHandle;
368 #if !defined(OS_MACOSX)
369 void ServiceProcessControl::Launcher::DoDetectLaunched() {
370 DCHECK(!notify_task_.is_null());
372 const uint32 kMaxLaunchDetectRetries = 10;
373 launched_ = CheckServiceProcessReady();
375 int exit_code = 0;
376 if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries) ||
377 base::WaitForExitCodeWithTimeout(process_handle_, &exit_code,
378 base::TimeDelta())) {
379 CloseProcessHandle();
380 BrowserThread::PostTask(
381 BrowserThread::UI, FROM_HERE, base::Bind(&Launcher::Notify, this));
382 return;
384 retry_count_++;
386 // If the service process is not launched yet then check again in 2 seconds.
387 const base::TimeDelta kDetectLaunchRetry = base::TimeDelta::FromSeconds(2);
388 base::MessageLoop::current()->PostDelayedTask(
389 FROM_HERE, base::Bind(&Launcher::DoDetectLaunched, this),
390 kDetectLaunchRetry);
393 void ServiceProcessControl::Launcher::DoRun() {
394 DCHECK(!notify_task_.is_null());
396 base::LaunchOptions options;
397 #if defined(OS_WIN)
398 options.start_hidden = true;
399 #endif
400 if (base::LaunchProcess(*cmd_line_, options, &process_handle_)) {
401 BrowserThread::PostTask(
402 BrowserThread::IO, FROM_HERE,
403 base::Bind(&Launcher::DoDetectLaunched, this));
404 } else {
405 BrowserThread::PostTask(
406 BrowserThread::UI, FROM_HERE, base::Bind(&Launcher::Notify, this));
409 #endif // !OS_MACOSX