Merge changes from topic "enable" into ub-launcher3-master

* changes:
  Overview Actions - Enable by default.
  TAPL test update - don't run all apps in overview tests with actions.
diff --git a/proguard.flags b/proguard.flags
index e556c94..37b8093 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -50,4 +50,13 @@
 -dontwarn android.graphics.**
 
 # Ignore warnings for hidden utility classes referenced from the shared lib
--dontwarn com.android.internal.util.**
\ No newline at end of file
+-dontwarn com.android.internal.util.**
+
+################ Do not optimize recents lib #############
+-keep class com.android.systemui.** {
+  *;
+}
+
+-keep class com.android.quickstep.** {
+  *;
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 947a861..f79ad25 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -105,6 +105,10 @@
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if ((ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)
+                && mCurrentAnimation == null) {
+            clearState();
+        }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = !canInterceptTouch();
             if (mNoIntercept) {
@@ -125,6 +129,11 @@
                     TaskView view = mRecentsView.getTaskViewAt(i);
                     if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
                             .isEventOverView(view, ev)) {
+                        // Disable swiping up and down if the task overlay is modal.
+                        if (view.isTaskOverlayModal()) {
+                            mTaskBeingDragged = null;
+                            break;
+                        }
                         mTaskBeingDragged = view;
                         if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
                             // Don't allow swipe down to open if we don't support swipe up
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index ec7cddf..eb3b190 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -87,5 +87,12 @@
          * Called when the overlay is no longer used.
          */
         public void reset() { }
+
+        /**
+         * Whether the overlay is modal, which means only tapping is enabled, but no swiping.
+         */
+        public boolean isOverlayModal() {
+            return false;
+        }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
index c6eafe6..a59c99c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -165,7 +165,7 @@
         mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY,
                 mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
 
-        float minVisibleChange = 1f / mStartRect.height();
+        float minVisibleChange = Math.abs(1f / mStartRect.height());
         mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
                 .setSpring(new SpringForce(1f)
                 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index e34e74c..007493d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -565,6 +565,13 @@
         return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev);
     }
 
+    @Override
+    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
+        // If the task overlay is modal, should disable left and right swiping.
+        if (getCurrentPageTaskView() != null && !getCurrentPageTaskView().isTaskOverlayModal()) {
+            super.determineScrollingStart(ev, touchSlopScale);
+        }
+    }
     protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
         return true;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index dce92ff..79b9a9d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -251,6 +251,10 @@
         }
     }
 
+    public boolean isTaskOverlayModal() {
+        return mSnapshotView.getTaskOverlay().isOverlayModal();
+    }
+
     public TaskMenuView getMenuView() {
         return mMenuView;
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 00adfbe..21a4918 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -81,12 +81,9 @@
             mWindowThresholdCrossed = windowThresholdCrossed;
             UI_HELPER_EXECUTOR.execute(() -> {
                 mController.setAnimationTargetsBehindSystemBars(!windowThresholdCrossed);
-                if (mShouldMinimizeSplitScreen && windowThresholdCrossed) {
-                    // NOTE: As a workaround for conflicting animations (Launcher animating the task
-                    // leash, and SystemUI resizing the docked stack, which resizes the task), we
-                    // currently only set the minimized mode, and not the inverse.
-                    // TODO: Synchronize the minimize animation with the launcher animation
-                    mController.setSplitScreenMinimized(windowThresholdCrossed);
+                SystemUiProxy p = SystemUiProxy.INSTANCE.getNoCreate();
+                if (p != null && mShouldMinimizeSplitScreen) {
+                    p.setSplitScreenMinimized(windowThresholdCrossed);
                 }
             });
         }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 101bb07..458d6a9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -295,4 +295,15 @@
             }
         }
     }
+
+    @Override
+    public void setSplitScreenMinimized(boolean minimized) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.setSplitScreenMinimized(minimized);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call stopScreenPinning", e);
+            }
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index e47df6c..ace6743 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -43,9 +42,10 @@
     private final int mCacheSize;
     private final TaskKeyLruCache<ThumbnailData> mCache;
     private final HighResLoadingState mHighResLoadingState;
