Merge "Unifying various tracing calls" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index a34638d..556f481 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -62,7 +62,7 @@
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.DejankBinderTracker;
+import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -409,7 +409,6 @@
     }
 
     private void onInputEvent(InputEvent ev) {
-        DejankBinderTracker.allowBinderTrackingInTests();
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev);
         }
@@ -418,6 +417,7 @@
             return;
         }
 
+        TraceHelper.INSTANCE.beginFlagsOverride(TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
         MotionEvent event = (MotionEvent) ev;
         if (event.getAction() == ACTION_DOWN) {
             GestureState newGestureState = new GestureState(
@@ -446,7 +446,7 @@
 
         ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
         mUncheckedConsumer.onMotionEvent(event);
-        DejankBinderTracker.disallowBinderTrackingInTests();
+        TraceHelper.INSTANCE.endFlagsOverride();
     }
 
     private InputConsumer newConsumer(GestureState gestureState, boolean useSharedState,
@@ -498,7 +498,7 @@
 
     private InputConsumer newBaseConsumer(GestureState gestureState, boolean useSharedState,
             MotionEvent event) {
-        RunningTaskInfo runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+        RunningTaskInfo runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.0",
                 () -> mAM.getRunningTask(0));
         if (!useSharedState) {
             sSwipeSharedState.clearAllState(false /* finishAnimation */);
@@ -512,7 +512,8 @@
         if (isExcludedAssistant(runningTaskInfo)) {
             // In the case where we are in the excluded assistant state, ignore it and treat the
             // running activity as the task behind the assistant
-            runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+
+            runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.assistant",
                     () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
             if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
                 final ComponentName homeComponent =
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index e3b12cb..8dd1fff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -23,8 +23,6 @@
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState.HIDE;
 import static com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState.PEEK;
@@ -65,11 +63,9 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory;
 import com.android.quickstep.BaseActivityInterface.AnimationFactory.ShelfAnimState;
@@ -371,13 +367,13 @@
         if (mWasLauncherAlreadyVisible) {
             mStateCallback.setState(STATE_LAUNCHER_DRAWN);
         } else {
-            TraceHelper.beginSection("WTS-init");
+            TraceHelper.INSTANCE.beginSection("WTS-init");
             View dragLayer = activity.getDragLayer();
             dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
 
                 @Override
                 public void onDraw() {
-                    TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
+                    TraceHelper.INSTANCE.endSection();
                     dragLayer.post(() ->
                             dragLayer.getViewTreeObserver().removeOnDrawListener(this));
                     if (activity != mActivity) {
@@ -420,13 +416,13 @@
     private void initializeLauncherAnimationController() {
         buildAnimationController();
 
-        DejankBinderTracker.whitelistIpcs(() -> {
-            // Only used in debug builds
-            if (LatencyTrackerCompat.isEnabled(mContext)) {
-                LatencyTrackerCompat.logToggleRecents(
-                        (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
-            }
-        });
+        TraceHelper.INSTANCE.beginSection("logToggleRecents", TraceHelper.FLAG_IGNORE_BINDERS);
+        // Only used in debug builds
+        if (LatencyTrackerCompat.isEnabled(mContext)) {
+            LatencyTrackerCompat.logToggleRecents(
+                    (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+        }
+        TraceHelper.INSTANCE.endSection();
 
         // 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
@@ -1148,9 +1144,10 @@
             }
             if (!finishTransitionPosted) {
                 // If we haven't posted a draw callback, set the state immediately.
-                RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, ENTER);
+                TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+                        TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
                 setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-                RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, EXIT);
+                TraceHelper.INSTANCE.endSection();
             }
         }
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index fbedc0f..d231d51 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -25,8 +25,7 @@
 
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
 import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -48,13 +47,13 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.BaseSwipeUpHandler;
 import com.android.quickstep.BaseSwipeUpHandler.Factory;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.SwipeSharedState;
 import com.android.quickstep.SysUINavigationMode;
@@ -63,7 +62,6 @@
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.NavBarPosition;
-import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -200,8 +198,7 @@
 
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
-                RaceConditionTracker.onEvent(DOWN_EVT, ENTER);
-                TraceHelper.beginSection("TouchInt");
+                TraceHelper.INSTANCE.beginSection(DOWN_EVT, FLAG_CHECK_FOR_RACE_CONDITIONS);
                 mActivePointerId = ev.getPointerId(0);
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
@@ -212,7 +209,7 @@
                     startTouchTrackingForWindowAnimation(ev.getEventTime(), false);
                 }
 
-                RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
+                TraceHelper.INSTANCE.endSection();
                 break;
             }
             case ACTION_POINTER_DOWN: {
@@ -358,8 +355,7 @@
      * the animation can still be running.
      */
     private void finishTouchTracking(MotionEvent ev) {
-        RaceConditionTracker.onEvent(UP_EVT, ENTER);
-        TraceHelper.endSection("TouchInt");
+        TraceHelper.INSTANCE.beginSection(UP_EVT, FLAG_CHECK_FOR_RACE_CONDITIONS);
 
         if (mPassedWindowMoveSlop && mInteractionHandler != null) {
             if (ev.getActionMasked() == ACTION_CANCEL) {
@@ -393,7 +389,7 @@
         mVelocityTracker.recycle();
         mVelocityTracker = null;
         mMotionPauseDetector.clear();
-        RaceConditionTracker.onEvent(UP_EVT, EXIT);
+        TraceHelper.INSTANCE.endSection();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 1545ec5..0655c73 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -46,8 +46,8 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.appprediction.PredictionUiStateManager;
 import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.AppWindowAnimationHelper;
@@ -221,7 +221,7 @@
 
     @Override
     public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode());
+        return TraceHelper.whitelistIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
deleted file mode 100644
index d8aa235..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
+++ /dev/null
@@ -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();
-    }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 2111e2c..1d920f9 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -16,8 +16,8 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.util.RaceConditionTracker.enterEvt;
-import static com.android.launcher3.util.RaceConditionTracker.exitEvt;
+import static com.android.launcher3.util.RaceConditionReproducer.enterEvt;
+import static com.android.launcher3.util.RaceConditionReproducer.exitEvt;
 
 import android.content.Intent;
 
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 00a06ae..d24de8e 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -36,11 +36,11 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.uioverrides.DisplayRotationListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TraceHelper;
 
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
@@ -66,8 +66,10 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mIsSafeModeEnabled = DejankBinderTracker.whitelistIpcs(() ->
-                getPackageManager().isSafeMode());
+
+
+        mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
+                () -> getPackageManager().isSafeMode());
         mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
 
         // Update theme
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c92d917..a19ba21 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -32,8 +32,6 @@
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -115,7 +113,6 @@
 import com.android.launcher3.qsb.QsbContainerView;
 import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -132,7 +129,6 @@
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.ShortcutUtil;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
@@ -214,9 +210,11 @@
     private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
     // Type: SparseArray<Parcelable>
     private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+
     public static final String ON_CREATE_EVT = "Launcher.onCreate";
-    private static final String ON_START_EVT = "Launcher.onStart";
-    private static final String ON_RESUME_EVT = "Launcher.onResume";
+    public static final String ON_START_EVT = "Launcher.onStart";
+    public static final String ON_RESUME_EVT = "Launcher.onResume";
+    public static final String ON_NEW_INTENT_EVT = "Launcher.onNewIntent";
 
     private LauncherStateManager mStateManager;
 
@@ -313,8 +311,7 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        DejankBinderTracker.allowBinderTrackingInTests();
-        RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
+        TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT, TraceHelper.FLAG_UI_EVENT);
         if (DEBUG_STRICT_MODE) {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectDiskReads()
@@ -329,10 +326,8 @@
                     .penaltyDeath()
                     .build());
         }
-        TraceHelper.beginSection("Launcher-onCreate");
 
         super.onCreate(savedInstanceState);
-        TraceHelper.partitionSection("Launcher-onCreate", "super call");
 
         LauncherAppState app = LauncherAppState.getInstance(this);
         mOldConfig = new Configuration(getResources().getConfiguration());
@@ -414,8 +409,6 @@
 
         mRotationHelper.initialize();
 
-        TraceHelper.endSection("Launcher-onCreate");
-        RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
         mStateManager.addStateListener(new LauncherStateManager.StateListener() {
             @Override
             public void onStateTransitionStart(LauncherState toState) {
@@ -435,7 +428,8 @@
                 }
             }
         });
-        DejankBinderTracker.disallowBinderTrackingInTests();
+
+        TraceHelper.INSTANCE.endSection();
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
@@ -940,16 +934,14 @@
 
     @Override
     protected void onStart() {
-        DejankBinderTracker.allowBinderTrackingInTests();
-        RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
+        TraceHelper.INSTANCE.beginSection(ON_START_EVT, TraceHelper.FLAG_UI_EVENT);
         super.onStart();
         if (!mDeferOverlayCallbacks) {
             mOverlayManager.onActivityStarted(this);
         }
 
         mAppWidgetHost.setListenIfResumed(true);
-        RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
-        DejankBinderTracker.disallowBinderTrackingInTests();
+        TraceHelper.INSTANCE.endSection();
     }
 
     private void handleDeferredResume() {
@@ -1044,11 +1036,8 @@
 
     @Override
     protected void onResume() {
-        DejankBinderTracker.allowBinderTrackingInTests();
-        RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
-        TraceHelper.beginSection("ON_RESUME");
+        TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT, TraceHelper.FLAG_UI_EVENT);
         super.onResume();
-        TraceHelper.partitionSection("ON_RESUME", "superCall");
 
         mHandler.removeCallbacks(mHandleDeferredResume);
         Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
@@ -1068,9 +1057,7 @@
             mOverlayManager.onActivityResumed(this);
         }
 
-        TraceHelper.endSection("ON_RESUME");
-        RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
-        DejankBinderTracker.disallowBinderTrackingInTests();
+        TraceHelper.INSTANCE.endSection();
     }
 
     @Override
@@ -1429,7 +1416,7 @@
 
     @Override
     protected void onNewIntent(Intent intent) {
-        TraceHelper.beginSection("NEW_INTENT");
+        TraceHelper.INSTANCE.beginSection(ON_NEW_INTENT_EVT);
         super.onNewIntent(intent);
 
         boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() &
@@ -1481,7 +1468,7 @@
             mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
         }
 
-        TraceHelper.endSection("NEW_INTENT");
+        TraceHelper.INSTANCE.endSection();
     }
 
     @Override
@@ -1984,7 +1971,7 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void startBinding() {
-        TraceHelper.beginSection("startBinding");
+        TraceHelper.INSTANCE.beginSection("startBinding");
         // Floating panels (except the full widget sheet) are associated with individual icons. If
         // we are starting a fresh bind, close all such panels as all the icons are about
         // to go away.
@@ -2002,7 +1989,7 @@
         if (mHotseat != null) {
             mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
         }
-        TraceHelper.endSection("startBinding");
+        TraceHelper.INSTANCE.endSection();
     }
 
     @Override
@@ -2195,112 +2182,113 @@
                 return null;
             }
         }
-
+        final AppWidgetHostView view;
         if (mIsSafeModeEnabled) {
-            PendingAppWidgetHostView view =
-                    new PendingAppWidgetHostView(this, item, mIconCache, true);
+            view = new PendingAppWidgetHostView(this, item, mIconCache, true);
             prepareAppWidget(view, item);
             return view;
         }
 
-        TraceHelper.beginSection("BIND_WIDGET");
+        TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId);
 
