service_process_control.cc revision bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293
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
3// found in the LICENSE file.
4
5#include "chrome/browser/service/service_process_control.h"
6
7#include "base/command_line.h"
8#include "base/file_path.h"
9#include "base/process_util.h"
10#include "base/thread.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/chrome_thread.h"
13#include "chrome/browser/io_thread.h"
14#include "chrome/common/child_process_host.h"
15#include "chrome/common/chrome_switches.h"
16#include "chrome/common/service_messages.h"
17#include "chrome/common/service_process_util.h"
18
19// ServiceProcessControl::Launcher implementation.
20// This class is responsible for launching the service process on the
21// PROCESS_LAUNCHER thread.
22class ServiceProcessControl::Launcher
23    : public base::RefCountedThreadSafe<ServiceProcessControl::Launcher> {
24 public:
25  Launcher(ServiceProcessControl* process, CommandLine* cmd_line)
26      : process_(process),
27        cmd_line_(cmd_line),
28        launched_(false) {
29  }
30
31  // Execute the command line to start the process asynchronously.
32  // After the comamnd is executed |task| is called with the process handle on
33  // the UI thread.
34  void Run(Task* task) {
35    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
36
37    ChromeThread::PostTask(ChromeThread::PROCESS_LAUNCHER, FROM_HERE,
38                           NewRunnableMethod(this, &Launcher::DoRun, task));
39  }
40
41  bool launched() const { return launched_; }
42
43 private:
44  void DoRun(Task* task) {
45    launched_ = base::LaunchApp(*cmd_line_.get(), false, true, NULL);
46
47    ChromeThread::PostTask(
48        ChromeThread::IO, FROM_HERE,
49        NewRunnableMethod(this, &Launcher::DoDetectLaunched, task));
50  }
51
52  void DoDetectLaunched(Task* task) {
53    if (CheckServiceProcessRunning(kServiceProcessCloudPrint)) {
54      ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
55          NewRunnableMethod(this, &Launcher::Notify, task));
56      return;
57    }
58
59    // If the service process is not launched yet then check again in 2 seconds.
60    const int kDetectLaunchRetry = 2000;
61    ChromeThread::PostDelayedTask(
62        ChromeThread::IO, FROM_HERE,
63        NewRunnableMethod(this, &Launcher::DoDetectLaunched, task),
64        kDetectLaunchRetry);
65  }
66
67  void Notify(Task* task) {
68    task->Run();
69    delete task;
70  }
71
72  ServiceProcessControl* process_;
73  scoped_ptr<CommandLine> cmd_line_;
74  bool launched_;
75};
76
77// ServiceProcessControl implementation.
78ServiceProcessControl::ServiceProcessControl(Profile* profile,
79                                             ServiceProcessType type)
80    : profile_(profile),
81      type_(type),
82      message_handler_(NULL) {
83}
84
85ServiceProcessControl::~ServiceProcessControl() {
86}
87
88void ServiceProcessControl::ConnectInternal(Task* task) {
89  // If the channel has already been established then we run the task
90  // and return.
91  if (channel_.get()) {
92    if (task) {
93      task->Run();
94      delete task;
95    }
96    return;
97  }
98
99  // Actually going to connect.
100  LOG(INFO) << "Connecting to Service Process IPC Server";
101  connect_done_task_.reset(task);
102
103  // Run the IPC channel on the shared IO thread.
104  base::Thread* io_thread = g_browser_process->io_thread();
105
106  // TODO(hclam): Handle error connecting to channel.
107  const std::string channel_id = GetServiceProcessChannelName();
108  channel_.reset(
109      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this, NULL,
110                           io_thread->message_loop(), true,
111                           g_browser_process->shutdown_event()));
112  channel_->set_sync_messages_with_no_timeout_allowed(false);
113}
114
115void ServiceProcessControl::Launch(Task* task) {
116  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
117
118  // If the service process is already running then connects to it.
119  if (CheckServiceProcessRunning(kServiceProcessCloudPrint)) {
120    ConnectInternal(task);
121    return;
122  }
123
124  // A service process should have a different mechanism for starting, but now
125  // we start it as if it is a child process.
126  FilePath exe_path = ChildProcessHost::GetChildPath(true);
127  if (exe_path.empty()) {
128    NOTREACHED() << "Unable to get service process binary name.";
129  }
130
131  CommandLine* cmd_line = new CommandLine(exe_path);
132  cmd_line->AppendSwitchASCII(switches::kProcessType,
133                              switches::kServiceProcess);
134
135  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
136  FilePath user_data_dir =
137      browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
138  if (!user_data_dir.empty())
139    cmd_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);
140
141  std::string logging_level = browser_command_line.GetSwitchValueASCII(
142      switches::kLoggingLevel);
143  if (!logging_level.empty())
144    cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level);
145
146  if (browser_command_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
147    cmd_line->AppendSwitch(switches::kWaitForDebugger);
148  }
149
150  // And then start the process asynchronously.
151  launcher_ = new Launcher(this, cmd_line);
152  launcher_->Run(
153      NewRunnableMethod(this, &ServiceProcessControl::OnProcessLaunched, task));
154}
155
156void ServiceProcessControl::OnProcessLaunched(Task* task) {
157  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
158  if (launcher_->launched()) {
159    // After we have successfully created the service process we try to connect
160    // to it. The launch task is transfered to a connect task.
161    ConnectInternal(task);
162  } else if (task) {
163    // If we don't have process handle that means launching the service process
164    // has failed.
165    task->Run();
166    delete task;
167  }
168
169  // We don't need the launcher anymore.
170  launcher_ = NULL;
171}
172
173void ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
174  IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
175      IPC_MESSAGE_HANDLER(ServiceHostMsg_GoodDay, OnGoodDay)
176      IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_IsEnabled,
177                          OnCloudPrintProxyIsEnabled)
178  IPC_END_MESSAGE_MAP()
179}
180
181void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
182  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
183  if (!connect_done_task_.get())
184    return;
185  connect_done_task_->Run();
186  connect_done_task_.reset();
187}
188
189void ServiceProcessControl::OnChannelError() {
190  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
191  channel_.reset();
192  if (!connect_done_task_.get())
193    return;
194  connect_done_task_->Run();
195  connect_done_task_.reset();
196}
197
198bool ServiceProcessControl::Send(IPC::Message* message) {
199  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
200  if (!channel_.get())
201    return false;
202  return channel_->Send(message);
203}
204
205void ServiceProcessControl::OnGoodDay() {
206  if (!message_handler_)
207    return;
208
209  message_handler_->OnGoodDay();
210}
211
212void ServiceProcessControl::OnCloudPrintProxyIsEnabled(bool enabled,
213                                                       std::string email) {
214  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
215  if (cloud_print_status_callback_ != NULL) {
216    cloud_print_status_callback_->Run(enabled, email);
217    cloud_print_status_callback_.reset();
218  }
219}
220
221bool ServiceProcessControl::SendHello() {
222  return Send(new ServiceMsg_Hello());
223}
224
225bool ServiceProcessControl::Shutdown() {
226  bool ret = Send(new ServiceMsg_Shutdown());
227  channel_.reset();
228  return ret;
229}
230
231bool ServiceProcessControl::EnableRemotingWithTokens(
232    const std::string& user,
233    const std::string& remoting_token,
234    const std::string& talk_token) {
235  return Send(
236      new ServiceMsg_EnableRemotingWithTokens(user, remoting_token,
237                                              talk_token));
238}
239
240bool ServiceProcessControl::GetCloudPrintProxyStatus(
241    Callback2<bool, std::string>::Type* cloud_print_status_callback) {
242  DCHECK(cloud_print_status_callback);
243  cloud_print_status_callback_.reset(cloud_print_status_callback);
244  return Send(new ServiceMsg_IsCloudPrintProxyEnabled);
245}
246
247DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl);
248