[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:

committed by
Chromium LUCI CQ

parent
f72a30b143
commit
0caeecf83f
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();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user