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