Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/SandboxedProcessConnection.java

Issue 12321131: Renamed Sandboxed process to Child process (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebaesed and removed stub SandboxedProcessService Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 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.browser;
6
7 import android.content.ComponentName;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.ServiceConnection;
11 import android.os.AsyncTask;
12 import android.os.Bundle;
13 import android.os.Handler;
14 import android.os.IBinder;
15 import android.os.Looper;
16 import android.os.ParcelFileDescriptor;
17 import android.util.Log;
18
19 import java.io.IOException;
20 import java.util.concurrent.atomic.AtomicBoolean;
21
22 import org.chromium.base.CalledByNative;
23 import org.chromium.base.CpuFeatures;
24 import org.chromium.base.ThreadUtils;
25 import org.chromium.content.common.CommandLine;
26 import org.chromium.content.common.ISandboxedProcessCallback;
27 import org.chromium.content.common.ISandboxedProcessService;
28 import org.chromium.content.common.TraceEvent;
29
30 public class SandboxedProcessConnection implements ServiceConnection {
31 interface DeathCallback {
32 void onSandboxedProcessDied(int pid);
33 }
34
35 // Names of items placed in the bind intent or connection bundle.
36 public static final String EXTRA_COMMAND_LINE =
37 "com.google.android.apps.chrome.extra.sandbox_command_line";
38 public static final String EXTRA_NATIVE_LIBRARY_NAME =
39 "com.google.android.apps.chrome.extra.sandbox_native_library_name";
40 // Note the FDs may only be passed in the connection bundle.
41 public static final String EXTRA_FILES_PREFIX =
42 "com.google.android.apps.chrome.extra.sandbox_extraFile_";
43 public static final String EXTRA_FILES_ID_SUFFIX = "_id";
44 public static final String EXTRA_FILES_FD_SUFFIX = "_fd";
45
46 // Used to pass the CPU core count to sandboxed processes.
47 public static final String EXTRA_CPU_COUNT =
48 "com.google.android.apps.chrome.extra.cpu_count";
49 // Used to pass the CPU features mask to sandboxed processes.
50 public static final String EXTRA_CPU_FEATURES =
51 "com.google.android.apps.chrome.extra.cpu_features";
52
53 private final Context mContext;
54 private final int mServiceNumber;
55 private final SandboxedProcessConnection.DeathCallback mDeathCallback;
56
57 // Synchronization: While most internal flow occurs on the UI thread, the pu blic API
58 // (specifically bind and unbind) may be called from any thread, hence all e ntry point methods
59 // into the class are synchronized on the SandboxedProcessConnection instanc e to protect access
60 // to these members. But see also the TODO where AsyncBoundServiceConnection is created.
61 private ISandboxedProcessService mService = null;
62 private boolean mServiceConnectComplete = false;
63 private int mPID = 0; // Process ID of the corresponding sandboxed process.
64 private HighPriorityConnection mHighPriorityConnection = null;
65 private int mHighPriorityConnectionCount = 0;
66
67 private static final String TAG = "SandboxedProcessConnection";
68
69 private static class ConnectionParams {
70 final String[] mCommandLine;
71 final FileDescriptorInfo[] mFilesToBeMapped;
72 final ISandboxedProcessCallback mCallback;
73 final Runnable mOnConnectionCallback;
74
75 ConnectionParams(
76 String[] commandLine,
77 FileDescriptorInfo[] filesToBeMapped,
78 ISandboxedProcessCallback callback,
79 Runnable onConnectionCallback) {
80 mCommandLine = commandLine;
81 mFilesToBeMapped = filesToBeMapped;
82 mCallback = callback;
83 mOnConnectionCallback = onConnectionCallback;
84 }
85 }
86
87 // This is only valid while the connection is being established.
88 private ConnectionParams mConnectionParams;
89 private boolean mIsBound;
90
91 SandboxedProcessConnection(Context context, int number,
92 SandboxedProcessConnection.DeathCallback deathCallback) {
93 mContext = context;
94 mServiceNumber = number;
95 mDeathCallback = deathCallback;
96 }
97
98 int getServiceNumber() {
99 return mServiceNumber;
100 }
101
102 synchronized ISandboxedProcessService getService() {
103 return mService;
104 }
105
106 private Intent createServiceBindIntent() {
107 Intent intent = new Intent();
108 String n = org.chromium.content.app.SandboxedProcessService.class.getNam e();
109 intent.setClassName(mContext, n + mServiceNumber);
110 intent.setPackage(mContext.getPackageName());
111 return intent;
112 }
113
114 /**
115 * Bind to an ISandboxedProcessService. This must be followed by a call to s etupConnection()
116 * to setup the connection parameters. (These methods are separated to allow the client
117 * to pass whatever parameters they have available here, and complete the re mainder
118 * later while reducing the connection setup latency).
119 * @param nativeLibraryName The name of the shared native library to be load ed for the
120 * sandboxed process.
121 * @param commandLine (Optional) Command line for the sandboxed process. If omitted, then
122 * the command line parameters must instead be passed to setupConnection().
123 */
124 synchronized void bind(String nativeLibraryName, String[] commandLine) {
125 TraceEvent.begin();
126 assert !ThreadUtils.runningOnUiThread();
127
128 final Intent intent = createServiceBindIntent();
129
130 intent.putExtra(EXTRA_NATIVE_LIBRARY_NAME, nativeLibraryName);
131 if (commandLine != null) {
132 intent.putExtra(EXTRA_COMMAND_LINE, commandLine);
133 }
134
135 mIsBound = mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
136 if (!mIsBound) {
137 onBindFailed();
138 }
139 TraceEvent.end();
140 }
141
142 /** Setup a connection previous bound via a call to bind().
143 *
144 * This establishes the parameters that were not already supplied in bind.
145 * @param commandLine (Optional) will be ignored if the command line was alr eady sent in bind()
146 * @param fileToBeMapped a list of file descriptors that should be registere d
147 * @param callback Used for status updates regarding this process connection .
148 * @param onConnectionCallback will be run when the connection is setup and ready to use.
149 */
150 synchronized void setupConnection(
151 String[] commandLine,
152 FileDescriptorInfo[] filesToBeMapped,
153 ISandboxedProcessCallback callback,
154 Runnable onConnectionCallback) {
155 TraceEvent.begin();
156 assert mConnectionParams == null;
157 mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, c allback,
158 onConnectionCallback);
159 if (mServiceConnectComplete) {
160 doConnectionSetup();
161 }
162 TraceEvent.end();
163 }
164
165 /**
166 * Unbind the ISandboxedProcessService. It is safe to call this multiple tim es.
167 */
168 synchronized void unbind() {
169 if (mIsBound) {
170 mContext.unbindService(this);
171 mIsBound = false;
172 }
173 if (mService != null) {
174 if (mHighPriorityConnection != null) {
175 unbindHighPriority(true);
176 }
177 mService = null;
178 mPID = 0;
179 }
180 mConnectionParams = null;
181 mServiceConnectComplete = false;
182 }
183
184 // Called on the main thread to notify that the service is connected.
185 @Override
186 public void onServiceConnected(ComponentName className, IBinder service) {
187 TraceEvent.begin();
188 mServiceConnectComplete = true;
189 mService = ISandboxedProcessService.Stub.asInterface(service);
190 if (mConnectionParams != null) {
191 doConnectionSetup();
192 }
193 TraceEvent.end();
194 }
195
196 // Called on the main thread to notify that the bindService() call failed (r eturned false).
197 private void onBindFailed() {
198 mServiceConnectComplete = true;
199 if (mConnectionParams != null) {
200 doConnectionSetup();
201 }
202 }
203
204 /**
205 * Called when the connection parameters have been set, and a connection has been established
206 * (as signaled by onServiceConnected), or if the connection failed (mServic e will be false).
207 */
208 private void doConnectionSetup() {
209 TraceEvent.begin();
210 assert mServiceConnectComplete && mConnectionParams != null;
211 // Capture the callback before it is potentially nulled in unbind().
212 Runnable onConnectionCallback =
213 mConnectionParams != null ? mConnectionParams.mOnConnectionCallback : null;
214 if (onConnectionCallback == null) {
215 unbind();
216 } else if (mService != null) {
217 Bundle bundle = new Bundle();
218 bundle.putStringArray(EXTRA_COMMAND_LINE, mConnectionParams.mCommand Line);
219
220 FileDescriptorInfo[] fileInfos = mConnectionParams.mFilesToBeMapped;
221 ParcelFileDescriptor[] parcelFiles = new ParcelFileDescriptor[fileIn fos.length];
222 for (int i = 0; i < fileInfos.length; i++) {
223 if (fileInfos[i].mFd == -1) {
224 // If someone provided an invalid FD, they are doing somethi ng wrong.
225 Log.e(TAG, "Invalid FD (id=" + fileInfos[i].mId + ") for pro cess connection, "
226 + "aborting connection.");
227 return;
228 }
229 String idName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_ID_SUFFIX;
230 String fdName = EXTRA_FILES_PREFIX + i + EXTRA_FILES_FD_SUFFIX;
231 if (fileInfos[i].mAutoClose) {
232 // Adopt the FD, it will be closed when we close the ParcelF ileDescriptor.
233 parcelFiles[i] = ParcelFileDescriptor.adoptFd(fileInfos[i].m Fd);
234 } else {
235 try {
236 parcelFiles[i] = ParcelFileDescriptor.fromFd(fileInfos[i ].mFd);
237 } catch(IOException e) {
238 Log.e(TAG,
239 "Invalid FD provided for process connection, abort ing connection.",
240 e);
241 return;
242 }
243
244 }
245 bundle.putParcelable(fdName, parcelFiles[i]);
246 bundle.putInt(idName, fileInfos[i].mId);
247 }
248 // Add the CPU properties now.
249 bundle.putInt(EXTRA_CPU_COUNT, CpuFeatures.getCount());
250 bundle.putLong(EXTRA_CPU_FEATURES, CpuFeatures.getMask());
251
252 try {
253 mPID = mService.setupConnection(bundle, mConnectionParams.mCallb ack);
254 } catch (android.os.RemoteException re) {
255 Log.e(TAG, "Failed to setup connection.", re);
256 }
257 // We proactivley close the FDs rather than wait for GC & finalizer.
258 try {
259 for (ParcelFileDescriptor parcelFile : parcelFiles) {
260 if (parcelFile != null) parcelFile.close();
261 }
262 } catch (IOException ioe) {
263 Log.w(TAG, "Failed to close FD.", ioe);
264 }
265 }
266 mConnectionParams = null;
267 if (onConnectionCallback != null) {
268 onConnectionCallback.run();
269 }
270 TraceEvent.end();
271 }
272
273 // Called on the main thread to notify that the sandboxed service did not di sconnect gracefully.
274 @Override
275 public void onServiceDisconnected(ComponentName className) {
276 int pid = mPID; // Stash pid & connection callback since unbind() will clear them.
277 Runnable onConnectionCallback =
278 mConnectionParams != null ? mConnectionParams.mOnConnectionCallback : null;
279 Log.w(TAG, "onServiceDisconnected (crash?): pid=" + pid);
280 unbind(); // We don't want to auto-restart on crash. Let the browser do that.
281 if (pid != 0) {
282 mDeathCallback.onSandboxedProcessDied(pid);
283 }
284 if (onConnectionCallback != null) {
285 onConnectionCallback.run();
286 }
287 }
288
289 /**
290 * Bind the service with a new high priority connection. This will make the service
291 * as important as the main process.
292 */
293 synchronized void bindHighPriority() {
294 if (mService == null) {
295 Log.w(TAG, "The connection is not bound for " + mPID);
296 return;
297 }
298 if (mHighPriorityConnection == null) {
299 mHighPriorityConnection = new HighPriorityConnection();
300 mHighPriorityConnection.bind();
301 }
302 mHighPriorityConnectionCount++;
303 }
304
305 /**
306 * Unbind the service as the high priority connection.
307 */
308 synchronized void unbindHighPriority(boolean force) {
309 if (mService == null) {
310 Log.w(TAG, "The connection is not bound for " + mPID);
311 return;
312 }
313 mHighPriorityConnectionCount--;
314 if (force || (mHighPriorityConnectionCount == 0 && mHighPriorityConnecti on != null)) {
315 mHighPriorityConnection.unbind();
316 mHighPriorityConnection = null;
317 }
318 }
319
320 private class HighPriorityConnection implements ServiceConnection {
321
322 private boolean mHBound = false;
323
324 void bind() {
325 final Intent intent = createServiceBindIntent();
326
327 mHBound = mContext.bindService(intent, this,
328 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
329 }
330
331 void unbind() {
332 if (mHBound) {
333 mContext.unbindService(this);
334 mHBound = false;
335 }
336 }
337
338 @Override
339 public void onServiceConnected(ComponentName className, IBinder service) {
340 }
341
342 @Override
343 public void onServiceDisconnected(ComponentName className) {
344 }
345 }
346
347 /**
348 * @return The connection PID, or 0 if not yet connected.
349 */
350 synchronized public int getPid() {
351 return mPID;
352 }
353 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698