Merge "Exclude automotive from Condition Provider Test." into pie-cts-dev
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 6a3678d..2fd2521 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -42,6 +42,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
@@ -121,8 +122,10 @@
             tests.add(new EnableHintsTest());
             tests.add(new ReceiveAppBlockNoticeTest());
             tests.add(new ReceiveAppUnblockNoticeTest());
-            tests.add(new ReceiveChannelBlockNoticeTest());
-            tests.add(new ReceiveGroupBlockNoticeTest());
+            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+                tests.add(new ReceiveChannelBlockNoticeTest());
+                tests.add(new ReceiveGroupBlockNoticeTest());
+            }
             tests.add(new RequestUnbindTest());
             tests.add(new RequestBindTest());
             tests.add(new MessageBundleTest());
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index d6b39fd..68ec7cc 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -394,15 +394,17 @@
             if (granted != wasGranted) {
                 // Toggle the permission
 
+                boolean willShowPopup = (wasGranted && legacyApp);
+
                 if (!itemView.getActionList().contains(AccessibilityAction.ACTION_CLICK)) {
-                    click(toggleView, false);
+                    click(toggleView, willShowPopup);
                 } else {
-                    click(itemView, false);
+                    click(itemView, willShowPopup);
                 }
 
                 waitForIdle();
 
-                if (wasGranted && legacyApp) {
+                if (willShowPopup) {
                     scrollToBottomIfWatch();
                     String packageName = getInstrumentation().getContext().getPackageManager()
                             .getPermissionControllerPackageName();
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
index 863f51b..0931792 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
@@ -50,6 +50,7 @@
     protected boolean mIsMultiuserSupported;
     protected boolean mIsManagedUserSupported;
 
+    private int mInitialUserId;
     private ArrayList<Integer> mOriginalUsers;
 
     @Override
@@ -72,6 +73,7 @@
         }
 
         if (mIsMultiuserSupported) {
+            mInitialUserId = getDevice().getCurrentUser();
             mOriginalUsers = new ArrayList<>(getDevice().listUsers());
         }
     }
@@ -183,7 +185,7 @@
         if (!mIsMultiuserSupported) {
             return;
         }
-        getDevice().switchUser(getPrimaryUserId());
+        getDevice().switchUser(mInitialUserId);
         for (int userId : getDevice().listUsers()) {
             if (!mOriginalUsers.contains(userId)) {
                 getDevice().removeUser(userId);
@@ -191,6 +193,18 @@
         }
     }
 
+    protected int getOrCreateSecondaryUser() throws Exception {
+        if (getDevice().isUserSecondary(mInitialUserId)) {
+            return mInitialUserId;
+        }
+        for (int userId : getDevice().listUsers()) {
+            if (getDevice().isUserSecondary(userId)) {
+                return userId;
+            }
+        }
+        return createUser();
+    }
+
     protected int createUser() throws Exception{
         return getDevice().createUser("TestUser_" + System.currentTimeMillis());
     }
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
index 72c6a44..9549eb0 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
@@ -62,7 +62,7 @@
         if (!mIsMultiuserSupported) {
             return;
         }
-        final int secondUserID = createUser();
+        final int secondUserID = getOrCreateSecondaryUser();
 
         getDevice().startUser(secondUserID);
         getDevice().switchUser(secondUserID);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 34adcd1..c0663ad 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -44,6 +44,7 @@
 
     private static final String TAG = "Statsd.HostAtomTests";
 
+    private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
     private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     private static final String FEATURE_WIFI = "android.hardware.wifi";
     private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
@@ -111,6 +112,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         // Setup, set charging state to full.
         setChargingState(5);
         Thread.sleep(WAIT_TIME_SHORT);
@@ -163,6 +165,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         // Setup, unplug device.
         unplugDevice();
         Thread.sleep(WAIT_TIME_SHORT);
@@ -215,6 +218,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         // Setup, set battery level to full.
         setBatteryLevel(100);
         Thread.sleep(WAIT_TIME_SHORT);
@@ -301,6 +305,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         // Setup, turn off battery saver.
         turnBatterySaverOff();
         Thread.sleep(WAIT_TIME_SHORT);
@@ -338,6 +343,7 @@
             return;
         }
         if (!hasFeature(FEATURE_WATCH, false)) return;
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         if (!hasBattery()) return;
         StatsdConfig.Builder config = getPulledConfig();
         FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
