Skip to content

Commit

Permalink
Add new NOT_PERCEPTIBLE ChildBindingState
Browse files Browse the repository at this point in the history
Rename MODERATE to VISIBLE to correlate more clearly to the binding
in Android. Add a new NOT_PERCEPTIBLE ChildBindingState used by
BindingManager for renderers used in the current session, but not the
current Tab.

This new state is only available on Android Q+ behind a flag.

Bug: 1351814
Change-Id: I8d3f86fbb19bd6abb63921341038c5092a2bbaa6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3818264
Reviewed-by: Bo Liu <boliu@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Yaron Friedman <yfriedman@chromium.org>
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1035151}
  • Loading branch information
ckitagawa-work authored and Chromium LUCI CQ committed Aug 15, 2022
1 parent 7f1f7d6 commit 6fd7b45
Show file tree
Hide file tree
Showing 24 changed files with 673 additions and 233 deletions.
5 changes: 3 additions & 2 deletions base/android/child_process_binding_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ namespace base {
namespace android {

// Defines the state of bindgings with child process. See ChildProcessConnection
// to see what the bindings are. Note these values are used as array indices.
// to see what the bindings are.
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base
enum class ChildBindingState {
UNBOUND,
WAIVED,
MODERATE,
NOT_PERCEPTIBLE,
VISIBLE,
STRONG,
MAX_VALUE = STRONG
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
Expand Down Expand Up @@ -48,8 +49,9 @@
*/
public class ChildProcessConnection {
private static final String TAG = "ChildProcessConn";
private static final int NUM_BINDING_STATES = ChildBindingState.MAX_VALUE + 1;
private static final int FALLBACK_TIMEOUT_IN_SECONDS = 10;
private static final boolean SUPPORT_NOT_PERCEPTIBLE_BINDING =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;

/**
* Used to notify the consumer about the process start. These callbacks will be invoked before
Expand Down Expand Up @@ -120,6 +122,13 @@ public static boolean supportVariableConnections() {
return BindService.supportVariableConnections();
}

/**
* Run time check if not perceptible binding is supported.
*/
public static boolean supportNotPerceptibleBinding() {
return SUPPORT_NOT_PERCEPTIBLE_BINDING;
}

/**
* The string passed to bindToCaller to identify this class loader.
*/
Expand Down Expand Up @@ -229,18 +238,23 @@ public boolean hasUsableZygoteInfo() {
// Strong binding will make the service priority equal to the priority of the activity.
private ChildServiceConnection mStrongBinding;

// Moderate binding will make the service priority equal to the priority of a visible process
// Visible binding will make the service priority equal to the priority of a visible process
// while the app is in the foreground.
// This is also used as the initial binding before any priorities are set.
private ChildServiceConnection mModerateBinding;
private ChildServiceConnection mVisibleBinding;

// On Android Q+ a not perceptible binding will make the service priority below that of a
// perceptible process of a backgrounded app. Only created on Android Q+.
private ChildServiceConnection mNotPerceptibleBinding;

// Low priority binding maintained in the entire lifetime of the connection, i.e. between calls
// to start() and stop().
private ChildServiceConnection mWaivedBinding;

// Refcount of bindings.
private int mStrongBindingCount;
private int mModerateBindingCount;
private int mVisibleBindingCount;
private int mNotPerceptibleBindingCount;

private int mGroup;
private int mImportanceInGroup;
Expand Down Expand Up @@ -348,8 +362,14 @@ private void createBindings(ComponentName serviceName) {
int defaultFlags = Context.BIND_AUTO_CREATE
| (mBindAsExternalService ? Context.BIND_EXTERNAL_SERVICE : 0);

mModerateBinding = mConnectionFactory.createConnection(
mVisibleBinding = mConnectionFactory.createConnection(
intent, defaultFlags, mConnectionDelegate, mInstanceName);
if (supportNotPerceptibleBinding()) {
mNotPerceptibleBinding = mConnectionFactory.createConnection(intent,
defaultFlags | Context.BIND_NOT_PERCEPTIBLE, mConnectionDelegate,
mInstanceName);
}

mStrongBinding = mConnectionFactory.createConnection(
intent, defaultFlags | Context.BIND_IMPORTANT, mConnectionDelegate, mInstanceName);
mWaivedBinding = mConnectionFactory.createConnection(intent,
Expand Down Expand Up @@ -667,7 +687,8 @@ private String buildDebugStateString() {
StringBuilder s = new StringBuilder();
s.append("bindings:");
s.append(mWaivedBinding.isBound() ? "W" : " ");
s.append(mModerateBinding.isBound() ? "M" : " ");
s.append(mVisibleBinding.isBound() ? "V" : " ");
s.append(supportNotPerceptibleBinding() && mNotPerceptibleBinding.isBound() ? "N" : " ");
s.append(mStrongBinding.isBound() ? "S" : " ");
return s.toString();
}
Expand Down Expand Up @@ -792,8 +813,8 @@ private boolean bind(boolean useStrongBinding) {
mStrongBindingCount++;
success = mStrongBinding.bindServiceConnection();
} else {
mModerateBindingCount++;
success = mModerateBinding.bindServiceConnection();
mVisibleBindingCount++;
success = mVisibleBinding.bindServiceConnection();
}
if (!success) {
// Note this error condition is generally transient so `sAlwaysFallback` is
Expand Down Expand Up @@ -832,10 +853,15 @@ private boolean retireBindingsAndBindFallback() {
assert mFallbackServiceName != null;
Log.w(TAG, "Fallback to %s", mFallbackServiceName);
boolean isStrongBindingBound = mStrongBinding.isBound();
boolean isModerateBindingBound = mModerateBinding.isBound();
boolean isVisibleBindingBound = mVisibleBinding.isBound();
boolean isNotPerceptibleBindingBound =
supportNotPerceptibleBinding() && mNotPerceptibleBinding.isBound();
boolean isWaivedBindingBound = mWaivedBinding.isBound();
mStrongBinding.retire();
mModerateBinding.retire();
mVisibleBinding.retire();
if (supportNotPerceptibleBinding()) {
mNotPerceptibleBinding.retire();
}
mWaivedBinding.retire();
createBindings(mFallbackServiceName);
// Expect all bindings to succeed or fail together. So early out as soon as
Expand All @@ -845,8 +871,13 @@ private boolean retireBindingsAndBindFallback() {
return false;
}
}
if (isModerateBindingBound) {
if (!mModerateBinding.bindServiceConnection()) {
if (isVisibleBindingBound) {
if (!mVisibleBinding.bindServiceConnection()) {
return false;
}
}
if (isNotPerceptibleBindingBound) {
if (!mNotPerceptibleBinding.bindServiceConnection()) {
return false;
}
}
Expand All @@ -866,7 +897,10 @@ protected void unbind() {
mUnbound = true;
mStrongBinding.unbindServiceConnection();
mWaivedBinding.unbindServiceConnection();
mModerateBinding.unbindServiceConnection();
if (supportNotPerceptibleBinding()) {
mNotPerceptibleBinding.unbindServiceConnection();
}
mVisibleBinding.unbindServiceConnection();
updateBindingState();

if (mMemoryPressureCallback != null) {
Expand Down Expand Up @@ -931,38 +965,76 @@ public void removeStrongBinding() {
}
}

public boolean isModerateBindingBound() {
public boolean isVisibleBindingBound() {
assert isRunningOnLauncherThread();
return mVisibleBinding.isBound();
}

public int getVisibleBindingCount() {
assert isRunningOnLauncherThread();
return mVisibleBindingCount;
}

public void addVisibleBinding() {
assert isRunningOnLauncherThread();
if (!isConnected()) {
Log.w(TAG, "The connection is not bound for %d", getPid());
return;
}
if (mVisibleBindingCount == 0) {
mVisibleBinding.bindServiceConnection();
updateBindingState();
}
mVisibleBindingCount++;
}

public void removeVisibleBinding() {
assert isRunningOnLauncherThread();
if (!isConnected()) {
return;
}
assert mVisibleBindingCount > 0;
mVisibleBindingCount--;
if (mVisibleBindingCount == 0) {
mVisibleBinding.unbindServiceConnection();
updateBindingState();
}
}

public boolean isNotPerceptibleBindingBound() {
assert isRunningOnLauncherThread();
return mModerateBinding.isBound();
return supportNotPerceptibleBinding() && mNotPerceptibleBinding.isBound();
}

public int getModerateBindingCount() {
public int getNotPerceptibleBindingCount() {
assert isRunningOnLauncherThread();
return mModerateBindingCount;
return mNotPerceptibleBindingCount;
}

public void addModerateBinding() {
public void addNotPerceptibleBinding() {
assert isRunningOnLauncherThread();
assert supportNotPerceptibleBinding();
if (!isConnected()) {
Log.w(TAG, "The connection is not bound for %d", getPid());
return;
}
if (mModerateBindingCount == 0) {
mModerateBinding.bindServiceConnection();
if (mNotPerceptibleBindingCount == 0) {
mNotPerceptibleBinding.bindServiceConnection();
updateBindingState();
}
mModerateBindingCount++;
mNotPerceptibleBindingCount++;
}

public void removeModerateBinding() {
public void removeNotPerceptibleBinding() {
assert isRunningOnLauncherThread();
assert supportNotPerceptibleBinding();
if (!isConnected()) {
return;
}
assert mModerateBindingCount > 0;
mModerateBindingCount--;
if (mModerateBindingCount == 0) {
mModerateBinding.unbindServiceConnection();
assert mNotPerceptibleBindingCount > 0;
mNotPerceptibleBindingCount--;
if (mNotPerceptibleBindingCount == 0) {
mNotPerceptibleBinding.unbindServiceConnection();
updateBindingState();
}
}
Expand Down Expand Up @@ -1018,8 +1090,10 @@ private void updateBindingState() {
newBindingState = ChildBindingState.UNBOUND;
} else if (mStrongBinding.isBound()) {
newBindingState = ChildBindingState.STRONG;
} else if (mModerateBinding.isBound()) {
newBindingState = ChildBindingState.MODERATE;
} else if (mVisibleBinding.isBound()) {
newBindingState = ChildBindingState.VISIBLE;
} else if (supportNotPerceptibleBinding() && mNotPerceptibleBinding.isBound()) {
newBindingState = ChildBindingState.NOT_PERCEPTIBLE;
} else {
assert mWaivedBinding.isBound();
newBindingState = ChildBindingState.WAIVED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public void testServiceStartsSuccessfully() {
ChildProcessConnection connection = createDefaultTestConnection();
assertNotNull(mFirstServiceConnection);
connection.start(false /* useStrongBinding */, mServiceCallback);
Assert.assertTrue(connection.isModerateBindingBound());
Assert.assertTrue(connection.isVisibleBindingBound());
Assert.assertFalse(connection.didOnServiceConnectedForTesting());
verify(mServiceCallback, never()).onChildStarted();
verify(mServiceCallback, never()).onChildStartFailed(any());
Expand All @@ -260,7 +260,7 @@ public void testServiceStartsAndFailsToBind() {
doReturn(false).when(mFirstServiceConnection).bindServiceConnection();
connection.start(false /* useStrongBinding */, mServiceCallback);

Assert.assertFalse(connection.isModerateBindingBound());
Assert.assertFalse(connection.isVisibleBindingBound());
Assert.assertFalse(connection.didOnServiceConnectedForTesting());
verify(mServiceCallback, never()).onChildStarted();
verify(mServiceCallback, never()).onChildStartFailed(any());
Expand Down Expand Up @@ -501,10 +501,15 @@ public void testKill() throws RemoteException {
verify(mConnectionCallback, times(1)).onConnected(connection);

// Add strong binding so that connection is oom protected.
connection.removeModerateBinding();
connection.removeVisibleBinding();
assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
connection.addModerateBinding();
assertEquals(ChildBindingState.MODERATE, connection.bindingStateCurrentOrWhenDied());
if (ChildProcessConnection.supportNotPerceptibleBinding()) {
connection.addNotPerceptibleBinding();
assertEquals(
ChildBindingState.NOT_PERCEPTIBLE, connection.bindingStateCurrentOrWhenDied());
}
connection.addVisibleBinding();
assertEquals(ChildBindingState.VISIBLE, connection.bindingStateCurrentOrWhenDied());
connection.addStrongBinding();
assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());

Expand Down
8 changes: 8 additions & 0 deletions chrome/browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9119,6 +9119,14 @@ const FeatureEntry kFeatureEntries[] = {
FEATURE_VALUE_TYPE(::features::kReduceGpuPriorityOnBackground)},
#endif

#if BUILDFLAG(IS_ANDROID)
{"binding-manager-use-not-perceptible-binding",
flag_descriptions::kBindingManagerUseNotPerceptibleBindingName,
flag_descriptions::kBindingManagerUseNotPerceptibleBindingDescription,
kOsAndroid,
FEATURE_VALUE_TYPE(::features::kBindingManagerUseNotPerceptibleBinding)},
#endif

// NOTE: Adding a new flag requires adding a corresponding entry to enum
// "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
// Histograms" in tools/metrics/histograms/README.md (run the
Expand Down
5 changes: 5 additions & 0 deletions chrome/browser/flag-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,11 @@
"owners": ["pmeenan"],
"expiry_milestone": 120
},
{
"name": "binding-manager-use-not-perceptible-binding",
"owners": ["ckitagawa", "yfriedman"],
"expiry_milestone": 109
},
{
"name": "biometric-authentication-for-filling",
"owners": ["sygiet@google.com", "vsemeniuk@google.com"],
Expand Down
6 changes: 6 additions & 0 deletions chrome/browser/flag_descriptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3280,6 +3280,12 @@ const char kBackGestureRefactorAndroidName[] = "Back Gesture Refactor";
const char kBackGestureRefactorAndroidDescription[] =
"Enable Back Gesture Refactor.";

const char kBindingManagerUseNotPerceptibleBindingName[] =
"Reduced background renderer binding";
const char kBindingManagerUseNotPerceptibleBindingDescription[] =
"When enabled, uses a not perceptible binding for background renderers "
"used in the current session if able.";

const char kBulkTabRestoreAndroidName[] = "Recent Tabs Bulk Restore";
const char kBulkTabRestoreAndroidDescription[] =
"Enables restoration of bulk tab closures (e.g. close all tabs, close "
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/flag_descriptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,9 @@ extern const char kAppMenuMobileSiteOptionDescription[];
extern const char kBackGestureRefactorAndroidName[];
extern const char kBackGestureRefactorAndroidDescription[];

extern const char kBindingManagerUseNotPerceptibleBindingName[];
extern const char kBindingManagerUseNotPerceptibleBindingDescription[];

extern const char kBulkTabRestoreAndroidName[];
extern const char kBulkTabRestoreAndroidDescription[];

Expand Down
19 changes: 16 additions & 3 deletions components/crash/content/browser/crash_metrics_reporter_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -201,16 +201,29 @@ void CrashMetricsReporter::ChildProcessExited(
&reported_counts);
}
break;
case base::android::ChildBindingState::MODERATE:
case base::android::ChildBindingState::VISIBLE:
if (intentional_kill || info.normal_termination) {
ReportCrashCount(
ProcessedCrashCounts::
kRendererForegroundInvisibleWithModerateBindingKilled,
kRendererForegroundInvisibleWithVisibleBindingKilled,
&reported_counts);
} else {
ReportCrashCount(
ProcessedCrashCounts::
kRendererForegroundInvisibleWithModerateBindingOom,
kRendererForegroundInvisibleWithVisibleBindingOom,
&reported_counts);
}
break;
case base::android::ChildBindingState::NOT_PERCEPTIBLE:
if (intentional_kill || info.normal_termination) {
ReportCrashCount(
ProcessedCrashCounts::
kRendererForegroundInvisibleWithNotPerceptibleBindingKilled,
&reported_counts);
} else {
ReportCrashCount(
ProcessedCrashCounts::
kRendererForegroundInvisibleWithNotPerceptibleBindingOom,
&reported_counts);
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class CrashMetricsReporter {
kUtilityForegroundOom = 17,
kUtilityCrashAll = 18,
kRendererProcessHostShutdown = 19,
kMaxValue = kRendererProcessHostShutdown
kRendererForegroundInvisibleWithVisibleBindingKilled = 20,
kRendererForegroundInvisibleWithVisibleBindingOom = 21,
kRendererForegroundInvisibleWithNotPerceptibleBindingKilled = 22,
kRendererForegroundInvisibleWithNotPerceptibleBindingOom = 23,
kMaxValue = kRendererForegroundInvisibleWithNotPerceptibleBindingOom
};
using ReportedCrashTypeSet = base::flat_set<ProcessedCrashCounts>;

Expand Down

0 comments on commit 6fd7b45

Please sign in to comment.