ChildProcessService.java revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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.content.browser.ChildProcessConnection; 22import org.chromium.content.browser.ChildProcessLauncher; 23import org.chromium.content.common.IChildProcessCallback; 24import org.chromium.content.common.IChildProcessService; 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 } 132 if (mLinkerParams != null) { 133 if (mLinkerParams.mWaitForSharedRelro) 134 Linker.initServiceProcess(mLinkerParams.mBaseLoadAddress); 135 else 136 Linker.disableSharedRelros(); 137 138 Linker.setTestRunnerClassName(mLinkerParams.mTestRunnerClassName); 139 } 140 } 141 try { 142 LibraryLoader.loadNow(); 143 } catch (ProcessInitException e) { 144 Log.e(TAG, "Failed to load native library, exiting child process", e); 145 System.exit(-1); 146 } 147 synchronized (mMainThread) { 148 while (mCommandLineParams == null) { 149 mMainThread.wait(); 150 } 151 } 152 LibraryLoader.initialize(mCommandLineParams); 153 synchronized (mMainThread) { 154 mLibraryInitialized = true; 155 mMainThread.notifyAll(); 156 while (mFileIds == null) { 157 mMainThread.wait(); 158 } 159 } 160 assert mFileIds.size() == mFileFds.size(); 161 int[] fileIds = new int[mFileIds.size()]; 162 int[] fileFds = new int[mFileFds.size()]; 163 for (int i = 0; i < mFileIds.size(); ++i) { 164 fileIds[i] = mFileIds.get(i); 165 fileFds[i] = mFileFds.get(i).detachFd(); 166 } 167 ContentMain.initApplicationContext(sContext.get().getApplicationContext()); 168 nativeInitChildProcess(sContext.get().getApplicationContext(), 169 ChildProcessService.this, fileIds, fileFds, 170 mCpuCount, mCpuFeatures); 171 ContentMain.start(); 172 nativeExitChildProcess(); 173 } catch (InterruptedException e) { 174 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 175 } catch (ProcessInitException e) { 176 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 177 } 178 } 179 }, MAIN_THREAD_NAME); 180 mMainThread.start(); 181 } 182 183 @Override 184 public void onDestroy() { 185 Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid()); 186 super.onDestroy(); 187 if (mCommandLineParams == null) { 188 // This process was destroyed before it even started. Nothing more to do. 189 return; 190 } 191 synchronized (mMainThread) { 192 try { 193 while (!mLibraryInitialized) { 194 // Avoid a potential race in calling through to native code before the library 195 // has loaded. 196 mMainThread.wait(); 197 } 198 } catch (InterruptedException e) { 199 } 200 } 201 // Try to shutdown the MainThread gracefully, but it might not 202 // have chance to exit normally. 203 nativeShutdownMainThread(); 204 } 205 206 @Override 207 public IBinder onBind(Intent intent) { 208 // We call stopSelf() to request that this service be stopped as soon as the client 209 // unbinds. Otherwise the system may keep it around and available for a reconnect. The 210 // child processes do not currently support reconnect; they must be initialized from 211 // scratch every time. 212 stopSelf(); 213 214 synchronized (mMainThread) { 215 mCommandLineParams = intent.getStringArrayExtra( 216 ChildProcessConnection.EXTRA_COMMAND_LINE); 217 mLinkerParams = null; 218 if (Linker.isUsed()) 219 mLinkerParams = new LinkerParams(intent); 220 mIsBound = true; 221 mMainThread.notifyAll(); 222 } 223 224 return mBinder; 225 } 226 227 /** 228 * Called from native code to share a surface texture with another child process. 229 * Through using the callback object the browser is used as a proxy to route the 230 * call to the correct process. 231 * 232 * @param pid Process handle of the child process to share the SurfaceTexture with. 233 * @param surfaceObject The Surface or SurfaceTexture to share with the other child process. 234 * @param primaryID Used to route the call to the correct client instance. 235 * @param secondaryID Used to route the call to the correct client instance. 236 */ 237 @SuppressWarnings("unused") 238 @CalledByNative 239 private void establishSurfaceTexturePeer( 240 int pid, Object surfaceObject, int primaryID, int secondaryID) { 241 if (mCallback == null) { 242 Log.e(TAG, "No callback interface has been provided."); 243 return; 244 } 245 246 Surface surface = null; 247 boolean needRelease = false; 248 if (surfaceObject instanceof Surface) { 249 surface = (Surface) surfaceObject; 250 } else if (surfaceObject instanceof SurfaceTexture) { 251 surface = new Surface((SurfaceTexture) surfaceObject); 252 needRelease = true; 253 } else { 254 Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject); 255 return; 256 } 257 try { 258 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID); 259 } catch (RemoteException e) { 260 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 261 return; 262 } finally { 263 if (needRelease) { 264 surface.release(); 265 } 266 } 267 } 268 269 @SuppressWarnings("unused") 270 @CalledByNative 271 private Surface getViewSurface(int surfaceId) { 272 if (mCallback == null) { 273 Log.e(TAG, "No callback interface has been provided."); 274 return null; 275 } 276 277 try { 278 return mCallback.getViewSurface(surfaceId); 279 } catch (RemoteException e) { 280 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 281 return null; 282 } 283 } 284 285 /** 286 * The main entry point for a child process. This should be called from a new thread since 287 * it will not return until the child process exits. See child_process_service.{h,cc} 288 * 289 * @param applicationContext The Application Context of the current process. 290 * @param service The current ChildProcessService object. 291 * @param fileIds A list of file IDs that should be registered for access by the renderer. 292 * @param fileFds A list of file descriptors that should be registered for access by the 293 * renderer. 294 */ 295 private static native void nativeInitChildProcess(Context applicationContext, 296 ChildProcessService service, int[] extraFileIds, int[] extraFileFds, 297 int cpuCount, long cpuFeatures); 298 299 /** 300 * Force the child process to exit. 301 */ 302 private static native void nativeExitChildProcess(); 303 304 private native void nativeShutdownMainThread(); 305} 306