• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/stl_util-inl.h"
11 #include "base/threading/thread.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/io_thread.h"
15 #include "chrome/browser/upgrade_detector.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/service_messages.h"
18 #include "chrome/common/service_process_util.h"
19 #include "content/browser/browser_thread.h"
20 #include "content/common/child_process_host.h"
21 #include "content/common/notification_service.h"
22 #include "ui/base/ui_base_switches.h"
23 
24 
25 // ServiceProcessControl implementation.
ServiceProcessControl(Profile * profile)26 ServiceProcessControl::ServiceProcessControl(Profile* profile)
27     : profile_(profile) {
28 }
29 
~ServiceProcessControl()30 ServiceProcessControl::~ServiceProcessControl() {
31   STLDeleteElements(&connect_done_tasks_);
32   STLDeleteElements(&connect_success_tasks_);
33   STLDeleteElements(&connect_failure_tasks_);
34 }
35 
ConnectInternal()36 void ServiceProcessControl::ConnectInternal() {
37   // If the channel has already been established then we run the task
38   // and return.
39   if (channel_.get()) {
40     RunConnectDoneTasks();
41     return;
42   }
43 
44   // Actually going to connect.
45   VLOG(1) << "Connecting to Service Process IPC Server";
46   // Run the IPC channel on the shared IO thread.
47   base::Thread* io_thread = g_browser_process->io_thread();
48 
49   // TODO(hclam): Handle error connecting to channel.
50   const IPC::ChannelHandle channel_id = GetServiceProcessChannel();
51   channel_.reset(
52       new IPC::SyncChannel(channel_id, IPC::Channel::MODE_NAMED_CLIENT, this,
53                            io_thread->message_loop(), true,
54                            g_browser_process->shutdown_event()));
55 }
56 
RunConnectDoneTasks()57 void ServiceProcessControl::RunConnectDoneTasks() {
58   // The tasks executed here may add more tasks to the vector. So copy
59   // them to the stack before executing them. This way recursion is
60   // avoided.
61   TaskList tasks;
62   tasks.swap(connect_done_tasks_);
63   RunAllTasksHelper(&tasks);
64   DCHECK(tasks.empty());
65 
66   if (is_connected()) {
67     tasks.swap(connect_success_tasks_);
68     RunAllTasksHelper(&tasks);
69     DCHECK(tasks.empty());
70 
71     STLDeleteElements(&connect_failure_tasks_);
72   } else {
73     tasks.swap(connect_failure_tasks_);
74     RunAllTasksHelper(&tasks);
75     DCHECK(tasks.empty());
76 
77     STLDeleteElements(&connect_success_tasks_);
78   }
79 
80   DCHECK(connect_done_tasks_.empty());
81   DCHECK(connect_success_tasks_.empty());
82   DCHECK(connect_failure_tasks_.empty());
83 }
84 
85 // static
RunAllTasksHelper(TaskList * task_list)86 void ServiceProcessControl::RunAllTasksHelper(TaskList* task_list) {
87   TaskList::iterator index = task_list->begin();
88   while (index != task_list->end()) {
89     (*index)->Run();
90     delete (*index);
91     index = task_list->erase(index);
92   }
93 }
94 
Launch(Task * success_task,Task * failure_task)95 void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) {
96   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
97 
98   if (success_task) {
99     if (success_task == failure_task) {
100       // If the tasks are the same, then the same task needs to be invoked
101       // for success and failure.
102       failure_task = NULL;
103       connect_done_tasks_.push_back(success_task);
104     } else {
105       connect_success_tasks_.push_back(success_task);
106     }
107   }
108 
109   if (failure_task)
110     connect_failure_tasks_.push_back(failure_task);
111 
112   // If we already in the process of launching, then we are done.
113   if (launcher_) {
114     return;
115   }
116 
117   // If the service process is already running then connects to it.
118   if (CheckServiceProcessReady()) {
119     ConnectInternal();
120     return;
121   }
122 
123   // A service process should have a different mechanism for starting, but now
124   // we start it as if it is a child process.
125   FilePath exe_path = ChildProcessHost::GetChildPath(true);
126   if (exe_path.empty()) {
127     NOTREACHED() << "Unable to get service process binary name.";
128   }
129 
130   CommandLine* cmd_line = new CommandLine(exe_path);
131   cmd_line->AppendSwitchASCII(switches::kProcessType,
132                               switches::kServiceProcess);
133 
134   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
135   FilePath user_data_dir =
136       browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
137   if (!user_data_dir.empty())
138     cmd_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);
139 
140   std::string logging_level = browser_command_line.GetSwitchValueASCII(
141       switches::kLoggingLevel);
142   if (!logging_level.empty())
143     cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level);
144 
145   std::string v_level = browser_command_line.GetSwitchValueASCII(
146       switches::kV);
147   if (!v_level.empty())
148     cmd_line->AppendSwitchASCII(switches::kV, v_level);
149 
150   std::string v_modules = browser_command_line.GetSwitchValueASCII(
151       switches::kVModule);
152   if (!v_modules.empty())
153     cmd_line->AppendSwitchASCII(switches::kVModule, v_modules);
154 
155   if (browser_command_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
156     cmd_line->AppendSwitch(switches::kWaitForDebugger);
157   }
158 
159   if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
160     cmd_line->AppendSwitch(switches::kEnableLogging);
161   }
162 
163   std::string locale = g_browser_process->GetApplicationLocale();
164   cmd_line->AppendSwitchASCII(switches::kLang, locale);
165 
166   // And then start the process asynchronously.
167   launcher_ = new Launcher(this, cmd_line);
168   launcher_->Run(
169       NewRunnableMethod(this, &ServiceProcessControl::OnProcessLaunched));
170 }
171 
OnProcessLaunched()172 void ServiceProcessControl::OnProcessLaunched() {
173   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
174   if (launcher_->launched()) {
175     // After we have successfully created the service process we try to connect
176     // to it. The launch task is transfered to a connect task.
177     ConnectInternal();
178   } else {
179     // If we don't have process handle that means launching the service process
180     // has failed.
181     RunConnectDoneTasks();
182   }
183 
184   // We don't need the launcher anymore.
185   launcher_ = NULL;
186 }
187 
OnMessageReceived(const IPC::Message & message)188 bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
189   bool handled = true;
190   IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
191     IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_IsEnabled,
192                         OnCloudPrintProxyIsEnabled)
193     IPC_MESSAGE_HANDLER(ServiceHostMsg_RemotingHost_HostInfo,
194                          OnRemotingHostInfo)
195     IPC_MESSAGE_UNHANDLED(handled = false)
196   IPC_END_MESSAGE_MAP()
197   return handled;
198 }
199 
OnChannelConnected(int32 peer_pid)200 void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
202   channel_->set_sync_messages_with_no_timeout_allowed(false);
203 
204   // We just established a channel with the service process. Notify it if an
205   // upgrade is available.
206   if (UpgradeDetector::GetInstance()->notify_upgrade()) {
207     Send(new ServiceMsg_UpdateAvailable);
208   } else {
209     if (registrar_.IsEmpty())
210       registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED,
211                      NotificationService::AllSources());
212   }
213   RunConnectDoneTasks();
214 }
215 
OnChannelError()216 void ServiceProcessControl::OnChannelError() {
217   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218   channel_.reset();
219   RunConnectDoneTasks();
220 }
221 
Send(IPC::Message * message)222 bool ServiceProcessControl::Send(IPC::Message* message) {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224   if (!channel_.get())
225     return false;
226   return channel_->Send(message);
227 }
228 
229 // NotificationObserver implementation.
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)230 void ServiceProcessControl::Observe(NotificationType type,
231                                     const NotificationSource& source,
232                                     const NotificationDetails& details) {
233   if (type == NotificationType::UPGRADE_RECOMMENDED) {
234     Send(new ServiceMsg_UpdateAvailable);
235   }
236 }
237 
OnCloudPrintProxyIsEnabled(bool enabled,std::string email)238 void ServiceProcessControl::OnCloudPrintProxyIsEnabled(bool enabled,
239                                                        std::string email) {
240   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241   if (cloud_print_status_callback_ != NULL) {
242     cloud_print_status_callback_->Run(enabled, email);
243     cloud_print_status_callback_.reset();
244   }
245 }
246 
OnRemotingHostInfo(const remoting::ChromotingHostInfo & host_info)247 void ServiceProcessControl::OnRemotingHostInfo(
248     const remoting::ChromotingHostInfo& host_info) {
249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250   for (std::set<MessageHandler*>::iterator it = message_handlers_.begin();
251        it != message_handlers_.end(); ++it) {
252     (*it)->OnRemotingHostInfo(host_info);
253   }
254 }
255 
GetCloudPrintProxyStatus(Callback2<bool,std::string>::Type * cloud_print_status_callback)256 bool ServiceProcessControl::GetCloudPrintProxyStatus(
257     Callback2<bool, std::string>::Type* cloud_print_status_callback) {
258   DCHECK(cloud_print_status_callback);
259   cloud_print_status_callback_.reset(cloud_print_status_callback);
260   return Send(new ServiceMsg_IsCloudPrintProxyEnabled);
261 }
262 
Shutdown()263 bool ServiceProcessControl::Shutdown() {
264   bool ret = Send(new ServiceMsg_Shutdown());
265   channel_.reset();
266   return ret;
267 }
268 
SetRemotingHostCredentials(const std::string & user,const std::string & talk_token)269 bool ServiceProcessControl::SetRemotingHostCredentials(
270     const std::string& user,
271     const std::string& talk_token) {
272   return Send(
273       new ServiceMsg_SetRemotingHostCredentials(user, talk_token));
274 }
275 
EnableRemotingHost()276 bool ServiceProcessControl::EnableRemotingHost() {
277   return Send(new ServiceMsg_EnableRemotingHost());
278 }
279 
DisableRemotingHost()280 bool ServiceProcessControl::DisableRemotingHost() {
281   return Send(new ServiceMsg_DisableRemotingHost());
282 }
283 
RequestRemotingHostStatus()284 bool ServiceProcessControl::RequestRemotingHostStatus() {
285   if (CheckServiceProcessReady()) {
286     remoting::ChromotingHostInfo failure_host_info;
287     failure_host_info.enabled = false;
288 
289     Launch(NewRunnableMethod(this, &ServiceProcessControl::Send,
290                              new ServiceMsg_GetRemotingHostInfo),
291            NewRunnableMethod(this,
292                              &ServiceProcessControl::OnRemotingHostInfo,
293                              failure_host_info));
294     return true;
295   }
296   return false;
297 }
298 
AddMessageHandler(MessageHandler * message_handler)299 void ServiceProcessControl::AddMessageHandler(
300     MessageHandler* message_handler) {
301   message_handlers_.insert(message_handler);
302 }
303 
RemoveMessageHandler(MessageHandler * message_handler)304 void ServiceProcessControl::RemoveMessageHandler(
305     MessageHandler* message_handler) {
306   message_handlers_.erase(message_handler);
307 }
308 
309 DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl);
310 
Launcher(ServiceProcessControl * process,CommandLine * cmd_line)311 ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process,
312                                           CommandLine* cmd_line)
313     : process_(process),
314       cmd_line_(cmd_line),
315       launched_(false),
316       retry_count_(0) {
317 }
318 
319 // Execute the command line to start the process asynchronously.
320 // After the command is executed, |task| is called with the process handle on
321 // the UI thread.
Run(Task * task)322 void ServiceProcessControl::Launcher::Run(Task* task) {
323   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324   notify_task_.reset(task);
325   BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
326                          NewRunnableMethod(this, &Launcher::DoRun));
327 }
328 
~Launcher()329 ServiceProcessControl::Launcher::~Launcher() {}
330 
Notify()331 void ServiceProcessControl::Launcher::Notify() {
332   DCHECK(notify_task_.get());
333   notify_task_->Run();
334   notify_task_.reset();
335 }
336 
337 #if !defined(OS_MACOSX)
DoDetectLaunched()338 void ServiceProcessControl::Launcher::DoDetectLaunched() {
339   DCHECK(notify_task_.get());
340   const uint32 kMaxLaunchDetectRetries = 10;
341   launched_ = CheckServiceProcessReady();
342   if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) {
343     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
344         NewRunnableMethod(this, &Launcher::Notify));
345     return;
346   }
347   retry_count_++;
348 
349   // If the service process is not launched yet then check again in 2 seconds.
350   const int kDetectLaunchRetry = 2000;
351   MessageLoop::current()->PostDelayedTask(
352       FROM_HERE,
353       NewRunnableMethod(this, &Launcher::DoDetectLaunched),
354       kDetectLaunchRetry);
355 }
356 
DoRun()357 void ServiceProcessControl::Launcher::DoRun() {
358   DCHECK(notify_task_.get());
359   if (base::LaunchApp(*cmd_line_, false, true, NULL)) {
360     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
361                             NewRunnableMethod(this,
362                                               &Launcher::DoDetectLaunched));
363   } else {
364     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
365                             NewRunnableMethod(this, &Launcher::Notify));
366   }
367 }
368 #endif  // !OS_MACOSX
369