Chromium Code Reviews| 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.browser; | 5 package org.chromium.content.browser; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.os.Bundle; | 8 import android.os.Bundle; |
| 9 import android.os.IBinder; | 9 import android.os.IBinder; |
| 10 import android.os.RemoteException; | 10 import android.os.RemoteException; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 129 sBindingManager.releaseAllModerateBindings(); | 129 sBindingManager.releaseAllModerateBindings(); |
| 130 } | 130 } |
| 131 } | 131 } |
| 132 return connection; | 132 return connection; |
| 133 } | 133 } |
| 134 | 134 |
| 135 private static final long FREE_CONNECTION_DELAY_MILLIS = 1; | 135 private static final long FREE_CONNECTION_DELAY_MILLIS = 1; |
| 136 | 136 |
| 137 private static void freeConnection(ChildProcessConnection connection) { | 137 private static void freeConnection(ChildProcessConnection connection) { |
| 138 assert LauncherThread.runningOnLauncherThread(); | 138 assert LauncherThread.runningOnLauncherThread(); |
| 139 synchronized (sSpareConnectionLock) { | 139 if (connection == sSpareSandboxedConnection) clearSpareConnection(); |
| 140 if (connection.equals(sSpareSandboxedConnection)) sSpareSandboxedCon nection = null; | |
| 141 } | |
| 142 | 140 |
| 143 // Freeing a service should be delayed. This is so that we avoid immedia tely reusing the | 141 // Freeing a service should be delayed. This is so that we avoid immedia tely reusing the |
| 144 // freed service (see http://crbug.com/164069): the framework might keep a service process | 142 // freed service (see http://crbug.com/164069): the framework might keep a service process |
| 145 // alive when it's been unbound for a short time. If a new connection to the same service | 143 // alive when it's been unbound for a short time. If a new connection to the same service |
| 146 // is bound at that point, the process is reused and bad things happen ( mostly static | 144 // is bound at that point, the process is reused and bad things happen ( mostly static |
| 147 // variables are set when we don't expect them to). | 145 // variables are set when we don't expect them to). |
| 148 final ChildProcessConnection conn = connection; | 146 final ChildProcessConnection conn = connection; |
| 149 LauncherThread.postDelayed(new Runnable() { | 147 LauncherThread.postDelayed(new Runnable() { |
| 150 @Override | 148 @Override |
| 151 public void run() { | 149 public void run() { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 177 }, FREE_CONNECTION_DELAY_MILLIS); | 175 }, FREE_CONNECTION_DELAY_MILLIS); |
| 178 } | 176 } |
| 179 | 177 |
| 180 // Represents an invalid process handle; same as base/process/process.h kNul lProcessHandle. | 178 // Represents an invalid process handle; same as base/process/process.h kNul lProcessHandle. |
| 181 private static final int NULL_PROCESS_HANDLE = 0; | 179 private static final int NULL_PROCESS_HANDLE = 0; |
| 182 | 180 |
| 183 // Map from pid to ChildService connection. | 181 // Map from pid to ChildService connection. |
| 184 private static Map<Integer, ChildProcessConnection> sServiceMap = | 182 private static Map<Integer, ChildProcessConnection> sServiceMap = |
| 185 new ConcurrentHashMap<Integer, ChildProcessConnection>(); | 183 new ConcurrentHashMap<Integer, ChildProcessConnection>(); |
| 186 | 184 |
| 187 // Lock and monitor for these members {{{ | 185 // These variables are used for the warm up sandboxed connection. |
| 188 private static final Object sSpareConnectionLock = new Object(); | 186 // |sSpareSandboxedConnection| is non-null when there is a pending connectio n. Note it's cleared |
| 189 // A pre-allocated and pre-bound connection ready for connection setup, or n ull. | 187 // to null again after the connection is used for a real child process. |
| 188 // |sSpareConnectionStarting| is true if ChildProcessConnection.StartCallbac k has not fired. | |
| 189 // This is used for a child process allocation to determine if StartCallback should be chained. | |
| 190 // |sSpareConnectionStartCallback| is the chained StartCallback. This is als o used to determine | |
| 191 // if there is already a child process launch that's used this this connecti on. | |
| 190 private static ChildProcessConnection sSpareSandboxedConnection; | 192 private static ChildProcessConnection sSpareSandboxedConnection; |
| 191 // If sSpareSandboxedConnection is not null, this indicates whether the serv ice is | |
| 192 // ready for connection setup. Wait on the monitor lock to be notified when this | |
| 193 // state changes. sSpareSandboxedConnection may be null after waiting, if st arting | |
| 194 // the service failed. | |
| 195 private static boolean sSpareConnectionStarting; | 193 private static boolean sSpareConnectionStarting; |
| 196 // }}} | 194 private static ChildProcessConnection.StartCallback sSpareConnectionStartCal lback; |
| 197 | 195 |
| 198 // Manages oom bindings used to bind chind services. | 196 // Manages oom bindings used to bind chind services. |
| 199 private static BindingManager sBindingManager = BindingManagerImpl.createBin dingManager(); | 197 private static BindingManager sBindingManager = BindingManagerImpl.createBin dingManager(); |
| 200 | 198 |
| 201 // Whether the main application is currently brought to the foreground. | 199 // Whether the main application is currently brought to the foreground. |
| 202 private static boolean sApplicationInForeground = true; | 200 private static boolean sApplicationInForeground = true; |
| 203 | 201 |
| 204 // TODO(boliu): This should be internal to content. | 202 // TODO(boliu): This should be internal to content. |
| 205 public static BindingManager getBindingManager() { | 203 public static BindingManager getBindingManager() { |
| 206 return sBindingManager; | 204 return sBindingManager; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 261 /** | 259 /** |
| 262 * Should be called early in startup so the work needed to spawn the child p rocess can be done | 260 * Should be called early in startup so the work needed to spawn the child p rocess can be done |
| 263 * in parallel to other startup work. Spare connection is created in sandbox ed child process. | 261 * in parallel to other startup work. Spare connection is created in sandbox ed child process. |
| 264 * @param context the application context used for the connection. | 262 * @param context the application context used for the connection. |
| 265 */ | 263 */ |
| 266 public static void warmUp(final Context context) { | 264 public static void warmUp(final Context context) { |
| 267 assert ThreadUtils.runningOnUiThread(); | 265 assert ThreadUtils.runningOnUiThread(); |
| 268 LauncherThread.post(new Runnable() { | 266 LauncherThread.post(new Runnable() { |
| 269 @Override | 267 @Override |
| 270 public void run() { | 268 public void run() { |
| 271 synchronized (sSpareConnectionLock) { | 269 if (sSpareSandboxedConnection != null) return; |
| 272 if (sSpareSandboxedConnection == null) { | 270 ChildProcessCreationParams params = ChildProcessCreationParams.g etDefault(); |
| 273 ChildProcessCreationParams params = ChildProcessCreation Params.getDefault(); | |
| 274 sSpareConnectionStarting = true; | |
| 275 | 271 |
| 276 ChildProcessConnection.StartCallback startCallback = | 272 ChildProcessConnection.StartCallback startCallback = |
| 277 new ChildProcessConnection.StartCallback() { | 273 new ChildProcessConnection.StartCallback() { |
| 278 @Override | 274 @Override |
| 279 public void onChildStarted() { | 275 public void onChildStarted() { |
| 280 synchronized (sSpareConnectionLock) { | 276 assert LauncherThread.runningOnLauncherThread(); |
| 281 sSpareConnectionStarting = false; | 277 sSpareConnectionStarting = false; |
| 282 sSpareConnectionLock.notify(); | 278 if (sSpareConnectionStartCallback != null) { |
| 283 } | 279 sSpareConnectionStartCallback.onChildStarted (); |
| 284 } | 280 clearSpareConnection(); |
|
Robert Sesek
2017/04/17 18:29:16
Why is this only done in the condition?
boliu
2017/04/17 18:34:18
If there is no chained callback, then it means onC
Robert Sesek
2017/04/17 18:37:04
Acknowledged. Might be worth leaving a note since
boliu
2017/04/17 18:57:13
Added a comment.
| |
| 281 } | |
| 282 } | |
| 285 | 283 |
| 286 @Override | 284 @Override |
| 287 public void onChildStartFailed() { | 285 public void onChildStartFailed() { |
| 288 Log.e(TAG, "Failed to warm up the spare sandbox service"); | 286 assert LauncherThread.runningOnLauncherThread(); |
| 289 synchronized (sSpareConnectionLock) { | 287 Log.e(TAG, "Failed to warm up the spare sandbox service"); |
| 290 sSpareSandboxedConnection = null; | 288 if (sSpareConnectionStartCallback != null) { |
| 291 sSpareConnectionStarting = false; | 289 sSpareConnectionStartCallback.onChildStartFa iled(); |
| 292 sSpareConnectionLock.notify(); | 290 } |
| 293 } | 291 clearSpareConnection(); |
| 294 } | 292 } |
| 295 }; | 293 }; |
| 296 ChildSpawnData spawnData = new ChildSpawnData(context, | 294 ChildSpawnData spawnData = new ChildSpawnData(context, null /* c ommandLine */, |
| 297 null /* commandLine */, -1 /* child process id * /, | 295 -1 /* child process id */, null /* filesToBeMapped */, |
| 298 null /* filesToBeMapped */, null /* launchCallba ck */, | 296 null /* launchCallback */, null /* child process callbac k */, |
| 299 null /* child process callback */, true /* inSan dbox */, | 297 true /* inSandbox */, SPARE_CONNECTION_ALWAYS_IN_FOREGRO UND, params); |
| 300 SPARE_CONNECTION_ALWAYS_IN_FOREGROUND, params); | 298 sSpareSandboxedConnection = |
| 301 sSpareSandboxedConnection = allocateBoundConnection( | 299 allocateBoundConnection(spawnData, startCallback, true / * forWarmUp */); |
| 302 spawnData, startCallback, true /* forWarmUp */); | 300 sSpareConnectionStarting = sSpareSandboxedConnection != null; |
| 303 } | |
| 304 } | |
| 305 } | 301 } |
| 306 }); | 302 }); |
| 307 } | 303 } |
| 308 | 304 |
| 305 private static void clearSpareConnection() { | |
| 306 assert LauncherThread.runningOnLauncherThread(); | |
| 307 sSpareSandboxedConnection = null; | |
| 308 sSpareConnectionStarting = false; | |
| 309 sSpareConnectionStartCallback = null; | |
| 310 } | |
| 311 | |
| 309 /** | 312 /** |
| 310 * Spawns and connects to a child process. May be called on any thread. It w ill not block, but | 313 * Spawns and connects to a child process. May be called on any thread. It w ill not block, but |
| 311 * will instead callback to {@link #nativeOnChildProcessStarted} when the co nnection is | 314 * will instead callback to {@link #nativeOnChildProcessStarted} when the co nnection is |
| 312 * established. Note this callback will not necessarily be from the same thr ead (currently it | 315 * established. Note this callback will not necessarily be from the same thr ead (currently it |
| 313 * always comes from the main thread). | 316 * always comes from the main thread). |
| 314 * | 317 * |
| 315 * @param context Context used to obtain the application context. | 318 * @param context Context used to obtain the application context. |
| 316 * @param paramId Key used to retrieve ChildProcessCreationParams. | 319 * @param paramId Key used to retrieve ChildProcessCreationParams. |
| 317 * @param commandLine The child process command line argv. | 320 * @param commandLine The child process command line argv. |
| 318 * @param filesToBeMapped File IDs, FDs, offsets, and lengths to pass throug h. | 321 * @param filesToBeMapped File IDs, FDs, offsets, and lengths to pass throug h. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 365 final FileDescriptorInfo[] filesToBeMapped, final LaunchCallback lau nchCallback, | 368 final FileDescriptorInfo[] filesToBeMapped, final LaunchCallback lau nchCallback, |
| 366 final IBinder childProcessCallback, final boolean inSandbox, | 369 final IBinder childProcessCallback, final boolean inSandbox, |
| 367 final boolean alwaysInForeground, final ChildProcessCreationParams c reationParams) { | 370 final boolean alwaysInForeground, final ChildProcessCreationParams c reationParams) { |
| 368 assert LauncherThread.runningOnLauncherThread(); | 371 assert LauncherThread.runningOnLauncherThread(); |
| 369 try { | 372 try { |
| 370 TraceEvent.begin("ChildProcessLauncher.startInternal"); | 373 TraceEvent.begin("ChildProcessLauncher.startInternal"); |
| 371 | 374 |
| 372 ChildProcessConnection allocatedConnection = null; | 375 ChildProcessConnection allocatedConnection = null; |
| 373 String packageName = creationParams != null ? creationParams.getPack ageName() | 376 String packageName = creationParams != null ? creationParams.getPack ageName() |
| 374 : context.getPackageName(); | 377 : context.getPackageName(); |
| 375 synchronized (sSpareConnectionLock) { | 378 ChildProcessConnection.StartCallback startCallback = |
| 376 if (inSandbox && sSpareSandboxedConnection != null | 379 new ChildProcessConnection.StartCallback() { |
| 377 && SPARE_CONNECTION_ALWAYS_IN_FOREGROUND == alwaysInFore ground | 380 @Override |
| 378 && sSpareSandboxedConnection.getPackageName().equals(pac kageName) | 381 public void onChildStarted() {} |
| 379 // Object identity check for getDefault should be enough . The default is | 382 |
| 380 // not supposed to change once set. | 383 @Override |
| 381 && creationParams == ChildProcessCreationParams.getDefau lt()) { | 384 public void onChildStartFailed() { |
| 382 while (sSpareConnectionStarting) { | 385 assert LauncherThread.runningOnLauncherThread(); |
| 383 try { | 386 Log.e(TAG, "ChildProcessConnection.start failed, try ing again"); |
| 384 sSpareConnectionLock.wait(); | 387 LauncherThread.post(new Runnable() { |
| 385 } catch (InterruptedException ex) { | 388 @Override |
| 389 public void run() { | |
| 390 // The child process may already be bound to another client | |
| 391 // (this can happen if multi-process WebView is used in more | |
| 392 // than one process), so try starting the pr ocess again. | |
| 393 // This connection that failed to start has not been freed, | |
| 394 // so a new bound connection will be allocat ed. | |
| 395 startInternal(context, commandLine, childPro cessId, | |
| 396 filesToBeMapped, launchCallback, chi ldProcessCallback, | |
| 397 inSandbox, alwaysInForeground, creat ionParams); | |
| 398 } | |
| 399 }); | |
| 386 } | 400 } |
| 387 } | 401 }; |
| 388 allocatedConnection = sSpareSandboxedConnection; | 402 |
| 389 sSpareSandboxedConnection = null; | 403 if (inSandbox && sSpareSandboxedConnection != null |
| 404 && sSpareConnectionStartCallback == null | |
| 405 && SPARE_CONNECTION_ALWAYS_IN_FOREGROUND == alwaysInForegrou nd | |
| 406 && sSpareSandboxedConnection.getPackageName().equals(package Name) | |
| 407 // Object identity check for getDefault should be enough. Th e default is | |
| 408 // not supposed to change once set. | |
| 409 && creationParams == ChildProcessCreationParams.getDefault() ) { | |
| 410 allocatedConnection = sSpareSandboxedConnection; | |
| 411 if (sSpareConnectionStarting) { | |
| 412 sSpareConnectionStartCallback = startCallback; | |
| 413 } else { | |
| 414 clearSpareConnection(); | |
| 390 } | 415 } |
| 391 } | 416 } |
| 392 if (allocatedConnection == null) { | 417 if (allocatedConnection == null) { |
| 393 ChildProcessConnection.StartCallback startCallback = | |
| 394 new ChildProcessConnection.StartCallback() { | |
| 395 @Override | |
| 396 public void onChildStarted() {} | |
| 397 | |
| 398 @Override | |
| 399 public void onChildStartFailed() { | |
| 400 Log.e(TAG, "ChildProcessConnection.start failed, trying again"); | |
| 401 LauncherThread.post(new Runnable() { | |
| 402 @Override | |
| 403 public void run() { | |
| 404 // The child process may already be boun d to another client | |
| 405 // (this can happen if multi-process Web View is used in more | |
| 406 // than one process), so try starting th e process again. | |
| 407 // This connection that failed to start has not been freed, | |
| 408 // so a new bound connection will be all ocated. | |
| 409 startInternal(context, commandLine, chil dProcessId, | |
| 410 filesToBeMapped, launchCallback, | |
| 411 childProcessCallback, inSandbox, alwaysInForeground, | |
| 412 creationParams); | |
| 413 } | |
| 414 }); | |
| 415 } | |
| 416 }; | |
| 417 | 418 |
| 418 ChildSpawnData spawnData = new ChildSpawnData(context, commandLi ne, childProcessId, | 419 ChildSpawnData spawnData = new ChildSpawnData(context, commandLi ne, childProcessId, |
| 419 filesToBeMapped, launchCallback, childProcessCallback, i nSandbox, | 420 filesToBeMapped, launchCallback, childProcessCallback, i nSandbox, |
| 420 alwaysInForeground, creationParams); | 421 alwaysInForeground, creationParams); |
| 421 allocatedConnection = | 422 allocatedConnection = |
| 422 allocateBoundConnection(spawnData, startCallback, false /* forWarmUp */); | 423 allocateBoundConnection(spawnData, startCallback, false /* forWarmUp */); |
| 423 if (allocatedConnection == null) { | 424 if (allocatedConnection == null) { |
| 424 return null; | 425 return null; |
| 425 } | 426 } |
| 426 } | 427 } |
| 427 | 428 |
| 428 Log.d(TAG, "Setting up connection to process: slot=%d", | |
| 429 allocatedConnection.getServiceNumber()); | |
| 430 triggerConnectionSetup(allocatedConnection, commandLine, childProces sId, | 429 triggerConnectionSetup(allocatedConnection, commandLine, childProces sId, |
| 431 filesToBeMapped, childProcessCallback, launchCallback); | 430 filesToBeMapped, childProcessCallback, launchCallback); |
| 432 return allocatedConnection; | 431 return allocatedConnection; |
| 433 } finally { | 432 } finally { |
| 434 TraceEvent.end("ChildProcessLauncher.startInternal"); | 433 TraceEvent.end("ChildProcessLauncher.startInternal"); |
| 435 } | 434 } |
| 436 } | 435 } |
| 437 | 436 |
| 438 /** | 437 /** |
| 439 * Create the common bundle to be passed to child processes. | 438 * Create the common bundle to be passed to child processes. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 450 bundle.putLong(ChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.get Mask()); | 449 bundle.putLong(ChildProcessConstants.EXTRA_CPU_FEATURES, CpuFeatures.get Mask()); |
| 451 bundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS, Linker.getInstance() .getSharedRelros()); | 450 bundle.putBundle(Linker.EXTRA_LINKER_SHARED_RELROS, Linker.getInstance() .getSharedRelros()); |
| 452 return bundle; | 451 return bundle; |
| 453 } | 452 } |
| 454 | 453 |
| 455 @VisibleForTesting | 454 @VisibleForTesting |
| 456 static void triggerConnectionSetup(final ChildProcessConnection connection, | 455 static void triggerConnectionSetup(final ChildProcessConnection connection, |
| 457 String[] commandLine, int childProcessId, FileDescriptorInfo[] files ToBeMapped, | 456 String[] commandLine, int childProcessId, FileDescriptorInfo[] files ToBeMapped, |
| 458 final IBinder childProcessCallback, final LaunchCallback launchCallb ack) { | 457 final IBinder childProcessCallback, final LaunchCallback launchCallb ack) { |
| 459 assert LauncherThread.runningOnLauncherThread(); | 458 assert LauncherThread.runningOnLauncherThread(); |
| 459 Log.d(TAG, "Setting up connection to process: slot=%d", connection.getSe rviceNumber()); | |
| 460 ChildProcessConnection.ConnectionCallback connectionCallback = | 460 ChildProcessConnection.ConnectionCallback connectionCallback = |
| 461 new ChildProcessConnection.ConnectionCallback() { | 461 new ChildProcessConnection.ConnectionCallback() { |
| 462 @Override | 462 @Override |
| 463 public void onConnected(int pid) { | 463 public void onConnected(int pid) { |
| 464 Log.d(TAG, "on connect callback, pid=%d", pid); | 464 Log.d(TAG, "on connect callback, pid=%d", pid); |
| 465 if (pid != NULL_PROCESS_HANDLE) { | 465 if (pid != NULL_PROCESS_HANDLE) { |
| 466 sBindingManager.addNewConnection(pid, connection); | 466 sBindingManager.addNewConnection(pid, connection); |
| 467 sServiceMap.put(pid, connection); | 467 sServiceMap.put(pid, connection); |
| 468 } | 468 } |
| 469 // If the connection fails and pid == 0, the Java-side c leanup was already | 469 // If the connection fails and pid == 0, the Java-side c leanup was already |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 513 | 513 |
| 514 try { | 514 try { |
| 515 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); | 515 ((ChildProcessConnectionImpl) sServiceMap.get(pid)).crashServiceForT esting(); |
| 516 } catch (RemoteException ex) { | 516 } catch (RemoteException ex) { |
| 517 return false; | 517 return false; |
| 518 } | 518 } |
| 519 | 519 |
| 520 return true; | 520 return true; |
| 521 } | 521 } |
| 522 } | 522 } |
| OLD | NEW |