Verifying events from TouchInteractionService

There is a guaranteed order in which TIS events will be registered
relative to other TIS events. However, relative to the touch events
arriving to the activity, TIS events can come in any order.

Now the event checker verifies 2 independent ordered event sequences:
from TIS, and “the rest” (Main).

Change-Id: I5872e0e3b0b498050a91c67105fbe4a29411375a
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 edaef30..693f223 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -145,7 +145,7 @@
         @BinderThread
         @Override
         public void onOverviewToggle() {
-            TestLogging.recordEvent("onOverviewToggle");
+            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
             mOverviewCommandHelper.onOverviewToggle();
         }
 
@@ -428,13 +428,17 @@
             Log.e(TAG, "Unknown event " + ev);
             return;
         }
+        MotionEvent event = (MotionEvent) ev;
+
+        TestLogging.recordMotionEvent(
+                TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
+
         if (!mDeviceState.isUserUnlocked()) {
             return;
         }
 
         Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
                 TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
-        MotionEvent event = (MotionEvent) ev;
         if (event.getAction() == ACTION_DOWN) {
             GestureState newGestureState = new GestureState(mOverviewComponentObserver,
                     ActiveGestureLog.INSTANCE.generateAndSetLogId());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 8ae4f06..05c206f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -3,6 +3,7 @@
 import android.view.MotionEvent;
 
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.quickstep.InputConsumer;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -35,7 +36,7 @@
 
     protected void setActive(MotionEvent ev) {
         mState = STATE_ACTIVE;
-        TestLogging.recordEvent("pilferPointers");
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
         mInputMonitor.pilferPointers();
 
         // Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index d01e1a4..ba1d38c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -38,6 +38,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DefaultDisplay;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
@@ -203,7 +204,7 @@
 
     private void startRecentsTransition() {
         mThresholdCrossed = true;
-        TestLogging.recordEvent("pilferPointers");
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         Intent intent = new Intent(Intent.ACTION_MAIN)
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 1b0e05a..3ee3c2d 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
@@ -45,6 +45,7 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.BaseActivityInterface;
@@ -303,7 +304,7 @@
         if (mInteractionHandler == null) {
             return;
         }
-        TestLogging.recordEvent("pilferPointers");
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
         mInputMonitorCompat.pilferPointers();
 
         mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 32a67ca..f161cc0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
@@ -107,7 +108,7 @@
                 ActiveGestureLog.INSTANCE.addLog("startQuickstep");
             }
             if (mInputMonitor != null) {
-                TestLogging.recordEvent("pilferPointers");
+                TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
                 mInputMonitor.pilferPointers();
             }
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 6bfc3fd..823b254 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.quickstep.GestureState;
@@ -64,7 +65,7 @@
 
     private void onInterceptTouch() {
         if (mInputMonitor != null) {
-            TestLogging.recordEvent("pilferPointers");
+            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
             mInputMonitor.pilferPointers();
         }
     }
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 294bb7b..8b7ce10 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
@@ -58,6 +58,7 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -334,9 +335,8 @@
             Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
         if (mTask != null) {
             final ActivityOptions opts;
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-                TestLogging.recordEvent("startActivityFromRecentsAsync:" + mTask);
-            }
+            TestLogging.recordEvent(
+                    TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
             if (animate) {
                 opts = mActivity.getActivityLaunchOptions(this);
                 if (freezeTaskList) {
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index f3c5191..217a41c 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -42,6 +42,7 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.ViewCache;
@@ -330,9 +331,7 @@
             return;
         }
         try {
-            if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-                TestLogging.recordEvent("start: shortcut: " + packageName);
-            }
+            TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: shortcut", packageName);
             getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
                     startActivityOptions, user);
         } catch (SecurityException | IllegalStateException e) {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index dda38b3..8d4af11 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.DisplayRotationListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
@@ -167,9 +168,7 @@
                 startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
             } else if (user == null || user.equals(Process.myUserHandle())) {
                 // Could be launching some bookkeeping activity
-                if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-                    TestLogging.recordEvent("start: activity: " + intent);
-                }
+                TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: activity", intent);
                 startActivity(intent, optsBundle);
                 AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
                         Process.myUserHandle(), sourceContainer);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d250658..a53805f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1778,17 +1778,13 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-            TestLogging.recordEvent("Key event: " + event);
-        }
+        TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
         return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
     }
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (Utilities.IS_RUNNING_IN_TEST_HARNESS && ev.getAction() != MotionEvent.ACTION_MOVE) {
-            TestLogging.recordEvent("Touch event: " + ev);
-        }
+        TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
         return super.dispatchTouchEvent(ev);
     }
 
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index fd066c1..d522d81 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -17,13 +17,30 @@
 package com.android.launcher3.testing;
 
 import android.util.Log;