-        final LauncherAppWidgetProviderInfo appWidgetInfo;
+        try {
+            final LauncherAppWidgetProviderInfo appWidgetInfo;
 
-        if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
-            // If the provider is not ready, bind as a pending widget.
-            appWidgetInfo = null;
-        } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
-            // The widget id is not valid. Try to find the widget based on the provider info.
-            appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
-        } else {
-            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
-        }
-
-        // If the provider is ready, but the width is not yet restored, try to restore it.
-        if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
-                (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
-            if (appWidgetInfo == null) {
-                Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
-                        + " belongs to component " + item.providerName
-                        + ", as the provider is null");
-                getModelWriter().deleteItemFromDatabase(item);
-                return null;
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+                // If the provider is not ready, bind as a pending widget.
+                appWidgetInfo = null;
+            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                // The widget id is not valid. Try to find the widget based on the provider info.
+                appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+            } else {
+                appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
             }
 
-            // If we do not have a valid id, try to bind an id.
-            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
-                if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
-                    // Id has not been allocated yet. Allocate a new id.
-                    item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-                    item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
+            // If the provider is ready, but the width is not yet restored, try to restore it.
+            if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+                    && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+                if (appWidgetInfo == null) {
+                    Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+                            + " belongs to component " + item.providerName
+                            + ", as the provider is null");
+                    getModelWriter().deleteItemFromDatabase(item);
+                    return null;
+                }
 
