Update To 11.40.268.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.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
116     /* package */ static Context getContext() {
117         return sContext.get();
118     }
119
120     @Override
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                     boolean loadAtFixedAddressFailed = false;
165                     try {
166                         LibraryLoader.loadNow(getApplicationContext(), false);
167                         isLoaded = true;
168                     } catch (ProcessInitException e) {
169                         if (requestedSharedRelro) {
170                             Log.w(TAG, "Failed to load native library with shared RELRO, " +
171                                     "retrying without");
172                             loadAtFixedAddressFailed = true;
173                         } else {
174                             Log.e(TAG, "Failed to load native library", e);
175                         }
176                     }
177                     if (!isLoaded && requestedSharedRelro) {
178                         Linker.disableSharedRelros();
179                         try {
180                             LibraryLoader.loadNow(getApplicationContext(), false);
181                             isLoaded = true;
182                         } catch (ProcessInitException e) {
183                             Log.e(TAG, "Failed to load native library on retry", e);
184                         }
185                     }
186                     if (!isLoaded) {
187                         System.exit(-1);
188                     }
189                     LibraryLoader.registerRendererProcessHistogram(
190                             requestedSharedRelro,
191                             loadAtFixedAddressFailed);
192                     LibraryLoader.initialize();
193                     synchronized (mMainThread) {
194                         mLibraryInitialized = true;
195                         mMainThread.notifyAll();
196                         while (mFileIds == null) {
197                             mMainThread.wait();
198                         }
199                     }
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();
206                     }
207                     ContentMain.initApplicationContext(sContext.get().getApplicationContext());
208                     nativeInitChildProcess(sContext.get().getApplicationContext(),
209                             ChildProcessService.this, fileIds, fileFds,
210                             mCpuCount, mCpuFeatures);
211                     ContentMain.start();
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);
217                 }
218             }
219         }, MAIN_THREAD_NAME);
220         mMainThread.start();
221     }
222
223     @Override
224     public void onDestroy() {
225         Log.i(TAG, "Destroying ChildProcessService pid=" + Process.myPid());
226         super.onDestroy();
227         if (mCommandLineParams == null) {
228             // This process was destroyed before it even started. Nothing more to do.
229             return;
230         }
231         synchronized (mMainThread) {
232             try {
233                 while (!mLibraryInitialized) {
234                     // Avoid a potential race in calling through to native code before the library
235                     // has loaded.
236                     mMainThread.wait();
237                 }
238             } catch (InterruptedException e) {
239                 // Ignore
240             }
241         }
242         // Try to shutdown the MainThread gracefully, but it might not
243         // have chance to exit normally.
244         nativeShutdownMainThread();
245     }
246
247     @Override
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.
253         stopSelf();
254
255         synchronized (mMainThread) {
256             mCommandLineParams = intent.getStringArrayExtra(
257                     ChildProcessConnection.EXTRA_COMMAND_LINE);
258             // mLinkerParams is never used if Linker.isUsed() returns false.
259             // See onCreate().
260             mLinkerParams = new ChromiumLinkerParams(intent);
261             mIsBound = true;
262             mMainThread.notifyAll();
263         }
264
265         return mBinder;
266     }
267
268     /**
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.
272      *
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.
277      */
278     @SuppressWarnings("unused")
279     @CalledByNative
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.");
284             return;
285         }
286
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);
293             needRelease = true;
294         } else {
295             Log.e(TAG, "Not a valid surfaceObject: " + surfaceObject);
296             return;
297         }
298         try {
299             mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID);
300         } catch (RemoteException e) {
301             Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
302             return;
303         } finally {
304             if (needRelease) {
305                 surface.release();
306             }
307         }
308     }
309
310     @SuppressWarnings("unused")
311     @CalledByNative
312     private Surface getViewSurface(int surfaceId) {
313         if (mCallback == null) {
314             Log.e(TAG, "No callback interface has been provided.");
315             return null;
316         }
317
318         try {
319             return mCallback.getViewSurface(surfaceId).getSurface();
320         } catch (RemoteException e) {
321             Log.e(TAG, "Unable to call establishSurfaceTexturePeer: " + e);
322             return null;
323         }
324     }
325
326     @SuppressWarnings("unused")
327     @CalledByNative
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.");
332             return;
333         }
334
335         Surface surface = new Surface(surfaceTexture);
336         try {
337             mCallback.registerSurfaceTextureSurface(surfaceTextureId, clientId, surface);
338         } catch (RemoteException e) {
339             Log.e(TAG, "Unable to call registerSurfaceTextureSurface: " + e);
340         }
341         surface.release();
342     }
343
344     @SuppressWarnings("unused")
345     @CalledByNative
346     private void destroySurfaceTextureSurface(int surfaceTextureId, int clientId) {
347         if (mCallback == null) {
348             Log.e(TAG, "No callback interface has been provided.");
349             return;
350         }
351
352         try {
353             mCallback.unregisterSurfaceTextureSurface(surfaceTextureId, clientId);
354         } catch (RemoteException e) {
355             Log.e(TAG, "Unable to call unregisterSurfaceTextureSurface: " + e);
356         }
357     }
358
359     @SuppressWarnings("unused")
360     @CalledByNative
361     private Surface getSurfaceTextureSurface(int surfaceTextureId) {
362         if (mCallback == null) {
363             Log.e(TAG, "No callback interface has been provided.");
364             return null;
365         }
366
367         try {
368             return mCallback.getSurfaceTextureSurface(surfaceTextureId).getSurface();
369         } catch (RemoteException e) {
370             Log.e(TAG, "Unable to call getSurfaceTextureSurface: " + e);
371             return null;
372         }
373     }
374
375     /**
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}
378      *
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
383      * renderer.
384      */
385     private static native void nativeInitChildProcess(Context applicationContext,
386             ChildProcessService service, int[] extraFileIds, int[] extraFileFds,
387             int cpuCount, long cpuFeatures);
388
389     /**
390      * Force the child process to exit.
391      */
392     private static native void nativeExitChildProcess();
393
394     private native void nativeShutdownMainThread();
395 }