ChildProcessService.java revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 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. 4 5package org.chromium.content.app; 6 7import android.app.Service; 8import android.content.Context; 9import android.content.Intent; 10import android.graphics.SurfaceTexture; 11import android.os.Bundle; 12import android.os.IBinder; 13import android.os.ParcelFileDescriptor; 14import android.os.Process; 15import android.os.RemoteException; 16import android.util.Log; 17import android.view.Surface; 18 19import org.chromium.base.CalledByNative; 20import org.chromium.base.JNINamespace; 21import org.chromium.base.library_loader.LibraryLoader; 22import org.chromium.base.library_loader.Linker; 23import org.chromium.base.library_loader.ProcessInitException; 24import org.chromium.content.browser.ChildProcessConnection; 25import org.chromium.content.browser.ChildProcessLauncher; 26import org.chromium.content.common.IChildProcessCallback; 27import org.chromium.content.common.IChildProcessService; 28 29import java.util.ArrayList; 30import java.util.concurrent.atomic.AtomicReference; 31 32/** 33 * This is the base class for child services; the [Non]SandboxedProcessService0, 1.. etc 34 * subclasses provide the concrete service entry points, to enable the browser to connect 35 * to more than one distinct process (i.e. one process per service number, up to limit of N). 36 * The embedding application must declare these service instances in the application section 37 * of its AndroidManifest.xml, for example with N entries of the form:- 38 * <service android:name="org.chromium.content.app.[Non]SandboxedProcessServiceX" 39 * android:process=":[non]sandboxed_processX" /> 40 * for X in 0...N-1 (where N is {@link ChildProcessLauncher#MAX_REGISTERED_SERVICES}) 41 */ 42@JNINamespace("content") 43public class ChildProcessService extends Service { 44 private static final String MAIN_THREAD_NAME = "ChildProcessMain"; 45 private static final String TAG = "ChildProcessService"; 46 private IChildProcessCallback mCallback; 47 48 // This is the native "Main" thread for the renderer / utility process. 49 private Thread mMainThread; 50 // Parameters received via IPC, only accessed while holding the mMainThread monitor. 51 private String[] mCommandLineParams; 52 private int mCpuCount; 53 private long mCpuFeatures; 54 // Pairs IDs and file descriptors that should be registered natively. 55 private ArrayList<Integer> mFileIds; 56 private ArrayList<ParcelFileDescriptor> mFileFds; 57 // Linker-specific parameters for this child process service. 58 private ChromiumLinkerParams mLinkerParams; 59 60 private static AtomicReference<Context> sContext = new AtomicReference<Context>(null); 61 private boolean mLibraryInitialized = false; 62 // Becomes true once the service is bound. Access must synchronize around mMainThread. 63 private boolean mIsBound = false; 64 65 // Binder object used by clients for this service. 66 private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() { 67 // NOTE: Implement any IChildProcessService methods here. 68 @Override 69 public int setupConnection(Bundle args, IChildProcessCallback callback) { 70 mCallback = callback; 71 synchronized (mMainThread) { 72 // Allow the command line to be set via bind() intent or setupConnection, but 73 // the FD can only be transferred here. 74 if (mCommandLineParams == null) { 75 mCommandLineParams = args.getStringArray( 76 ChildProcessConnection.EXTRA_COMMAND_LINE); 77 } 78 // We must have received the command line by now 79 assert mCommandLineParams != null; 80 mCpuCount = args.getInt(ChildProcessConnection.EXTRA_CPU_COUNT); 81 mCpuFeatures = args.getLong(ChildProcessConnection.EXTRA_CPU_FEATURES); 82 assert mCpuCount > 0; 83 mFileIds = new ArrayList<Integer>(); 84 mFileFds = new ArrayList<ParcelFileDescriptor>(); 85 for (int i = 0;; i++) { 86 String fdName = ChildProcessConnection.EXTRA_FILES_PREFIX + i 87 + ChildProcessConnection.EXTRA_FILES_FD_SUFFIX; 88 ParcelFileDescriptor parcel = args.getParcelable(fdName); 89 if (parcel == null) { 90 // End of the file list. 91 break; 92 } 93 mFileFds.add(parcel); 94 String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i 95 + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX; 96 mFileIds.add(args.getInt(idName)); 97 } 98 Bundle sharedRelros = args.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS); 99 if (sharedRelros != null) { 100 Linker.useSharedRelros(sharedRelros); 101 sharedRelros = null; 102 } 103 mMainThread.notifyAll(); 104 } 105 return Process.myPid(); 106 } 107 }; 108 109 /* package */ static Context getContext() { 110 return sContext.get(); 111 } 112 113 @Override 114 public void onCreate() { 115 Log.i(TAG, "Creating new ChildProcessService pid=" + Process.myPid()); 116 if (sContext.get() != null) { 117 Log.e(TAG, "ChildProcessService created again in process!"); 118 } 119 sContext.set(this); 120 super.onCreate(); 121 122 mMainThread = new Thread(new Runnable() { 123 @Override 124 public void run() { 125 try { 126 boolean useLinker = Linker.isUsed(); 127 128 if (useLinker) { 129 synchronized (mMainThread) { 130 while (!mIsBound) { 131 mMainThread.wait(); 132 } 133 } 134 if (mLinkerParams != null) { 135 if (mLinkerParams.mWaitForSharedRelro) 136 Linker.initServiceProcess(mLinkerParams.mBaseLoadAddress); 137 else 138 Linker.disableSharedRelros(); 139 140 Linker.setTestRunnerClassName(mLinkerParams.mTestRunnerClassName); 141 } 142 } 143 try { 144 LibraryLoader.loadNow(); 145 } catch (ProcessInitException e) { 146 Log.e(TAG, "Failed to load native library, exiting child process", e); 147 System.exit(-1); 148 } 149 synchronized (mMainThread) { 150 while (mCommandLineParams == null) { 151 mMainThread.wait(); 152 } 153 } 154 LibraryLoader.initialize(mCommandLineParams); 155 synchronized (mMainThread) { 156 mLibraryInitialized = true; 157 mMainThread.notifyAll(); 158 while (mFileIds == null) { 159 mMainThread.wait(); 160 } 161 } 162 assert mFileIds.size() == mFileFds.size(); 163 int[] fileIds = new int[mFileIds.size()]; 164 int[] fileFds = new int[mFileFds.size()]; 165 for (int i = 0; i < mFileIds.size(); ++i) { 166 fileIds[i] = mFileIds.get(i); 167 fileFds[i] = mFileFds.get(i).detachFd(); 168 } 169 ContentMain.initApplicationContext(sContext.get().getApplicationContext()); 170 nativeInitChildProcess(sContext.get().getApplicationContext(), 171 ChildProcessService.this, fileIds, fileFds, 172 mCpuCount, mCpuFeatures); 173 ContentMain.start(); 174 nativeExitChildProcess(); 175 } catch (InterruptedException e) { 176 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 177 } catch (ProcessInitException e) { 178 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 179 } 180 } 181 }, MAIN_THREAD_NAME); 182 mMainThread.start(); 183 } 184 185 @Override 186 public void onDestroy() { 187 Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid()); 188 super.onDestroy(); 189 if (mCommandLineParams == null) { 190 // This process was destroyed before it even started. Nothing more to do. 191 return; 192 } 193 synchronized (mMainThread) { 194 try { 195 while (!mLibraryInitialized) { 196 // Avoid a potential race in calling through to native code before the library 197 // has loaded. 198 mMainThread.wait(); 199 } 200 } catch (InterruptedException e) { 201 // Ignore 202 } 203 } 204 // Try to shutdown the MainThread gracefully, but it might not 205 // have chance to exit normally. 206 nativeShutdownMainThread(); 207 } 208 209 @Override 210 public IBinder onBind(Intent intent) { 211 // We call stopSelf() to request that this service be stopped as soon as the client 212 // unbinds. Otherwise the system may keep it around and available for a reconnect. The 213 // child processes do not currently support reconnect; they must be initialized from 214 // scratch every time. 215 stopSelf(); 216 217 synchronized (mMainThread) { 218 mCommandLineParams = intent.getStringArrayExtra( 219 ChildProcessConnection.EXTRA_COMMAND_LINE); 220 mLinkerParams = null; 221 if (Linker.isUsed()) 222 mLinkerParams = new ChromiumLinkerParams(intent); 223 mIsBound = true; 224 mMainThread.notifyAll(); 225 } 226 227 return mBinder; 228 } 229 230 /** 231 * Called from native code to share a surface texture with another child process. 232 * Through using the callback object the browser is used as a proxy to route the 233 * call to the correct process. 234 * 235 * @param pid Process handle of the child process to share the SurfaceTexture with. 236 * @param surfaceObject The Surface or SurfaceTexture to share with the other child process. 237 * @param primaryID Used to route the call to the correct client instance. 238 * @param secondaryID Used to route the call to the correct client instance. 239 */ 240 @SuppressWarnings("unused") 241 @CalledByNative 242 private void establishSurfaceTexturePeer( 243 int pid, Object surfaceObject, int primaryID, int secondaryID) { 244 if (mCallback == null) { 245 Log.e(TAG, "No callback interface has been provided."); 246 return; 247 } 248 249 Surface surface = null; 250 boolean needRelease = false; 251 if (surfaceObject instanceof Surface) { 252 surface = (Surface) surfaceObject; 253 } else if (surfaceObject instanceof SurfaceTexture) { 254 surface = new Surface((SurfaceTexture) surfaceObject); 255 needRelease = true; 256 } else { 257 Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject); 258 return; 259 } 260 try { 261 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID); 262 } catch (RemoteException e) { 263 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 264 return; 265 } finally { 266 if (needRelease) { 267 surface.release(); 268 } 269 } 270 } 271 272 @SuppressWarnings("unused") 273 @CalledByNative 274 private Surface getViewSurface(int surfaceId) { 275 if (mCallback == null) { 276 Log.e(TAG, "No callback interface has been provided."); 277 return null; 278 } 279 280 try { 281 return mCallback.getViewSurface(surfaceId); 282 } catch (RemoteException e) { 283 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 284 return null; 285 } 286 } 287 288 /** 289 * The main entry point for a child process. This should be called from a new thread since 290 * it will not return until the child process exits. See child_process_service.{h,cc} 291 * 292 * @param applicationContext The Application Context of the current process. 293 * @param service The current ChildProcessService object. 294 * @param fileIds A list of file IDs that should be registered for access by the renderer. 295 * @param fileFds A list of file descriptors that should be registered for access by the 296 * renderer. 297 */ 298 private static native void nativeInitChildProcess(Context applicationContext, 299 ChildProcessService service, int[] extraFileIds, int[] extraFileFds, 300 int cpuCount, long cpuFeatures); 301 302 /** 303 * Force the child process to exit. 304 */ 305 private static native void nativeExitChildProcess(); 306 307 private native void nativeShutdownMainThread(); 308} 309