+    private final boolean mEnableTaskSnapshotPreloading;
 
     public static class HighResLoadingState {
-        private boolean mIsLowRamDevice;
+        private boolean mForceHighResThumbnails;
         private boolean mVisible;
         private boolean mFlingingFast;
         private boolean mHighResLoadingEnabled;
@@ -56,9 +56,9 @@
         }
 
         private HighResLoadingState(Context context) {
-            ActivityManager activityManager =
-                    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-            mIsLowRamDevice = activityManager.isLowRamDevice();
+            // If the device does not support low-res thumbnails, only attempt to load high-res
+            // thumbnails
+            mForceHighResThumbnails = !supportsLowResThumbnails();
         }
 
         public void addCallback(HighResLoadingStateChangedCallback callback) {
@@ -85,7 +85,7 @@
 
         private void updateState() {
             boolean prevState = mHighResLoadingEnabled;
-            mHighResLoadingEnabled = !mIsLowRamDevice && mVisible && !mFlingingFast;
+            mHighResLoadingEnabled = mForceHighResThumbnails || (mVisible && !mFlingingFast);
             if (prevState != mHighResLoadingEnabled) {
                 for (int i = mCallbacks.size() - 1; i >= 0; i--) {
                     mCallbacks.get(i).onHighResLoadingStateChanged(mHighResLoadingEnabled);
@@ -100,6 +100,7 @@
 
         Resources res = context.getResources();
         mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
+        mEnableTaskSnapshotPreloading = res.getBoolean(R.bool.config_enableTaskSnapshotPreloading);
         mCache = new TaskKeyLruCache<>(mCacheSize);
     }
 
@@ -110,7 +111,7 @@
         Preconditions.assertUIThread();
         // Fetch the thumbnail for this task and put it in the cache
         if (task.thumbnail == null) {
-            updateThumbnailInBackground(task.key, true /* reducedResolution */,
+            updateThumbnailInBackground(task.key, true /* lowResolution */,
                     t -> task.thumbnail = t);
         }
     }
@@ -133,8 +134,8 @@
             Task task, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
-        boolean reducedResolution = !mHighResLoadingState.isEnabled();
-        if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
+        boolean lowResolution = !mHighResLoadingState.isEnabled();
+        if (task.thumbnail != null && (!task.thumbnail.reducedResolution || lowResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
             callback.accept(task.thumbnail);
@@ -148,23 +149,23 @@
         });
     }
 
-    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
+    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean lowResolution,
             Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
         ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
-        if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
+        if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || lowResolution)) {
             // Already cached, lets use that thumbnail
             callback.accept(cachedThumbnail);
             return null;
         }
 
         ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
-                reducedResolution) {
+                lowResolution) {
             @Override
             public void run() {
                 ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
-                        key.id, reducedResolution);
+                        key.id, lowResolution);
                 if (isCanceled()) {
                     // We don't call back to the provided callback in this case
                     return;
@@ -212,15 +213,31 @@
      * @return Whether to enable background preloading of task thumbnails.
      */
     public boolean isPreloadingEnabled() {
-        return !mHighResLoadingState.mIsLowRamDevice && mHighResLoadingState.mVisible;
+        return mEnableTaskSnapshotPreloading && mHighResLoadingState.mVisible;
     }
 
     public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
-        public final boolean reducedResolution;
+        public final boolean mLowResolution;
 
-        ThumbnailLoadRequest(Handler handler, boolean reducedResolution) {
+        ThumbnailLoadRequest(Handler handler, boolean lowResolution) {
             super(handler, null);
-            this.reducedResolution = reducedResolution;
+            mLowResolution = lowResolution;
         }
     }
+
+    /**
+     * @return Whether device supports low-res thumbnails. Low-res files are an optimization
+     * for faster load times of snapshots. Devices can optionally disable low-res files so that
+     * they only store snapshots at high-res scale. The actual scale can be configured in
+     * frameworks/base config overlay.
+     */
+    private static boolean supportsLowResThumbnails() {
+        Resources res = Resources.getSystem();
+        int resId = res.getIdentifier("config_lowResTaskSnapshotScale", "dimen", "android");
+        if (resId != 0) {
+            return 0 < res.getFloat(resId);
+        }
+        return true;
+    }
+
 }
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 1229a63..cd94704 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -35,6 +35,7 @@
 
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.Wait;
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -199,7 +200,7 @@
                         + launcher.getNavigationModeMismatchError(),
                 () -> launcher.getNavigationModeMismatchError() == null,
                 60000 /* b/148422894 */, launcher);
-
+        AbstractLauncherUiTest.checkDetectedLeaks();
         return true;
     }
 
diff --git a/res/values/config.xml b/res/values/config.xml
index ef34dcd..8aff6da 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -117,6 +117,9 @@
     <!-- Recents -->
     <item type="id" name="overview_panel"/>
 