-                    // Also try to bind the widget. If the bind fails, the user will be shown
-                    // a click to setup UI, which will ask for the bind permission.
-                    PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
-                    pendingInfo.spanX = item.spanX;
-                    pendingInfo.spanY = item.spanY;
-                    pendingInfo.minSpanX = item.minSpanX;
-                    pendingInfo.minSpanY = item.minSpanY;
-                    Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
-                            pendingInfo);
+                // If we do not have a valid id, try to bind an id.
+                if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                    if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+                        // Id has not been allocated yet. Allocate a new id.
+                        item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                        item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
 
-                    boolean isDirectConfig =
-                            item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
-                    if (isDirectConfig && item.bindOptions != null) {
-                        Bundle newOptions = item.bindOptions.getExtras();
-                        if (options != null) {
-                            newOptions.putAll(options);
+                        // Also try to bind the widget. If the bind fails, the user will be shown
+                        // a click to setup UI, which will ask for the bind permission.
+                        PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo);
+                        pendingInfo.spanX = item.spanX;
+                        pendingInfo.spanY = item.spanY;
+                        pendingInfo.minSpanX = item.minSpanX;
+                        pendingInfo.minSpanY = item.minSpanY;
+                        Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
+                                pendingInfo);
+
+                        boolean isDirectConfig =
+                                item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+                        if (isDirectConfig && item.bindOptions != null) {
+                            Bundle newOptions = item.bindOptions.getExtras();
+                            if (options != null) {
+                                newOptions.putAll(options);
+                            }
+                            options = newOptions;
                         }