@@ -366,6 +372,7 @@
             return;
         }
         if (!hasFeature(FEATURE_WATCH, false)) return;
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         if (!hasBattery()) return;
         StatsdConfig.Builder config = getPulledConfig();
         FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index c937e30..ff50691 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -61,6 +61,7 @@
     private static final String TAG = "Statsd.UidAtomTests";
 
     // These constants are those in PackageManager.
+    private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
     private static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
     private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
     private static final String FEATURE_WIFI = "android.hardware.wifi";
@@ -550,6 +551,9 @@
         if (statsdDisabled()) {
             return;
         }
+        // For automotive, all wakeup alarm becomes normal alarm. So this
+        // test does not work.
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
 
         StatsdConfig.Builder config = createConfigBuilder();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
index 8fe6965..5eb19c3 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
@@ -63,6 +63,7 @@
 public class ValidationTests extends DeviceAtomTestCase {
 
     private static final String TAG = "Statsd.ValidationTests";
+    private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
     private static final boolean ENABLE_LOAD_TEST = false;
 
     @Override
@@ -81,6 +82,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         resetBatteryStats();
         unplugDevice();
         // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
@@ -146,6 +148,7 @@
         if (statsdDisabled()) {
             return;
         }
+        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
         turnScreenOn(); // To ensure that the ScreenOff later gets logged.
         // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
         // on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index defc04a..84a22d7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -68,7 +68,7 @@
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final int CONFIGURE_TIMEOUT = 5000; //ms
-    private static final int CAPTURE_TIMEOUT = 1000; //ms
+    private static final int CAPTURE_TIMEOUT = 1500; //ms
 
     // For testTriggerInteractions
     private static final int PREVIEW_WARMUP_FRAMES = 60;
diff --git a/tests/signature/api-check/system-annotation/AndroidTest.xml b/tests/signature/api-check/system-annotation/AndroidTest.xml
index 5d2f13f..2b01af4 100644
--- a/tests/signature/api-check/system-annotation/AndroidTest.xml
+++ b/tests/signature/api-check/system-annotation/AndroidTest.xml
@@ -44,4 +44,8 @@
         <option name="instrumentation-arg" key="annotation-for-exact-match" value="android.annotation.SystemApi" />
         <option name="runtime-hint" value="30s" />
     </test>
+
+    <!-- Controller that will skip the module if a native bridge situation is detected -->
+    <!-- For example: module wants to run arm32 and device is x86 -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
 </configuration>
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index ee35eed..0a198f2 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -108,10 +108,8 @@
      * Checks whether the cellular stack should be running on this device.
      */
     private boolean hasCellular() {
-        ConnectivityManager mgr =
-                (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
-        return mgr.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) &&
-               mTelephonyManager.isVoiceCapable();
+        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
+                mTelephonyManager.getPhoneCount() > 0;
     }
 
     private boolean isSimCardPresent() {
diff --git a/tests/tests/graphics/res/raw/f16.png b/tests/tests/graphics/res/raw/f16.png
new file mode 100644
index 0000000..2c3aed2
--- /dev/null
+++ b/tests/tests/graphics/res/raw/f16.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index be5f331..ce8f26a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -1679,7 +1679,7 @@
         };
         Listener l = new Listener();
         SourceCreator f = mCreators[0];
-        for (int resId : new int[] { R.drawable.png_test, R.raw.basi6a16 }) {
+        for (int resId : new int[] { R.drawable.png_test, R.raw.f16 }) {
             Bitmap normal = null;
             try {
                 normal = ImageDecoder.decodeBitmap(f.apply(resId));
@@ -1705,7 +1705,7 @@
                         // We do not support 565 in HARDWARE, so no RAM savings
                         // are possible.
                         assertEquals(normalByteCount, byteCount);
-                    } else { // R.raw.basi6a16
+                    } else { // R.raw.f16
                         // This image defaults to F16. MEMORY_POLICY_LOW_RAM
                         // forces "test" to decode to 8888. But if the device
                         // does not support F16 in HARDWARE, "normal" is also
@@ -1759,8 +1759,8 @@
                                    // If this were stored in drawable/, it would
                                    // be converted from 16-bit to 8. FIXME: Is
                                    // behavior still desirable now that we have
-                                   // F16?
-                                   R.raw.basi6a16 };
+                                   // F16? b/119760146
+                                   R.raw.f16 };
         // An opaque image can be converted to 565, but postProcess will promote
         // to 8888 in case alpha is added. The third image defaults to F16, so
         // even with postProcess it will only be promoted to 8888.
diff --git a/tests/tests/net/jni/NativeMultinetworkJni.c b/tests/tests/net/jni/NativeMultinetworkJni.c
index 2fa5291..69238cf 100644
--- a/tests/tests/net/jni/NativeMultinetworkJni.c
+++ b/tests/tests/net/jni/NativeMultinetworkJni.c
@@ -179,13 +179,17 @@
     setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
 
     // For reference see:
-    //     https://tools.ietf.org/html/draft-tsvwg-quic-protocol-01#section-6.1
-    uint8_t quic_packet[] = {
-        0x0c,                    // public flags: 64bit conn ID, 8bit sequence number
+    //     https://tools.ietf.org/html/draft-tsvwg-quic-protocol#section-6.1
+    uint8_t quic_packet[1200] = {
+        0x0d,                    // public flags:
+                                 //   - version present (0x01),
+                                 //   - 64bit connection ID (0x0c),
+                                 //   - 1 byte packet number (0x00)
         0, 0, 0, 0, 0, 0, 0, 0,  // 64bit connection ID
-        0x01,                    // sequence number
+        0xaa, 0xda, 0xca, 0xaa,  // reserved-space version number
+        1,                       // 1 byte packet number
         0x00,                    // private flags
-        0x07,                    // type: regular frame type "PING"
+        0x07,                    // PING frame (cuz why not)
     };
 
     arc4random_buf(quic_packet + 1, 8);  // random connection ID
@@ -213,7 +217,7 @@
                   i + 1, MAX_RETRIES, rcvd, errnum);
         }
     }
