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

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java

Issue 2874643002: Merging Managed and Important connection. (Closed)
Patch Set: Fix test + sync Created 3 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
similarity index 68%
rename from content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
rename to content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
index 77796e89e5eeb5f0a57e6fd8a425fa1576029d9a..509663b8df7c97f3ec1a8e88528c702f068b1cc2 100644
--- a/content/public/android/java/src/org/chromium/content/browser/BaseChildProcessConnection.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnection.java
@@ -32,8 +32,8 @@ import javax.annotation.Nullable;
/**
* Manages a connection between the browser activity and a child service.
*/
-public abstract class BaseChildProcessConnection {
- private static final String TAG = "BaseChildProcessConn";
+public class ChildProcessConnection {
+ private static final String TAG = "ChildProcessConn";
/**
* Used to notify the consumer about disconnection of the service. This callback is provided
@@ -42,7 +42,7 @@ public abstract class BaseChildProcessConnection {
*/
interface DeathCallback {
// Called on Launcher thread.
- void onChildProcessDied(BaseChildProcessConnection connection);
+ void onChildProcessDied(ChildProcessConnection connection);
}
/**
@@ -71,17 +71,11 @@ public abstract class BaseChildProcessConnection {
* Called when the connection to the service is established.
* @param connecion the connection object to the child process
*/
- void onConnected(BaseChildProcessConnection connection);
- }
-
- /** Used to create specialization connection instances. */
- interface Factory {
- BaseChildProcessConnection create(Context context, DeathCallback deathCallback,
- String serviceClassName, Bundle childProcessCommonParameters,
- ChildProcessCreationParams creationParams);
+ void onConnected(ChildProcessConnection connection);
}
/** Interface representing a connection to the Android service. Can be mocked in unit-tests. */
+ @VisibleForTesting
protected interface ChildServiceConnection {
boolean bind();
void unbind();
@@ -89,8 +83,7 @@ public abstract class BaseChildProcessConnection {
}
/** Implementation of ChildServiceConnection that does connect to a service. */
- protected class ChildServiceConnectionImpl
- implements ChildServiceConnection, ServiceConnection {
+ private class ChildServiceConnectionImpl implements ChildServiceConnection, ServiceConnection {
private final int mBindFlags;
private boolean mBound;
@@ -111,14 +104,14 @@ public abstract class BaseChildProcessConnection {
public boolean bind() {
if (!mBound) {
try {
- TraceEvent.begin("BaseChildProcessConnection.ChildServiceConnection.bind");
+ TraceEvent.begin("ChildProcessConnection.ChildServiceConnectionImpl.bind");
Intent intent = createServiceBindIntent();
if (mChildProcessCommonParameters != null) {
intent.putExtras(mChildProcessCommonParameters);
}
mBound = mContext.bindService(intent, this, mBindFlags);
} finally {
- TraceEvent.end("BaseChildProcessConnection.ChildServiceConnection.bind");
+ TraceEvent.end("ChildProcessConnection.ChildServiceConnectionImpl.bind");
}
}
return mBound;
@@ -142,7 +135,7 @@ public abstract class BaseChildProcessConnection {
LauncherThread.post(new Runnable() {
@Override
public void run() {
- BaseChildProcessConnection.this.onServiceConnectedOnLauncherThread(service);
+ ChildProcessConnection.this.onServiceConnectedOnLauncherThread(service);
}
});
}
@@ -153,18 +146,16 @@ public abstract class BaseChildProcessConnection {
LauncherThread.post(new Runnable() {
@Override
public void run() {
- BaseChildProcessConnection.this.onServiceDisconnectedOnLauncherThread();
+ ChildProcessConnection.this.onServiceDisconnectedOnLauncherThread();
}
});
}
}
- // binding flag provided via ChildProcessCreationParams.
// TODO(mnaganov): Get rid of it after the release of the next Android SDK.
private static final Map<ComponentName, Boolean> sNeedsExtrabindFlagsMap = new HashMap<>();
-
private final Context mContext;
- private final BaseChildProcessConnection.DeathCallback mDeathCallback;
+ private final ChildProcessConnection.DeathCallback mDeathCallback;
private final ComponentName mServiceName;
// Parameters passed to the child process through the service binding intent.
@@ -216,7 +207,31 @@ public abstract class BaseChildProcessConnection {
// Process ID of the corresponding child process.
private int mPid;
- protected BaseChildProcessConnection(Context context, DeathCallback deathCallback,
+ // Inital moderate binding.
+ private final ChildServiceConnection mInitialBinding;
+
+ // Strong binding will make the service priority equal to the priority of the activity.
+ private final ChildServiceConnection mStrongBinding;
+
+ // Moderate binding will make the service priority equal to the priority of a visible process
+ // while the app is in the foreground.
+ private final ChildServiceConnection mModerateBinding;
+
+ // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
+ // to start() and stop().
+ private final ChildServiceConnection mWaivedBinding;
+
+ // Incremented on addStrongBinding(), decremented on removeStrongBinding().
+ private int mStrongBindingCount;
+
+ // Indicates whether the connection only has the waived binding (if the connection is unbound,
+ // it contains the state at time of unbinding).
+ private boolean mWaivedBoundOnly;
+
+ // Set to true once unbind() was called.
+ private boolean mUnbound;
+
+ protected ChildProcessConnection(Context context, DeathCallback deathCallback,
String serviceClassName, Bundle childProcessCommonParameters,
ChildProcessCreationParams creationParams) {
assert LauncherThread.runningOnLauncherThread();
@@ -227,6 +242,13 @@ public abstract class BaseChildProcessConnection {
mServiceName = new ComponentName(packageName, serviceClassName);
mChildProcessCommonParameters = childProcessCommonParameters;
mCreationParams = creationParams;
+
+ int defaultFlags = Context.BIND_AUTO_CREATE
+ | (shouldBindAsExportedService() ? Context.BIND_EXTERNAL_SERVICE : 0);
+ mInitialBinding = createServiceConnection(defaultFlags);
+ mModerateBinding = createServiceConnection(defaultFlags);
+ mStrongBinding = createServiceConnection(defaultFlags | Context.BIND_IMPORTANT);
+ mWaivedBinding = createServiceConnection(defaultFlags | Context.BIND_WAIVE_PRIORITY);
}
public final Context getContext() {
@@ -240,11 +262,6 @@ public abstract class BaseChildProcessConnection {
: mContext.getPackageName();
}
- public final ChildProcessCreationParams getCreationParams() {
- assert LauncherThread.runningOnLauncherThread();
- return mCreationParams;
- }
-
public final IChildProcessService getService() {
assert LauncherThread.runningOnLauncherThread();
return mService;
@@ -255,6 +272,10 @@ public abstract class BaseChildProcessConnection {
return mServiceName;
}
+ public boolean isConnected() {
+ return mService != null;
+ }
+
/**
* @return the connection pid, or 0 if not yet connected
*/
@@ -267,28 +288,29 @@ public abstract class BaseChildProcessConnection {
* Starts a connection to an IChildProcessService. This must be followed by a call to
* setupConnection() to setup the connection parameters. start() and setupConnection() are
* separate to allow to pass whatever parameters are available in start(), and complete the
- * remainder later while reducing the connection setup latency.
+ * remainder addStrongBinding while reducing the connection setup latency.
+ * @param useStrongBinding whether a strong binding should be bound by default. If false, an
+ * initial moderate binding is used.
* @param startCallback (optional) callback when the child process starts or fails to start.
*/
- public void start(StartCallback startCallback) {
+ public void start(boolean useStrongBinding, StartCallback startCallback) {
assert LauncherThread.runningOnLauncherThread();
try {
- TraceEvent.begin("BaseChildProcessConnection.start");
+ TraceEvent.begin("ChildProcessConnection.start");
assert LauncherThread.runningOnLauncherThread();
assert mConnectionParams
- == null
- : "setupConnection() called before start() in BaseChildProcessConnection.";
+ == null : "setupConnection() called before start() in ChildProcessConnection.";
mStartCallback = startCallback;
- if (!bind()) {
+ if (!bind(useStrongBinding)) {
Log.e(TAG, "Failed to establish the service connection.");
// We have to notify the caller so that they can free-up associated resources.
// TODO(ppi): Can we hard-fail here?
- mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
+ mDeathCallback.onChildProcessDied(ChildProcessConnection.this);
}
} finally {
- TraceEvent.end("BaseChildProcessConnection.start");
+ TraceEvent.end("ChildProcessConnection.start");
}
}
@@ -311,7 +333,7 @@ public abstract class BaseChildProcessConnection {
return;
}
try {
- TraceEvent.begin("BaseChildProcessConnection.setupConnection");
+ TraceEvent.begin("ChildProcessConnection.setupConnection");
mConnectionCallback = connectionCallback;
mConnectionParams = new ConnectionParams(commandLine, filesToBeMapped, callback);
// Run the setup if the service is already connected. If not,
@@ -320,7 +342,7 @@ public abstract class BaseChildProcessConnection {
doConnectionSetupLocked();
}
} finally {
- TraceEvent.end("BaseChildProcessConnection.setupConnection");
+ TraceEvent.end("ChildProcessConnection.setupConnection");
}
}
@@ -343,18 +365,17 @@ public abstract class BaseChildProcessConnection {
return;
}
try {
- TraceEvent.begin(
- "BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
+ TraceEvent.begin("ChildProcessConnection.ChildServiceConnection.onServiceConnected");
mDidOnServiceConnected = true;
mService = IChildProcessService.Stub.asInterface(service);
StartCallback startCallback = mStartCallback;
mStartCallback = null;
- final boolean bindCheck =
- mCreationParams != null && mCreationParams.getBindToCallerCheck();
boolean boundToUs = false;
try {
+ boolean bindCheck =
+ mCreationParams != null && mCreationParams.getBindToCallerCheck();
boundToUs = bindCheck ? mService.bindToCaller() : true;
} catch (RemoteException ex) {
// Do not trigger the StartCallback here, since the service is already
@@ -383,7 +404,7 @@ public abstract class BaseChildProcessConnection {
doConnectionSetupLocked();
}
} finally {
- TraceEvent.end("BaseChildProcessConnection.ChildServiceConnection.onServiceConnected");
+ TraceEvent.end("ChildProcessConnection.ChildServiceConnection.onServiceConnected");
}
}
@@ -397,7 +418,7 @@ public abstract class BaseChildProcessConnection {
mServiceDisconnected = true;
Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d", mPid);
stop(); // We don't want to auto-restart on crash. Let the browser do that.
- mDeathCallback.onChildProcessDied(BaseChildProcessConnection.this);
+ mDeathCallback.onChildProcessDied(ChildProcessConnection.this);
// If we have a pending connection callback, we need to communicate the failure to
// the caller.
if (mConnectionCallback != null) {
@@ -423,7 +444,7 @@ public abstract class BaseChildProcessConnection {
*/
private void doConnectionSetupLocked() {
try {
- TraceEvent.begin("BaseChildProcessConnection.doConnectionSetupLocked");
+ TraceEvent.begin("ChildProcessConnection.doConnectionSetupLocked");
assert mServiceConnectComplete && mService != null;
assert mConnectionParams != null;
@@ -455,23 +476,142 @@ public abstract class BaseChildProcessConnection {
}
mConnectionParams = null;
} finally {
- TraceEvent.end("BaseChildProcessConnection.doConnectionSetupLocked");
+ TraceEvent.end("ChildProcessConnection.doConnectionSetupLocked");
}
}
- /** Subclasses should implement this method to bind/unbind to the actual service. */
- protected abstract boolean bind();
- protected abstract void unbind();
+ private boolean bind(boolean useStrongBinding) {
+ assert LauncherThread.runningOnLauncherThread();
+ assert !mUnbound;
- protected ChildServiceConnection createServiceConnection(int bindFlags) {
+ boolean success = useStrongBinding ? mStrongBinding.bind() : mInitialBinding.bind();
+ if (!success) return false;
+
+ updateWaivedBoundOnlyState();
+ mWaivedBinding.bind();
+ return true;
+ }
+
+ @VisibleForTesting
+ protected void unbind() {
assert LauncherThread.runningOnLauncherThread();
- return new ChildServiceConnectionImpl(bindFlags);
+ mUnbound = true;
+ mStrongBinding.unbind();
+ mWaivedBinding.unbind();
+ mModerateBinding.unbind();
+ mInitialBinding.unbind();
+ // Note that we don't update the waived bound only state here as to preserve the state when
+ // disconnected.
+ }
+
+ public boolean isInitialBindingBound() {
+ assert LauncherThread.runningOnLauncherThread();
+ return mInitialBinding.isBound();
+ }
+
+ public void addInitialBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ mInitialBinding.bind();
+ updateWaivedBoundOnlyState();
+ }
+
+ public boolean isStrongBindingBound() {
+ assert LauncherThread.runningOnLauncherThread();
+ return mStrongBinding.isBound();
+ }
+
+ public void removeInitialBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ mInitialBinding.unbind();
+ updateWaivedBoundOnlyState();
+ }
+
+ public void dropOomBindings() {
+ assert LauncherThread.runningOnLauncherThread();
+ mInitialBinding.unbind();
+
+ mStrongBindingCount = 0;
+ mStrongBinding.unbind();
+ updateWaivedBoundOnlyState();
+
+ mModerateBinding.unbind();
+ }
+
+ public void addStrongBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ if (!isConnected()) {
+ Log.w(TAG, "The connection is not bound for %d", getPid());
+ return;
+ }
+ if (mStrongBindingCount == 0) {
+ mStrongBinding.bind();
+ updateWaivedBoundOnlyState();
+ }
+ mStrongBindingCount++;
+ }
+
+ public void removeStrongBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ if (!isConnected()) {
+ Log.w(TAG, "The connection is not bound for %d", getPid());
+ return;
+ }
+ assert mStrongBindingCount > 0;
+ mStrongBindingCount--;
+ if (mStrongBindingCount == 0) {
+ mStrongBinding.unbind();
+ updateWaivedBoundOnlyState();
+ }
+ }
+
+ public boolean isModerateBindingBound() {
+ assert LauncherThread.runningOnLauncherThread();
+ return mModerateBinding.isBound();
+ }
+
+ public void addModerateBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ if (!isConnected()) {
+ Log.w(TAG, "The connection is not bound for %d", getPid());
+ return;
+ }
+ mModerateBinding.bind();
+ updateWaivedBoundOnlyState();
+ }
+
+ public void removeModerateBinding() {
+ assert LauncherThread.runningOnLauncherThread();
+ if (!isConnected()) {
+ Log.w(TAG, "The connection is not bound for %d", getPid());
+ return;
+ }
+ mModerateBinding.unbind();
+ updateWaivedBoundOnlyState();
+ }
+
+ /**
+ * @return true if the connection is bound and only bound with the waived binding or if the
+ * connection is unbound and was only bound with the waived binding when it disconnected.
+ */
+ public boolean isWaivedBoundOnlyOrWasWhenDied() {
+ // WARNING: this method can be called from a thread other than the launcher thread.
+ // Note that it returns the current waived bound only state and is racy. This not really
+ // preventable without changing the caller's API, short of blocking.
+ return mWaivedBoundOnly;
+ }
+
+ // Should be called every time the mInitialBinding or mStrongBinding are bound/unbound.
+ private void updateWaivedBoundOnlyState() {
+ if (!mUnbound) {
+ mWaivedBoundOnly = !mInitialBinding.isBound() && !mStrongBinding.isBound()
+ && !mModerateBinding.isBound();
+ }
}
protected boolean shouldBindAsExportedService() {
assert LauncherThread.runningOnLauncherThread();
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getCreationParams() != null
- && getCreationParams().getIsExternalService()
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mCreationParams != null
+ && mCreationParams.getIsExternalService()
&& isExportedService(getContext(), getServiceName());
}
@@ -493,12 +633,13 @@ public abstract class BaseChildProcessConnection {
}
@VisibleForTesting
- public void crashServiceForTesting() throws RemoteException {
- mService.crashIntentionallyForTesting();
+ protected ChildServiceConnection createServiceConnection(int bindFlags) {
+ assert LauncherThread.runningOnLauncherThread();
+ return new ChildServiceConnectionImpl(bindFlags);
}
@VisibleForTesting
- boolean isConnected() {
- return mService != null;
+ public void crashServiceForTesting() throws RemoteException {
+ mService.crashIntentionallyForTesting();
}
}

Powered by Google App Engine
This is Rietveld 408576698