-                        options = newOptions;
+                        boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                                item.appWidgetId, appWidgetInfo, options);
+
+                        // We tried to bind once. If we were not able to bind, we would need to
+                        // go through the permission dialog, which means we cannot skip the config
+                        // activity.
+                        item.bindOptions = null;
+                        item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
+
+                        // Bind succeeded
+                        if (success) {
+                            // If the widget has a configure activity, it is still needs to set it
+                            // up, otherwise the widget is ready to go.
+                            item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
+                                    ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+                                    : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                        }
+
+                        getModelWriter().updateItemInDatabase(item);
                     }
-                    boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
-                            item.appWidgetId, appWidgetInfo, options);
-
-                    // We tried to bind once. If we were not able to bind, we would need to
-                    // go through the permission dialog, which means we cannot skip the config
-                    // activity.
-                    item.bindOptions = null;
-                    item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
-
-                    // Bind succeeded
-                    if (success) {
-                        // If the widget has a configure activity, it is still needs to set it up,
-                        // otherwise the widget is ready to go.
-                        item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
-                                ? LauncherAppWidgetInfo.RESTORE_COMPLETED
-                                : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-                    }
-
+                } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
+                        && (appWidgetInfo.configure == null)) {
+                    // The widget was marked as UI not ready, but there is no configure activity to
+                    // update the UI.
+                    item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                     getModelWriter().updateItemInDatabase(item);
                 }
-            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
-                    && (appWidgetInfo.configure == null)) {
-                // The widget was marked as UI not ready, but there is no configure activity to
-                // update the UI.
-                item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
-                getModelWriter().updateItemInDatabase(item);
-            }
-        }
-
-        final AppWidgetHostView view;
-        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-            // Verify that we own the widget
-            if (appWidgetInfo == null) {
-                FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
-                getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
-                return null;
             }
 
-            item.minSpanX = appWidgetInfo.minSpanX;
-            item.minSpanY = appWidgetInfo.minSpanY;
-            view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
-        } else {
-            view = new PendingAppWidgetHostView(this, item, mIconCache, false);
-        }
-        prepareAppWidget(view, item);
+            if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+                // Verify that we own the widget
+                if (appWidgetInfo == null) {
+                    FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
+                    getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
+                    return null;
+                }
 
-        TraceHelper.endSection("BIND_WIDGET", "id=" + item.appWidgetId);
+                item.minSpanX = appWidgetInfo.minSpanX;
+                item.minSpanY = appWidgetInfo.minSpanY;
+                view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
+            } else {
+                view = new PendingAppWidgetHostView(this, item, mIconCache, false);
+            }
+            prepareAppWidget(view, item);
+        } finally {
+            TraceHelper.INSTANCE.endSection();
+        }
+
         return view;
     }
 
@@ -2378,7 +2366,7 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void finishBindingItems(int pageBoundFirst) {
-        TraceHelper.beginSection("finishBindingItems");
+        TraceHelper.INSTANCE.beginSection("finishBindingItems");
         mWorkspace.restoreInstanceStateForRemainingPages();
 
         setWorkspaceLoading(false);
@@ -2402,7 +2390,7 @@
                 mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows);
         getViewCache().setCacheSize(R.layout.folder_page, 2);
 
-        TraceHelper.endSection("finishBindingItems");
+        TraceHelper.INSTANCE.endSection();
     }
 
     private boolean canRunNewAppsAnimation() {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index a29b7e1..2d4a816 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -40,6 +40,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.MutableInt;
+import android.util.TimingLogger;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
@@ -169,82 +170,84 @@
             }
         }
 
-        TraceHelper.beginSection(TAG);
+        TraceHelper.INSTANCE.beginSection(TAG);
+        TimingLogger logger = new TimingLogger(TAG, "run");
         try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
-            TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
             loadWorkspace();
+            logger.addSplit("loadWorkspace");
 
             verifyNotStopped();
-            TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
             mResults.bindWorkspace();
+            logger.addSplit("bindWorkspace");
 
             // Notify the installer packages of packages with active installs on the first screen.
-            TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
             sendFirstScreenActiveInstallsBroadcast();
+            logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
 
             // Take a break
-            TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
             waitForIdle();
+            logger.addSplit("step 1 complete");
             verifyNotStopped();
 
             // second step
-            TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
             List<LauncherActivityInfo> allActivityList = loadAllApps();
+            logger.addSplit("loadAllApps");
 
-            TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
             verifyNotStopped();
             mResults.bindAllApps();
+            logger.addSplit("bindAllApps");
 
             verifyNotStopped();
-            TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
             IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
             setIgnorePackages(updateHandler);
             updateHandler.updateIcons(allActivityList,
                     LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                     mApp.getModel()::onPackageIconsUpdated);
+            logger.addSplit("update icon cache");
 
             // Take a break
-            TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
             waitForIdle();
