Make sure TAPL waits for Launcher activity to stop each time it stops

This helps to ensure that the test doesn't proceed while the activity is
still stopping and it's state hasn't settled.

We introduce a count of STOP events and make sure it's 0 except for
cases where stops are safe to ignore or we have waited for them.

Bug: 313926097
Flag: N/A
Test: presubmit
Change-Id: I51c73c89854fcaa390d47a57389da18b2ce0ee6c
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 44a1bf0..b39e76a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -412,6 +412,8 @@
     private final SettingsCache.OnChangeListener mNaturalScrollingChangedListener =
             enabled -> mIsNaturalScrollingEnabled = enabled;
 
+    private int mActivityStopCount; // Used only by tests
+
     public static Launcher getLauncher(Context context) {
         return fromContext(context);
     }
@@ -1053,10 +1055,18 @@
         mAppWidgetHolder.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
         FloatingIconView.resetIconLoadResult();
+        ++mActivityStopCount;
         AccessibilityManagerCompat.sendTestProtocolEventToTest(
                 this, LAUNCHER_ACTIVITY_STOPPED_MESSAGE);
     }
 
+    /** Return activity stop count and reset it. Used only by tests. */
+    public int getAndResetActivityStopCount() {
+        final int activityStopCount = mActivityStopCount;
+        mActivityStopCount = 0;
+        return activityStopCount;
+    }
+
     @Override
     protected void onStart() {
         TraceHelper.INSTANCE.beginSection(ON_START_EVT);
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 315b5e3..87be4d1 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -208,6 +208,16 @@
                                 .forceAllowRotationForTesting(Boolean.parseBoolean(arg)));
                 return response;
 
+            case TestProtocol.REQUEST_GET_AND_RESET_ACTIVITY_STOP_COUNT: {
+                final Bundle bundle = getLauncherUIProperty(Bundle::putInt,
+                        launcher -> launcher.getAndResetActivityStopCount());
+                if (bundle != null) return bundle;
+
+                // If Launcher activity wasn't created, 'it' was stopped 0 times.
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, 0);
+                return response;
+            }
+
             case TestProtocol.REQUEST_WORKSPACE_CELL_LAYOUT_SIZE:
                 return getLauncherUIProperty(Bundle::putIntArray, launcher -> {
                     final Workspace<?> workspace = launcher.getWorkspace();
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 3e188e6..16cfc66 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -177,6 +177,9 @@
     public static final String REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED =
             "unstash-bubble-bar-if-stashed";
 
+    public static final String REQUEST_GET_AND_RESET_ACTIVITY_STOP_COUNT =
+            "get-and-reset-activity-stops";
+
     /** Logs {@link Log#d(String, String)} if {@link #sDebugTracing} is true. */
     public static void testLogD(String tag, String message) {
         if (!sDebugTracing) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 79d8c60..3ee951c 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -598,6 +598,9 @@
         Wait.atMost("Launcher activity didn't stop",
                 () -> !launcherInstrumentation.isLauncherActivityStarted(),
                 DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation);
+
+        // Reset activity stop count.
+        launcherInstrumentation.getAndResetActivityStopCount();
     }
 
     public static ActivityInfo resolveSystemAppInfo(String category) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java b/tests/multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java
index 2b45902..e98dcf4 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/TestIsolationRule.java
@@ -44,11 +44,15 @@
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                base.evaluate();
-                // Make sure that Launcher workspace looks correct.
+                // Reset activity stop count.
+                mLauncher.getAndResetActivityStopCount();
 
+                base.evaluate();
+
+                // Make sure that Launcher workspace looks correct.
                 UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressHome();
                 AbstractLauncherUiTest.checkDetectedLeaks(mLauncher, mRequireOneActiveActivity);
+                mLauncher.assertNoUnexpectedStops();
             }
         };
     }
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index fef93b7..bfaaf5a 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -207,6 +207,7 @@
 
     private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE;
     private int mPointerCount = 0;
+    private final boolean mIsLauncherTest;
 
     private static Pattern getKeyEventPattern(String action, String keyCode) {
         return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode);
@@ -243,6 +244,7 @@
      */
     @Deprecated
     public LauncherInstrumentation(Instrumentation instrumentation, boolean isLauncherTest) {
+        mIsLauncherTest = isLauncherTest;
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
 
@@ -410,6 +412,11 @@
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    public int getAndResetActivityStopCount() {
+        return getTestInfo(TestProtocol.REQUEST_GET_AND_RESET_ACTIVITY_STOP_COUNT).getInt(
+                TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     Rect getGridTaskRectForTablet() {
         return ((Rect) getTestInfo(TestProtocol.REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET)
                 .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD));
@@ -623,10 +630,19 @@
 
     public void onTestStart() {
         mTestStartTime = System.currentTimeMillis();
+        assertNoUnexpectedStops();
     }
 
     public void onTestFinish() {
         mTestStartTime = -1;
+        assertNoUnexpectedStops();
+    }
+
+    /** Verify that the activity stop count is zero. */
+    public void assertNoUnexpectedStops() {
+        if (mIsLauncherTest) {
+            assertEquals("Unexpected activity stops", 0, getAndResetActivityStopCount());
+        }
     }
 
     private String formatSystemHealthMessage(String message) {
@@ -1000,6 +1016,9 @@
                 event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
                         .equals(event.getClassName().toString()),
                 () -> "Launcher activity didn't stop", actionName);
+
+        // Reset activity stop count.
+        getAndResetActivityStopCount();
     }
 
     /**
@@ -2252,6 +2271,7 @@
     }
 
     public Closable eventsCheck() {
+        assertNoUnexpectedStops();
         Assert.assertTrue("Nested event checking", mEventChecker == null);
         disableSensorRotation();
         final Integer initialPid = getPid();
@@ -2259,6 +2279,7 @@
         if (eventChecker.start()) mEventChecker = eventChecker;
 
         return () -> {
+            assertNoUnexpectedStops();
             if (initialPid != null && initialPid.intValue() != getPid()) {
                 if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
                 checkForAnomaly();