Adding binder tracking support in tests

- Whitelist specific binder calls when handling touch during swipe up
  gestures
- Only track binding for touch handling and certain launcher lifecycle
  changes for now

Bug: 140246642
Change-Id: I6ba30280dd17da358662870f8719ae851536ad8b
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 d7ed090..7c1bc4e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -65,6 +65,10 @@
 import android.view.MotionEvent;
 import android.view.Surface;
 
+import androidx.annotation.BinderThread;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.R;
 import com.android.launcher3.ResourceUtils;
@@ -75,6 +79,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.DefaultDisplay;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
@@ -106,10 +111,6 @@
 import java.util.LinkedList;
 import java.util.List;
 
-import androidx.annotation.BinderThread;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
 /**
  * Wrapper around a list for processing arguments.
  */
@@ -536,6 +537,7 @@
     }
 
     private void onInputEvent(InputEvent ev) {
+        DejankBinderTracker.allowBinderTrackingInTests();
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev);
         }
@@ -571,6 +573,7 @@
 
         TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
         mUncheckedConsumer.onMotionEvent(event);
+        DejankBinderTracker.disallowBinderTrackingInTests();
     }
 
     private boolean validSystemUiFlags() {
@@ -634,7 +637,8 @@
     }
 
     private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
-        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+        RunningTaskInfo runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+                () -> mAM.getRunningTask(0));
         if (!useSharedState) {
             sSwipeSharedState.clearAllState(false /* finishAnimation */);
         }
@@ -650,7 +654,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 = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
+            runningTaskInfo = DejankBinderTracker.whitelistIpcs(
+                    () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
             if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
                 final ComponentName homeComponent =
                     mOverviewComponentObserver.getHomeIntent().getComponent();
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 cd31fe0..db8eb27 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -68,6 +68,7 @@
 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;
@@ -423,9 +424,13 @@
     private void initializeLauncherAnimationController() {
         buildAnimationController();
 
-        if (LatencyTrackerCompat.isEnabled(mContext)) {
-            LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
-        }
+        DejankBinderTracker.whitelistIpcs(() -> {
+            // Only used in debug builds
+            if (LatencyTrackerCompat.isEnabled(mContext)) {
+                LatencyTrackerCompat.logToggleRecents(
+                        (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
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 03441c8..c117361 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
@@ -45,6 +45,7 @@
 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.views.ScrimView;
 import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.ClipAnimationHelper;
@@ -172,7 +173,7 @@
 
     @Override
     public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        return mActivity.isInMultiWindowMode();
+        return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode());
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
new file mode 100644
index 0000000..ff4c0f0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java
@@ -0,0 +1,159 @@
+/**
+ * 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/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 994ba65..e2ef337 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 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;
@@ -65,7 +66,8 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mIsSafeModeEnabled = getPackageManager().isSafeMode();
+        mIsSafeModeEnabled = DejankBinderTracker.whitelistIpcs(() ->
+                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 a2db411..4f8b20e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -116,6 +116,7 @@
 import com.android.launcher3.states.InternalStateHandler;
 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;
@@ -309,6 +310,7 @@
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        DejankBinderTracker.allowBinderTrackingInTests();
         RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
         if (DEBUG_STRICT_MODE) {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -430,6 +432,7 @@
                 }
             }
         });
+        DejankBinderTracker.disallowBinderTrackingInTests();
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
@@ -941,6 +944,7 @@
 
     @Override
     protected void onStart() {
+        DejankBinderTracker.allowBinderTrackingInTests();
         RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
         super.onStart();
         if (!mDeferOverlayCallbacks) {
@@ -949,6 +953,7 @@
 
         mAppWidgetHost.setListenIfResumed(true);
         RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
+        DejankBinderTracker.disallowBinderTrackingInTests();
     }
 
     private void handleDeferredResume() {
@@ -1043,6 +1048,7 @@
 
     @Override
     protected void onResume() {
+        DejankBinderTracker.allowBinderTrackingInTests();
         RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
         TraceHelper.beginSection("ON_RESUME");
         super.onResume();
@@ -1068,6 +1074,7 @@
 
         TraceHelper.endSection("ON_RESUME");
         RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
+        DejankBinderTracker.disallowBinderTrackingInTests();
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index fe9c2c4..cf4e8c7 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,6 +22,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.launcher3.uioverrides.DejankBinderTracker;
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 
 import java.util.concurrent.ExecutionException;
@@ -41,7 +42,8 @@
     public T get(Context context) {
         if (mValue == null) {
             if (Looper.myLooper() == Looper.getMainLooper()) {
-                mValue = mProvider.get(context.getApplicationContext());
+                mValue = DejankBinderTracker.whitelistIpcs(() ->
+                        mProvider.get(context.getApplicationContext()));
             } else {
                 try {
                     return MAIN_EXECUTOR.submit(() -> get(context)).get();
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
new file mode 100644
index 0000000..47f6ac6
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java
@@ -0,0 +1,57 @@
+/**
+ * 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 02ead4e..b5c1dae 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,13 +30,14 @@
     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
 endif
 
 LOCAL_MODULE := ub-launcher-aosp-tapl
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := system_current
 
 include $(BUILD_STATIC_JAVA_LIBRARY)