0

[Binder Strict Mode] Initial Allow List for slow Binder calls

There are currently many slow Binder calls on the UI thread. This CL
implements an initial allow list for these slow calls and aims to block
any new such calls from being introduced in the UI thread.

Ultimately, the goal is to get the list down to only the necessary
internal Android Binder calls while either removing the rest or
off-loading them to a background thread if possible.

Low-Coverage-Reason: COVERAGE_UNDERREPORTED
Bug: 391888805
Change-Id: I22b18958f27091a0ad0e5b7c15f2a6d371e5486a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6270116
Reviewed-by: Yaron Friedman <yfriedman@chromium.org>
Commit-Queue: Nafis Abedin <nafisabedin@google.com>
Cr-Commit-Position: refs/heads/main@{#1421369}
This commit is contained in:
Nafis Abedin
2025-02-18 07:12:16 -08:00
committed by Chromium LUCI CQ
parent f72a30b143
commit 0caeecf83f
2 changed files with 95 additions and 2 deletions
base/android/java/src/org/chromium/base
chrome/android/java/src/org/chromium/chrome/browser

@ -7,6 +7,7 @@ package org.chromium.base;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
@ -18,6 +19,8 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashSet;
import java.util.function.BiConsumer;
/**
@ -36,6 +39,72 @@ public class BinderCallsListener {
private static @Nullable BinderCallsListener sInstance;
private static final long LONG_BINDER_CALL_LIMIT_MILLIS = 2;
private static final HashSet<String> sSlowBinderCallAllowList = new HashSet<>();
// The comments mostly correspond to the slow use cases.
static {
Collections.addAll(
sSlowBinderCallAllowList,
// Callbacks for lifecycle events.
"android.app.IActivityTaskManager",
// Used for getActivityInfo, hasSystemFeature, and getSystemAvailableFeatures calls.
"android.content.pm.IPackageManager",
// Called by Choreographer.
"android.view.IWindowSession",
// Check whether UI mode is TV.
"android.app.IUiModeManager",
// Used to add Incognito launcher shortcut.
"android.content.pm.IShortcutService",
// Interactions with activities, services and content providers.
"android.app.IActivityManager",
// Used for getService calls.
"android.os.IServiceManager",
// Checks if power saving mode is enabled.
"android.os.IPowerManager",
// Used by Android code.
"android.content.IContentProvider",
"android.view.accessibility.IAccessibilityManager",
"android.os.IUserManager",
"android.hardware.devicestate.IDeviceStateManager",
"com.android.internal.telephony.ISub",
"com.android.internal.app.IAppOpsService",
"android.view.IGraphicsStats",
"android.app.job.IJobCallback",
"android.app.trust.ITrustManager",
"android.media.IAudioService",
// Gets activity task ID during startup; cached.
"android.app.IActivityClientController",
// Used to check if stylus is enabled.
"com.android.internal.view.IInputMethodManager",
// Registers content observers.
"android.content.IContentService",
// BackgroundTaskScheduler.
"android.app.job.IJobScheduler",
// ConnectivitiyManager#getNetworkInfo.
"android.net.IConnectivityManager",
// Used to get Window Insets.
"android.view.IWindowManager",
// Determines if specific permissions are revoked by policy.
"android.permission.IPermissionManager",
// Used to get the system locale to set the UI language.
"android.app.ILocaleManager",
// Gets all search widget IDs.
"com.android.internal.appwidget.IAppWidgetService",
// Registers HDR:SDR change listener.
"android.hardware.display.IDisplayManager",
// Register Clipboard listener.
"android.content.IClipboard",
// Register input device change listener.
"android.hardware.input.IInputManager",
// Creates notification channels for devices on Android O and above.
"android.app.INotificationManager",
// AppTask#getTaskInfo
"android.app.IAppTask",
// Used to determine if device can authenticate with a given level of strength.
"android.hardware.biometrics.IAuthService");
}
private @Nullable Object mImplementation;
private @Nullable InterfaceInvocationHandler mInvocationHandler;
private boolean mInstalled;
@ -124,6 +193,8 @@ public class BinderCallsListener {
private static class InterfaceInvocationHandler implements InvocationHandler {
private @Nullable String mCurrentInterfaceDescriptor;
private @Nullable BiConsumer<String, String> mObserver;
private int mCurrentTransactionId;
private long mCurrentTransactionStartTimeMillis;
@Override
public @Nullable Object invoke(Object proxy, Method method, Object[] args) {
@ -131,23 +202,44 @@ public class BinderCallsListener {
switch (method.getName()) {
case "onTransactStarted":
IBinder binder = (IBinder) args[0];
mCurrentTransactionId++;
mCurrentTransactionStartTimeMillis = SystemClock.uptimeMillis();
try {
mCurrentInterfaceDescriptor = binder.getInterfaceDescriptor();
} catch (RemoteException e) {
mCurrentInterfaceDescriptor = null;
return null;
}
TraceEvent.begin("BinderCallsListener.invoke", mCurrentInterfaceDescriptor);
if (mObserver != null) {
mObserver.accept("onTransactStarted", mCurrentInterfaceDescriptor);
}
if (!sSlowBinderCallAllowList.contains(mCurrentInterfaceDescriptor)) {
return mCurrentTransactionId;
}
return null;
case "onTransactEnded":
TraceEvent.end("BinderCallsListener.invoke", mCurrentInterfaceDescriptor);
if (mObserver != null) {
mObserver.accept("onTransactEnded", mCurrentInterfaceDescriptor);
}
Integer session = (Integer) args[0];
if (session == null || session != mCurrentTransactionId) {
return null;
}
long transactionDurationMillis =
SystemClock.uptimeMillis() - mCurrentTransactionStartTimeMillis;
if (transactionDurationMillis >= LONG_BINDER_CALL_LIMIT_MILLIS) {
Log.e(
TAG,
"Slow call on UI thread by: %s duration=%dms (max allowed: %dms)",
mCurrentInterfaceDescriptor,
transactionDurationMillis,
LONG_BINDER_CALL_LIMIT_MILLIS);
}
return null;
}
return null;

@ -7,6 +7,7 @@ package org.chromium.chrome.browser;
import android.app.Application;
import android.content.res.Configuration;
import org.chromium.base.AndroidInfo;
import org.chromium.base.BinderCallsListener;
import org.chromium.base.BundleUtils;
import org.chromium.base.library_loader.LibraryLoader;
@ -57,7 +58,7 @@ public class ChromeApplicationImpl extends SplitCompatApplication.Impl {
.setAsyncNotificationManagerFlag(
ChromeFeatureList.sAsyncNotificationManager.isEnabled());
if (ChromeFeatureList.sTraceBinderIpc.isEnabled()) {
if (ChromeFeatureList.sTraceBinderIpc.isEnabled() && AndroidInfo.isDebugAndroid()) {
BinderCallsListener.getInstance().installListener();
}