Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / app / ChildProcessService.java
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.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;
29
30 import java.util.ArrayList;
31 import java.util.concurrent.atomic.AtomicReference;
32
33 /**
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})
42  */
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;
48
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;
60
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;
65
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.
69         @Override
70         public int setupConnection(Bundle args, IChildProcessCallback callback) {
71             mCallback = 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);
78                 }
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);
83                 assert mCpuCount > 0;
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);
90                     if (parcel == null) {
91                         // End of the file list.
92                         break;
93                     }
94                     mFileFds.add(parcel);
95                     String idName = ChildProcessConnection.EXTRA_FILES_PREFIX + i
96                             + ChildProcessConnection.EXTRA_FILES_ID_SUFFIX;
97                     mFileIds.add(args.getInt(idName));
98                 }
99                 Bundle sharedRelros = args.getBundle(Linker.EXTRA_LINKER_SHARED_RELROS);
100                 if (sharedRelros != null) {
101                     Linker.useSharedRelros(sharedRelros);
102                     sharedRelros = null;
103                 }
104                 mMainThread.notifyAll();
105             }
106             return Process.myPid();
107         }
108
109         @Override
110         public void crashIntentionallyForTesting() {
111             Process.killProcess(Process.myPid());
112         }
113     };
114
115     /* package */ static Context getContext() {
116         return sContext.get();
117     }
118
119     @Override
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!");
124         }
125         sContext.set(this);
126         super.onCreate();
127
128         mMainThread = new Thread(new Runnable() {
129             @Override
130             public void run()  {
131                 try {
132                     boolean useLinker = Linker.isUsed();
133                     boolean requestedSharedRelro = false;
134                     if (useLinker) {
135                         synchronized (mMainThread) {
136                             while (!mIsBound) {
137                                 mMainThread.wait();
138                             }
139                         }
140                         if (mLinkerParams != null) {
141                             if (mLinkerParams.mWaitForSharedRelro) {
142                                 requestedSharedRelro = true;
143                                 Linker.initServiceProcess(mLinkerParams.mBaseLoadAddress);
144                             } else {
145                                 Linker.disableSharedRelros();
146                             }
147                             Linker.setTestRunnerClassName(mLinkerParams.mTestRunnerClassName);
148                         }
149                     }
150                     boolean isLoaded = false;
151                     synchronized (mMainThread) {
152                         while (mCommandLineParams == null) {
153                             mMainThread.wait();
154                         }
155                     }
156                     CommandLine.init(mCommandLineParams);
157                     try {
158                         LibraryLoader.loadNow(getApplicationContext(), false);
159                         isLoaded = true;
160                     } catch (ProcessInitException e) {
161                         if (requestedSharedRelro) {
162                             Log.w(TAG, "Failed to load native library with shared RELRO, " +
163                                   "retrying without");
164                         } else {
165                             Log.e(TAG, "Failed to load native library", e);
166                         }
167                     }
168                     if (!isLoaded && requestedSharedRelro) {
169                         Linker.disableSharedRelros();
170                         try {
171                             LibraryLoader.loadNow(getApplicationContext(), false);
172                             isLoaded = true;
173                         } catch (ProcessInitException e) {
174                             Log.e(TAG, "Failed to load native library on retry", e);
175                         }
176                     }
177                     if (!isLoaded) {
178                         System.exit(-1);
179                     }
180                     LibraryLoader.initialize();
181                     synchronized (mMainThread) {
182                         mLibraryInitialized = true;
183                         mMainThread.notifyAll();
184                         while (mFileIds == null) {
185                             mMainThread.wait();
186                         }
187                     }
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();
194                     }
195                     ContentMain.initApplicationContext(sContext.get().getApplicationContext());
196                     nativeInitChildProcess(sContext.get().getApplicationContext(),
197                             ChildProcessService.this, fileIds, fileFds,
198                             mCpuCount, mCpuFeatures);
199                     ContentMain.start();
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);
205                 }
206             }
207         }, MAIN_THREAD_NAME);
208         mMainThread.start();
209     }
210
211     @Override
212     public void onDestroy() {
213         Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid());
214         super.onDestroy();
215         if (mCommandLineParams == null) {
216             // This process was destroyed before it even started. Nothing more to do.
217             return;
218         }
219         synchronized (mMainThread) {
220             try {
221                 while (!mLibraryInitialized) {
222                     // Avoid a potential race in calling through to native code before the library
223                     // has loaded.
224                     mMainThread.wait();
225                 }
226             } catch (InterruptedException e) {
227                 // Ignore
228             }
229         }
230         // Try to shutdown the MainThread gracefully, but it might not
231         // have chance to exit normally.
232         nativeShutdownMainThread();
233     }
234
235     @Override
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.
241         stopSelf();
242
243         synchronized (mMainThread) {
244             mCommandLineParams = intent.getStringArrayExtra(
245                     ChildProcessConnection.EXTRA_COMMAND_LINE);
246             mLinkerParams = null;
247             if (Linker.isUsed())
248                 mLinkerParams = new ChromiumLinkerParams(intent);
249             mIsBound = true;
250             mMainThread.notifyAll();
251         }
252
253         return mBinder;
254     }
255
256     /**
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.
260      *
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.
265      */
266     @SuppressWarnings("unused")
267     @CalledByNative
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.");
272             return;
273         }
274
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);
281             needRelease = true;
282         } else {
283             Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject);
284             return;
285         }
286         try {
287             mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID);
288         } catch (RemoteException e) {
289             Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
290             return;
291         } finally {
292             if (needRelease) {
293                 surface.release();
294             }
295         }
296     }
297
298     @SuppressWarnings("unused")
299     @CalledByNative
300     private Surface getViewSurface(int surfaceId) {
301         if (mCallback == null) {
302             Log.e(TAG, "No callback interface has been provided.");
303             return null;
304         }
305
306         try {
307             return mCallback.getViewSurface(surfaceId).getSurface();
308         } catch (RemoteException e) {
309             Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
310             return null;
311         }
312     }
313
314     @SuppressWarnings("unused")
315     @CalledByNative
316     private Surface getSurfaceTextureSurface(int primaryId, int secondaryId) {
317         if (mCallback == null) {
318             Log.e(TAG, "No callback interface has been provided.");
319             return null;
320         }
321
322         try {
323             return mCallback.getSurfaceTextureSurface(primaryId, secondaryId).getSurface();
324         } catch (RemoteException e) {
325             Log.e(TAG, "Unable to call getSurfaceTextureSurface: " + e);
326             return null;
327         }
328     }
329
330     /**
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}
333      *
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
338      * renderer.
339      */
340     private static native void nativeInitChildProcess(Context applicationContext,
341             ChildProcessService service, int[] extraFileIds, int[] extraFileFds,
342             int cpuCount, long cpuFeatures);
343
344     /**
345      * Force the child process to exit.
346      */
347     private static native void nativeExitChildProcess();
348
349     private native void nativeShutdownMainThread();
350 }