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.CalledByNative;
20 import org.chromium.base.CommandLine;
21 import org.chromium.base.JNINamespace;
22 import org.chromium.base.library_loader.LibraryLoader;
23 import org.chromium.base.library_loader.Linker;
24 import org.chromium.base.library_loader.ProcessInitException;
25 import org.chromium.content.browser.ChildProcessConnection;
26 import org.chromium.content.browser.ChildProcessLauncher;
27 import org.chromium.content.common.IChildProcessCallback;
28 import org.chromium.content.common.IChildProcessService;
30 import java.util.ArrayList;
31 import java.util.concurrent.atomic.AtomicReference;
34 * This is the base class for child services; the [Non]SandboxedProcessService0, 1.. etc
35 * subclasses provide the concrete service entry points, to enable the browser to connect
36 * to more than one distinct process (i.e. one process per service number, up to limit of N).
37 * The embedding application must declare these service instances in the application section
38 * of its AndroidManifest.xml, for example with N entries of the form:-
39 * <service android:name="org.chromium.content.app.[Non]SandboxedProcessServiceX"
40 * android:process=":[non]sandboxed_processX" />
41 * for X in 0...N-1 (where N is {@link ChildProcessLauncher#MAX_REGISTERED_SERVICES})
43 @JNINamespace("content")
44 public class ChildProcessService extends Service {
45 private static final String MAIN_THREAD_NAME = "ChildProcessMain";
46 private static final String TAG = "ChildProcessService";
47 private IChildProcessCallback mCallback;
49 // This is the native "Main" thread for the renderer / utility process.
50 private Thread mMainThread;
51 // Parameters received via IPC, only accessed while holding the mMainThread monitor.
52 private String[] mCommandLineParams;
53 private int mCpuCount;
54 private long mCpuFeatures;
55 // Pairs IDs and file descriptors that should be registered natively.
56 private ArrayList<Integer> mFileIds;
57 private ArrayList<ParcelFileDescriptor> mFileFds;
58 // Linker-specific parameters for this child process service.
59 private ChromiumLinkerParams mLinkerParams;
61 private static AtomicReference<Context> sContext = new AtomicReference<Context>(null);
62 private boolean mLibraryInitialized = false;
63 // Becomes true once the service is bound. Access must synchronize around mMainThread.
64 private boolean mIsBound = false;
66 // Binder object used by clients for this service.
67 private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() {
68 // NOTE: Implement any IChildProcessService methods here.
70 public int setupConnection(Bundle args, IChildProcessCallback callback) {
72 synchronized (mMainThread) {
73 // Allow the command line to be set via bind() intent or setupConnection, but
74 // the FD can only be transferred here.
75 if (mCommandLineParams == null) {
76 mCommandLineParams = args.getStringArray(
77 ChildProcessConnection.EXTRA_COMMAND_LINE);
79 // We must have received the command line by now
80 assert mCommandLineParams != null;
81 mCpuCount = args.getInt(ChildProcessConnection.EXTRA_CPU_COUNT);
82 mCpuFeatures = args.getLong(ChildProcessConnection.EXTRA_CPU_FEATURES);
84 mFileIds = new ArrayList<Integer>();
85 mFileFds = new ArrayList<ParcelFileDescriptor>();
86 for (int i = 0;; i++) {
87 String fdName = ChildProcessConnection.EXTRA_FILES_PREFIX + i
88 + ChildProcessConnection.EXTRA_FILES_FD_SUFFIX;
89 ParcelFileDescriptor parcel = args.getParcelable(fdName);
91 // End of the file list.
95 String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i
96 + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX;
97 mFileIds.add(args.getInt(idName));
99 Bundle sharedRelros = args.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
100 if (sharedRelros != null) {
101 Linker.useSharedRelros(sharedRelros);
104 mMainThread.notifyAll();
106 return Process.myPid();
110 public void crashIntentionallyForTesting() {
111 Process.killProcess(Process.myPid());
115 /* package */ static Context getContext() {
116 return sContext.get();
120 public void onCreate() {
121 Log.i(TAG, "Creating new ChildProcessService pid=" + Process.myPid());
122 if (sContext.get() != null) {
123 Log.e(TAG, "ChildProcessService created again in process!");
128 mMainThread = new Thread(new Runnable() {
132 boolean useLinker = Linker.isUsed();
133 boolean requestedSharedRelro = false;
135 synchronized (mMainThread) {
140 if (mLinkerParams != null) {
141 if (mLinkerParams.mWaitForSharedRelro) {
142 requestedSharedRelro = true;
143 Linker.initServiceProcess(mLinkerParams.mBaseLoadAddress);
145 Linker.disableSharedRelros();
147 Linker.setTestRunnerClassName(mLinkerParams.mTestRunnerClassName);
150 boolean isLoaded = false;
151 synchronized (mMainThread) {
152 while (mCommandLineParams == null) {
156 CommandLine.init(mCommandLineParams);
158 LibraryLoader.loadNow(getApplicationContext(), false);
160 } catch (ProcessInitException e) {
161 if (requestedSharedRelro) {
162 Log.w(TAG, "Failed to load native library with shared RELRO, " +
165 Log.e(TAG, "Failed to load native library", e);
168 if (!isLoaded && requestedSharedRelro) {
169 Linker.disableSharedRelros();
171 LibraryLoader.loadNow(getApplicationContext(), false);
173 } catch (ProcessInitException e) {
174 Log.e(TAG, "Failed to load native library on retry", e);
180 LibraryLoader.initialize();
181 synchronized (mMainThread) {
182 mLibraryInitialized = true;
183 mMainThread.notifyAll();
184 while (mFileIds == null) {
188 assert mFileIds.size() == mFileFds.size();
189 int[] fileIds = new int[mFileIds.size()];
190 int[] fileFds = new int[mFileFds.size()];
191 for (int i = 0; i < mFileIds.size(); ++i) {
192 fileIds[i] = mFileIds.get(i);
193 fileFds[i] = mFileFds.get(i).detachFd();
195 ContentMain.initApplicationContext(sContext.get().getApplicationContext());
196 nativeInitChildProcess(sContext.get().getApplicationContext(),
197 ChildProcessService.this, fileIds, fileFds,
198 mCpuCount, mCpuFeatures);
200 nativeExitChildProcess();
201 } catch (InterruptedException e) {
202 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e);
203 } catch (ProcessInitException e) {
204 Log.w(TAG, MAIN_THREAD_NAME + " startup failed: " + e);
207 }, MAIN_THREAD_NAME);
212 public void onDestroy() {
213 Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid());
215 if (mCommandLineParams == null) {
216 // This process was destroyed before it even started. Nothing more to do.
219 synchronized (mMainThread) {
221 while (!mLibraryInitialized) {
222 // Avoid a potential race in calling through to native code before the library
226 } catch (InterruptedException e) {
230 // Try to shutdown the MainThread gracefully, but it might not
231 // have chance to exit normally.
232 nativeShutdownMainThread();
236 public IBinder onBind(Intent intent) {
237 // We call stopSelf() to request that this service be stopped as soon as the client
238 // unbinds. Otherwise the system may keep it around and available for a reconnect. The
239 // child processes do not currently support reconnect; they must be initialized from
240 // scratch every time.
243 synchronized (mMainThread) {
244 mCommandLineParams = intent.getStringArrayExtra(
245 ChildProcessConnection.EXTRA_COMMAND_LINE);
246 mLinkerParams = null;
248 mLinkerParams = new ChromiumLinkerParams(intent);
250 mMainThread.notifyAll();
257 * Called from native code to share a surface texture with another child process.
258 * Through using the callback object the browser is used as a proxy to route the
259 * call to the correct process.
261 * @param pid Process handle of the child process to share the SurfaceTexture with.
262 * @param surfaceObject The Surface or SurfaceTexture to share with the other child process.
263 * @param primaryID Used to route the call to the correct client instance.
264 * @param secondaryID Used to route the call to the correct client instance.
266 @SuppressWarnings("unused")
268 private void establishSurfaceTexturePeer(
269 int pid, Object surfaceObject, int primaryID, int secondaryID) {
270 if (mCallback == null) {
271 Log.e(TAG, "No callback interface has been provided.");
275 Surface surface = null;
276 boolean needRelease = false;
277 if (surfaceObject instanceof Surface) {
278 surface = (Surface) surfaceObject;
279 } else if (surfaceObject instanceof SurfaceTexture) {
280 surface = new Surface((SurfaceTexture) surfaceObject);
283 Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject);
287 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID);
288 } catch (RemoteException e) {
289 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
298 @SuppressWarnings("unused")
300 private Surface getViewSurface(int surfaceId) {
301 if (mCallback == null) {
302 Log.e(TAG, "No callback interface has been provided.");
307 return mCallback.getViewSurface(surfaceId).getSurface();
308 } catch (RemoteException e) {
309 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
314 @SuppressWarnings("unused")
316 private Surface getSurfaceTextureSurface(int primaryId, int secondaryId) {
317 if (mCallback == null) {
318 Log.e(TAG, "No callback interface has been provided.");
323 return mCallback.getSurfaceTextureSurface(primaryId, secondaryId).getSurface();
324 } catch (RemoteException e) {
325 Log.e(TAG, "Unable to call getSurfaceTextureSurface: " + e);
331 * The main entry point for a child process. This should be called from a new thread since
332 * it will not return until the child process exits. See child_process_service.{h,cc}
334 * @param applicationContext The Application Context of the current process.
335 * @param service The current ChildProcessService object.
336 * @param fileIds A list of file IDs that should be registered for access by the renderer.
337 * @param fileFds A list of file descriptors that should be registered for access by the
340 private static native void nativeInitChildProcess(Context applicationContext,
341 ChildProcessService service, int[] extraFileIds, int[] extraFileFds,
342 int cpuCount, long cpuFeatures);
345 * Force the child process to exit.
347 private static native void nativeExitChildProcess();
349 private native void nativeShutdownMainThread();