+            logger.addSplit("step 2 complete");
             verifyNotStopped();
 
             // third step
-            TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
             loadDeepShortcuts();
+            logger.addSplit("loadDeepShortcuts");
 
             verifyNotStopped();
-            TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
             mResults.bindDeepShortcuts();
+            logger.addSplit("bindDeepShortcuts");
 
             // Take a break
-            TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
             waitForIdle();
+            logger.addSplit("step 3 complete");
             verifyNotStopped();
 
             // fourth step
-            TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
             List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
+            logger.addSplit("load widgets");
 
             verifyNotStopped();
-            TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
             mResults.bindWidgets();
-
+            logger.addSplit("bindWidgets");
             verifyNotStopped();
 
-            TraceHelper.partitionSection(TAG, "step 4.3: save widgets in icon cache");
             updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
                     mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
+            logger.addSplit("save widgets in icon cache");
 
             verifyNotStopped();
-            TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update");
             updateHandler.finish();
+            logger.addSplit("finish icon update");
 
             transaction.commit();
         } catch (CancellationException e) {
             // Loader stopped, ignore
-            TraceHelper.partitionSection(TAG, "Cancelled");
+            logger.addSplit("Cancelled");
+        } finally {
+            logger.dumpToLog();
         }
-        TraceHelper.endSection(TAG);
+        TraceHelper.INSTANCE.endSection();
     }
 
     public synchronized void stopLocked() {
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index cf4e8c7..520a9ed 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,7 +22,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 
 import java.util.concurrent.ExecutionException;
@@ -42,8 +41,8 @@
     public T get(Context context) {
         if (mValue == null) {
             if (Looper.myLooper() == Looper.getMainLooper()) {
-                mValue = DejankBinderTracker.whitelistIpcs(() ->
-                        mProvider.get(context.getApplicationContext()));
+                mValue = TraceHelper.whitelistIpcs("main.thread.object",
+                        () -> mProvider.get(context.getApplicationContext()));
             } else {
                 try {
                     return MAIN_EXECUTOR.submit(() -> get(context)).get();
diff --git a/src/com/android/launcher3/util/RaceConditionTracker.java b/src/com/android/launcher3/util/RaceConditionTracker.java
deleted file mode 100644
index 6954d0e..0000000
--- a/src/com/android/launcher3/util/RaceConditionTracker.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-/**
- * Event tracker for reliably reproducing race conditions in tests.
- * The app should call onEvent() for events that the test will try to reproduce in all possible
- * orders.
- */
-public class RaceConditionTracker {
-    public final static boolean ENTER = true;
-    public final static boolean EXIT = false;
-    static final String ENTER_POSTFIX = "enter";
-    static final String EXIT_POSTFIX = "exit";
-
-    public interface EventProcessor {
-        void onEvent(String eventName);
-    }
-
-    private static EventProcessor sEventProcessor;
-
-    static void setEventProcessor(EventProcessor eventProcessor) {
-        sEventProcessor = eventProcessor;
-    }
-
-    public static void onEvent(String eventName) {
-        if (sEventProcessor != null) sEventProcessor.onEvent(eventName);
-    }
-
-    public static void onEvent(String eventName, boolean isEnter) {
-        if (sEventProcessor != null) {
-            sEventProcessor.onEvent(enterExitEvt(eventName, isEnter));
-        }
-    }
-
-    public static String enterExitEvt(String eventName, boolean isEnter) {
-        return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
-    }
-
-    public static String enterEvt(String eventName) {
-        return enterExitEvt(eventName, ENTER);
-    }
-
-    public static String exitEvt(String eventName) {
-        return enterExitEvt(eventName, EXIT);
-    }
-}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index c24bb67..073fb6a 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -15,19 +15,14 @@
  */
 package com.android.launcher3.util;
 
-import static android.util.Log.VERBOSE;
-import static android.util.Log.isLoggable;
-
-import android.os.SystemClock;
 import android.os.Trace;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.MutableLong;
 
-import com.android.launcher3.config.FeatureFlags;
+import androidx.annotation.MainThread;
+
+import java.util.function.Supplier;
 
 /**
- * A wrapper around {@link Trace} with some utility information.
+ * A wrapper around {@link Trace} to allow better testing.
  *
  * To enable any tracing log, execute the following command:
  * $ adb shell setprop log.tag.LAUNCHER_TRACE VERBOSE
@@ -35,65 +30,51 @@
  */
 public class TraceHelper {
 
-    private static final boolean ENABLED = isLoggable("LAUNCHER_TRACE", VERBOSE);
+    // Track binder class for this trace
+    public static final int FLAG_ALLOW_BINDER_TRACKING = 1 << 0;
 
-    private static final boolean SYSTEM_TRACE = ENABLED;
-    private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
+    // Temporarily ignore blocking binder calls for this trace.
+    public static final int FLAG_IGNORE_BINDERS = 1 << 1;
 
-    public static void beginSection(String sectionName) {
-        if (ENABLED) {
-            synchronized (sUpTimes) {
-                MutableLong time = sUpTimes.get(sectionName);
-                if (time == null) {
-                    time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
-                    sUpTimes.put(sectionName, time);
-                }
-                if (time.value >= 0) {
-                    if (SYSTEM_TRACE) {
-                        Trace.beginSection(sectionName);
-                    }
-                    time.value = SystemClock.uptimeMillis();
-                }
-            }
-        }
+    public static final int FLAG_CHECK_FOR_RACE_CONDITIONS = 1 << 2;
+
+    public static final int FLAG_UI_EVENT =
+            FLAG_ALLOW_BINDER_TRACKING | FLAG_CHECK_FOR_RACE_CONDITIONS;
+
+    /**
+     * Static instance of Trace helper, overridden in tests.
+     */
+    public static TraceHelper INSTANCE = new TraceHelper();
+
+    public void beginSection(String sectionName) {
+        beginSection(sectionName, 0);
     }
 
-    public static void partitionSection(String sectionName, String partition) {
-        if (ENABLED) {
-            synchronized (sUpTimes) {
-                MutableLong time = sUpTimes.get(sectionName);
-                if (time != null && time.value >= 0) {
-
-                    if (SYSTEM_TRACE) {
-                        Trace.endSection();
-                        Trace.beginSection(sectionName);
-                    }
-
-                    long now = SystemClock.uptimeMillis();
-                    Log.d(sectionName, partition + " : " + (now - time.value));
-                    time.value = now;
-                }
-            }
-        }
+    public void beginSection(String sectionName, int flags) {
+        Trace.beginSection(sectionName);
     }
 
-    public static void endSection(String sectionName) {
-        if (ENABLED) {
-            endSection(sectionName, "End");
-        }
+    public void endSection() {
+        Trace.endSection();
     }
 
-    public static void endSection(String sectionName, String msg) {
-        if (ENABLED) {
-            synchronized (sUpTimes) {
-                MutableLong time = sUpTimes.get(sectionName);
-                if (time != null && time.value >= 0) {
-                    if (SYSTEM_TRACE) {
-                        Trace.endSection();
-                    }
-                    Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
-                }
-            }
+    /**
+     * Similar to {@link #beginSection} but doesn't add a trace section.
+     */
+    public void beginFlagsOverride(int flags) { }
+
+    public void endFlagsOverride() { }
+
+    /**
+     * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}.
+     */
+    @MainThread
+    public static <T> T whitelistIpcs(String rpcName, Supplier<T> supplier) {
+        INSTANCE.beginSection(rpcName, FLAG_IGNORE_BINDERS);
+        try {
+            return supplier.get();
+        } finally {
+            INSTANCE.endSection();
         }
     }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
deleted file mode 100644
index 47f6ac6..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
+++ /dev/null
@@ -1,57 +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 android.os.IBinder;
-
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-
-/**
- * A binder proxy transaction listener for tracking non-whitelisted binder calls.
- */
-public class DejankBinderTracker {
-    public static void whitelistIpcs(Runnable runnable) {}
-
-    public static <T> T whitelistIpcs(Supplier<T> supplier) {
-        return  null;
-    }
-
-    public static void allowBinderTrackingInTests() {}
-
-    public static void disallowBinderTrackingInTests() {}
-
-    public DejankBinderTracker(BiConsumer<String, Integer> unexpectedTransactionCallback) {    }
-
-    public void startTracking() {}
-
-    public void stopTracking() {}
-
-    public Object onTransactStarted(IBinder binder, int transactionCode, int flags) {
-        return null;
-    }
-
-    public Object onTransactStarted(IBinder binder, int transactionCode) {
-        return null;
-    }
-
-    public void onTransactEnded(Object session) {}
-
-    public static boolean isMainThread() {
-        return true;
-    }
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index b5c1dae..31a9960 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,7 +30,6 @@
     LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
 
     LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
-        ../quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java \
         ../src/com/android/launcher3/ResourceUtils.java \
         ../src/com/android/launcher3/util/SecureSettingsObserver.java \
         ../src/com/android/launcher3/testing/TestProtocol.java
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
index 8f89173..ed2ec7b 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.util;
 
 import static com.android.launcher3.util.Executors.createAndStartNewLooper;
-import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
-import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -64,15 +62,30 @@
  *
  * When we register event XXX:enter, we hold all other events until we register XXX:exit.
  */
-public class RaceConditionReproducer implements RaceConditionTracker.EventProcessor {
+public class RaceConditionReproducer {
     private static final String TAG = "RaceConditionReproducer";
+
+    private static final boolean ENTER = true;
+    private static final boolean EXIT = false;
+    private static final String ENTER_POSTFIX = "enter";
+    private static final String EXIT_POSTFIX = "exit";
+
     private static final long SHORT_TIMEOUT_MS = 2000;
     private static final long LONG_TIMEOUT_MS = 60000;
     // Handler used to resume postponed events.
-    private static final Handler POSTPONED_EVENT_RESUME_HANDLER = createEventResumeHandler();
+    private static final Handler POSTPONED_EVENT_RESUME_HANDLER =
+            new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
 
-    private static Handler createEventResumeHandler() {
-        return new Handler(createAndStartNewLooper("RaceConditionEventResumer"));
+    public static String enterExitEvt(String eventName, boolean isEnter) {
+        return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
+    }
+
+    public static String enterEvt(String eventName) {
+        return enterExitEvt(eventName, ENTER);
+    }
+
+    public static String exitEvt(String eventName) {
+        return enterExitEvt(eventName, EXIT);
     }
 
     /**
@@ -209,7 +222,8 @@
                 parseReproString(mReproString) : generateSequenceToFollowLocked();
         Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
         checkIfCompletedSequenceToFollowLocked();
-        RaceConditionTracker.setEventProcessor(this);
+
+        TraceHelperForTest.setRaceConditionReproducer(this);
     }
 
     /**
@@ -218,7 +232,8 @@
      * Returns whether we need more iterations.
      */
     public synchronized boolean finishIteration() {
-        RaceConditionTracker.setEventProcessor(null);
+        TraceHelperForTest.setRaceConditionReproducer(null);
+
         runResumeAllEventsCallbackLocked();
         assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
         assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
@@ -243,7 +258,6 @@
     /**
      * Called when the app issues an event.
      */
-    @Override
     public void onEvent(String event) {
         final Semaphore waitObject = tryRegisterEvent(event);
         if (waitObject != null) {
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
index 3fc268e..d156d1f 100644
--- a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
@@ -22,6 +22,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,17 +38,24 @@
         return res;
     }
 
-    private static void run3_3_TestAction() throws InterruptedException {
+    RaceConditionReproducer eventProcessor;
+
+    @Before
+    public void setup() {
+        eventProcessor = new RaceConditionReproducer();
+    }
+
+    private void run3_3_TestAction() throws InterruptedException {
         Thread tb = new Thread(() -> {
-            RaceConditionTracker.onEvent("B1");
-            RaceConditionTracker.onEvent("B2");
-            RaceConditionTracker.onEvent("B3");
+            eventProcessor.onEvent("B1");
+            eventProcessor.onEvent("B2");
+            eventProcessor.onEvent("B3");
         });
         tb.start();
 
-        RaceConditionTracker.onEvent("A1");
-        RaceConditionTracker.onEvent("A2");
-        RaceConditionTracker.onEvent("A3");
+        eventProcessor.onEvent("A1");
+        eventProcessor.onEvent("A2");
+        eventProcessor.onEvent("A3");
 
         tb.join();
     }
@@ -56,7 +64,6 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads, 3 events each.
     public void test3_3() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
         boolean sawTheValidSequence = false;
 
         for (; ; ) {
@@ -80,25 +87,24 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads, 3 events, including enter-exit pairs each.
     public void test3_3_enter_exit() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
         boolean sawTheValidSequence = false;
 
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1:enter");
-                RaceConditionTracker.onEvent("B1:exit");
-                RaceConditionTracker.onEvent("B2");
-                RaceConditionTracker.onEvent("B3:enter");
-                RaceConditionTracker.onEvent("B3:exit");
+                eventProcessor.onEvent("B1:enter");
+                eventProcessor.onEvent("B1:exit");
+                eventProcessor.onEvent("B2");
+                eventProcessor.onEvent("B3:enter");
+                eventProcessor.onEvent("B3:exit");
             });
             tb.start();
 
-            RaceConditionTracker.onEvent("A1");
-            RaceConditionTracker.onEvent("A2:enter");
-            RaceConditionTracker.onEvent("A2:exit");
-            RaceConditionTracker.onEvent("A3:enter");
-            RaceConditionTracker.onEvent("A3:exit");
+            eventProcessor.onEvent("A1");
+            eventProcessor.onEvent("A2:enter");
+            eventProcessor.onEvent("A2:exit");
+            eventProcessor.onEvent("A3:enter");
+            eventProcessor.onEvent("A3:exit");
 
             tb.join();
             final boolean needMoreIterations = eventProcessor.finishIteration();
@@ -134,23 +140,21 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads with 2 events; 1 thread with 1 event.
     public void test2_1_2() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1");
-                RaceConditionTracker.onEvent("B2");
+                eventProcessor.onEvent("B1");
+                eventProcessor.onEvent("B2");
             });
             tb.start();
 
             Thread tc = new Thread(() -> {
-                RaceConditionTracker.onEvent("C1");
+                eventProcessor.onEvent("C1");
             });
             tc.start();
 
-            RaceConditionTracker.onEvent("A1");
-            RaceConditionTracker.onEvent("A2");
+            eventProcessor.onEvent("A1");
+            eventProcessor.onEvent("A2");
 
             tb.join();
             tc.join();
@@ -167,28 +171,26 @@
     @Ignore // The test is too long for continuous testing.
     // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
     public void test2_1_2_enter_exit() throws Exception {
-        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
-
         for (; ; ) {
             eventProcessor.startIteration();
             Thread tb = new Thread(() -> {
-                RaceConditionTracker.onEvent("B1:enter");
-                RaceConditionTracker.onEvent("B1:exit");
-                RaceConditionTracker.onEvent("B2:enter");
-                RaceConditionTracker.onEvent("B2:exit");
+                eventProcessor.onEvent("B1:enter");
+                eventProcessor.onEvent("B1:exit");
+                eventProcessor.onEvent("B2:enter");
+                eventProcessor.onEvent("B2:exit");
             });
             tb.start();
 
             Thread tc = new Thread(() -> {
-                RaceConditionTracker.onEvent("C1:enter");
-                RaceConditionTracker.onEvent("C1:exit");
+                eventProcessor.onEvent("C1:enter");
+                eventProcessor.onEvent("C1:exit");
             });
             tc.start();
 
-            RaceConditionTracker.onEvent("A1:enter");
-            RaceConditionTracker.onEvent("A1:exit");
-            RaceConditionTracker.onEvent("A2:enter");
-            RaceConditionTracker.onEvent("A2:exit");
+            eventProcessor.onEvent("A1:enter");
+            eventProcessor.onEvent("A1:exit");
+            eventProcessor.onEvent("A2:enter");
+            eventProcessor.onEvent("A2:exit");
 
             tb.join();
             tc.join();
diff --git a/tests/src/com/android/launcher3/util/TraceHelperForTest.java b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
new file mode 100644
index 0000000..9125f5f
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TraceHelperForTest.java
@@ -0,0 +1,100 @@
+/**
+ * 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.util;
+
+import java.util.LinkedList;
+import java.util.function.IntConsumer;
+
+public class TraceHelperForTest extends TraceHelper {
+
+    private static final TraceHelperForTest INSTANCE_FOR_TEST = new TraceHelperForTest();
+
+    private final ThreadLocal<LinkedList<TraceInfo>> mStack =
+            ThreadLocal.withInitial(LinkedList::new);
+
+    private RaceConditionReproducer mRaceConditionReproducer;
+    private IntConsumer mFlagsChangeListener;
+
+    public static void setRaceConditionReproducer(RaceConditionReproducer reproducer) {
+        TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+        INSTANCE_FOR_TEST.mRaceConditionReproducer = reproducer;
+    }
+
+    public static void setFlagsChangeListener(IntConsumer listener) {
+        TraceHelper.INSTANCE = INSTANCE_FOR_TEST;
+        INSTANCE_FOR_TEST.mFlagsChangeListener = listener;
+    }
+
+    private TraceHelperForTest() { }
+
+    @Override
+    public void beginSection(String sectionName, int flags) {
+        LinkedList<TraceInfo> stack = mStack.get();
+        stack.add(new TraceInfo(sectionName, flags));
+
+        if ((flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+                 && mRaceConditionReproducer != null) {
+            mRaceConditionReproducer.onEvent(RaceConditionReproducer.enterEvt(sectionName));
+        }
+        updateBinderTracking(stack);
+
+        super.beginSection(sectionName, flags);
+    }
+
+    @Override
+    public void endSection() {
+        LinkedList<TraceInfo> stack = mStack.get();
+        TraceInfo info = stack.pollLast();
+        if ((info.flags & TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS) != 0
+                && mRaceConditionReproducer != null) {
+            mRaceConditionReproducer.onEvent(RaceConditionReproducer.exitEvt(info.sectionName));
+        }
+        updateBinderTracking(stack);
+
+        super.endSection();
+    }
+
+    @Override
+    public void beginFlagsOverride(int flags) {
+        LinkedList<TraceInfo> stack = mStack.get();
+        stack.push(new TraceInfo(null, flags));
+        updateBinderTracking(stack);
+        super.beginFlagsOverride(flags);
+    }
+
+    @Override
+    public void endFlagsOverride() {
+        super.endFlagsOverride();
+        updateBinderTracking(mStack.get());
+    }
+
+    private void updateBinderTracking(LinkedList<TraceInfo> stack) {
+        if (mFlagsChangeListener != null) {
+            mFlagsChangeListener.accept(stack.stream()
+                    .mapToInt(s -> s.flags).reduce(0, (a, b) -> a | b));
+        }
+    }
+
+    private static class TraceInfo {
+        public final String sectionName;
+        public final int flags;
+
+        TraceInfo(String sectionName, int flags) {
+            this.sectionName = sectionName;
+            this.flags = flags;
+        }
+    }
+}