ChildProcessService.java revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 mNativeLibraryName; // Must be passed in via the bind command. 50 private String[] mCommandLineParams; 51 private int mCpuCount; 52 private long mCpuFeatures; 53 // Pairs IDs and file descriptors that should be registered natively. 54 private ArrayList<Integer> mFileIds; 55 private ArrayList<ParcelFileDescriptor> mFileFds; 56 57 private static AtomicReference<Context> sContext = new AtomicReference<Context>(null); 58 private boolean mLibraryInitialized = false; 59 60 // Binder object used by clients for this service. 61 private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() { 62 // NOTE: Implement any IChildProcessService methods here. 63 @Override 64 public int setupConnection(Bundle args, IChildProcessCallback callback) { 65 mCallback = callback; 66 synchronized (mMainThread) { 67 // Allow the command line to be set via bind() intent or setupConnection, but 68 // the FD can only be transferred here. 69 if (mCommandLineParams == null) { 70 mCommandLineParams = args.getStringArray( 71 ChildProcessConnection.EXTRA_COMMAND_LINE); 72 } 73 // We must have received the command line by now 74 assert mCommandLineParams != null; 75 mCpuCount = args.getInt(ChildProcessConnection.EXTRA_CPU_COUNT); 76 mCpuFeatures = args.getLong(ChildProcessConnection.EXTRA_CPU_FEATURES); 77 assert mCpuCount > 0; 78 mFileIds = new ArrayList<Integer>(); 79 mFileFds = new ArrayList<ParcelFileDescriptor>(); 80 for (int i = 0;; i++) { 81 String fdName = ChildProcessConnection.EXTRA_FILES_PREFIX + i 82 + ChildProcessConnection.EXTRA_FILES_FD_SUFFIX; 83 ParcelFileDescriptor parcel = args.getParcelable(fdName); 84 if (parcel == null) { 85 // End of the file list. 86 break; 87 } 88 mFileFds.add(parcel); 89 String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i 90 + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX; 91 mFileIds.add(args.getInt(idName)); 92 } 93 mMainThread.notifyAll(); 94 } 95 return Process.myPid(); 96 } 97 }; 98 99 /* package */ static Context getContext() { 100 return sContext.get(); 101 } 102 103 @Override 104 public void onCreate() { 105 Log.i(TAG, "Creating new ChildProcessService pid=" + Process.myPid()); 106 if (sContext.get() != null) { 107 Log.e(TAG, "ChildProcessService created again in process!"); 108 } 109 sContext.set(this); 110 super.onCreate(); 111 112 mMainThread = new Thread(new Runnable() { 113 @Override 114 public void run() { 115 try { 116 synchronized (mMainThread) { 117 while (mNativeLibraryName == null) { 118 mMainThread.wait(); 119 } 120 } 121 LibraryLoader.setLibraryToLoad(mNativeLibraryName); 122 try { 123 LibraryLoader.loadNow(); 124 } catch (ProcessInitException e) { 125 Log.e(TAG, "Failed to load native library, exiting child process", e); 126 return; 127 } 128 synchronized (mMainThread) { 129 while (mCommandLineParams == null) { 130 mMainThread.wait(); 131 } 132 } 133 LibraryLoader.initialize(mCommandLineParams); 134 synchronized (mMainThread) { 135 mLibraryInitialized = true; 136 mMainThread.notifyAll(); 137 while (mFileIds == null) { 138 mMainThread.wait(); 139 } 140 } 141 assert mFileIds.size() == mFileFds.size(); 142 int[] fileIds = new int[mFileIds.size()]; 143 int[] fileFds = new int[mFileFds.size()]; 144 for (int i = 0; i < mFileIds.size(); ++i) { 145 fileIds[i] = mFileIds.get(i); 146 fileFds[i] = mFileFds.get(i).detachFd(); 147 } 148 ContentMain.initApplicationContext(sContext.get().getApplicationContext()); 149 nativeInitChildProcess(sContext.get().getApplicationContext(), 150 ChildProcessService.this, fileIds, fileFds, 151 mCpuCount, mCpuFeatures); 152 ContentMain.start(); 153 nativeExitChildProcess(); 154 } catch (InterruptedException e) { 155 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 156 } catch (ProcessInitException e) { 157 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e); 158 } 159 } 160 }, MAIN_THREAD_NAME); 161 mMainThread.start(); 162 } 163 164 @Override 165 public void onDestroy() { 166 Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid()); 167 super.onDestroy(); 168 if (mCommandLineParams == null) { 169 // This process was destroyed before it even started. Nothing more to do. 170 return; 171 } 172 synchronized (mMainThread) { 173 try { 174 while (!mLibraryInitialized) { 175 // Avoid a potential race in calling through to native code before the library 176 // has loaded. 177 mMainThread.wait(); 178 } 179 } catch (InterruptedException e) { 180 } 181 } 182 // Try to shutdown the MainThread gracefully, but it might not 183 // have chance to exit normally. 184 nativeShutdownMainThread(); 185 } 186 187 @Override 188 public IBinder onBind(Intent intent) { 189 // We call stopSelf() to request that this service be stopped as soon as the client 190 // unbinds. Otherwise the system may keep it around and available for a reconnect. The 191 // child processes do not currently support reconnect; they must be initialized from 192 // scratch every time. 193 stopSelf(); 194 195 synchronized (mMainThread) { 196 mNativeLibraryName = intent.getStringExtra( 197 ChildProcessConnection.EXTRA_NATIVE_LIBRARY_NAME); 198 mCommandLineParams = intent.getStringArrayExtra( 199 ChildProcessConnection.EXTRA_COMMAND_LINE); 200 mMainThread.notifyAll(); 201 } 202 203 return mBinder; 204 } 205 206 /** 207 * Called from native code to share a surface texture with another child process. 208 * Through using the callback object the browser is used as a proxy to route the 209 * call to the correct process. 210 * 211 * @param pid Process handle of the child process to share the SurfaceTexture with. 212 * @param surfaceObject The Surface or SurfaceTexture to share with the other child process. 213 * @param primaryID Used to route the call to the correct client instance. 214 * @param secondaryID Used to route the call to the correct client instance. 215 */ 216 @SuppressWarnings("unused") 217 @CalledByNative 218 private void establishSurfaceTexturePeer( 219 int pid, Object surfaceObject, int primaryID, int secondaryID) { 220 if (mCallback == null) { 221 Log.e(TAG, "No callback interface has been provided."); 222 return; 223 } 224 225 Surface surface = null; 226 boolean needRelease = false; 227 if (surfaceObject instanceof Surface) { 228 surface = (Surface)surfaceObject; 229 } else if (surfaceObject instanceof SurfaceTexture) { 230 surface = new Surface((SurfaceTexture)surfaceObject); 231 needRelease = true; 232 } else { 233 Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject); 234 return; 235 } 236 try { 237 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID); 238 } catch (RemoteException e) { 239 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 240 return; 241 } finally { 242 if (needRelease) { 243 surface.release(); 244 } 245 } 246 } 247 248 @SuppressWarnings("unused") 249 @CalledByNative 250 private Surface getViewSurface(int surfaceId) { 251 if (mCallback == null) { 252 Log.e(TAG, "No callback interface has been provided."); 253 return null; 254 } 255 256 try { 257 return mCallback.getViewSurface(surfaceId); 258 } catch (RemoteException e) { 259 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e); 260 return null; 261 } 262 } 263 264 /** 265 * The main entry point for a child process. This should be called from a new thread since 266 * it will not return until the child process exits. See child_process_service.{h,cc} 267 * 268 * @param applicationContext The Application Context of the current process. 269 * @param service The current ChildProcessService object. 270 * @param fileIds A list of file IDs that should be registered for access by the renderer. 271 * @param fileFds A list of file descriptors that should be registered for access by the 272 * renderer. 273 */ 274 private static native void nativeInitChildProcess(Context applicationContext, 275 ChildProcessService service, int[] extraFileIds, int[] extraFileFds, 276 int cpuCount, long cpuFeatures); 277 278 /** 279 * Force the child process to exit. 280 */ 281 private static native void nativeExitChildProcess(); 282 283 private native void nativeShutdownMainThread(); 284} 285