Merge "Adding main thread binder tracing in development build." into udc-qpr-dev

This commit is contained in:
Sunny Goyal
2023-06-01 17:27:21 +00:00
committed by Android (Google) Code Review
25 changed files with 280 additions and 1197 deletions
@@ -1,159 +0,0 @@
/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.uioverrides;
import static android.os.IBinder.FLAG_ONEWAY;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.MainThread;
import java.util.HashSet;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
* A binder proxy transaction listener for tracking non-whitelisted binder calls.
*/
public class DejankBinderTracker implements Binder.ProxyTransactListener {
private static final String TAG = "DejankBinderTracker";
private static final Object sLock = new Object();
private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>();
static {
// Common IPCs that are ok to block the main thread.
sWhitelistedFrameworkClasses.add("android.view.IWindowSession");
sWhitelistedFrameworkClasses.add("android.os.IPowerManager");
}
private static boolean sTemporarilyIgnoreTracking = false;
// Used by the client to limit binder tracking to specific regions
private static boolean sTrackingAllowed = false;
private BiConsumer<String, Integer> mUnexpectedTransactionCallback;
private boolean mIsTracking = false;
/**
* Temporarily ignore blocking binder calls for the duration of this {@link Runnable}.
*/
@MainThread
public static void whitelistIpcs(Runnable runnable) {
sTemporarilyIgnoreTracking = true;
runnable.run();
sTemporarilyIgnoreTracking = false;
}
/**
* Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
*/
@MainThread
public static <T> T whitelistIpcs(Supplier<T> supplier) {
sTemporarilyIgnoreTracking = true;
T value = supplier.get();
sTemporarilyIgnoreTracking = false;
return value;
}
/**
* Enables binder tracking during a test.
*/
@MainThread
public static void allowBinderTrackingInTests() {
sTrackingAllowed = true;
}
/**
* Disables binder tracking during a test.
*/
@MainThread
public static void disallowBinderTrackingInTests() {
sTrackingAllowed = false;
}
public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {
mUnexpectedTransactionCallback = unexpectedTransactionCallback;
}
@MainThread
public void startTracking() {
if (!Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
&& !Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) {
Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception());
return;
}
if (mIsTracking) {
return;
}
mIsTracking = true;
Binder.setProxyTransactListener(this);
}
@MainThread
public void stopTracking() {
if (!mIsTracking) {
return;
}
mIsTracking = false;
Binder.setProxyTransactListener(null);
}
// Override the hidden Binder#onTransactStarted method
public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
if (!mIsTracking
|| !sTrackingAllowed
|| sTemporarilyIgnoreTracking
|| (flags & FLAG_ONEWAY) == FLAG_ONEWAY
|| !isMainThread()) {
return null;
}
String descriptor;
try {
descriptor = binder.getInterfaceDescriptor();
if (sWhitelistedFrameworkClasses.contains(descriptor)) {
return null;
}
} catch (RemoteException e) {
e.printStackTrace();
descriptor = binder.getClass().getSimpleName();
}
mUnexpectedTransactionCallback.accept(descriptor, transactionCode);
return null;
}
@Override
public Object onTransactStarted(IBinder binder, int transactionCode) {
// Do nothing
return null;
}
@Override
public void onTransactEnded(Object session) {
// Do nothing
}
public static boolean isMainThread() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
}
@@ -112,6 +112,7 @@ import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds;
@@ -587,7 +588,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
if (mWasLauncherAlreadyVisible) {
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
} else {
Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init");
SafeCloseable traceToken = TraceHelper.INSTANCE.beginAsyncSection("WTS-init");
View dragLayer = activity.getDragLayer();
dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
boolean mHandled = false;
@@ -599,7 +600,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
}
mHandled = true;
TraceHelper.INSTANCE.endSection(traceToken);
traceToken.close();
dragLayer.post(() ->
dragLayer.getViewTreeObserver().removeOnDrawListener(this));
if (activity != mActivity) {
@@ -681,11 +682,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private void initializeLauncherAnimationController() {
buildAnimationController();
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
(int) (mLauncherFrameDrawnTime - mTouchTimeMs));
TraceHelper.INSTANCE.endSection(traceToken);
try (SafeCloseable c = TraceHelper.INSTANCE.allowIpcs("logToggleRecents")) {
LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS,
(int) (mLauncherFrameDrawnTime - mTouchTimeMs));
}
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
// high-res thumbnail loader here once we are sure that we will end up in an overview state
@@ -2039,10 +2039,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
private void setScreenshotCapturedState() {
// If we haven't posted a draw callback, set the state immediately.
Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT);
mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
TraceHelper.INSTANCE.endSection(traceToken);
TraceHelper.INSTANCE.endSection();
}
private void finishCurrentTransitionToRecents() {
@@ -0,0 +1,187 @@
/**
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep;
import static android.os.IBinder.FLAG_ONEWAY;
import android.os.Binder;
import android.os.Binder.ProxyTransactListener;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import java.util.LinkedList;
import java.util.Set;
import java.util.function.Consumer;
import kotlin.random.Random;
/**
* A binder proxy transaction listener for tracking binder calls on main thread.
*/
public class BinderTracker {
private static final String TAG = "BinderTracker";
// Common IPCs that are ok to block the main thread.
private static final Set<String> sAllowedFrameworkClasses = Set.of(
"android.view.IWindowSession",
"android.os.IPowerManager",
"android.os.IServiceManager");
/**
* Starts tracking binder class and returns a {@link SafeCloseable} to end tracking
*/
public static SafeCloseable startTracking(Consumer<BinderCallSite> callback) {
TraceHelper current = TraceHelper.INSTANCE;
TraceHelperExtension helper = new TraceHelperExtension(callback);
TraceHelper.INSTANCE = helper;
Binder.setProxyTransactListener(helper);
return () -> {
Binder.setProxyTransactListener(null);
TraceHelper.INSTANCE = current;
};
}
private static final LinkedList<String> mMainThreadTraceStack = new LinkedList<>();
private static final LinkedList<String> mMainThreadIgnoreIpcStack = new LinkedList<>();
private static class TraceHelperExtension extends TraceHelper implements ProxyTransactListener {
private final Consumer<BinderCallSite> mUnexpectedTransactionCallback;
TraceHelperExtension(Consumer<BinderCallSite> unexpectedTransactionCallback) {
mUnexpectedTransactionCallback = unexpectedTransactionCallback;
}
@Override
public void beginSection(String sectionName) {
if (isMainThread()) {
mMainThreadTraceStack.add(sectionName);
}
super.beginSection(sectionName);
}
@Override
public SafeCloseable beginAsyncSection(String sectionName) {
if (!isMainThread()) {
return super.beginAsyncSection(sectionName);
}
mMainThreadTraceStack.add(sectionName);
int cookie = Random.Default.nextInt();
Trace.beginAsyncSection(sectionName, cookie);
return () -> {
Trace.endAsyncSection(sectionName, cookie);
mMainThreadTraceStack.remove(sectionName);
};
}
@Override
public void endSection() {
super.endSection();
if (isMainThread()) {
mMainThreadTraceStack.pollLast();
}
}
@Override
public SafeCloseable allowIpcs(String rpcName) {
if (!isMainThread()) {
return super.allowIpcs(rpcName);
}
mMainThreadTraceStack.add(rpcName);
mMainThreadIgnoreIpcStack.add(rpcName);
int cookie = Random.Default.nextInt();
Trace.beginAsyncSection(rpcName, cookie);
return () -> {
Trace.endAsyncSection(rpcName, cookie);
mMainThreadTraceStack.remove(rpcName);
mMainThreadIgnoreIpcStack.remove(rpcName);
};
}
@Override
public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
if (!isMainThread() || (flags & FLAG_ONEWAY) == FLAG_ONEWAY) {
return null;
}
String ipcBypass = mMainThreadIgnoreIpcStack.peekLast();
String descriptor;
try {
descriptor = binder.getInterfaceDescriptor();
if (sAllowedFrameworkClasses.contains(descriptor)) {
return null;
}
} catch (RemoteException e) {
Log.e(TAG, "Error getting IPC descriptor", e);
descriptor = binder.getClass().getSimpleName();
}
if (ipcBypass == null) {
mUnexpectedTransactionCallback.accept(new BinderCallSite(
mMainThreadTraceStack.peekLast(), descriptor, transactionCode));
} else {
Log.d(TAG, "MainThread-IPC " + descriptor + " ignored due to " + ipcBypass);
}
return null;
}
@Override
public Object onTransactStarted(IBinder binder, int transactionCode) {
// Do nothing
return null;
}
@Override
public void onTransactEnded(Object session) {
// Do nothing
}
}
private static boolean isMainThread() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
/**
* Information about a binder call
*/
public static class BinderCallSite {
@Nullable
public final String activeTrace;
public final String descriptor;
public final int transactionCode;
BinderCallSite(String activeTrace, String descriptor, int transactionCode) {
this.activeTrace = activeTrace;
this.descriptor = descriptor;
this.transactionCode = transactionCode;
}
}
}
@@ -16,10 +16,13 @@
package com.android.quickstep;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -49,4 +52,14 @@ public class InstantAppResolverImpl extends InstantAppResolver {
ComponentName cn = info.getTargetComponent();
return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
}
@Override
public boolean isInstantApp(String packageName, int userId) {
try {
return ActivityThread.getPackageManager().isInstantApp(packageName, userId);
} catch (RemoteException e) {
Log.e(TAG, "Failed to determine whether package is instant app " + packageName, e);
return false;
}
}
}
@@ -60,5 +60,10 @@ public class QuickstepProcessInitializer extends MainProcessInitializer {
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRenderer.setContextPriority(
ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
if (BuildConfig.IS_STUDIO_BUILD) {
BinderTracker.startTracking(call -> Log.e("BinderCall",
call.descriptor + " called on mainthread under " + call.activeTrace));
}
}
}
@@ -393,11 +393,12 @@ public interface TaskShortcutFactory {
@Override
public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
TaskIdAttributeContainer taskContainer) {
return InstantAppResolver.newInstance(activity).isInstantApp(activity,
taskContainer.getTask().getTopComponent().getPackageName()) ?
Collections.singletonList(new SystemShortcut.Install(activity,
taskContainer.getItemInfo(), taskContainer.getTaskView())) :
null;
Task t = taskContainer.getTask();
return InstantAppResolver.newInstance(activity).isInstantApp(
t.getTopComponent().getPackageName(), t.getKey().userId)
? Collections.singletonList(new SystemShortcut.Install(activity,
taskContainer.getItemInfo(), taskContainer.getTaskView()))
: null;
}
};
@@ -33,6 +33,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -51,7 +52,8 @@ public final class TaskUtils {
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public static CharSequence getTitle(Context context, Task task) {
return getTitle(context, task.key.userId, task.getTopComponent().getPackageName());
return TraceHelper.allowIpcs("TaskUtils.getTitle", () ->
getTitle(context, task.key.userId, task.getTopComponent().getPackageName()));
}
public static CharSequence getTitle(
@@ -102,6 +102,7 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -700,8 +701,7 @@ public class TouchInteractionService extends Service
return;
}
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
final int action = event.getActionMasked();
// Note this will create a new consumer every mouse click, as after ACTION_UP from the click
@@ -797,7 +797,7 @@ public class TouchInteractionService extends Service
if (cleanUpConsumer) {
reset();
}
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
traceToken.close();
ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
@@ -28,7 +28,6 @@ import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -229,8 +228,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
// Until we detect the gesture, handle events as we receive them
mInputEventReceiver.setBatchingEnabled(false);
Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
FLAG_CHECK_FOR_RACE_CONDITIONS);
TraceHelper.INSTANCE.beginSection(DOWN_EVT);
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
@@ -241,7 +239,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
startTouchTrackingForWindowAnimation(ev.getEventTime());
}
TraceHelper.INSTANCE.endSection(traceToken);
TraceHelper.INSTANCE.endSection();
break;
}
case ACTION_POINTER_DOWN: {
@@ -417,8 +415,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
* the animation can still be running.
*/
private void finishTouchTracking(MotionEvent ev) {
Object traceToken = TraceHelper.INSTANCE.beginSection(UP_EVT,
FLAG_CHECK_FOR_RACE_CONDITIONS);
TraceHelper.INSTANCE.beginSection(UP_EVT);
if (mPassedWindowMoveSlop && mInteractionHandler != null) {
if (ev.getActionMasked() == ACTION_CANCEL) {
@@ -455,7 +452,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
onInteractionGestureFinished();
}
cleanupAfterGesture();
TraceHelper.INSTANCE.endSection(traceToken);
TraceHelper.INSTANCE.endSection();
}
private void cleanupAfterGesture() {
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import com.android.launcher3.config.FeatureFlags;
/**
* Utility class to test and check binder calls during development.
*/
public class BinderTracker {
private static final String TAG = "BinderTracker";
public static void start() {
if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
Binder.setProxyTransactListener(new Tracker());
}
public static void stop() {
if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
Binder.setProxyTransactListener(null);
}
private static class Tracker implements Binder.ProxyTransactListener {
@Override
public Object onTransactStarted(IBinder iBinder, int code) {
if (Looper.myLooper() == Looper.getMainLooper()) {
Log.e(TAG, "Binder call on ui thread", new Exception());
}
return null;
}
@Override
public void onTransactEnded(Object session) { }
}
}
@@ -112,8 +112,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mContext = context;
mSizeStrategy = sizeStrategy;
// TODO(b/187074722): Don't create this per-TaskViewSimulator
mOrientationState = TraceHelper.allowIpcs("",
mOrientationState = TraceHelper.allowIpcs("TaskViewSimulator.init",
() -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
mOrientationState.setGestureActive(true);
mCurrentFullscreenParams = new FullscreenDrawParams(context);
@@ -32,6 +32,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -89,6 +90,7 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
@@ -1632,8 +1634,8 @@ public class TaskView extends FrameLayout implements Reusable {
if (taskContainer == null) {
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
taskContainer)) {
for (SystemShortcut s : TraceHelper.allowIpcs(
"TV.a11yInfo", () -> getEnabledShortcuts(this, taskContainer))) {
info.addAction(s.createAccessibilityAction(context));
}
}
@@ -1670,7 +1672,7 @@ public class TaskView extends FrameLayout implements Reusable {
if (taskContainer == null) {
continue;
}
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this,
for (SystemShortcut s : getEnabledShortcuts(this,
taskContainer)) {
if (s.hasHandlerForAction(action)) {
s.onClick(this);