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