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