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