+    <!-- Whether to enable background preloading of task thumbnails. -->
+    <bool name="config_enableTaskSnapshotPreloading">true</bool>
+
     <!-- Configuration resources -->
     <array name="dynamic_resources"> </array>
 </resources>
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f618fe1..e61b7a8 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -153,7 +153,7 @@
 
     public void onPackagesRemoved(UserHandle user, String... packages) {
         int op = PackageUpdatedTask.OP_REMOVE;
-        FileLog.d(TAG, "package removed received " + String.join("," + packages));
+        FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
         enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
     }
 
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 065eb37..1c2acfd 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -419,6 +419,8 @@
         }
         if (getOutlineProvider() instanceof RevealOutlineAnimation) {
             ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
+        } else {
+            mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
         }
         if (mOpenCloseAnimator != null) {
             mOpenCloseAnimator.cancel();
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index cae2c3a..254655c 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -29,7 +29,6 @@
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Property;
 import android.view.MotionEvent;
 import android.view.View;
@@ -41,8 +40,8 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.TouchController;
@@ -150,10 +149,20 @@
         return findActiveController(ev);
     }
 
+    private boolean isEventInLauncher(MotionEvent ev) {
+        final float x = ev.getX();
+        final float y = ev.getY();
+
+        return x >= mSystemGestureRegion.left && x < getWidth() - mSystemGestureRegion.right
+                && y >= mSystemGestureRegion.top && y < getHeight() - mSystemGestureRegion.bottom;
+    }
+
     private TouchController findControllerToHandleTouch(MotionEvent ev) {
-        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
-        if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
-            return topView;
+        if (isEventInLauncher(ev)) {
+            AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+            if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+                return topView;
+            }
         }
 
         for (TouchController controller : mControllers) {
@@ -245,17 +254,12 @@
     public boolean dispatchTouchEvent(MotionEvent ev) {
         switch (ev.getAction()) {
             case ACTION_DOWN: {
-                float x = ev.getX();
-                float y = ev.getY();
                 mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
 
-                if ((y < mSystemGestureRegion.top
-                        || x < mSystemGestureRegion.left
-                        || x > (getWidth() - mSystemGestureRegion.right)
-                        || y > (getHeight() - mSystemGestureRegion.bottom))) {
-                    mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
-                } else {
+                if (isEventInLauncher(ev)) {
                     mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+                } else {
+                    mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
                 }
                 break;
             }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 5e98184..e93df96 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -37,6 +37,7 @@
 import android.content.pm.PackageManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.StrictMode;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.By;
@@ -95,6 +96,9 @@
     public static final long DEFAULT_UI_TIMEOUT = 10000;
     private static final String TAG = "AbstractLauncherUiTest";
 
+    private static String sDetectedActivityLeak;
+    private static boolean sActivityLeakReported;
+
     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
     protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
     protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation();
@@ -102,6 +106,41 @@
     protected String mTargetPackage;
     private int mLauncherPid;
 
+    static {
+        if (TestHelpers.isInLauncherProcess()) {
+            StrictMode.VmPolicy.Builder builder =
+                    new StrictMode.VmPolicy.Builder()
+                            .detectActivityLeaks()
+                            .penaltyLog()
+                            .penaltyListener(Runnable::run, violation -> {
+                                // Runs in the main thread. We can't dumpheap in the main thread,
+                                // so let's just mark the fact that the leak has happened.
+                                if (sDetectedActivityLeak == null) {
+                                    sDetectedActivityLeak = violation.toString();
+                                }
+                            });
+            StrictMode.setVmPolicy(builder.build());
+        }
+    }
+
+    public static void checkDetectedLeaks() {
+        if (sDetectedActivityLeak != null && !sActivityLeakReported) {
+            sActivityLeakReported = true;
+
+            final UiDevice device = UiDevice.getInstance(getInstrumentation());
+            try {
+                device.executeShellCommand(
+                        "am dumpheap "
+                                + device.getLauncherPackageName()
+                                + " "
+                                + getInstrumentation().getTargetContext().getFilesDir().getPath()
+                                + "/ActivityLeakHeapDump.hprof");
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
     protected AbstractLauncherUiTest() {
         mLauncher.enableCheckEventsForSuccessfulGestures();
         try {
@@ -120,6 +159,8 @@
                                     checkLauncherIntegrity(launcher, containerType)));
         }
         mLauncher.enableDebugTracing();
+        // Avoid double-reporting of Launcher crashes.
+        mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
     }
 
     protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -190,6 +231,7 @@
         if (mLauncherPid != 0) {
             assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
         }
+        checkDetectedLeaks();
     }
 
     protected void clearLauncherData() throws IOException, InterruptedException {
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 1a68122..38f50c1 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -56,6 +56,7 @@
             private void evaluateInPortrait() throws Throwable {
                 mTest.mDevice.setOrientationNatural();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
+                AbstractLauncherUiTest.checkDetectedLeaks();
                 base.evaluate();
                 mTest.getDevice().pressHome();
             }
@@ -63,6 +64,7 @@
             private void evaluateInLandscape() throws Throwable {
                 mTest.mDevice.setOrientationLeft();
                 mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
+                AbstractLauncherUiTest.checkDetectedLeaks();
                 base.evaluate();
                 mTest.getDevice().pressHome();
             }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 7475efe..f8bbf21 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.widget.WidgetsRecyclerView;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -65,6 +64,7 @@
         test.waitForResumed("Launcher internal state is still Background");
         // Check that we switched to home.
         test.mLauncher.getWorkspace();
+        AbstractLauncherUiTest.checkDetectedLeaks();
     }
 
     // Please don't add negative test cases for methods that fail only after a long wait.
@@ -110,7 +110,6 @@
     }
 
     @Test
-    @Ignore
     public void testPressHomeOnAllAppsContextMenu() throws Exception {
         final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
         allApps.freeze();
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
index 5880eb6..e4f520f 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
@@ -102,10 +102,15 @@
             logSinceBoot =
                     UiDevice.getInstance(getInstrumentation())
                             .executeShellCommand("logcat -d -t " + systemBootTime.replace(" ", ""));
-        } catch (IOException e) {
+        } catch (IOException | OutOfMemoryError e) {
             return 0;
         }
 
+        if (matches("android\\:\\:uirenderer\\:\\:renderthread\\:\\:EglManager\\:\\:swapBuffers",
+                logSinceBoot)) {
+            return 148529608;
+        }
+
         for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
             if (matches(exceptionMatch.exceptionPattern, exception)) {
                 for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 08de156..4481e9d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -167,6 +167,8 @@
     private static boolean sCheckingEvents;
 
     private boolean mCheckEventsForSuccessfulGestures = false;
+    private int mExpectedPid;
+    private Runnable mOnLauncherCrashed;
 
     private static Pattern getTouchEventPattern(String prefix, String action) {
         // The pattern includes sanity checks that we don't get a multi-touch events or other
@@ -249,6 +251,10 @@
         mCheckEventsForSuccessfulGestures = true;
     }
 
+    public void setOnLauncherCrashed(Runnable onLauncherCrashed) {
+        mOnLauncherCrashed = onLauncherCrashed;
+    }
+
     Context getContext() {
         return mInstrumentation.getContext();
     }
@@ -324,7 +330,7 @@
         }
     }
 
-    private String getAnomalyMessage() {
+    private String getSystemAnomalyMessage() {
         UiObject2 object = mDevice.findObject(By.res("android", "alertTitle"));
         if (object != null) {
             return "System alert popup is visible: " + object.getText();
@@ -343,15 +349,33 @@
         return null;
     }
 
+    private String getAnomalyMessage() {
+        if (mExpectedPid != 0 && mExpectedPid != getPid()) {
+            mExpectedPid = 0;
+            if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
+            return "Launcher crashed";
+        }
+
+        final String systemAnomalyMessage = getSystemAnomalyMessage();
+        if (systemAnomalyMessage != null) {
+            return "http://go/tapl : Tests are broken by a non-Launcher system error: "
+                    + systemAnomalyMessage;
+        }
+
+        return null;
+    }
+
     public void checkForAnomaly() {
         final String anomalyMessage = getAnomalyMessage();
         if (anomalyMessage != null) {
-            String message = "http://go/tapl : Tests are broken by a non-Launcher system error: "
-                    + anomalyMessage;
-            log("Hierarchy dump for: " + message);
+            if (sCheckingEvents) {
+                sCheckingEvents = false;
+                sEventChecker.finishNoWait();
+            }
+            log("Hierarchy dump for: " + anomalyMessage);
             dumpViewHierarchy();
 
-            Assert.fail(formatSystemHealthMessage(message));
+            Assert.fail(formatSystemHealthMessage(anomalyMessage));
         }
     }
 
@@ -650,14 +674,6 @@
                     mDevice.waitForIdle();
 
                     if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
-                        if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
-                                hasLauncherObject(WIDGETS_RES_ID)
-                                        && !mDevice.isNaturalOrientation()) {
-                            expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
-                        }
-                    }
-
-                    if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
                         expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
                         expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
                     }
@@ -1245,10 +1261,13 @@
             };
         }
         sCheckingEvents = true;
+        mExpectedPid = getPid();
         if (sEventChecker == null) sEventChecker = new LogEventChecker();
         sEventChecker.start();
 
         return () -> {
+            checkForAnomaly();
+
             if (sCheckingEvents) {
                 sCheckingEvents = false;
                 if (mCheckEventsForSuccessfulGestures) {