-    if (rcvd < sent) {
+    if (rcvd < 9) {
         ALOGD("QUIC UDP %s: sent=%zd but rcvd=%zd, errno=%d", kPort, sent, rcvd, errnum);
         if (rcvd <= 0) {
             ALOGD("Does this network block UDP port %s?", kPort);
@@ -229,8 +233,7 @@
         return -EPROTO;
     }
 
-    // TODO: log, and compare to the IP address encoded in the
-    // response, since this should be a public reset packet.
+    // TODO: Replace this quick 'n' dirty test with proper QUIC-capable code.
 
     close(fd);
     return 0;
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
index d7f0853..cdd2588 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -27,6 +27,7 @@
 import android.os.ProxyFileDescriptorCallback;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
 import android.os.storage.OnObbStateChangeListener;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -69,15 +70,22 @@
     private static final String TEST1_NEW_CONTENTS = "1\n";
 
     private StorageManager mStorageManager;
+    private UserManager mUserManager;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
     }
 
     public void testMountAndUnmountObbNormal() throws IOException {
+        // Mount obb only works for system user. Skip for secondary users.
+        if (!mUserManager.isSystemUser()) {
+            return;
+        }
+
         for (File target : getTargetFiles()) {
             target = new File(target, "test1_new.obb");
             Log.d(TAG, "Testing path " + target);
@@ -104,6 +112,11 @@
     }
 
     public void testAttemptMountNonObb() {
+        // Mount obb only works for system user. Skip for secondary users.
+        if (!mUserManager.isSystemUser()) {
+            return;
+        }
+
         for (File target : getTargetFiles()) {
             target = new File(target, "test1_nosig.obb");
             Log.d(TAG, "Testing path " + target);
@@ -141,6 +154,11 @@
     }
 
     public void testMountAndUnmountTwoObbs() throws IOException {
+        // Mount obb only works for system user. Skip for secondary users.
+        if (!mUserManager.isSystemUser()) {
+            return;
+        }
+
         for (File target : getTargetFiles()) {
             Log.d(TAG, "Testing target " + target);
             final File test1 = new File(target, "test1.obb");
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
index 9ed199e..9e65d0b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
@@ -267,7 +267,8 @@
 
     private boolean supportsHardware() {
         final PackageManager pm = getInstrumentation().getContext().getPackageManager();
-        return !pm.hasSystemFeature("android.hardware.type.television")
+        return !pm.hasSystemFeature("android.hardware.type.automotive")
+                && !pm.hasSystemFeature("android.hardware.type.television")
                 && !pm.hasSystemFeature("android.hardware.type.watch");
     }
 
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
index 7628c82..79b6bd1 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerThrottlingTest.java
@@ -18,7 +18,7 @@
 
 import static android.content.pm.cts.shortcutmanager.common.Constants.INLINE_REPLY_REMOTE_INPUT_CAPTION;
 
-import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetThrottling;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resetAllThrottling;
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.runCommandForNoOutput;
 
 import android.content.ComponentName;
@@ -64,7 +64,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        resetThrottling(getInstrumentation());
+        resetAllThrottling(getInstrumentation());
 
         UiDevice.getInstance(getInstrumentation()).pressHome();
 
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index 267b705..6112b73 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -2824,6 +2824,13 @@
         Fields.verifyTimeEquals(expected, t);
     }
 
+    @Test
+    public void test_bug118835133() {
+        Time t = new Time("Asia/Singapore");
+        Fields.set(t, 2018, 9, 30, 12, 48, 32, 0 /* isDst */, 0, 0, 0);
+        // With http://b/118835133 toMillis() returns -1.
+        assertEquals(1540874912000L, t.toMillis(true /* ignoreDst */));
+    }
     private static void verifyNormalizeResult(boolean normalizeArgument, Time toNormalize,
             Time expectedTime, long expectedTimeMillis) {
         long actualTimeMillis = toNormalize.normalize(normalizeArgument /* ignore isDst */);
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index f69e961..acd6849 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -119,7 +119,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -2946,6 +2948,92 @@
         assertFalse(mockView1.hasCalledOnTouchEvent());
     }
 
+    /**
+     * Ensure two MotionEvents are equal, for the purposes of this test only.
+     * Only compare actions, source, and times.
+     * Do not compare coordinates, because the injected event has coordinates relative to
+     * the screen, while the event received by view will be adjusted relative to the parent.
+     *
+     * Due to event batching, if two or more input events are injected / occur between two
+     * consecutive vsync's, they might end up getting combined into a single MotionEvent.
+     * It is caller's responsibility to ensure that the events were injected with a gap that's
+     * larger than time between two vsyncs, in order for this function to behave predictably.
+     *
+     * Recycle both MotionEvents.
+     */
+    private static void compareAndRecycleMotionEvents(MotionEvent event1, MotionEvent event2) {
+        if (event1 == null && event2 == null) {
+            return;
+        }
+
+        if (event1 == null) {
+            event2.recycle();
+            fail("Expected non-null event in first position");
+        }
+        if (event2 == null) {
+            event1.recycle();
+            fail("Expected non-null event in second position");
+        }
+
+        assertEquals(event1.getAction(), event2.getAction());
+        assertEquals(event1.getPointerCount(), event2.getPointerCount());
+        assertEquals(event1.getSource(), event2.getSource());
+        assertEquals(event1.getDownTime(), event2.getDownTime());
+        // If resampling occurs, the "real" (injected) events will become historical data,
+        // and resampled events will be inserted into MotionEvent and returned by the standard api.
+        // Since the injected event should contain no history, but the event received by
+        // the view might, we could distinguish them. But for simplicity, only require that
+        // the events are close in time if historical data is present.
+        if (event1.getHistorySize() == 0 && event2.getHistorySize() == 0) {
+            assertEquals(event1.getEventTime(), event2.getEventTime());
+        } else {
+            assertEquals(event1.getEventTime(), event2.getEventTime(), 20 /*delta*/);
+        }
+
+        event1.recycle();
+        event2.recycle();
+    }
+
+    @Test
+    public void testOnTouchListener() {
+        BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
+        class TestTouchListener implements View.OnTouchListener {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                events.add(MotionEvent.obtain(event));
+                return true;
+            }
+        }
+
+        // Inject some touch events
+        TestTouchListener listener = new TestTouchListener();
+        View view = mActivity.findViewById(R.id.mock_view);
+        view.setOnTouchListener(listener);
+
+        int[] xy = new int[2];
+        view.getLocationOnScreen(xy);
+
+        final int viewWidth = view.getWidth();
+        final int viewHeight = view.getHeight();
+        final float x = xy[0] + viewWidth / 2.0f;
+        final float y = xy[1] + viewHeight / 2.0f;
+
+        final long downTime = SystemClock.uptimeMillis();
+        MotionEvent downEvent =
+                MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
+        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        mInstrumentation.getUiAutomation().injectInputEvent(downEvent, true);
+        final long eventTime = SystemClock.uptimeMillis();
+        MotionEvent upEvent =
+                MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        mInstrumentation.getUiAutomation().injectInputEvent(upEvent, true);
+
+        compareAndRecycleMotionEvents(downEvent, events.poll());
+        compareAndRecycleMotionEvents(upEvent, events.poll());
+        assertTrue(events.isEmpty());
+    }
+
     @Test
     public void testInvalidate1() throws Throwable {
         final MockView view = (MockView) mActivity.findViewById(R.id.mock_view);