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.
5 package org.chromium.content.app;
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;
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;
31 import java.util.ArrayList;
32 import java.util.concurrent.atomic.AtomicReference;
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})
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;
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;
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;
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.
71 public int setupConnection(Bundle args, IChildProcessCallback 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);
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);
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);
92 // End of the file list.
96 String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i
97 + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX;
98 mFileIds.add(args.getInt(idName));
100 Bundle sharedRelros = args.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
101 if (sharedRelros != null) {
102 Linker.useSharedRelros(sharedRelros);
105 mMainThread.notifyAll();
107 return Process.myPid();
111 public void crashIntentionallyForTesting() {
112 Process.killProcess(Process.myPid());
116 /* package */ static Context getContext() {
117 return sContext.get();
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!");
129 mMainThread = new Thread(new Runnable() {
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) {
140 CommandLine.init(mCommandLineParams);
141 boolean useLinker = Linker.isUsed();
142 boolean requestedSharedRelro = false;
144 synchronized (mMainThread) {
149 assert mLinkerParams != null;
150 if (mLinkerParams.mWaitForSharedRelro) {
151 requestedSharedRelro = true;
152 Linker.initServiceProcess(mLinkerParams.mBaseLoadAddress);
154 Linker.disableSharedRelros();
156 Linker.setTestRunnerClassName(mLinkerParams.mTestRunnerClassName);
158 boolean isLoaded = false;
159 if (CommandLine.getInstance().hasSwitch(
160 BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER)) {
161 android.os.Debug.waitForDebugger();
164 boolean loadAtFixedAddressFailed = false;
166 LibraryLoader.loadNow(getApplicationContext(), false);
168 } catch (ProcessInitException e) {
169 if (requestedSharedRelro) {
170 Log.w(TAG, "Failed to load native library with shared RELRO, " +
172 loadAtFixedAddressFailed = true;
174 Log.e(TAG, "Failed to load native library", e);
177 if (!isLoaded && requestedSharedRelro) {
178 Linker.disableSharedRelros();
180 LibraryLoader.loadNow(getApplicationContext(), false);
182 } catch (ProcessInitException e) {
183 Log.e(TAG, "Failed to load native library on retry", e);
189 LibraryLoader.registerRendererProcessHistogram(
190 requestedSharedRelro,
191 loadAtFixedAddressFailed);
192 LibraryLoader.initialize();
193 synchronized (mMainThread) {
194 mLibraryInitialized = true;
195 mMainThread.notifyAll();
196 while (mFileIds == null) {
200 assert mFileIds.size() == mFileFds.size();
201 int[] fileIds = new int[mFileIds.size()];
202 int[] fileFds = new int[mFileFds.size()];
203 for (int i = 0; i < mFileIds.size(); ++i) {
204 fileIds[i] = mFileIds.get(i);
205 fileFds[i] = mFileFds.get(i).detachFd();
207 ContentMain.initApplicationContext(sContext.get().getApplicationContext());
208 nativeInitChildProcess(sContext.get().getApplicationContext(),
209 ChildProcessService.this, fileIds, fileFds,
210 mCpuCount, mCpuFeatures);
212 nativeExitChildProcess();
213 } catch (InterruptedException e) {
214 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e);
215 } catch (ProcessInitException e) {
216 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e);
219 }, MAIN_THREAD_NAME);
224 public void onDestroy() {
225 Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid());
227 if (mCommandLineParams == null) {
228 // This process was destroyed before it even started. Nothing more to do.
231 synchronized (mMainThread) {
233 while (!mLibraryInitialized) {
234 // Avoid a potential race in calling through to native code before the library
238 } catch (InterruptedException e) {
242 // Try to shutdown the MainThread gracefully, but it might not
243 // have chance to exit normally.
244 nativeShutdownMainThread();
248 public IBinder onBind(Intent intent) {
249 // We call stopSelf() to request that this service be stopped as soon as the client
250 // unbinds. Otherwise the system may keep it around and available for a reconnect. The
251 // child processes do not currently support reconnect; they must be initialized from
252 // scratch every time.
255 synchronized (mMainThread) {
256 mCommandLineParams = intent.getStringArrayExtra(
257 ChildProcessConnection.EXTRA_COMMAND_LINE);
258 // mLinkerParams is never used if Linker.isUsed() returns false.
260 mLinkerParams = new ChromiumLinkerParams(intent);
262 mMainThread.notifyAll();
269 * Called from native code to share a surface texture with another child process.
270 * Through using the callback object the browser is used as a proxy to route the
271 * call to the correct process.
273 * @param pid Process handle of the child process to share the SurfaceTexture with.
274 * @param surfaceObject The Surface or SurfaceTexture to share with the other child process.
275 * @param primaryID Used to route the call to the correct client instance.
276 * @param secondaryID Used to route the call to the correct client instance.
278 @SuppressWarnings("unused")
280 private void establishSurfaceTexturePeer(
281 int pid, Object surfaceObject, int primaryID, int secondaryID) {
282 if (mCallback == null) {
283 Log.e(TAG, "No callback interface has been provided.");
287 Surface surface = null;
288 boolean needRelease = false;
289 if (surfaceObject instanceof Surface) {
290 surface = (Surface) surfaceObject;
291 } else if (surfaceObject instanceof SurfaceTexture) {
292 surface = new Surface((SurfaceTexture) surfaceObject);
295 Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject);
299 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID);
300 } catch (RemoteException e) {
301 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
310 @SuppressWarnings("unused")
312 private Surface getViewSurface(int surfaceId) {
313 if (mCallback == null) {
314 Log.e(TAG, "No callback interface has been provided.");
319 return mCallback.getViewSurface(surfaceId).getSurface();
320 } catch (RemoteException e) {
321 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
326 @SuppressWarnings("unused")
328 private void createSurfaceTextureSurface(
329 int surfaceTextureId, int clientId, SurfaceTexture surfaceTexture) {
330 if (mCallback == null) {
331 Log.e(TAG, "No callback interface has been provided.");
335 Surface surface = new Surface(surfaceTexture);
337 mCallback.registerSurfaceTextureSurface(surfaceTextureId, clientId, surface);
338 } catch (RemoteException e) {
339 Log.e(TAG, "Unable to call registerSurfaceTextureSurface: " + e);
344 @SuppressWarnings("unused")
346 private void destroySurfaceTextureSurface(int surfaceTextureId, int clientId) {
347 if (mCallback == null) {
348 Log.e(TAG, "No callback interface has been provided.");
353 mCallback.unregisterSurfaceTextureSurface(surfaceTextureId, clientId);
354 } catch (RemoteException e) {
355 Log.e(TAG, "Unable to call unregisterSurfaceTextureSurface: " + e);
359 @SuppressWarnings("unused")
361 private Surface getSurfaceTextureSurface(int surfaceTextureId) {
362 if (mCallback == null) {
363 Log.e(TAG, "No callback interface has been provided.");
368 return mCallback.getSurfaceTextureSurface(surfaceTextureId).getSurface();
369 } catch (RemoteException e) {
370 Log.e(TAG, "Unable to call getSurfaceTextureSurface: " + e);
376 * The main entry point for a child process. This should be called from a new thread since
377 * it will not return until the child process exits. See child_process_service.{h,cc}
379 * @param applicationContext The Application Context of the current process.
380 * @param service The current ChildProcessService object.
381 * @param fileIds A list of file IDs that should be registered for access by the renderer.
382 * @param fileFds A list of file descriptors that should be registered for access by the
385 private static native void nativeInitChildProcess(Context applicationContext,
386 ChildProcessService service, int[] extraFileIds, int[] extraFileFds,
387 int cpuCount, long cpuFeatures);
390 * Force the child process to exit.
392 private static native void nativeExitChildProcess();
394 private native void nativeShutdownMainThread();