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