+import android.view.MotionEvent;
 
 import com.android.launcher3.Utilities;
 
 public final class TestLogging {
-    public static void recordEvent(String event) {
+    private static void recordEventSlow(String sequence, String event) {
+        Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
+    }
+
+    public static void recordEvent(String sequence, String event) {
         if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-            Log.d(TestProtocol.TAPL_EVENTS_TAG, event);
+            recordEventSlow(sequence, event);
+        }
+    }
+
+    public static void recordEvent(String sequence, String message, Object parameter) {
+        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            recordEventSlow(sequence, message + ": " + parameter);
+        }
+    }
+
+    public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
+        if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
+            recordEventSlow(sequence, message + ": " + event);
         }
     }
 }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 2f053c9..35a7f3e 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -33,6 +33,8 @@
     public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
     public static final int HINT_STATE_ORDINAL = 7;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
+    public static final String SEQUENCE_MAIN = "Main";
+    public static final String SEQUENCE_TIS = "TIS";
 
     public static String stateOrdinalToString(int ordinal) {
         switch (ordinal) {
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index afb50e0..468f54c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -21,6 +21,8 @@
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.testing.TestProtocol;
+
 import java.util.regex.Pattern;
 
 public class AddToHomeScreenPrompt {
@@ -38,6 +40,13 @@
 
     public void addAutomatically() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+            if (mLauncher.getNavigationModel()
+                    != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
+                mLauncher.expectEvent(
+                        TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+                mLauncher.expectEvent(
+                        TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+            }
             mLauncher.waitForObjectInContainer(
                     mWidgetCell.getParent().getParent().getParent().getParent(),
                     By.text(ADD_AUTOMATICALLY)).click();
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3f814fd..8932291 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -22,6 +22,8 @@
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.testing.TestProtocol;
+
 import java.util.regex.Pattern;
 
 /**
@@ -56,6 +58,6 @@
 
     @Override
     protected void expectActivityStartEvents() {
-        mLauncher.expectEvent(START_EVENT);
+        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_EVENT);
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index fadfd9f..f8dd89c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -18,6 +18,8 @@
 
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.testing.TestProtocol;
+
 import java.util.regex.Pattern;
 
 /**
@@ -45,6 +47,6 @@
 
     @Override
     protected void expectActivityStartEvents() {
-        mLauncher.expectEvent(START_SHORTCUT_EVENT);
+        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_SHORTCUT_EVENT);
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 9f29a1a..2acab97 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -131,7 +131,7 @@
             }
 
             case THREE_BUTTON:
-                mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                 mLauncher.runToState(
                         () -> mLauncher.waitForSystemUiObject("recent_apps").click(),
                         OVERVIEW_STATE_ORDINAL);
@@ -195,14 +195,14 @@
             case THREE_BUTTON:
                 // Double press the recents button.
                 UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
-                mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                 mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
                 mLauncher.getOverview();
-                mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
                 recentsButton.click();
                 break;
         }
-        mLauncher.expectEvent(TASK_START_EVENT);
+        mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
     }
 
     protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5eb164e..8d98cef 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -75,8 +75,10 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.Deque;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -101,13 +103,17 @@
     static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
             "(?<time>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
                     + "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
-                    + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<event>.*)");
+                    + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / "
+                    + "(?<event>.*)");
 
     private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
     private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
     private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
 
+    static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
+    static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+
     // Types for launcher containers that the user is interacting with. "Background" is a
     // pseudo-container corresponding to inactive launcher covered by another app.
     public enum ContainerType {
@@ -169,19 +175,27 @@
 
     private Consumer<ContainerType> mOnSettledStateAction;
 
+    // Map from an event sequence name to an ordered list of expected events in that sequence.
     // Not null when we are collecting expected events to compare with actual ones.
-    private List<Pattern> mExpectedEvents;
+    private Map<String, List<Pattern>> mExpectedEvents;
 
     private Date mStartRecordingTime;
     private boolean mCheckEventsForSuccessfulGestures = false;
 
-    private static Pattern getTouchEventPattern(String action) {
+    private static Pattern getTouchEventPattern(String prefix, String action) {
         // The pattern includes sanity checks that we don't get a multi-touch events or other
         // surprises.
         return Pattern.compile(
-                "Touch event: MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
-                        +
-                        ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+                prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
+                        + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+    }
+
+    private static Pattern getTouchEventPattern(String action) {
+        return getTouchEventPattern("Touch event", action);
+    }
+
+    private static Pattern getTouchEventPatternTIS(String action) {
+        return getTouchEventPattern("TouchInteractionService.onInputEvent", action);
     }
 
     /**
@@ -646,10 +660,14 @@
                         if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
                                 hasLauncherObject(WIDGETS_RES_ID)
                                         && !mDevice.isNaturalOrientation()) {
-                            expectEvent(EVENT_PILFER_POINTERS);
+                            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);
+                    }
                     runToState(
                             waitForSystemUiObject("home")::click,
                             NORMAL_STATE_ORDINAL,
@@ -929,8 +947,12 @@
     }
 
     void clickLauncherObject(UiObject2 object) {
-        expectEvent(LauncherInstrumentation.EVENT_TOUCH_DOWN);
-        expectEvent(LauncherInstrumentation.EVENT_TOUCH_UP);
+        expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
+        expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+        if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+            expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+            expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+        }
         object.click();
     }
 
@@ -1071,17 +1093,23 @@
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 if (gestureScope != GestureScope.OUTSIDE) {
-                    expectEvent(EVENT_TOUCH_DOWN);
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
+                }
+                if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
                 }
                 break;
             case MotionEvent.ACTION_UP:
                 if (gestureScope != GestureScope.INSIDE) {
-                    expectEvent(EVENT_PILFER_POINTERS);
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
                 }
                 if (gestureScope != GestureScope.OUTSIDE) {
-                    expectEvent(gestureScope == GestureScope.INSIDE
+                    expectEvent(TestProtocol.SEQUENCE_MAIN, gestureScope == GestureScope.INSIDE
                             ? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
                 }
+                if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+                    expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+                }
                 break;
         }
 
@@ -1210,8 +1238,11 @@
         return tasks;
     }
 
-    private List<String> getEvents() {
-        final ArrayList<String> events = new ArrayList<>();
+    // Returns actual events retrieved from logcat. The return value's key set is the set of all
+    // sequence names that actually had at least one event, and the values are lists of events in
+    // the given sequence, in the order they were recorded.
+    private Map<String, List<String>> getEvents() {
+        final Map<String, List<String>> events = new HashMap<>();
         try {
             final String logcatEvents = mDevice.executeShellCommand(
                     "logcat -d -v year --pid=" + getPid() + " -t "
@@ -1225,7 +1256,8 @@
                     continue;
                 }
 
-                events.add(matcher.group("event"));
+                eventsListForSequence(matcher.group("sequence"), events).add(
+                        matcher.group("event"));
             }
             return events;
         } catch (IOException e) {
@@ -1235,9 +1267,20 @@
         }
     }
 
+    // Returns an event list for a given sequence, adding it to the map as needed.
+    private static <T> List<T> eventsListForSequence(
+            String sequenceName, Map<String, List<T>> events) {
+        List<T> eventSequence = events.get(sequenceName);
+        if (eventSequence == null) {
+            eventSequence = new ArrayList<>();
+            events.put(sequenceName, eventSequence);
+        }
+        return eventSequence;
+    }
+
     private void startRecordingEvents() {
         Assert.assertTrue("Already recording events", mExpectedEvents == null);
-        mExpectedEvents = new ArrayList<>();
+        mExpectedEvents = new HashMap<>();
         mStartRecordingTime = new Date();
         log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
     }
@@ -1271,65 +1314,124 @@
             final String message = getEventMismatchMessage(true);
             if (message != null) {
                 Assert.fail(formatSystemHealthMessage(
-                        "http://go/tapl : unexpected event sequence: " + message));
+                        "http://go/tapl : successful gesture produced " + message));
             }
         };
     }
 
-    void expectEvent(Pattern expected) {
-        if (mExpectedEvents != null) mExpectedEvents.add(expected);
+    void expectEvent(String sequence, Pattern expected) {
+        if (mExpectedEvents != null) {
+            eventsListForSequence(sequence, mExpectedEvents).add(expected);
+        }
     }
 
+    // Returns non-null error message if the actual events in logcat don't match expected events.
+    // If we are not checking events, returns null.
     private String getEventMismatchMessage(boolean waitForExpectedCount) {
         if (mExpectedEvents == null) return null;
 
         try {
-            List<String> actual = getEvents();
+            Map<String, List<String>> actual = getEvents();
 
             if (waitForExpectedCount) {
                 // Wait until Launcher generates the expected number of events.
                 final long endTime = SystemClock.uptimeMillis() + WAIT_TIME_MS;
                 while (SystemClock.uptimeMillis() < endTime
-                        && actual.size() < mExpectedEvents.size()) {
+                        && !receivedEnoughEvents(actual)) {
                     SystemClock.sleep(100);
                     actual = getEvents();
                 }
             }
 
-            for (int i = 0; i < mExpectedEvents.size(); ++i) {
-                if (i >= actual.size()) {
-                    return formatEventMismatchMessage("too few actual events", actual, i);
-                }
-                if (!mExpectedEvents.get(i).matcher(actual.get(i)).find()) {
-                    return formatEventMismatchMessage("a mismatched event", actual, i);
-                }
-            }
-
-            if (actual.size() > mExpectedEvents.size()) {
-                return formatEventMismatchMessage(
-                        "too many actual events", actual, mExpectedEvents.size());
-            }
+            return getEventMismatchErrorMessage(actual);
         } finally {
             stopRecordingEvents();
         }
-
-        return null;
     }
 
-    private String formatEventList(List events, int position) {
+    // Returns whether there is a sufficient number of events in the logcat to match the expected
+    // events.
+    private boolean receivedEnoughEvents(Map<String, List<String>> actual) {
+        for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+            final List<String> actualEventSequence = actual.get(expectedNamedSequence.getKey());
+            if (actualEventSequence == null
+                    || actualEventSequence.size() < expectedNamedSequence.getValue().size()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // If the list of actual events matches the list of expected events, returns -1, otherwise
+    // the position of the mismatch.
+    private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
+        for (int i = 0; i < expected.size(); ++i) {
+            if (i >= actual.size()
+                    || !expected.get(i).matcher(actual.get(i)).find()) {
+                return i;
+            }
+        }
+
+        if (actual.size() > expected.size()) return expected.size();
+
+        return -1;
+    }
+
+    // Returns non-null error message if the actual events passed as a param don't match expected
+    // events.
+    private String getEventMismatchErrorMessage(Map<String, List<String>> actualEvents) {
         final StringBuilder sb = new StringBuilder();
+
+        // Check that all expected even sequences match the actual data.
+        for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+            List<String> actualEventSequence = actualEvents.get(expectedNamedSequence.getKey());
+            if (actualEventSequence == null) actualEventSequence = new ArrayList<>();
+            final int mismatchPosition = getMismatchPosition(
+                    expectedNamedSequence.getValue(), actualEventSequence);
+            if (mismatchPosition != -1) {
+                formatSequenceWithMismatch(
+                        sb,
+                        expectedNamedSequence.getKey(),
+                        expectedNamedSequence.getValue(),
+                        actualEventSequence,
+                        mismatchPosition);
+            }
+        }
+
+        // Check for unexpected event sequences in the actual data.
+        for (Map.Entry<String, List<String>> actualNamedSequence : actualEvents.entrySet()) {
+            if (!mExpectedEvents.containsKey(actualNamedSequence.getKey())) {
+                formatSequenceWithMismatch(
+                        sb,
+                        actualNamedSequence.getKey(),
+                        new ArrayList<>(),
+                        actualNamedSequence.getValue(),
+                        0);
+            }
+        }
+
+        return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
+    }
+
+    private static void formatSequenceWithMismatch(
+            StringBuilder sb,
+            String sequenceName,
+            List<Pattern> expected,
+            List<String> actualEvents,
+            int mismatchPosition) {
+        sb.append("\n>> Sequence " + sequenceName);
+        sb.append("\n  Expected:");
+        formatEventListWithMismatch(sb, expected, mismatchPosition);
+        sb.append("\n  Actual:");
+        formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
+    }
+
+    private static void formatEventListWithMismatch(StringBuilder sb, List events, int position) {
         for (int i = 0; i < events.size(); ++i) {
-            sb.append("\n| ");
+            sb.append("\n  | ");
             sb.append(i == position ? "---> " : "     ");
             sb.append(events.get(i).toString());
         }
-        if (position == events.size()) sb.append("\n| ---> (end)");
-        return sb.toString();
-    }
-
-    private String formatEventMismatchMessage(String message, List<String> actual, int position) {
-        return message + ":"
-                + "\nExpected:" + formatEventList(mExpectedEvents, position)
-                + "\nActual:" + formatEventList(actual, position);
+        if (position == events.size()) sb.append("\n  | ---> (end)");
     }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 410e5a1..f955cf2 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -22,6 +22,8 @@
 
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.testing.TestProtocol;
+
 import java.util.regex.Pattern;
 
 /**
@@ -76,7 +78,7 @@
                         event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
                         () -> "Launching task didn't open a new window: "
                                 + mTask.getParent().getContentDescription());
-                mLauncher.expectEvent(TASK_START_EVENT);
+                mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
             }
             return new Background(mLauncher);
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index a0d5443..3f5dc8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -260,8 +260,8 @@
     public Widgets openAllWidgets() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             verifyActiveContainer();
-            mLauncher.expectEvent(EVENT_CTRL_W_DOWN);
-            mLauncher.expectEvent(EVENT_CTRL_W_UP);
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_DOWN);
+            mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_UP);
             mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
             try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
                 return new Widgets(mLauncher);