OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.content.app; | 5 package org.chromium.content.app; |
6 | 6 |
7 import android.app.Service; | 7 import android.app.Service; |
8 import android.content.Context; | |
9 import android.content.Intent; | 8 import android.content.Intent; |
10 import android.graphics.SurfaceTexture; | |
11 import android.os.Bundle; | 9 import android.os.Bundle; |
12 import android.os.IBinder; | 10 import android.os.IBinder; |
13 import android.os.Parcelable; | |
14 import android.os.Process; | |
15 import android.os.RemoteException; | |
16 import android.view.Surface; | |
17 | 11 |
18 import org.chromium.base.BaseSwitches; | |
19 import org.chromium.base.CommandLine; | |
20 import org.chromium.base.ContextUtils; | |
21 import org.chromium.base.Log; | |
22 import org.chromium.base.annotations.CalledByNative; | |
23 import org.chromium.base.annotations.JNINamespace; | 12 import org.chromium.base.annotations.JNINamespace; |
24 import org.chromium.base.annotations.SuppressFBWarnings; | 13 import org.chromium.base.annotations.SuppressFBWarnings; |
25 import org.chromium.base.library_loader.LibraryLoader; | |
26 import org.chromium.base.library_loader.Linker; | |
27 import org.chromium.base.library_loader.ProcessInitException; | |
28 import org.chromium.content.browser.ChildProcessConstants; | |
29 import org.chromium.content.browser.ChildProcessCreationParams; | |
30 import org.chromium.content.browser.ChildProcessLauncher; | 14 import org.chromium.content.browser.ChildProcessLauncher; |
31 import org.chromium.content.browser.FileDescriptorInfo; | |
32 import org.chromium.content.common.ContentSwitches; | |
33 import org.chromium.content.common.IChildProcessCallback; | |
34 import org.chromium.content.common.IChildProcessService; | |
35 import org.chromium.content.common.SurfaceWrapper; | |
36 | |
37 import java.util.concurrent.Semaphore; | |
38 import java.util.concurrent.atomic.AtomicReference; | |
39 | 15 |
40 /** | 16 /** |
41 * This is the base class for child services; the [Non]SandboxedProcessService0, 1.. etc | 17 * This is the base class for child services; the [Non]SandboxedProcessService0, 1.. etc |
42 * subclasses provide the concrete service entry points, to enable the browser t o connect | 18 * subclasses provide the concrete service entry points, to enable the browser t o connect |
43 * to more than one distinct process (i.e. one process per service number, up to limit of N). | 19 * to more than one distinct process (i.e. one process per service number, up to limit of N). |
44 * The embedding application must declare these service instances in the applica tion section | 20 * The embedding application must declare these service instances in the applica tion section |
45 * of its AndroidManifest.xml, for example with N entries of the form:- | 21 * of its AndroidManifest.xml, for example with N entries of the form:- |
46 * <service android:name="org.chromium.content.app.[Non]SandboxedProcessServ iceX" | 22 * <service android:name="org.chromium.content.app.[Non]SandboxedProcessServ iceX" |
47 * android:process=":[non]sandboxed_processX" /> | 23 * android:process=":[non]sandboxed_processX" /> |
48 * for X in 0...N-1 (where N is {@link ChildProcessLauncher#MAX_REGISTERED_SERVI CES}) | 24 * for X in 0...N-1 (where N is {@link ChildProcessLauncher#MAX_REGISTERED_SERVI CES}) |
49 */ | 25 */ |
50 @JNINamespace("content") | 26 @JNINamespace("content") |
51 @SuppressWarnings("SynchronizeOnNonFinalField") | |
52 public class ChildProcessService extends Service { | 27 public class ChildProcessService extends Service { |
53 private static final String MAIN_THREAD_NAME = "ChildProcessMain"; | 28 private final ChildProcessServiceImpl mChildProcessServiceImpl = new ChildPr ocessServiceImpl(); |
54 private static final String TAG = "ChildProcessService"; | |
55 protected static final FileDescriptorInfo[] EMPTY_FILE_DESCRIPTOR_INFO = {}; | |
56 private IChildProcessCallback mCallback; | |
57 | |
58 // This is the native "Main" thread for the renderer / utility process. | |
59 private Thread mMainThread; | |
60 // Parameters received via IPC, only accessed while holding the mMainThread monitor. | |
61 private String[] mCommandLineParams; | |
62 private int mCpuCount; | |
63 private long mCpuFeatures; | |
64 // File descriptors that should be registered natively. | |
65 private FileDescriptorInfo[] mFdInfos; | |
66 // Linker-specific parameters for this child process service. | |
67 private ChromiumLinkerParams mLinkerParams; | |
68 // Child library process type. | |
69 private int mLibraryProcessType; | |
70 | |
71 private static AtomicReference<Context> sContext = new AtomicReference<Conte xt>(null); | |
72 private boolean mLibraryInitialized = false; | |
73 // Becomes true once the service is bound. Access must synchronize around mM ainThread. | |
74 private boolean mIsBound = false; | |
75 | |
76 private final Semaphore mActivitySemaphore = new Semaphore(1); | |
77 | |
78 // Return a Linker instance. If testing, the Linker needs special setup. | |
79 private Linker getLinker() { | |
80 if (Linker.areTestsEnabled()) { | |
81 // For testing, set the Linker implementation and the test runner | |
82 // class name to match those used by the parent. | |
83 assert mLinkerParams != null; | |
84 Linker.setupForTesting( | |
85 mLinkerParams.mLinkerImplementationForTesting, | |
86 mLinkerParams.mTestRunnerClassNameForTesting); | |
87 } | |
88 return Linker.getInstance(); | |
89 } | |
90 | |
91 // Binder object used by clients for this service. | |
92 private final IChildProcessService.Stub mBinder = new IChildProcessService.S tub() { | |
93 // NOTE: Implement any IChildProcessService methods here. | |
94 @Override | |
95 public int setupConnection(Bundle args, IChildProcessCallback callback) { | |
96 mCallback = callback; | |
97 getServiceInfo(args); | |
98 return Process.myPid(); | |
99 } | |
100 | |
101 @Override | |
102 public void crashIntentionallyForTesting() { | |
103 Process.killProcess(Process.myPid()); | |
104 } | |
105 }; | |
106 | 29 |
107 @Override | 30 @Override |
108 public void onCreate() { | 31 public void onCreate() { |
109 Log.i(TAG, "Creating new ChildProcessService pid=%d", Process.myPid()); | |
110 if (sContext.get() != null) { | |
111 throw new RuntimeException("Illegal child process reuse."); | |
112 } | |
113 sContext.set(this); | |
114 super.onCreate(); | 32 super.onCreate(); |
115 | 33 mChildProcessServiceImpl.create(getApplicationContext(), |
116 ContextUtils.initApplicationContext(getApplicationContext()); | 34 getApplicationContext()); |
117 | |
118 mMainThread = new Thread(new Runnable() { | |
119 @Override | |
120 @SuppressFBWarnings("DM_EXIT") | |
121 public void run() { | |
122 try { | |
123 // CommandLine must be initialized before everything else. | |
124 synchronized (mMainThread) { | |
125 while (mCommandLineParams == null) { | |
126 mMainThread.wait(); | |
127 } | |
128 } | |
129 CommandLine.init(mCommandLineParams); | |
130 | |
131 Linker linker = null; | |
132 boolean requestedSharedRelro = false; | |
133 if (Linker.isUsed()) { | |
134 synchronized (mMainThread) { | |
135 while (!mIsBound) { | |
136 mMainThread.wait(); | |
137 } | |
138 } | |
139 linker = getLinker(); | |
140 if (mLinkerParams.mWaitForSharedRelro) { | |
141 requestedSharedRelro = true; | |
142 linker.initServiceProcess(mLinkerParams.mBaseLoadAdd ress); | |
143 } else { | |
144 linker.disableSharedRelros(); | |
145 } | |
146 } | |
147 boolean isLoaded = false; | |
148 if (CommandLine.getInstance().hasSwitch( | |
149 BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER)) { | |
150 android.os.Debug.waitForDebugger(); | |
151 } | |
152 | |
153 boolean loadAtFixedAddressFailed = false; | |
154 try { | |
155 LibraryLoader.get(mLibraryProcessType).loadNow(getApplic ationContext()); | |
156 isLoaded = true; | |
157 } catch (ProcessInitException e) { | |
158 if (requestedSharedRelro) { | |
159 Log.w(TAG, "Failed to load native library with share d RELRO, " | |
160 + "retrying without"); | |
161 loadAtFixedAddressFailed = true; | |
162 } else { | |
163 Log.e(TAG, "Failed to load native library", e); | |
164 } | |
165 } | |
166 if (!isLoaded && requestedSharedRelro) { | |
167 linker.disableSharedRelros(); | |
168 try { | |
169 LibraryLoader.get(mLibraryProcessType).loadNow(getAp plicationContext()); | |
170 isLoaded = true; | |
171 } catch (ProcessInitException e) { | |
172 Log.e(TAG, "Failed to load native library on retry", e); | |
173 } | |
174 } | |
175 if (!isLoaded) { | |
176 System.exit(-1); | |
177 } | |
178 LibraryLoader.get(mLibraryProcessType) | |
179 .registerRendererProcessHistogram(requestedSharedRel ro, | |
180 loadAtFixedAddressFailed); | |
181 LibraryLoader.get(mLibraryProcessType).initialize(); | |
182 synchronized (mMainThread) { | |
183 mLibraryInitialized = true; | |
184 mMainThread.notifyAll(); | |
185 while (mFdInfos == null) { | |
186 mMainThread.wait(); | |
187 } | |
188 } | |
189 for (FileDescriptorInfo fdInfo : mFdInfos) { | |
190 nativeRegisterGlobalFileDescriptor( | |
191 fdInfo.mId, fdInfo.mFd.detachFd(), fdInfo.mOffse t, fdInfo.mSize); | |
192 } | |
193 nativeInitChildProcess(ChildProcessService.this, mCpuCount, mCpuFeatures); | |
194 if (mActivitySemaphore.tryAcquire()) { | |
195 ContentMain.start(); | |
196 nativeExitChildProcess(); | |
197 } | |
198 } catch (InterruptedException e) { | |
199 Log.w(TAG, "%s startup failed: %s", MAIN_THREAD_NAME, e); | |
200 } catch (ProcessInitException e) { | |
201 Log.w(TAG, "%s startup failed: %s", MAIN_THREAD_NAME, e); | |
202 } | |
203 } | |
204 }, MAIN_THREAD_NAME); | |
205 mMainThread.start(); | |
206 } | 35 } |
207 | 36 |
208 @Override | 37 @Override |
209 @SuppressFBWarnings("DM_EXIT") | 38 @SuppressFBWarnings("DM_EXIT") |
Maria
2016/06/20 18:49:22
this warning belongs on the IMPL now
Xi Han
2016/06/20 19:12:34
Removed.
| |
210 public void onDestroy() { | 39 public void onDestroy() { |
211 Log.i(TAG, "Destroying ChildProcessService pid=%d", Process.myPid()); | |
212 super.onDestroy(); | 40 super.onDestroy(); |
213 if (mActivitySemaphore.tryAcquire()) { | 41 mChildProcessServiceImpl.destroy(); |
214 // TODO(crbug.com/457406): This is a bit hacky, but there is no know n better solution | |
215 // as this service will get reused (at least if not sandboxed). | |
216 // In fact, we might really want to always exit() from onDestroy(), not just from | |
217 // the early return here. | |
218 System.exit(0); | |
219 return; | |
220 } | |
221 synchronized (mMainThread) { | |
222 try { | |
223 while (!mLibraryInitialized) { | |
224 // Avoid a potential race in calling through to native code before the library | |
225 // has loaded. | |
226 mMainThread.wait(); | |
227 } | |
228 } catch (InterruptedException e) { | |
229 // Ignore | |
230 } | |
231 } | |
232 // Try to shutdown the MainThread gracefully, but it might not | |
233 // have chance to exit normally. | |
234 nativeShutdownMainThread(); | |
235 } | 42 } |
236 | 43 |
237 @Override | 44 @Override |
238 public IBinder onBind(Intent intent) { | 45 public IBinder onBind(Intent intent) { |
239 // We call stopSelf() to request that this service be stopped as soon as the client | 46 // We call stopSelf() to request that this service be stopped as soon as the client |
240 // unbinds. Otherwise the system may keep it around and available for a reconnect. The | 47 // unbinds. Otherwise the system may keep it around and available for a reconnect. The |
241 // child processes do not currently support reconnect; they must be init ialized from | 48 // child processes do not currently support reconnect; they must be init ialized from |
242 // scratch every time. | 49 // scratch every time. |
243 stopSelf(); | 50 stopSelf(); |
244 initializeParams(intent); | 51 return mChildProcessServiceImpl.bind(intent); |
245 return mBinder; | |
246 } | 52 } |
247 | 53 |
248 /** | 54 /** |
249 * Helper method to initialize the params from intent. | 55 * Helper method to initialize the params from intent. |
250 * @param intent Intent to launch the service. | 56 * @param intent Intent to launch the service. |
251 */ | 57 */ |
252 protected void initializeParams(Intent intent) { | 58 protected void initializeParams(Intent intent) { |
253 synchronized (mMainThread) { | 59 mChildProcessServiceImpl.initializeParams(intent); |
254 mCommandLineParams = | |
255 intent.getStringArrayExtra(ChildProcessConstants.EXTRA_COMMA ND_LINE); | |
256 // mLinkerParams is never used if Linker.isUsed() returns false. | |
257 // See onCreate(). | |
258 mLinkerParams = new ChromiumLinkerParams(intent); | |
259 mLibraryProcessType = ChildProcessCreationParams.getLibraryProcessTy pe(intent); | |
260 mIsBound = true; | |
261 mMainThread.notifyAll(); | |
262 } | |
263 } | 60 } |
264 | 61 |
265 /** | 62 /** |
266 * Helper method to get the information about the service from a given bundl e. | 63 * Helper method to get the information about the service from a given bundl e. |
267 * @param bundle Bundle that contains the information to start the service. | 64 * @param bundle Bundle that contains the information to start the service. |
268 */ | 65 */ |
269 void getServiceInfo(Bundle bundle) { | 66 protected void getServiceInfo(Bundle bundle) { |
270 // Required to unparcel FileDescriptorInfo. | 67 mChildProcessServiceImpl.getServiceInfo(bundle); |
271 bundle.setClassLoader(getClassLoader()); | |
272 synchronized (mMainThread) { | |
273 // Allow the command line to be set via bind() intent or setupConnec tion, but | |
274 // the FD can only be transferred here. | |
275 if (mCommandLineParams == null) { | |
276 mCommandLineParams = | |
277 bundle.getStringArray(ChildProcessConstants.EXTRA_COMMAN D_LINE); | |
278 } | |
279 // We must have received the command line by now | |
280 assert mCommandLineParams != null; | |
281 mCpuCount = bundle.getInt(ChildProcessConstants.EXTRA_CPU_COUNT); | |
282 mCpuFeatures = bundle.getLong(ChildProcessConstants.EXTRA_CPU_FEATUR ES); | |
283 assert mCpuCount > 0; | |
284 Parcelable[] fdInfosAsParcelable = | |
285 bundle.getParcelableArray(ChildProcessConstants.EXTRA_FILES) ; | |
286 if (fdInfosAsParcelable != null) { | |
287 // For why this arraycopy is necessary: | |
288 // http://stackoverflow.com/questions/8745893/i-dont-get-why-thi s-classcastexception-occurs | |
289 mFdInfos = new FileDescriptorInfo[fdInfosAsParcelable.length]; | |
290 System.arraycopy(fdInfosAsParcelable, 0, mFdInfos, 0, fdInfosAsP arcelable.length); | |
291 } else { | |
292 String processType = ContentSwitches.getSwitchValue( | |
293 mCommandLineParams, ContentSwitches.SWITCH_PROCESS_TYPE) ; | |
294 assert ContentSwitches.SWITCH_DOWNLOAD_PROCESS.equals(processTyp e); | |
295 mFdInfos = EMPTY_FILE_DESCRIPTOR_INFO; | |
296 } | |
297 Bundle sharedRelros = bundle.getBundle(Linker.EXTRA_LINKER_SHARED_RE LROS); | |
298 if (sharedRelros != null) { | |
299 getLinker().useSharedRelros(sharedRelros); | |
300 sharedRelros = null; | |
301 } | |
302 mMainThread.notifyAll(); | |
303 } | |
304 } | 68 } |
305 | |
306 /** | |
307 * Called from native code to share a surface texture with another child pro cess. | |
308 * Through using the callback object the browser is used as a proxy to route the | |
309 * call to the correct process. | |
310 * | |
311 * @param pid Process handle of the child process to share the SurfaceTextur e with. | |
312 * @param surfaceObject The Surface or SurfaceTexture to share with the othe r child process. | |
313 * @param primaryID Used to route the call to the correct client instance. | |
314 * @param secondaryID Used to route the call to the correct client instance. | |
315 */ | |
316 @SuppressWarnings("unused") | |
317 @CalledByNative | |
318 private void establishSurfaceTexturePeer( | |
319 int pid, Object surfaceObject, int primaryID, int secondaryID) { | |
320 if (mCallback == null) { | |
321 Log.e(TAG, "No callback interface has been provided."); | |
322 return; | |
323 } | |
324 | |
325 Surface surface = null; | |
326 boolean needRelease = false; | |
327 if (surfaceObject instanceof Surface) { | |
328 surface = (Surface) surfaceObject; | |
329 } else if (surfaceObject instanceof SurfaceTexture) { | |
330 surface = new Surface((SurfaceTexture) surfaceObject); | |
331 needRelease = true; | |
332 } else { | |
333 Log.e(TAG, "Not a valid surfaceObject: %s", surfaceObject); | |
334 return; | |
335 } | |
336 try { | |
337 mCallback.establishSurfacePeer(pid, surface, primaryID, secondaryID) ; | |
338 } catch (RemoteException e) { | |
339 Log.e(TAG, "Unable to call establishSurfaceTexturePeer: %s", e); | |
340 return; | |
341 } finally { | |
342 if (needRelease) { | |
343 surface.release(); | |
344 } | |
345 } | |
346 } | |
347 | |
348 @SuppressWarnings("unused") | |
349 @CalledByNative | |
350 private Surface getViewSurface(int surfaceId) { | |
351 if (mCallback == null) { | |
352 Log.e(TAG, "No callback interface has been provided."); | |
353 return null; | |
354 } | |
355 | |
356 try { | |
357 SurfaceWrapper wrapper = mCallback.getViewSurface(surfaceId); | |
358 return wrapper != null ? wrapper.getSurface() : null; | |
359 } catch (RemoteException e) { | |
360 Log.e(TAG, "Unable to call getViewSurface: %s", e); | |
361 return null; | |
362 } | |
363 } | |
364 | |
365 @SuppressWarnings("unused") | |
366 @CalledByNative | |
367 private void createSurfaceTextureSurface( | |
368 int surfaceTextureId, int clientId, SurfaceTexture surfaceTexture) { | |
369 if (mCallback == null) { | |
370 Log.e(TAG, "No callback interface has been provided."); | |
371 return; | |
372 } | |
373 | |
374 Surface surface = new Surface(surfaceTexture); | |
375 try { | |
376 mCallback.registerSurfaceTextureSurface(surfaceTextureId, clientId, surface); | |
377 } catch (RemoteException e) { | |
378 Log.e(TAG, "Unable to call registerSurfaceTextureSurface: %s", e); | |
379 } | |
380 surface.release(); | |
381 } | |
382 | |
383 @SuppressWarnings("unused") | |
384 @CalledByNative | |
385 private void destroySurfaceTextureSurface(int surfaceTextureId, int clientId ) { | |
386 if (mCallback == null) { | |
387 Log.e(TAG, "No callback interface has been provided."); | |
388 return; | |
389 } | |
390 | |
391 try { | |
392 mCallback.unregisterSurfaceTextureSurface(surfaceTextureId, clientId ); | |
393 } catch (RemoteException e) { | |
394 Log.e(TAG, "Unable to call unregisterSurfaceTextureSurface: %s", e); | |
395 } | |
396 } | |
397 | |
398 @SuppressWarnings("unused") | |
399 @CalledByNative | |
400 private Surface getSurfaceTextureSurface(int surfaceTextureId) { | |
401 if (mCallback == null) { | |
402 Log.e(TAG, "No callback interface has been provided."); | |
403 return null; | |
404 } | |
405 | |
406 try { | |
407 return mCallback.getSurfaceTextureSurface(surfaceTextureId).getSurfa ce(); | |
408 } catch (RemoteException e) { | |
409 Log.e(TAG, "Unable to call getSurfaceTextureSurface: %s", e); | |
410 return null; | |
411 } | |
412 } | |
413 | |
414 /** | |
415 * Helper for registering FileDescriptorInfo objects with GlobalFileDescript ors. | |
416 * This includes the IPC channel, the crash dump signals and resource relate d | |
417 * files. | |
418 */ | |
419 private static native void nativeRegisterGlobalFileDescriptor( | |
420 int id, int fd, long offset, long size); | |
421 | |
422 /** | |
423 * The main entry point for a child process. This should be called from a ne w thread since | |
424 * it will not return until the child process exits. See child_process_servi ce.{h,cc} | |
425 * | |
426 * @param service The current ChildProcessService object. | |
427 * renderer. | |
428 */ | |
429 private static native void nativeInitChildProcess( | |
430 ChildProcessService service, int cpuCount, long cpuFeatures); | |
431 | |
432 /** | |
433 * Force the child process to exit. | |
434 */ | |
435 private static native void nativeExitChildProcess(); | |
436 | |
437 private native void nativeShutdownMainThread(); | |
438 } | 69 } |
OLD | NEW |