[automerger skipped] CTS: Fix Errorprone flags am: 9f6a8267d3
am: 519d8b6efe  -s ours

Change-Id: I62083395e7120a5be4196472e91b51744ecfcfac
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index a21108e..ae12e10 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -63,6 +63,4 @@
         echo ">> Unit test for $M failed" >&2
 done
 
-alias gpylint='gpylint --disable=F0401 --disable=C6304 --rcfile=$CAMERA_ITS_TOP"/build/scripts/gpylint_rcfile"'
-# F0401 ignores import errors since gpylint does not have the python paths
-# C6304 ignore Copyright line errors.
+alias gpylint='gpylint --rcfile=$CAMERA_ITS_TOP"/build/scripts/gpylint_rcfile"'
diff --git a/apps/CameraITS/build/scripts/gpylint_rcfile b/apps/CameraITS/build/scripts/gpylint_rcfile
index 37f43f7..f92c613 100644
--- a/apps/CameraITS/build/scripts/gpylint_rcfile
+++ b/apps/CameraITS/build/scripts/gpylint_rcfile
@@ -13,7 +13,10 @@
 # --enable=similarities". If you want to run only the classes checker, but have
 # no Warning level messages displayed, use"--disable=all --enable=classes
 # --disable=W"
-disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression
+disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression, F0401, C6304, C0111
+# F0401 ignores import errors since gpylint does not have the python paths
+# C6304 ignore Copyright line errors.
+# C0111 ignore Docstring at top of file.
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option
diff --git a/apps/CameraITS/tests/scene1/test_black_white.py b/apps/CameraITS/tests/scene1/test_black_white.py
index 18bc001..8de541c 100644
--- a/apps/CameraITS/tests/scene1/test_black_white.py
+++ b/apps/CameraITS/tests/scene1/test_black_white.py
@@ -12,19 +12,20 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import its.image
+import os.path
+
 import its.caps
 import its.device
+import its.image
 import its.objects
-from matplotlib import pylab
-import os.path
 import matplotlib
-import matplotlib.pyplot
+from matplotlib import pylab
+
+NAME = os.path.basename(__file__).split(".")[0]
+
 
 def main():
-    """Test that the device will produce full black+white images.
-    """
-    NAME = os.path.basename(__file__).split(".")[0]
+    """Test that the device will produce full black+white images."""
 
     r_means = []
     g_means = []
@@ -40,16 +41,14 @@
         if debug:
             fmt = largest_yuv
         else:
-            match_ar = (largest_yuv['width'], largest_yuv['height'])
+            match_ar = (largest_yuv["width"], largest_yuv["height"])
             fmt = its.objects.get_smallest_yuv_format(props, match_ar=match_ar)
 
-        expt_range = props['android.sensor.info.exposureTimeRange']
-        sens_range = props['android.sensor.info.sensitivityRange']
+        expt_range = props["android.sensor.info.exposureTimeRange"]
+        sens_range = props["android.sensor.info.sensitivityRange"]
 
         # Take a shot with very low ISO and exposure time. Expect it to
         # be black.
-        print "Black shot: sens = %d, exp time = %.4fms" % (
-                sens_range[0], expt_range[0]/1000000.0)
         req = its.objects.manual_capture_request(sens_range[0], expt_range[0])
         cap = cam.do_capture(req, fmt)
         img = its.image.convert_capture_to_rgb_image(cap)
@@ -60,11 +59,15 @@
         g_means.append(black_means[1])
         b_means.append(black_means[2])
         print "Dark pixel means:", black_means
+        r_exp = cap["metadata"]["android.sensor.exposureTime"]
+        r_iso = cap["metadata"]["android.sensor.sensitivity"]
+        print "Black shot write values: sens = %d, exp time = %.4fms" % (
+                sens_range[0], expt_range[0]/1000000.0)
+        print "Black shot read values: sens = %d, exp time = %.4fms\n" % (
+                r_iso, r_exp/1000000.0)
 
         # Take a shot with very high ISO and exposure time. Expect it to
         # be white.
-        print "White shot: sens = %d, exp time = %.2fms" % (
-                sens_range[1], expt_range[1]/1000000.0)
         req = its.objects.manual_capture_request(sens_range[1], expt_range[1])
         cap = cam.do_capture(req, fmt)
         img = its.image.convert_capture_to_rgb_image(cap)
@@ -75,19 +78,28 @@
         g_means.append(white_means[1])
         b_means.append(white_means[2])
         print "Bright pixel means:", white_means
+        r_exp = cap["metadata"]["android.sensor.exposureTime"]
+        r_iso = cap["metadata"]["android.sensor.sensitivity"]
+        print "White shot write values: sens = %d, exp time = %.2fms" % (
+                sens_range[1], expt_range[1]/1000000.0)
+        print "White shot read values: sens = %d, exp time = %.2fms\n" % (
+                r_iso, r_exp/1000000.0)
 
         # Draw a plot.
-        pylab.plot([0,1], r_means, 'r')
-        pylab.plot([0,1], g_means, 'g')
-        pylab.plot([0,1], b_means, 'b')
-        pylab.ylim([0,1])
+        pylab.title("test_black_white")
+        pylab.plot([0, 1], r_means, "-ro")
+        pylab.plot([0, 1], g_means, "-go")
+        pylab.plot([0, 1], b_means, "-bo")
+        pylab.xlabel("Capture Number")
+        pylab.ylabel("Output Values (Normalized)")
+        pylab.ylim([0, 1])
         matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
 
-        for val in black_means:
-            assert(val < 0.025)
-        for val in white_means:
-            assert(val > 0.975)
+        for black_mean in black_means:
+            assert black_mean < 0.025
+        for white_mean in white_means:
+            assert white_mean > 0.975
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
 
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 245fc54..f159901 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -53,7 +53,6 @@
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
-LOCAL_JAVA_LIBRARIES += bouncycastle
 LOCAL_JAVA_LIBRARIES += voip-common
 
 LOCAL_PACKAGE_NAME := CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index cae2b55..6f75fc5 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -3052,6 +3052,19 @@
                 android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
+        <activity android:name=".audio.ProAudioActivity"
+                  android:label="@string/pro_audio_latency_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_audio" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
+        </activity>
+
+        <!-- ProAudio test invokes the "Loopback" App -->
+        <activity android:name="org.drrickorang.loopback"/>
+
         <activity android:name=".audio.AudioLoopbackActivity"
                   android:label="@string/audio_loopback_test">
             <intent-filter>
diff --git a/apps/CtsVerifier/proguard.flags b/apps/CtsVerifier/proguard.flags
index e4249c4..adecb7a 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -35,7 +35,6 @@
 
 # Jack seems less rigorous than proguard when it comes to warning about
 # transitive dependencies.
--dontwarn com.android.org.bouncycastle.**
 -dontwarn com.android.okhttp.**
 -dontwarn org.opencv.**
 -dontwarn android.support.test.internal.runner.hidden.ExposedInstrumentationApi
diff --git a/apps/CtsVerifier/res/layout/pro_audio.xml b/apps/CtsVerifier/res/layout/pro_audio.xml
new file mode 100644
index 0000000..71edb71
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pro_audio.xml
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/scrollView"
+    style="@style/RootLayoutPadding">
+
+<LinearLayout android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioHasLLAlbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHasLLALbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasMIDILbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHasMIDILbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasUSBHostLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioMidiHasUSBHostLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioMidiHasUSBPeripheralLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioMidiHasUSBPeripheralLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <CheckBox android:id="@+id/proAudioHasHDMICheckBox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/proAudioHasHDMICheckBox"
+        android:onClick="onCheckboxClicked"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioHDMISupportLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioHDMISupportLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <TextView
+        android:text="@string/proAudioInputLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp"
+        android:id="@+id/proAudioInputLbl"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:text="@string/proAudioOutputLbl"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="10dp"
+        android:paddingRight="10dp"
+        android:id="@+id/proAudioOutputLbl"
+        android:textSize="18sp"/>
+
+    <Button
+        android:text="@string/audio_proaudio_roundtrip"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/proAudio_runRoundtripBtn"/>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioRoundTripLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioRoundTripLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:text="@string/proAudioConfidenceLbl"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18sp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp"
+            android:id="@+id/proAudioConfidenceLbl"
+            android:textSize="18sp"/>
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index a19ad9c..c666226a 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2798,9 +2798,9 @@
     <string name="device_owner_disable_statusbar_test">Disable status bar</string>
     <string name="device_owner_disable_statusbar_test_info">
             Please press the below button to disable the status bar and verify that quick settings, notifications
-            and the assist gesture are no longer available.\n
+            and the assistant gesture are no longer available.\n
             Next, press the button to reenable the status bar and verify that quick settings, notification
-            and the assist gesture are available again.\n
+            and the assistant gesture are available again.\n
             Please mark the test accordingly.
     </string>
     <string name="device_owner_disable_statusbar_button">Disable status bar</string>
@@ -2812,7 +2812,7 @@
             switch off the screen. Then press the power button to switch the screen back on and verify that
             no keyguard was shown.\n
             Next, press the button to reenable the keyguard and repeat the above steps, this time verifying that
-            a keyguard was shown again.\n
+            a keyguard was shown.\n
             Please mark the test accordingly.
     </string>
     <string name="device_owner_disable_keyguard_button">Disable keyguard</string>
@@ -2842,7 +2842,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.
+            7) The assistant gesture isn\'t available.
     </string>
     <string name="device_owner_lock_task_ui_system_info_test">Enable system info</string>
     <string name="device_owner_lock_task_ui_system_info_test_info">
@@ -2858,7 +2858,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.\n\n
+            7) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_notifications_test">Enable notifications</string>
@@ -2875,7 +2875,7 @@
             button, etc., isn\'t shown.\n
             5) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_home_test">Enable Home button</string>
@@ -2893,7 +2893,7 @@
             button, etc., isn\'t shown.\n
             6) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            7) The assist gesture isn\'t available.\n\n
+            7) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_recents_test">Enable Overview button</string>
@@ -2910,7 +2910,7 @@
             button, etc., isn\'t shown.\n
             4) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            5) The assist gesture isn\'t available.\n\n
+            5) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_global_actions_test">Enable global actions</string>
@@ -2928,7 +2928,7 @@
             4) The Overview button is hidden and the Overview gesture (swipe-up) does not work.\n
             5) Press the power button to turn off the screen, and press it again to turn the screen
             back on. Lock screen shouldn\'t be shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_keyguard_test">Enable keyguard</string>
@@ -2945,7 +2945,7 @@
             4) The Overview button is hidden and the Overview gesture (swipe-up) does not work.\n
             5) Long-press the power button. The power button menu, which usually shows the power-off
             button, etc., isn\'t shown.\n
-            6) The assist gesture isn\'t available.\n\n
+            6) The assistant gesture isn\'t available.\n\n
             Mark the test as \'pass\' only if ALL of the above requirements are met.
     </string>
     <string name="device_owner_lock_task_ui_stop_lock_task_test">Stop LockTask mode</string>
@@ -3979,7 +3979,6 @@
     <string name="connectedPeripheral">Connected Peripheral</string>
     <string name="audio_uap_attribs_test">USB Audio Peripheral Attributes Test</string>
     <string name="uapPeripheralProfileStatus">Peripheral Profile Status</string>
-
     <string name="audio_uap_play_test">USB Audio Peripheral Play Test</string>
     <string name="uapPlayTestInstructions">Connect the USB Audio Interface Peripheral and press the
         PLAY button below. Verify that a tone is correctly played.</string>
@@ -4004,6 +4003,29 @@
     <string name="uapButtonsRecognized">Recognized</string>
     <string name="uapButtonsNotRecognized">Not Recognized</string>
 
+    <!--  Pro Audio Tests -->
+    <string name="pro_audio_latency_test">Pro Audio Test</string>
+
+    <string name="proAudioHasLLAlbl">Has Low-Latency Audio</string>
+    <string name="proAudioInputLbl">Audio Input:</string>
+    <string name="proAudioOutputLbl">Audio Output:</string>
+    <string name="proAudioRoundTripLbl">Round Trip Latency:</string>
+    <string name="proAudioConfidenceLbl">Confidence:</string>
+
+    <string name="proAudioMidiHasMIDILbl">Has MIDI Support</string>
+    <string name="proAudioMIDIInputLbl">MIDI Input:</string>
+    <string name="proAudioMIDIOutputLbl">MIDI Output:</string>
+    <string name="proAudioMidiHasUSBHostLbl">USB Host Mode:</string>
+    <string name="proAudioMidiHasUSBPeripheralLbl">USB Peripheral Mode:</string>
+    <string name="proAudioHDMISupportLbl">HDMI Support:</string>
+    <string name="proAudioHasHDMICheckBox">Has HDMI Support</string>
+
+    <string name="audio_proaudio_loopbackbtn">Start Loopback</string>
+    <string name="audio_proaudio_loopbackInfoBtn">Loopback Instructions</string>
+    <string name="audio_proaudio_roundtrip">Round-Trip Test</string>
+    <string name="audio_proaudio_NA">N/A</string>
+    <string name="audio_proaudio_pending">pending...</string>
+
     <!-- Audio general text -->
     <string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
     <string name="audio_general_headset_no">No</string>
@@ -4396,6 +4418,23 @@
         \"transport\" button and verify that it is recognized by the test.
        </string>
 
+    <!-- Pro Audio Test -->
+    <string name="proaudio_test">Pro Audio Test</string>
+    <string name="proaudio_info">
+       This test requires that you have connected a supported USB Audio Peripheral device
+       (not a headset) and that peripheral\'s audio outputs are connected to the peripherals\'s
+       audio inputs. Alternatively, for devices with an analog audio jack or USB-c Digital
+       to Analog dongle, a Loopback Plug can be used. Also, any if there is an input level
+       control on the peripheral, it must be set to a non-zero value. When the test has
+       verified support for a valid audio peripheral, press the \"Round-Trip Test\" button
+       to complete the test. Note that it may be necessary to run the latency test more than
+       once to get a sufficient confidence value.
+    </string>
+    <string name="proaudio_hdmi_infotitle">HDMI Support</string>
+    <string name="proaudio_hdmi_message">Please connect an HDMI peripheral to validate
+        HDMI output attributes.
+    </string>
+
     <!-- Telecom tests -->
     <string name="telecom_enable_phone_account_test"> Telecom Enable Phone Account Test</string>
     <string name="telecom_enable_phone_account_info">
@@ -4479,4 +4518,5 @@
         Click the button below to confirm that the incoming call was answered.
     </string>
     <string name="telecom_incoming_self_mgd_confirm_answer_button">Confirm Answer</string>
+
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
new file mode 100644
index 0000000..30bc5fc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
+
+import java.util.List;
+
+public class ProAudioActivity
+        extends PassFailButtons.Activity
+        implements View.OnClickListener {
+    private static final String TAG = ProAudioActivity.class.getName();
+    private static final boolean DEBUG = false;
+
+    // Flags
+    private boolean mClaimsLowLatencyAudio;    // CDD ProAudio section C-1-1
+    private boolean mClaimsMIDI;               // CDD ProAudio section C-1-4
+    private boolean mClaimsUSBHostMode;        // CDD ProAudio section C-1-3
+    private boolean mClaimsUSBPeripheralMode;  // CDD ProAudio section C-1-3
+    private boolean mClaimsHDMI;               // CDD ProAudio section C-1-3
+
+    // Values
+    private static final double LATENCY_MS_LIMIT = 20.0; // CDD ProAudio section C-1-2
+    private double mRoundTripLatency;
+    private static final double CONFIDENCE_LIMIT = 0.75; // TBD
+    private double mRoundTripConfidence;
+
+    // Peripheral(s)
+    AudioManager mAudioManager;
+    private boolean mIsPeripheralAttached;  // CDD ProAudio section C-1-3
+    private AudioDeviceInfo mOutputDevInfo;
+    private AudioDeviceInfo mInputDevInfo;
+
+    private AudioDeviceInfo mHDMIDeviceInfo;
+
+    // Widgets
+    TextView mInputDeviceTxt;
+    TextView mOutputDeviceTxt;
+    TextView mRoundTripLatencyTxt;
+    TextView mRoundTripConfidenceTxt;
+    TextView mHDMISupportLbl;
+
+    CheckBox mClaimsHDMICheckBox;
+
+    public ProAudioActivity() {
+        super();
+    }
+
+    private boolean claimsLowLatencyAudio() {
+        // CDD Section C-1-1: android.hardware.audio.low_latency
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
+    }
+
+    private boolean claimsMIDI() {
+        // CDD Section C-1-4: android.software.midi
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private boolean claimsUSBHostMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST);
+    }
+
+    private boolean claimsUSBPeripheralMode() {
+        return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
+    }
+
+    private void showConnectedAudioPeripheral() {
+        mInputDeviceTxt.setText(
+                mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+                        : "");
+        mOutputDeviceTxt.setText(
+                mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+                        : "");
+    }
+
+    // HDMI Stuff
+    private boolean isHDMIValid() {
+        if (mHDMIDeviceInfo == null) {
+            return false;
+        }
+
+        // MUST support output in stereo and eight channels...
+        boolean has2Chans = false;
+        boolean has8Chans = false;
+        int[] channelCounts = mHDMIDeviceInfo.getChannelCounts();
+        for (int count : channelCounts) {
+            if (count == 2) {
+                has2Chans = true;
+            } else if (count == 8) {
+                has8Chans = true;
+            }
+        }
+        if (!has2Chans || !has8Chans) {
+            return false;
+        }
+
+        // at 20-bit or 24-bit depth
+        boolean hasFloatEncoding = false;
+        int[] encodings = mHDMIDeviceInfo.getEncodings();
+        for (int encoding : encodings) {
+            if (encoding == AudioFormat.ENCODING_PCM_FLOAT) {
+                hasFloatEncoding = true;
+                break;
+            }
+        }
+        if (!hasFloatEncoding) {
+            return false;
+        }
+
+         // and 192 kHz
+        boolean has192K = false;
+        int[] sampleRates = mHDMIDeviceInfo.getSampleRates();
+        for (int rate : sampleRates) {
+            if (rate >= 192000) {
+                has192K = true;
+            }
+        }
+        if (!has192K) {
+            return false;
+        }
+
+        // without bit-depth loss or resampling (hmmmmm....).
+
+        return true;
+    }
+
+    private void calculatePass() {
+        boolean hasPassed =
+                mClaimsLowLatencyAudio && mClaimsMIDI &&
+                mClaimsUSBHostMode && mClaimsUSBPeripheralMode &&
+                (!mClaimsHDMI || isHDMIValid()) &&
+                mOutputDevInfo != null && mInputDevInfo != null &&
+                mRoundTripLatency != 0.0 && mRoundTripLatency <= LATENCY_MS_LIMIT &&
+                mRoundTripConfidence >= CONFIDENCE_LIMIT;
+        getPassButton().setEnabled(hasPassed);
+    }
+
+    //
+    // Loopback App Stuff
+    //
+    private final static String LOOPBACK_PACKAGE_NAME = "org.drrickorang.loopback";
+
+    // Test Intents
+    // From Loopback App LoobackActivity.java
+    private static final String INTENT_TEST_TYPE = "TestType";
+
+    // from Loopback App Constant.java
+    public static final int LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY = 222;
+
+    public boolean isLoopbackAppInstalled() {
+        try {
+            getPackageManager().getPackageInfo(
+                    LOOPBACK_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            // This indicates that the specified app (Loopback in this case) is NOT installed
+            // fall through...
+        }
+        return false;
+    }
+
+    // arbitrary request code
+    private static final int LATENCY_RESULTS_REQUEST_CODE = 1;
+    private static final String KEY_CTSINVOCATION = "CTS-Test";
+    private static final String KEY_ROUND_TRIP_TIME = "RoundTripTime";
+    private static final String KEY_ROUND_TRIP_CONFIDENCE = "Confidence";
+
+    // We may need to iterate and average round-trip measurements
+    // So add this plumbing though NOT USED.
+    private static final String KEY_NUMITERATIONS = "NumIterations";
+    private static final int NUM_ROUNDTRIPITERATIONS = 3;
+
+    private void runRoundTripTest() {
+        if (!isLoopbackAppInstalled()) {
+            Toast.makeText(this, "Loopback App not installed", Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        if (!mIsPeripheralAttached) {
+            Toast.makeText(this, "Please connect a USB audio peripheral with loopback cables" +
+                    " before running the latency test.",
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        mRoundTripLatency = 0.0;
+        mRoundTripConfidence = 0.0;
+        Intent intent = new Intent(Intent.CATEGORY_LAUNCHER);
+        intent.setComponent(
+            new ComponentName(LOOPBACK_PACKAGE_NAME,LOOPBACK_PACKAGE_NAME + ".LoopbackActivity"));
+
+        intent.putExtra(KEY_CTSINVOCATION, "CTS-Verifier Invocation");
+        intent.putExtra(INTENT_TEST_TYPE, LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY);
+        intent.putExtra(KEY_NUMITERATIONS, NUM_ROUNDTRIPITERATIONS);
+
+        startActivityForResult(intent, LATENCY_RESULTS_REQUEST_CODE);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // Check which request we're responding to
+        if (resultCode == RESULT_OK) {
+            Toast.makeText(this, "Round Trip Test Complete.", Toast.LENGTH_SHORT).show();
+            if (requestCode == LATENCY_RESULTS_REQUEST_CODE) {
+                Bundle extras = data != null ? data.getExtras() : null;
+                if (extras != null) {
+                    mRoundTripLatency =  extras.getDouble(KEY_ROUND_TRIP_TIME);
+                    mRoundTripLatencyTxt.setText(String.format("%.2f ms", mRoundTripLatency));
+                    mRoundTripConfidence = extras.getDouble(KEY_ROUND_TRIP_CONFIDENCE);
+                    mRoundTripConfidenceTxt.setText(String.format("%.2f", mRoundTripConfidence));
+                }
+            }
+            calculatePass();
+        } else {
+            Toast.makeText(this, "Round Trip Test Canceled.", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pro_audio);
+
+        mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+        mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.proaudio_test, R.string.proaudio_info, -1);
+
+        mClaimsLowLatencyAudio = claimsLowLatencyAudio();
+        ((TextView)findViewById(R.id.proAudioHasLLALbl)).setText("" + mClaimsLowLatencyAudio);
+
+        mClaimsMIDI = claimsMIDI();
+        ((TextView)findViewById(R.id.proAudioHasMIDILbl)).setText("" + mClaimsMIDI);
+
+        mClaimsUSBHostMode = claimsUSBHostMode();
+        ((TextView)findViewById(R.id.proAudioMidiHasUSBHostLbl)).setText("" + mClaimsUSBHostMode);
+
+        mClaimsUSBPeripheralMode = claimsUSBPeripheralMode();
+        ((TextView)findViewById(
+                R.id.proAudioMidiHasUSBPeripheralLbl)).setText("" + mClaimsUSBPeripheralMode);
+
+        // Connected Device
+        mInputDeviceTxt = ((TextView)findViewById(R.id.proAudioInputLbl));
+        mOutputDeviceTxt = ((TextView)findViewById(R.id.proAudioOutputLbl));
+
+        // Round-trip Latency
+        mRoundTripLatencyTxt = (TextView)findViewById(R.id.proAudioRoundTripLbl);
+        mRoundTripConfidenceTxt = (TextView)findViewById(R.id.proAudioConfidenceLbl);
+        ((Button)findViewById(R.id.proAudio_runRoundtripBtn)).setOnClickListener(this);
+
+        // HDMI
+        mHDMISupportLbl = (TextView)findViewById(R.id.proAudioHDMISupportLbl);
+        mClaimsHDMICheckBox = (CheckBox)findViewById(R.id.proAudioHasHDMICheckBox);
+        mClaimsHDMICheckBox.setOnClickListener(this);
+
+        calculatePass();
+    }
+
+    private void scanPeripheralList(AudioDeviceInfo[] devices) {
+        // CDD Section C-1-3: USB port, host-mode support
+
+        // Can't just use the first record because then we will only get
+        // Source OR sink, not both even on devices that are both.
+        mOutputDevInfo = null;
+        mInputDevInfo = null;
+
+        // Any valid peripherals
+        // Do we leave in the Headset test to support a USB-Dongle?
+        for (AudioDeviceInfo devInfo : devices) {
+            if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||     // USB Peripheral
+                devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET ||    // USB dongle+LBPlug
+                devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+                devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+                if (devInfo.isSink()) {
+                    mOutputDevInfo = devInfo;
+                }
+                if (devInfo.isSource()) {
+                    mInputDevInfo = devInfo;
+                }
+            } else if (devInfo.isSink() && devInfo.getType() == AudioDeviceInfo.TYPE_HDMI) {
+                mHDMIDeviceInfo = devInfo;
+            }
+        }
+
+        mIsPeripheralAttached = mOutputDevInfo != null || mInputDevInfo != null;
+        showConnectedAudioPeripheral();
+
+        if (mHDMIDeviceInfo != null) {
+            mClaimsHDMICheckBox.setChecked(true);
+            mHDMISupportLbl.setText(getResources().getString(
+                    isHDMIValid() ? R.string.pass_button_text : R.string.fail_button_text));
+        }
+        mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+
+        calculatePass();
+    }
+
+    private class ConnectListener extends AudioDeviceCallback {
+        /*package*/ ConnectListener() {}
+
+        //
+        // AudioDevicesManager.OnDeviceConnectionListener
+        //
+        @Override
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        switch (view.getId()) {
+        case R.id.proAudio_runRoundtripBtn:
+            runRoundTripTest();
+            break;
+
+        case R.id.proAudioHasHDMICheckBox:
+            if (mClaimsHDMICheckBox.isChecked()) {
+                AlertDialog.Builder builder =
+                        new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
+                builder.setTitle(getResources().getString(R.string.proaudio_hdmi_infotitle));
+                builder.setMessage(getResources().getString(R.string.proaudio_hdmi_message));
+                builder.setPositiveButton(android.R.string.yes,
+                    new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {}
+                 });
+                builder.setIcon(android.R.drawable.ic_dialog_alert);
+                builder.show();
+
+                mClaimsHDMI = true;
+                mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_pending));
+            } else {
+                mClaimsHDMI = false;
+                mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+            }
+            break;
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
index 531b40b..e25f758 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
@@ -57,11 +57,19 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        // skip current test if device doesn't support cellular
+        if (!EmergencyCallUtil.isPhoneDevice(this)) {
+          String skipInfo = getResources().getString(R.string.emergency_call_skip_info);
+          TestResult.setPassedResult(this, super.getClass().getName(), skipInfo);
+          Toast toast = Toast.makeText(getApplicationContext(), skipInfo, Toast.LENGTH_LONG);
+          toast.show();
+          this.finish();
+          return;
+        }
         // override the test info
         mTextView.setText(R.string.location_emergency_call_test_info);
         EmergencyCallUtil.setDefaultDialer(this, this.getPackageName());
         setPassFailButtonClickListeners();
-
     }
 
     @Override
@@ -74,15 +82,7 @@
 
     @Override
     public void onClick(View target) {
-        // skip current test if device doesn't support cellular
-        if (!EmergencyCallUtil.isPhoneDevice(this)) {
-            String skipInfo = getResources().getString(R.string.emergency_call_skip_info);
-            TestResult.setPassedResult(this, super.getClass().getName(), skipInfo);
-            Toast toast = Toast.makeText(getApplicationContext(), skipInfo, Toast.LENGTH_LONG);
-            toast.show();
-            this.finish();
-            return;
-        }
+
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         final FrameLayout frameView = new FrameLayout(this);
         builder.setView(frameView);
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
index c48d16f..0268e86 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
@@ -83,14 +83,20 @@
         } else if ((dir = makeResultDir()) == null) {
             failed("Cannot create directory for device info files");
         } else {
+            File jsonFile;
             try {
-                File jsonFile = new File(dir, getClass().getSimpleName() + ".deviceinfo.json");
+                jsonFile = new File(dir, getClass().getSimpleName() + ".deviceinfo.json");
                 jsonFile.createNewFile();
                 mResultFilePath = jsonFile.getAbsolutePath();
-                DeviceInfoStore store = new DeviceInfoStore(jsonFile);
-                store.open();
-                collectDeviceInfo(store);
-                store.close();
+                try (DeviceInfoStore store = new DeviceInfoStore(jsonFile)) {
+                    store.open();
+                    collectDeviceInfo(store);
+                } finally {
+                    if (jsonFile != null && jsonFile.exists() &&
+                            jsonFile.length() == 0) {
+                        jsonFile.delete();
+                    }
+                }
                 if (mResultCode == ResultCode.STARTED) {
                     mResultCode = ResultCode.COMPLETED;
                 }
diff --git a/common/device-side/util/Android.bp b/common/device-side/util/Android.bp
index 2000cb8..be5e520 100644
--- a/common/device-side/util/Android.bp
+++ b/common/device-side/util/Android.bp
@@ -17,7 +17,7 @@
     sdk_version: "test_current",
 
     srcs: [
-       "src/**/*.java",
+        "src/**/*.java",
         "src/**/*.aidl",
     ],
 
@@ -33,4 +33,6 @@
         "android.test.runner.stubs",
         "android.test.base.stubs",
     ],
-}
+
+    jarjar_rules: "protobuf-jarjar-rules.txt",
+}
\ No newline at end of file
diff --git a/common/device-side/util/protobuf-jarjar-rules.txt b/common/device-side/util/protobuf-jarjar-rules.txt
new file mode 100644
index 0000000..9914809
--- /dev/null
+++ b/common/device-side/util/protobuf-jarjar-rules.txt
@@ -0,0 +1,3 @@
+rule com.google.protobuf.** com.google.compatibility.device.util.com.google.protobuf.@1
+rule android.**.nano.** com.google.compatibility.device.util.android.@1.nano.@2
+rule **.android.**.nano.** com.google.compatibility.device.util.@1.android.@2.nano.@3
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
index 792db40..ba357a16 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
@@ -18,12 +18,15 @@
 import static com.android.compatibility.common.util.SettingsUtils.putGlobalSetting;
 import static com.android.compatibility.common.util.TestUtils.waitUntil;
 
+import android.content.pm.PackageManager;
 import android.os.BatteryManager;
 import android.os.PowerManager;
 import android.provider.Settings.Global;
 import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import org.junit.Assume;
+
 public class BatteryUtils {
     private static final String TAG = "CtsBatteryUtils";
 
@@ -103,4 +106,15 @@
         AmUtils.waitForBroadcastIdle();
         Log.d(TAG, "Screen turned " + (on ? "ON" : "OFF"));
     }
+
+    /** @return true if the device supports battery saver. */
+    public static boolean isBatterySaverSupported() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    /** "Assume" the current device supports battery saver. */
+    public static void assumeBatterySaverFeature() {
+        Assume.assumeTrue("Device doesn't support battery saver", isBatterySaverSupported());
+    }
 }
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
index 966ac1a..03f69fa 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
@@ -54,7 +54,7 @@
      * Closes the writer.
      */
     @Override
-    public void close() throws IOException {
+    public void close() throws Exception {
         mJsonWriter.endObject();
         mJsonWriter.flush();
         mJsonWriter.close();
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index 5e2a97b..5a0fb8b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -61,12 +61,18 @@
     }
 
     protected void installTestAppForUser(String apk, int userId) throws Exception {
+        installTestAppForUser(apk, false, userId);
+    }
+
+    protected void installTestAppForUser(String apk, boolean instant, int userId) throws Exception {
         if (userId < 0) {
             userId = mPrimaryUserId;
         }
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        Assert.assertNull(getDevice().installPackageForUser(
-                buildHelper.getTestFile(apk), true, false, userId, "-t"));
+        new InstallMultiple(instant)
+                .addApk(apk)
+                .allowTest()
+                .forUser(userId)
+                .run();
     }
 
     protected boolean isAppVisibleForUser(String packageName, int userId,
@@ -76,4 +82,14 @@
         String output = getDevice().executeShellCommand(command);
         return output.contains(packageName);
     }
+
+    protected class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+        public InstallMultiple() {
+            this(false);
+        }
+        public InstallMultiple(boolean instant) {
+            super(getDevice(), getBuild(), getAbi());
+            addArg(instant ? "--instant" : "");
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index cc87584..09b2123 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -78,6 +78,11 @@
         return (T) this;
     }
 
+    T allowTest() {
+        addArg("-t");
+        return (T) this;
+    }
+
     T locationAuto() {
         addArg("--install-location 0");
         return (T) this;
@@ -98,15 +103,24 @@
         return (T) this;
     }
 
+    T forUser(int userId) {
+        addArg("--user " + userId);
+        return (T) this;
+    }
+
     void run() throws DeviceNotAvailableException {
-        run(true);
+        run(true, null);
     }
 
     void runExpectingFailure() throws DeviceNotAvailableException {
-        run(false);
+        run(false, null);
     }
 
-    private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
+    void runExpectingFailure(String failure) throws DeviceNotAvailableException {
+        run(false, failure);
+    }
+
+    private void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
         final ITestDevice device = mDevice;
 
         // Create an install session
@@ -159,11 +173,15 @@
         cmd.append("pm install-commit");
         cmd.append(' ').append(sessionId);
 
-        result = device.executeShellCommand(cmd.toString());
-        if (expectingSuccess) {
-            TestCase.assertTrue(result, result.startsWith("Success"));
+        result = device.executeShellCommand(cmd.toString()).trim();
+        if (failure == null) {
+            if (expectingSuccess) {
+                TestCase.assertTrue(result, result.startsWith("Success"));
+            } else {
+                TestCase.assertFalse(result, result.startsWith("Success"));
+            }
         } else {
-            TestCase.assertFalse(result, result.startsWith("Success"));
+            TestCase.assertTrue(result, result.startsWith(failure));
         }
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
index f4f6d9e..89361ae 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
@@ -15,8 +15,9 @@
  */
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
 import org.junit.After;
@@ -25,7 +26,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class ClassloaderSplitsTest extends BaseHostJUnit4Test implements IBuildReceiver {
+public class ClassloaderSplitsTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.classloadersplitapp";
     private static final String TEST_CLASS = PKG + ".SplitAppTest";
 
@@ -57,36 +58,63 @@
     }
 
     @Test
-    public void testBaseClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).run();
+    @AppModeFull
+    public void testBaseClassLoader_full() throws Exception {
+        testBaseClassLoader(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testBaseClassLoader_instant() throws Exception {
+        testBaseClassLoader(true);
+    }
+    private void testBaseClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
     }
 
     @Test
-    public void testFeatureAClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
+    @AppModeFull
+    public void testFeatureAClassLoader_full() throws Exception {
+        testFeatureAClassLoader(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testFeatureAClassLoader_instant() throws Exception {
+        testFeatureAClassLoader(true);
+    }
+    private void testFeatureAClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
     }
 
     @Test
-    public void testFeatureBClassLoader() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @AppModeFull
+    public void testFeatureBClassLoader_full() throws Exception {
+        testFeatureBClassLoader(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testFeatureBClassLoader_instant() throws Exception {
+        testFeatureBClassLoader(true);
+    }
+    private void testFeatureBClassLoader(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
     }
 
     @Test
-    public void testReceiverClassLoaders() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testReceiverClassLoaders_full() throws Exception {
+        testReceiverClassLoaders(false);
+    }
+    private void testReceiverClassLoaders(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
         runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
     }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        public InstallMultiple() {
-            super(getDevice(), getBuild(), null);
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
index 4280b93..2ef6bac 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.ITestDevice;
@@ -34,6 +35,7 @@
  * Set of tests that verify that corrupt APKs are properly rejected by PackageManager and
  * do not cause the system to crash.
  */
+@AppModeFull(reason = "the corrupt APKs were provided as-is and we cannot modify them to comply with instant mode")
 public class CorruptApkTests extends DeviceTestCase implements IBuildReceiver {
     private final String B71360999_PKG = "com.android.appsecurity.b71360999";
     private final String B71361168_PKG = "com.android.appsecurity.b71361168";
@@ -133,4 +135,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 56d605e..f98406c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -33,6 +34,7 @@
 /**
  * Tests for ephemeral packages.
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class EphemeralTest extends DeviceTestCase
         implements IAbiReceiver, IBuildReceiver {
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
index 4609e8a..a22ee80 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -29,6 +30,7 @@
 /**
  * Tests for ephemeral packages.
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class InstantAppUserTest extends DeviceTestCase
         implements IAbiReceiver, IBuildReceiver {
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
index 51fa77b..e7ac153 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -25,6 +26,7 @@
 /**
  * Tests for the instant cookie APIs
  */
+@AppModeFull(reason = "Already handles instant installs when needed")
 public class InstantCookieHostTest extends DeviceTestCase implements IBuildReceiver {
     private static final String INSTANT_COOKIE_APP_APK = "CtsInstantCookieApp.apk";
     private static final String INSTANT_COOKIE_APP_PKG = "test.instant.cookie";
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
index 8795fe8..4110a8c9 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
@@ -15,11 +15,18 @@
  */
 package android.appsecurity.cts;
 
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 
-public class IsolatedSplitsTests extends DeviceTestCase implements IBuildReceiver {
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class IsolatedSplitsTests extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.isolatedsplitapp";
     private static final String TEST_CLASS = PKG + ".SplitAppTest";
 
@@ -38,68 +45,129 @@
     private static final String APK_FEATURE_C = "CtsIsolatedSplitAppFeatureC.apk";
     private static final String APK_FEATURE_C_pl = "CtsIsolatedSplitAppFeatureC_pl.apk";
 
-    private IBuildInfo mBuildInfo;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallBase() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).run();
+    @Test
+    @AppModeFull
+    public void testInstallBase_full() throws Exception {
+        testInstallBase(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallBase_instant() throws Exception {
+        testInstallBase(true);
+    }
+    private void testInstallBase(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
     }
 
-    public void testInstallBaseAndConfigSplit() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_BASE_pl).run();
+    @Test
+    @AppModeFull
+    public void testInstallBaseAndConfigSplit_full() throws Exception {
+        testInstallBaseAndConfigSplit(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallBaseAndConfigSplit_instant() throws Exception {
+        testInstallBaseAndConfigSplit(true);
+    }
+    private void testInstallBaseAndConfigSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_BASE_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
     }
 
-    public void testInstallMissingDependency() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_B).runExpectingFailure();
+    @Test
+    @AppModeFull
+    public void testInstallMissingDependency_full() throws Exception {
+        testInstallMissingDependency(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallMissingDependency_instant() throws Exception {
+        testInstallMissingDependency(true);
+    }
+    private void testInstallMissingDependency(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_B).runExpectingFailure();
     }
 
-    public void testInstallOneFeatureSplit() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallOneFeatureSplit_full() throws Exception {
+        testInstallOneFeatureSplit(false);
+    }
+    private void testInstallOneFeatureSplit(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAReceivers");
     }
 
-    public void testInstallOneFeatureSplitAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_BASE_pl)
+    @Test
+    @AppModeFull
+    public void testInstallOneFeatureSplitAndConfigSplits_full() throws Exception {
+        testInstallOneFeatureSplitAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallOneFeatureSplitAndConfigSplits_instant() throws Exception {
+        testInstallOneFeatureSplitAndConfigSplits(true);
+    }
+    private void testInstallOneFeatureSplitAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_BASE_pl)
                 .addApk(APK_FEATURE_A_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
     }
 
-    public void testInstallDependentFeatureSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallDependentFeatureSplits_full() throws Exception {
+        testInstallDependentFeatureSplits(false);
+    }
+    private void testInstallDependentFeatureSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBReceivers");
     }
 
-    public void testInstallDependentFeatureSplitsAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull
+    public void testInstallDependentFeatureSplitsAndConfigSplits_full() throws Exception {
+        testInstallDependentFeatureSplitsAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallDependentFeatureSplitsAndConfigSplits_instant() throws Exception {
+        testInstallDependentFeatureSplitsAndConfigSplits(true);
+    }
+    private void testInstallDependentFeatureSplitsAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl).addApk(APK_FEATURE_B_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAPolishLocale");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBPolishLocale");
     }
 
-    public void testInstallAllFeatureSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull(reason = "b/109878606; instant applications can't send broadcasts to manifest receivers")
+    public void testInstallAllFeatureSplits_full() throws Exception {
+        testInstallAllFeatureSplits(false);
+    }
+    private void testInstallAllFeatureSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_FEATURE_C).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureADefault");
@@ -108,8 +176,18 @@
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureAAndBAndCReceivers");
     }
 
-    public void testInstallAllFeatureSplitsAndConfigSplits() throws Exception {
-        new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
+    @Test
+    @AppModeFull
+    public void testInstallAllFeatureSplitsAndConfigSplits_full() throws Exception {
+        testInstallAllFeatureSplitsAndConfigSplits(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallAllFeatureSplitsAndConfigSplits_instant() throws Exception {
+        testInstallAllFeatureSplitsAndConfigSplits(true);
+    }
+    private void testInstallAllFeatureSplitsAndConfigSplits(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B)
                 .addApk(APK_FEATURE_C).addApk(APK_BASE_pl).addApk(APK_FEATURE_A_pl)
                 .addApk(APK_FEATURE_C_pl).run();
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadDefault");
@@ -117,15 +195,4 @@
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureBDefault");
         Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "shouldLoadFeatureCDefault");
     }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildInfo = buildInfo;
-    }
-
-    private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
-        public InstallMultiple() {
-            super(getDevice(), mBuildInfo, null);
-        }
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
index 15c3d3c..fecbcd1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MajorVersionTest.java
@@ -16,118 +16,125 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test that install of apps using major version codes is being handled properly.
  */
-public class MajorVersionTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MajorVersionTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.majorversion";
     private static final String APK_000000000000ffff = "CtsMajorVersion000000000000ffff.apk";
     private static final String APK_00000000ffffffff = "CtsMajorVersion00000000ffffffff.apk";
     private static final String APK_000000ff00000000 = "CtsMajorVersion000000ff00000000.apk";
     private static final String APK_000000ffffffffff = "CtsMajorVersion000000ffffffffff.apk";
 
-    private IAbi mAbi;
-    private CompatibilityBuildHelper mBuildHelper;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mBuildHelper);
-
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallMinorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+    @Test
+    @AppModeFull
+    public void testInstallMinorVersion_full() throws Exception {
+        testInstallMinorVersion(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallMinorVersion_instant() throws Exception {
+        testInstallMinorVersion(true);
+    }
+    private void testInstallMinorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000000000ffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallMajorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), false, false));
+    @Test
+    @AppModeFull
+    public void testInstallMajorVersion_full() throws Exception {
+        testInstallMajorVersion(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallMajorVersion_instant() throws Exception {
+        testInstallMajorVersion(true);
+    }
+    private void testInstallMajorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000ff00000000).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallUpdateAcrossMinorMajorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), false, false));
+    @Test
+    @AppModeFull
+    public void testInstallUpdateAcrossMinorMajorVersion_full() throws Exception {
+        testInstallUpdateAcrossMinorMajorVersion(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallUpdateAcrossMinorMajorVersion_instant() throws Exception {
+        testInstallUpdateAcrossMinorMajorVersion(true);
+    }
+    private void testInstallUpdateAcrossMinorMajorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000000000ffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_00000000ffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ff00000000).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ffffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ffffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    public void testInstallDowngradeAcrossMajorMinorVersion() throws Exception {
-        assertNull(getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ffffffffff), false, false));
+    @Test
+    @AppModeFull
+    public void testInstallDowngradeAcrossMajorMinorVersion_full() throws Exception {
+        testInstallDowngradeAcrossMajorMinorVersion(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testInstallDowngradeAcrossMajorMinorVersion_instant() throws Exception {
+        testInstallDowngradeAcrossMajorMinorVersion(true);
+    }
+    private void testInstallDowngradeAcrossMajorMinorVersion(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK_000000ffffffffff).run();
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_00000000ffffffff), true, false));
+        new InstallMultiple(instant).addApk(APK_00000000ffffffff)
+                .runExpectingFailure("Failure [INSTALL_FAILED_VERSION_DOWNGRADE]");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000ff00000000), true, false));
+        new InstallMultiple(instant).addApk(APK_000000ff00000000)
+                .runExpectingFailure("Failure [INSTALL_FAILED_VERSION_DOWNGRADE]");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        assertEquals("INSTALL_FAILED_VERSION_DOWNGRADE", getDevice().installPackage(
-                mBuildHelper.getTestFile(APK_000000000000ffff), true, false));
+        new InstallMultiple(instant).addApk(APK_000000000000ffff)
+                .runExpectingFailure("Failure [INSTALL_FAILED_VERSION_DOWNGRADE]");
         assertTrue(getDevice().getInstalledPackageNames().contains(PKG));
         runVersionDeviceTests("testCheckVersion");
-        getDevice().uninstallPackage(PKG);
     }
 
-    private void runVersionDeviceTests(String testMethodName)
-            throws DeviceNotAvailableException {
+    private void runVersionDeviceTests(String testMethodName) throws Exception {
         runDeviceTests(PKG, PKG + ".VersionTest", testMethodName);
     }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
-    }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
index cf903db..b8ef29e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
@@ -15,6 +15,7 @@
  */
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.testtype.DeviceTestCase;
@@ -42,6 +43,7 @@
         mBuildHelper = new CompatibilityBuildHelper(buildInfo);
     }
 
+    @AppModeFull(reason = "Overlays cannot be instant apps")
     public void testInstallingOverlayHasNoEffect() throws Exception {
         assertFalse(getDevice().getInstalledPackageNames().contains(PKG));
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
index 680798a..e363069 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageResolutionHostTest.java
@@ -19,8 +19,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.After;
@@ -38,12 +38,10 @@
     private static final String TINY_PKG = "android.appsecurity.cts.orderedactivity";
 
     private String mOldVerifierValue;
-    private CompatibilityBuildHelper mBuildHelper;
 
     @Before
     public void setUp() throws Exception {
         getDevice().uninstallPackage(TINY_PKG);
-        mBuildHelper = new CompatibilityBuildHelper(getBuild());
     }
 
     @After
@@ -52,26 +50,56 @@
     }
 
     @Test
-    public void testResolveOrderedActivity() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull
+    public void testResolveOrderedActivity_full() throws Exception {
+        testResolveOrderedActivity(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testResolveOrderedActivity_instant() throws Exception {
+        testResolveOrderedActivity(true);
+    }
+    private void testResolveOrderedActivity(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryActivityOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 
     @Test
-    public void testResolveOrderedService() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull
+    public void testResolveOrderedService_full() throws Exception {
+        testResolveOrderedService(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testResolveOrderedService_instant() throws Exception {
+        testResolveOrderedService(true);
+    }
+    private void testResolveOrderedService(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryServiceOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 
     @Test
-    public void testResolveOrderedReceiver() throws Exception {
-        getDevice().installPackage(mBuildHelper.getTestFile(TINY_APK), true);
+    @AppModeFull
+    public void testResolveOrderedReceiver_full() throws Exception {
+        testResolveOrderedReceiver(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testResolveOrderedReceiver_instant() throws Exception {
+        testResolveOrderedReceiver(true);
+    }
+    private void testResolveOrderedReceiver(boolean instant) throws Exception {
+        new InstallMultiple(instant)
+                .addApk(TINY_APK)
+                .run();
         Utils.runDeviceTests(getDevice(), TINY_PKG,
                 ".PackageResolutionTest", "queryReceiverOrdered");
-        getDevice().uninstallPackage(TINY_PKG);
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
index 5bb70d1..41785f8 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
@@ -19,7 +19,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.After;
@@ -66,7 +68,16 @@
     }
 
     @Test
-    public void testUninstalledPackageVisibility() throws Exception {
+    @AppModeFull
+    public void testUninstalledPackageVisibility_full() throws Exception {
+        testUninstalledPackageVisibility(false);
+    }
+    @Test
+    @AppModeInstant
+    public void testUninstalledPackageVisibility_instant() throws Exception {
+        testUninstalledPackageVisibility(true);
+    }
+    private void testUninstalledPackageVisibility(boolean instant) throws Exception {
         if (!mSupportsMultiUser) {
             return;
         }
@@ -135,8 +146,7 @@
         getDevice().uninstallPackage(TEST_PKG);
     }
 
-    protected void uninstallWithKeepDataForUser(String packageName, int userId)
-            throws DeviceNotAvailableException {
+    private void uninstallWithKeepDataForUser(String packageName, int userId) throws Exception {
         final String command = "pm uninstall -k --user " + userId + " " + packageName;
         getDevice().executeShellCommand(command);
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index 66cee05..aa28a97 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -16,6 +16,7 @@
 
 package android.appsecurity.cts;
 
+import android.platform.test.annotations.AppModeFull;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
@@ -31,6 +32,7 @@
 /**
  * Tests that verify intent filters.
  */
+@AppModeFull(reason="Instant applications can never be system or privileged")
 public class PrivilegedUpdateTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
     private static final String TAG = "PrivilegedUpdateTests";
     private static final String SHIM_PKG = "com.android.cts.priv.ctsshim";
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index 4afeb9bf..41ce7a1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -16,72 +16,67 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Set of tests that verify behavior of runtime permissions, including both
  * dynamic granting and behavior of legacy apps.
  */
-public class UsesLibraryHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@AppModeFull(reason = "TODO verify whether or not these should run in instant mode")
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class UsesLibraryHostTest extends BaseAppSecurityTest {
     private static final String PKG = "com.android.cts.useslibrary";
 
     private static final String APK = "CtsUsesLibraryApp.apk";
     private static final String APK_COMPAT = "CtsUsesLibraryAppCompat.apk";
 
-    private IAbi mAbi;
-    private CompatibilityBuildHelper mBuildHelper;
 
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mBuildHelper = new CompatibilityBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUp() throws Exception {
         Utils.prepareSingleUser(getDevice());
-        assertNotNull(mAbi);
-        assertNotNull(mBuildHelper);
-
         getDevice().uninstallPackage(PKG);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
+    @After
+    public void tearDown() throws Exception {
         getDevice().uninstallPackage(PKG);
     }
 
-    public void testUsesLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testUsesLibrary");
+    @Test
+    @AppModeFull
+    public void testUsesLibrary_full() throws Exception {
+        testUsesLibrary(false);
+    }
+    private void testUsesLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testUsesLibrary");
     }
 
-    public void testMissingLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testMissingLibrary");
+    @Test
+    @AppModeFull
+    public void testMissingLibrary_full() throws Exception {
+        testMissingLibrary(false);
+    }
+    public void testMissingLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testMissingLibrary");
     }
 
-    public void testDuplicateLibrary() throws Exception {
-        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
-        runDeviceTests(PKG, ".UsesLibraryTest", "testDuplicateLibrary");
+    @Test
+    @AppModeFull
+    public void testDuplicateLibrary_full() throws Exception {
+        testDuplicateLibrary(false);
     }
-
-    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
-            throws DeviceNotAvailableException {
-        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+    public void testDuplicateLibrary(boolean instant) throws Exception {
+        new InstallMultiple(instant).addApk(APK).run();
+        Utils.runDeviceTests(getDevice(), PKG, ".UsesLibraryTest", "testDuplicateLibrary");
     }
 }
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
index 05f4573..cdaa469 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.cts.classloadersplitapp"
-    android:isolatedSplits="true">
+    android:isolatedSplits="true"
+    android:targetSandboxVersion="2">
 
     <application android:label="ClassloaderSplitApp"
                  android:classLoader="dalvik.system.PathClassLoader">
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
index a334acf..96807d6 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_a/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.classloadersplitapp"
-        featureSplit="feature_a">
+        featureSplit="feature_a"
+        android:targetSandboxVersion="2">
 
     <application android:classLoader="dalvik.system.DelegateLastClassLoader">
         <activity android:name=".feature_a.FeatureAActivity">
diff --git a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
index 8d9ac52..fa975ad 100644
--- a/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ClassLoaderSplitApp/feature_b/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.classloadersplitapp"
-        featureSplit="feature_b">
+        featureSplit="feature_b"
+        android:targetSandboxVersion="2">
 
     <uses-split android:name="feature_a" />
 
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
index 7038685..d2eea34 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTestCase.java
@@ -152,6 +152,11 @@
         return true;
     }
 
+    protected boolean isTelevision() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        return pm.hasSystemFeature("android.hardware.type.television");
+    }
+
     protected void assertActivityFailed() {
         final Result result = mActivity.getResult();
         assertEquals(REQUEST_CODE, result.requestCode);
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index 466dabf..dc44bafb 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -32,6 +32,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
@@ -361,8 +362,10 @@
 
         // Close app screen.
         mDevice.pressBack();
-        // Close main screen.
-        mDevice.pressBack();
+        if (!isTelevision()) {
+            // Close main screen.
+            mDevice.pressBack();
+        }
 
         // Finally, make sure it's reset by requesting it again.
         sendOpenExternalDirectoryIntent(volume, dir);
@@ -401,8 +404,10 @@
 
         // Close app screen.
         mDevice.pressBack();
-        // Close main screen.
-        mDevice.pressBack();
+        if (!isTelevision()) {
+            // Close main screen.
+            mDevice.pressBack();
+        }
 
         // Then tries again - should be denied.
         sendOpenExternalDirectoryIntent(volume, dir);
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
index 2f3a374..a903c37 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.cts.isolatedsplitapp"
-    android:isolatedSplits="true">
+    android:isolatedSplits="true"
+    android:targetSandboxVersion="2">
 
     <application android:label="IsolatedSplitApp">
 
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
index 958b8d0..d9ca271 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_a"
-        featureSplit="feature_a">
+        featureSplit="feature_a"
+        android:targetSandboxVersion="2">
 
     <application>
         <activity android:name=".FeatureAActivity">
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
index d89a1f2..8b4f16d 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_b"
-        featureSplit="feature_b">
+        featureSplit="feature_b"
+        android:targetSandboxVersion="2">
 
     <uses-split android:name="feature_a" />
 
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
index 64b087c..012543b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
@@ -16,7 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.isolatedsplitapp.feature_c"
-        featureSplit="feature_c">
+        featureSplit="feature_c"
+        android:targetSandboxVersion="2">
 
     <application>
         <activity android:name=".FeatureCActivity">
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
index 2a63cd7..190c5d0 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000000000ffff/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x00000000" android:versionCode="0x0000ffff">
-
+        android:versionCodeMajor="0x00000000" android:versionCode="0x0000ffff"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
index 934deec..a2a90fc 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version00000000ffffffff/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x00000000" android:versionCode="0xffffffff">
-
+        android:versionCodeMajor="0x00000000" android:versionCode="0xffffffff"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
index c7b9dd0..33a1dc2 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ff00000000/AndroidManifest.xml
@@ -15,8 +15,9 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x000000ff" android:versionCode="0x00000000">
-
+        android:versionCodeMajor="0x000000ff" android:versionCode="0x00000000"
+        android:targetSandboxVersion="2">
+  
     <application/>
 
     <instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
index 91d4e39..bb0a3e6 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/Version000000ffffffffff/AndroidManifest.xml
@@ -15,7 +15,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.majorversion"
-        android:versionCodeMajor="0x000000ff" android:versionCode="0xffffffff">
+        android:versionCodeMajor="0x000000ff" android:versionCode="0xffffffff"
+        android:targetSandboxVersion="2">
 
     <application/>
 
diff --git a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
index 67fe9f4..97e63d0 100644
--- a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := current
 LOCAL_PACKAGE_NAME := CtsOrderedActivityApp
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
index 383f000..6d08f9e 100644
--- a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="android.appsecurity.cts.orderedactivity"
         android:versionCode="10"
-        android:versionName="1.0">
+        android:versionName="1.0"
+        android:targetSandboxVersion="2">
     <application android:label="@string/app_name">
         <!-- Activities used for queries -->
         <activity android:name=".OrderActivity2">
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
index 247617e..942cae0 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.simpleappinstall">
+       package="com.android.cts.simpleappinstall"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple app to test that apps cannot be installed over existing app with
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
index da48194..1491d9c 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-       package="com.android.cts.simpleappinstall">
+       package="com.android.cts.simpleappinstall"
+       android:targetSandboxVersion="2">
 
     <!--
     A simple second app to test that apps cannot be installed over existing app
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
index 7a62f09..545dcbe 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/Android.mk
@@ -33,7 +33,7 @@
     ../UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp22
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
index 58ee1c7..3ab5ae3 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/Android.mk
@@ -31,7 +31,7 @@
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp23
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
index 8528752..d8593cc 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/Android.mk
@@ -32,7 +32,7 @@
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp25
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
index 52c8ba4..133abcf 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/Android.mk
@@ -31,7 +31,7 @@
 LOCAL_RESOURCE_DIR := cts/hostsidetests/appsecurity/test-apps/UsePermissionApp23/res
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionApp26
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 # tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
index 4822a11..da5eba3 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := current
 LOCAL_PACKAGE_NAME := CtsPkgInstallTinyApp
 LOCAL_DEX_PREOPT := false
 
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
index def2931..1ead3a2 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest.xml
@@ -16,7 +16,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="android.appsecurity.cts.tinyapp"
         android:versionCode="10"
-        android:versionName="1.0">
+        android:versionName="1.0"
+        android:targetSandboxVersion="2">
     <application android:label="@string/app_name">
         <activity
                 android:name=".MainActivity"
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/Android.mk b/hostsidetests/backup/SyncAdapterSettingsApp/Android.mk
new file mode 100644
index 0000000..45e5b2d
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsBackupSyncAdapterSettingsApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml b/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml
new file mode 100644
index 0000000..3cda4e6
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.cts.backup.syncadaptersettingsapp">
+
+    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+
+    <application android:label="BackupSyncAdapterSettings">
+        <uses-library android:name="android.test.runner"/>
+        <service android:name=".SyncAdapterSettingsAuthenticator">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.accounts.AccountAuthenticator"
+                android:resource="@xml/authenticator"/>
+        </service>
+
+        <service android:name=".SyncAdapterSettingsService"
+                 android:exported="false">
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter"/>
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                       android:resource="@xml/syncadapter"/>
+        </service>
+
+        <provider android:name=".SyncAdapterSettingsProvider"
+                  android:authorities="android.cts.backup.syncadaptersettingsapp.provider"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.cts.backup.syncadaptersettingsapp"/>
+</manifest>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml
new file mode 100644
index 0000000..a0ec2f2
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/authenticator.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+                       android:accountType="android.cts.backup.syncadaptersettingsapp"
+                       android:label="BackupSyncAdapterSettings"/>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml
new file mode 100644
index 0000000..bbe551c
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/res/xml/syncadapter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+              android:contentAuthority="android.cts.backup.syncadaptersettingsapp.provider"
+              android:accountType="android.cts.backup.syncadaptersettingsapp"
+/>
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java
new file mode 100644
index 0000000..9bb0dca
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAdapter.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.cts.backup.syncadaptersettingsapp;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SyncResult;
+import android.os.Bundle;
+
+/**
+ * Sync adapter for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsAdapter extends AbstractThreadedSyncAdapter {
+
+    public SyncAdapterSettingsAdapter(Context context) {
+        super(context, false);
+    }
+
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+    }
+}
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java
new file mode 100644
index 0000000..2eb4209
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsAuthenticator.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup.syncadaptersettingsapp;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * Authenticator for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsAuthenticator extends Service {
+
+    private Authenticator mAuthenticator;
+
+    @Override
+    public void onCreate() {
+        mAuthenticator = new Authenticator(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAuthenticator.getIBinder();
+    }
+
+    public static class Authenticator extends AbstractAccountAuthenticator {
+
+        public Authenticator(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+                String authTokenType, String[] requiredFeatures, Bundle options)
+                throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
+                Bundle options) throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String authTokenType) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
+                String[] features) throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
+
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java
new file mode 100644
index 0000000..1f6839a
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsProvider.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup.syncadaptersettingsapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Provider for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsProvider extends ContentProvider {
+    public static final String AUTHORITY = "android.cts.backup.syncadaptersettingsapp.provider";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
+
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java
new file mode 100644
index 0000000..12ccb8c
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.cts.backup.syncadaptersettingsapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Service for the sync adapter settings test.
+ */
+public class SyncAdapterSettingsService extends Service {
+
+    private static SyncAdapterSettingsAdapter sAdapter;
+    private static final Object LOCK = new Object();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        synchronized (LOCK) {
+            if (sAdapter == null) {
+                sAdapter = new SyncAdapterSettingsAdapter(getApplicationContext());
+            }
+        }
+        return sAdapter.getSyncAdapterBinder();
+    }
+}
diff --git a/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java
new file mode 100644
index 0000000..e8d61e7
--- /dev/null
+++ b/hostsidetests/backup/SyncAdapterSettingsApp/src/android/cts/backup/syncadaptersettingsapp/SyncAdapterSettingsTest.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.cts.backup.syncadaptersettingsapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Instrumentation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Scanner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side SyncAdapterSettingsHostSideTest. These
+ * are not designed to be called in any other way, as they rely on state set up by the host side
+ * test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SyncAdapterSettingsTest {
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    private static final String ACCOUNT_NAME = "SyncAdapterSettings";
+    private static final String ACCOUNT_TYPE = "android.cts.backup.syncadaptersettingsapp";
+
+    private Context mContext;
+    private Account mAccount;
+
+    @Before
+    public void setUp() {
+        mContext = getInstrumentation().getTargetContext();
+        mAccount = getAccount();
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=true.
+     *
+     * Test logic:
+     * 1. Enable MasterSyncAutomatically.
+     * 2. Backup android package, disable MasterSyncAutomatically and restore android package.
+     * 3. Check restored MasterSyncAutomatically=true is the same with backup value.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOn_isRestored() throws Exception {
+        ContentResolver.setMasterSyncAutomatically(true);
+
+        backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        ContentResolver.setMasterSyncAutomatically(false);
+        restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertTrue(ContentResolver.getMasterSyncAutomatically());
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=false.
+     *
+     * Test logic:
+     * 1. Disable MasterSyncAutomatically.
+     * 2. Backup android package, enable MasterSyncAutomatically and restore android package.
+     * 3. Check restored MasterSyncAutomatically=false is the same with backup value.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOff_isRestored() throws Exception {
+        ContentResolver.setMasterSyncAutomatically(false);
+
+        backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        ContentResolver.setMasterSyncAutomatically(true);
+        restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertFalse(ContentResolver.getMasterSyncAutomatically());
+    }
+
+    /**
+     * Test that if syncEnabled=true and isSyncable=1 in restore data, syncEnabled will be true
+     * and isSyncable will be left as the current value in the device.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=true, isSyncable=1.
+     * 2. Backup android package, set syncEnabled=false and set isSyncable=0. Then restore
+     * android package.
+     * 3. Check restored syncEnabled=true and isSyncable=0. Then remove account.
+     *
+     * @see AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)
+     */
+    @Test
+    public void testIsSyncableChanged_ifTurnOnSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                true, /* isSyncable= */1);
+
+        backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(false, 0);
+        restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */true, /* isSyncable= */0);
+        removeAccount(mContext);
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=0 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 0.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=false, isSyncable=0.
+     * 2. Backup android package, set syncEnabled=true and set isSyncable=1. Then restore android
+     * package.
+     * 3. Check restored syncEnabled=false and isSyncable=0. Then remove account.
+     *
+     * @see AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)
+     */
+    @Test
+    public void testIsSyncableIsZero_ifTurnOffSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                false, /* isSyncable= */0);
+
+        backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(true, 1);
+        restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */false, /* isSyncable= */0);
+        removeAccount(mContext);
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=1 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 1.
+     * According to
+     * {@link AccountSyncSettingsBackupHelper#restoreExistingAccountSyncSettingsFromJSON(JSONObject)}
+     * isSyncable will be set to 2, but function
+     * {@link ContentService#setIsSyncable(Account, String, int)} would call
+     * {@link ContentService#normalizeSyncable(int)} and set isSyncable to 1.
+     *
+     * Test logic:
+     * 1. Add an account if account does not exist and set syncEnabled=false, isSyncable=1.
+     * 2. Backup android package, set syncEnabled=true and set isSyncable=0. Then restore android
+     * package.
+     * 3. Check restored syncEnabled=false and isSyncable=1. Then remove account.
+     */
+    @Test
+    public void testIsSyncableIsOne_ifTurnOffSyncEnabled() throws Exception {
+        addAccount(mContext);
+        initSettings(/* masterSyncAutomatically= */true, /* syncAutomatically= */
+                false, /* isSyncable= */1);
+
+        backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        setSyncAutomaticallyAndIsSyncable(true, 0);
+        restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertSettings(/* syncAutomatically= */false, /* isSyncable= */1);
+        removeAccount(mContext);
+    }
+
+    private void initSettings(boolean masterSyncAutomatically, boolean syncAutomatically,
+            int isSyncable) throws Exception {
+        ContentResolver.setMasterSyncAutomatically(masterSyncAutomatically);
+        setSyncAutomaticallyAndIsSyncable(syncAutomatically, isSyncable);
+    }
+
+    private void setSyncAutomaticallyAndIsSyncable(boolean syncAutomatically, int isSyncable)
+            throws Exception {
+        ContentResolver.setSyncAutomatically(mAccount, SyncAdapterSettingsProvider.AUTHORITY,
+                syncAutomatically);
+
+        ContentResolver.setIsSyncable(mAccount, SyncAdapterSettingsProvider.AUTHORITY, isSyncable);
+    }
+
+    private void assertSettings(boolean syncAutomatically, int isSyncable) throws Exception {
+        assertEquals(syncAutomatically, ContentResolver.getSyncAutomatically(mAccount,
+                SyncAdapterSettingsProvider.AUTHORITY));
+        assertEquals(isSyncable,
+                ContentResolver.getIsSyncable(mAccount, SyncAdapterSettingsProvider.AUTHORITY));
+    }
+
+    /**
+     * Get the account.
+     */
+    private Account getAccount() {
+        return new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+    }
+
+    /**
+     * Add an account, if it doesn't exist yet.
+     */
+    private void addAccount(Context context) {
+        final String password = "password";
+
+        final AccountManager am = context.getSystemService(AccountManager.class);
+
+        if (!Arrays.asList(am.getAccountsByType(mAccount.type)).contains(mAccount)) {
+            am.addAccountExplicitly(mAccount, password, new Bundle());
+        }
+    }
+
+    /**
+     * Remove the account.
+     */
+    private void removeAccount(Context context) {
+        final AccountManager am = context.getSystemService(AccountManager.class);
+        final Account[] accounts = am.getAccountsByType(ACCOUNT_TYPE);
+
+        for (int i = 0, size = accounts.length; i < size; i++) {
+            Account account = accounts[i];
+            am.removeAccountExplicitly(account);
+        }
+    }
+
+    private static Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    // TODO: Unify these methods with BaseBackupCtsTest
+    private static FileInputStream executeStreamedShellCommand(
+            Instrumentation instrumentation, String command) throws IOException {
+        final ParcelFileDescriptor pfd =
+                instrumentation.getUiAutomation().executeShellCommand(command);
+        return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+    }
+
+    // TODO: Unify these methods with BaseBackupCtsTest
+    private String exec(String command) throws Exception {
+        try (InputStream in = executeStreamedShellCommand(getInstrumentation(), command)) {
+            BufferedReader br = new BufferedReader(
+                    new InputStreamReader(in, StandardCharsets.UTF_8));
+            String str;
+            StringBuilder out = new StringBuilder();
+            while ((str = br.readLine()) != null) {
+                out.append(str).append("\n");
+            }
+            return out.toString();
+        }
+    }
+
+    /**
+     * Execute shell command "bmgr backupnow <packageName>" and assert success.
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private void backupNowAndAssertSuccess(String packageName) throws Exception {
+        String backupnowOutput = backupNow(packageName);
+        assertBackupIsSuccessful(packageName, backupnowOutput);
+    }
+
+    /**
+     * Execute shell command "bmgr backupnow <packageName>" and return output from this command.
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private String backupNow(String packageName) throws Exception {
+        return exec("bmgr backupnow " + packageName);
+    }
+
+    /**
+     * Parsing the output of "bmgr backupnow" command and checking that the package under test
+     * was backed up successfully.
+     *
+     * Expected format: "Package <packageName> with result: Success"
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private void assertBackupIsSuccessful(String packageName, String backupnowOutput) {
+        Scanner in = new Scanner(backupnowOutput);
+        boolean success = false;
+        while (in.hasNextLine()) {
+            String line = in.nextLine();
+
+            if (line.contains(packageName)) {
+                String result = line.split(":")[1].trim();
+                if ("Success".equals(result)) {
+                    success = true;
+                }
+            }
+        }
+        in.close();
+        assertTrue(success);
+    }
+
+    /**
+     * Execute shell command "bmgr restore <packageName>" and assert success.
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private void restoreAndAssertSuccess(String packageName) throws Exception {
+        String restoreOutput = restore(packageName);
+        assertRestoreIsSuccessful(restoreOutput);
+    }
+
+    /**
+     * Execute shell command "bmgr restore <packageName>" and return output from this command.
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private String restore(String packageName) throws Exception {
+        return exec("bmgr restore " + packageName);
+    }
+
+    /**
+     * Parsing the output of "bmgr restore" command and checking that the package under test
+     * was restored successfully.
+     *
+     * Expected format: "restoreFinished: 0"
+     *
+     * TODO: Unify these methods with BaseBackupCtsTest
+     */
+    private void assertRestoreIsSuccessful(String restoreOutput) {
+        assertTrue("Restore not successful", restoreOutput.contains("restoreFinished: 0"));
+    }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java
new file mode 100644
index 0000000..0fe4633
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/SyncAdapterSettingsHostSideTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.cts.backup;
+
+import static org.junit.Assert.assertNull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for checking sync adapter settings value backup and restore works correctly.
+ * The app uses AccountSyncSettingsBackupHelper to do sync adapter settings backup of those values.
+ * The tests use "bmgr backupnow android" for backup
+ *
+ * Invokes device side tests provided by
+ * {@link android.cts.backup.syncadaptersettingsapp.SyncAdapterSettingsTest}.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class SyncAdapterSettingsHostSideTest extends BaseBackupHostSideTest {
+
+    /** The name of the APK that a sync adapter settings will be run for */
+    private static final String APP_APK = "CtsBackupSyncAdapterSettingsApp.apk";
+
+    /** The name of the package of the app under test */
+    private static final String APP_PACKAGE = "android.cts.backup.syncadaptersettingsapp";
+
+    /** The name of the device side test class */
+    private static final String TEST_CLASS = APP_PACKAGE + ".SyncAdapterSettingsTest";
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        installPackage(APP_APK);
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!mIsBackupSupported) {
+            return;
+        }
+
+        // Clear backup data and uninstall the package (in that order!)
+        // BackupManagerService won't let us wipe the data if package is uninstalled
+        clearBackupDataInLocalTransport(ANDROID_PACKAGE);
+        assertNull(uninstallPackage(APP_PACKAGE));
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=true.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOn_isRestored() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testMasterSyncAutomatically_whenOn_isRestored");
+    }
+
+    /**
+     * Test backup and restore of MasterSyncAutomatically=false.
+     */
+    @Test
+    public void testMasterSyncAutomatically_whenOff_isRestored() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testMasterSyncAutomatically_whenOff_isRestored");
+    }
+
+    /**
+     * Test that if syncEnabled=true and isSyncable=1 in restore data, syncEnabled will be true
+     * and isSyncable will be left as the current value in the device.
+     */
+    @Test
+    public void testIsSyncableChanged_ifTurnOnSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableChanged_ifTurnOnSyncEnabled");
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=0 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 0.
+     */
+    @Test
+    public void testIsSyncableIsZero_ifTurnOffSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableIsZero_ifTurnOffSyncEnabled");
+    }
+
+    /**
+     * Test that if syncEnabled=false and isSyncable=1 in restore data, syncEnabled will be false
+     * and isSyncable will be set to 1.
+     */
+    @Test
+    public void testIsSyncableIsOne_ifTurnOffSyncEnabled() throws Exception {
+        if (!mIsBackupSupported) {
+            CLog.i("android.software.backup feature is not supported on this device");
+            return;
+        }
+
+        checkDeviceTest("testIsSyncableIsOne_ifTurnOffSyncEnabled");
+    }
+
+    private void checkDeviceTest(String methodName) throws DeviceNotAvailableException {
+        checkDeviceTest(APP_PACKAGE, TEST_CLASS, methodName);
+    }
+}
diff --git a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
index 3069bac..8ac2416 100644
--- a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
+++ b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/Android.mk
@@ -20,12 +20,13 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations android-support-test ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSyncInvalidAccountAuthorityTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
index f082cc8..c8343d4 100644
--- a/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
+++ b/hostsidetests/content/test-apps/CtsSyncInvalidAccountAuthorityTestCases/src/android/content/sync/cts/StubProvider.java
@@ -16,11 +16,11 @@
 
 package android.content.sync.cts;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.database.Cursor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.net.Uri;
 
 public class StubProvider extends ContentProvider {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
index 054a5a5..d330ebc 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
@@ -76,6 +76,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -399,9 +400,14 @@
     private void verifyOsStartupEventPresent(List<SecurityEvent> events) {
         final SecurityEvent event = findEvent("os startup", events, TAG_OS_STARTUP);
         // Verified boot state
-        assertTrue(ImmutableSet.of("green", "yellow", "orange").contains(getString(event, 0)));
+        assertOneOf(ImmutableSet.of("green", "yellow", "orange"), getString(event, 0));
         // dm-verity mode
-        assertTrue(ImmutableSet.of("enforcing", "eio").contains(getString(event, 1)));
+        assertOneOf(ImmutableSet.of("enforcing", "eio", "disabled"), getString(event, 1));
+    }
+
+    private void assertOneOf(Set<String> allowed, String s) {
+        assertTrue(String.format("\"%s\" is not one of [%s]", s, String.join(", ", allowed)),
+                allowed.contains(s));
     }
 
     private void verifyCryptoSelfTestEventPresent(List<SecurityEvent> events) {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
index a386baa..7af838a 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProfileTimeoutTestHelper.java
@@ -29,7 +29,8 @@
  */
 public class ProfileTimeoutTestHelper extends InstrumentationTestCase {
     // This should be sufficiently smaller than ManagedProfileTest.PROFILE_TIMEOUT_DELAY_SEC.
-    private static final int TIMEOUT_MS = 5_000;
+    // This should also be sufficiently larger than time required to run "input tap" on emulator.
+    private static final int TIMEOUT_MS = 30_000;
     private static final ComponentName ADMIN_COMPONENT = new ComponentName(
             BasicAdminReceiver.class.getPackage().getName(), BasicAdminReceiver.class.getName());
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index ef51156..0c783c5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -94,6 +94,14 @@
     protected static final int FLAG_EPHEMERAL = 0x00000100;
     protected static final int FLAG_MANAGED_PROFILE = 0x00000020;
 
+    /**
+     * The {@link android.os.BatteryManager} flags value representing all charging types; {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_AC}, {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_USB}, and {@link
+     * android.os.BatteryManager#BATTERY_PLUGGED_WIRELESS}.
+     */
+    private static final int STAY_ON_WHILE_PLUGGED_IN_FLAGS = 7;
+
     protected static interface Settings {
         public static final String GLOBAL_NAMESPACE = "global";
         public static interface Global {
@@ -155,6 +163,7 @@
         removeTestUsers();
         // Unlock keyguard before test
         wakeupAndDismissKeyguard();
+        stayAwake();
         // Go to home.
         executeShellCommand("input keyevent KEYCODE_HOME");
     }
@@ -887,6 +896,11 @@
         executeShellCommand("wm dismiss-keyguard");
     }
 
+    private void stayAwake() throws Exception {
+        executeShellCommand(
+                "settings put global stay_on_while_plugged_in " + STAY_ON_WHILE_PLUGGED_IN_FLAGS);
+    }
+
     protected void startActivityAsUser(int userId, String packageName, String activityName)
         throws Exception {
         wakeupAndDismissKeyguard();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 2dd5a2a..1548d7f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -85,7 +85,7 @@
 
     private static final String PROFILE_CREDENTIAL = "1234";
     // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
-    private static final int PROFILE_TIMEOUT_DELAY_MS = 10_000;
+    private static final int PROFILE_TIMEOUT_DELAY_MS = 40_000;
 
     //The maximum time to wait for user to be unlocked.
     private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
@@ -296,10 +296,12 @@
     }
 
     private void simulateUserInteraction(int timeMs) throws Exception {
+        final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
         final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
-        for (int i = 0; i < timeMs; i += timeMs/10) {
-            Thread.sleep(timeMs/10);
+        while (System.nanoTime() < endTime) {
             helper.tapScreenCenter();
+            // Just in case to prevent busy loop.
+            Thread.sleep(100);
         }
     }
 
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
index aef3086..7823b7e 100644
--- a/hostsidetests/edi/AndroidTest.xml
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -15,7 +15,7 @@
 -->
 <configuration description="Config for CTS EDI host test cases">
     <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="misc" />
+    <option name="config-descriptor:metadata" key="component" value="deviceinfo" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsEdiHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/edi/OWNERS b/hostsidetests/edi/OWNERS
new file mode 100644
index 0000000..73518d9
--- /dev/null
+++ b/hostsidetests/edi/OWNERS
@@ -0,0 +1,4 @@
+aaronholden@google.com
+agathaman@google.com
+nickrose@google.com
+samlin@google.com
diff --git a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
index 41fde5c..f472e900 100644
--- a/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/errorsapp/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
index f5ce5d9..0c1ce19 100644
--- a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
@@ -26,13 +26,13 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Used by ErrorTest. Spawns misbehaving activities so reports will appear in Dropbox.
  */
@@ -57,6 +57,9 @@
         mDropbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
         mResultsReceivedSignal = new CountDownLatch(1);
         mStartMs = System.currentTimeMillis();
+
+        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+                "appops set " + mContext.getPackageName() + " android:get_usage_stats allow");
     }
 
     @Test
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/BusyWaitUtils.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/BusyWaitUtils.java
similarity index 90%
rename from hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/BusyWaitUtils.java
rename to hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/BusyWaitUtils.java
index 11e858d..399b16d 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/BusyWaitUtils.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/BusyWaitUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.inputmethodservice.cts.devicetest;
+package android.inputmethodservice.cts.common;
 
 import static org.junit.Assert.fail;
 
@@ -23,12 +23,12 @@
 /**
  * Utility class for busy waiting.
  */
-final class BusyWaitUtils {
+public final class BusyWaitUtils {
 
     private static final long POLLING_INTERVAL = TimeUnit.MILLISECONDS.toMillis(50);
 
     @FunctionalInterface
-    interface PollingCondition {
+    public interface PollingCondition {
         boolean check() throws Exception;
     }
 
@@ -43,7 +43,7 @@
      *                this message.
      * @throws Exception
      */
-    static void pollingCheck(final PollingCondition condition, final long timeout,
+    public static void pollingCheck(final PollingCondition condition, final long timeout,
             final String message) throws Exception {
         if (waitFor(condition, timeout)) {
             return;
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
index 3d46894..9bdb7ef 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java
@@ -36,9 +36,20 @@
         return "settings get " + SETTING_DEFAULT_IME;
     }
 
-    /** Command to set current IME to {@code imeId}. */
-    public static String setCurrentIme(final String imeId) {
-        return "settings put " + SETTING_DEFAULT_IME + " " + imeId;
+    /** Command to set current IME to {@code imeId} synchronously */
+    public static String setCurrentImeSync(final String imeId) {
+        return "ime set " + imeId;
+    }
+
+    public static String getEnabledImes() {
+        return "ime list -s";
+    }
+    public static String getAvailableImes() {
+        return "ime list -s -a";
+    }
+
+    public static String listPackage(String packageName) {
+        return "pm list package " + packageName;
     }
 
     /** Command to enable IME of {@code imeId}. */
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index 47ae8eb..348ea99 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -19,6 +19,7 @@
 import static android.inputmethodservice.cts.DeviceEvent.isFrom;
 import static android.inputmethodservice.cts.DeviceEvent.isNewerThan;
 import static android.inputmethodservice.cts.DeviceEvent.isType;
+import static android.inputmethodservice.cts.common.BusyWaitUtils.pollingCheck;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType
         .ON_BIND_INPUT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE;
@@ -37,7 +38,6 @@
         .COMMAND_SWITCH_TO_NEXT_INPUT;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_ARG_STRING1;
 import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_COMMAND;
-import static android.inputmethodservice.cts.devicetest.BusyWaitUtils.pollingCheck;
 import static android.inputmethodservice.cts.devicetest.MoreCollectors.startingFrom;
 
 import android.inputmethodservice.cts.DeviceEvent;
@@ -191,7 +191,7 @@
         helper.findUiObject(EditTextAppConstants.EDIT_TEXT_RES_NAME).click();
 
         final String initialIme = helper.shell(ShellCommandUtils.getCurrentIme());
-        helper.shell(ShellCommandUtils.setCurrentIme(Ime2Constants.IME_ID));
+        helper.shell(ShellCommandUtils.setCurrentImeSync(Ime2Constants.IME_ID));
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(startActivityTime))
                         .anyMatch(isFrom(Ime2Constants.CLASS).and(isType(ON_START_INPUT))),
@@ -225,7 +225,7 @@
         final long imeForceStopTime = SystemClock.uptimeMillis();
         helper.shell(ShellCommandUtils.uninstallPackage(Ime1Constants.PACKAGE));
 
-        helper.shell(ShellCommandUtils.setCurrentIme(Ime2Constants.IME_ID));
+        helper.shell(ShellCommandUtils.setCurrentImeSync(Ime2Constants.IME_ID));
         helper.findUiObject(EditTextAppConstants.EDIT_TEXT_RES_NAME).click();
         pollingCheck(() -> helper.queryAllEvents()
                         .filter(isNewerThan(imeForceStopTime))
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index c1557e1..19b18ea 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -16,6 +16,7 @@
 
 package android.inputmethodservice.cts.hostside;
 
+import static android.inputmethodservice.cts.common.BusyWaitUtils.pollingCheck;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
@@ -65,12 +66,46 @@
         shell(ShellCommandUtils.resetImes());
     }
 
-    private void installPossibleInstantPackage(String apkFileName, boolean instant)
-            throws Exception {
+    /**
+     * Install an app apk file synchronously.
+     *
+     * <p>This methods waits until package is available in PackageManger</p>
+     *
+     * <p>Note: For installing IME APKs use {@link #installImePackageSync(String, String)}
+     * instead.</p>
+     * @param apkFileName App apk to install
+     * @param packageName packageName of the installed apk
+     * @param options adb shell install options.
+     * @throws Exception
+     */
+    private void installPackageSync(
+            String apkFileName, String packageName, String... options) throws Exception {
+        installPackage(apkFileName, options);
+        pollingCheck(() ->
+            shell(ShellCommandUtils.listPackage(packageName)).contains(packageName),
+            TIMEOUT,
+            packageName + " should be installed.");
+    }
+
+    /**
+     * Install IME packages synchronously.
+     *
+     * <p>This method verifies that IME is available in IMMS.</p>
+     * @param apkFileName IME apk to install
+     * @param imeId of the IME being installed.
+     * @throws Exception
+     */
+    private void installImePackageSync(String apkFileName, String imeId) throws Exception {
+        installPackage(apkFileName, "-r");
+        waitUntilImesAreAvailable(imeId);
+    }
+
+    private void installPossibleInstantPackage(
+        String apkFileName, String packageName, boolean instant) throws Exception {
         if (instant) {
-            installPackage(apkFileName, "-r", "--instant");
+            installPackageSync(apkFileName, packageName, "-r", "--instant");
         } else {
-            installPackage(apkFileName, "-r");
+            installPackageSync(apkFileName, packageName, "-r");
         }
     }
 
@@ -78,19 +113,21 @@
         final TestInfo testSwitchIme1ToIme2 = new TestInfo(DeviceTestConstants.PACKAGE,
                 DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_SWITCH_IME1_TO_IME2);
         sendTestStartEvent(testSwitchIme1ToIme2);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
-        installPackage(Ime2Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+        installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testSwitchIme1ToIme2));
     }
 
     @AppModeFull
     @Test
-    public void testSwitchImeull() throws Exception {
+    public void testSwitchImeFull() throws Exception {
         testSwitchIme(false);
     }
 
@@ -104,13 +141,16 @@
         final TestInfo testCreateIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
                 DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
         sendTestStartEvent(testCreateIme1);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID);
+
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
         assertTrue(runDeviceTestMethod(testCreateIme1));
 
-        uninstallPackageIfExists(Ime1Constants.PACKAGE);
+        uninstallPackageSyncIfExists(Ime1Constants.PACKAGE);
         assertImeNotSelectedInSecureSettings(Ime1Constants.IME_ID, TIMEOUT);
     }
 
@@ -130,10 +170,12 @@
         final TestInfo testCreateIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
                 DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
         sendTestStartEvent(testCreateIme1);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
         assertTrue(runDeviceTestMethod(testCreateIme1));
 
         shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
@@ -157,12 +199,14 @@
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
                 DeviceTestConstants.TEST_SWITCH_INPUTMETHOD);
         sendTestStartEvent(testSetInputMethod);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
-        installPackage(Ime2Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+        installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testSetInputMethod));
     }
@@ -184,14 +228,16 @@
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
                 DeviceTestConstants.TEST_SWITCH_NEXT_INPUT);
         sendTestStartEvent(testSwitchInputs);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
-        installPackage(Ime2Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+        installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         // Make sure that there is at least one more IME that specifies
         // supportsSwitchingToNextInputMethod="true"
         shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testSwitchInputs));
     }
@@ -213,12 +259,14 @@
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
                 DeviceTestConstants.TEST_SWITCH_PREVIOUS_INPUT);
         sendTestStartEvent(testSwitchInputs);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
-        installPackage(Ime2Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+        installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testSwitchInputs));
     }
@@ -240,12 +288,14 @@
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
                 DeviceTestConstants.TEST_INPUT_UNBINDS_ON_IME_STOPPED);
         sendTestStartEvent(testUnbind);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
-        installPackage(Ime2Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+        installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
         shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testUnbind));
     }
@@ -267,10 +317,12 @@
                 DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_CLASS,
                 DeviceTestConstants.TEST_INPUT_UNBINDS_ON_APP_STOPPED);
         sendTestStartEvent(testUnbind);
-        installPossibleInstantPackage(EditTextAppConstants.APK, instant);
-        installPackage(Ime1Constants.APK, "-r");
+        installPossibleInstantPackage(
+            EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+        installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
         shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
-        shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
+        waitUntilImesAreEnabled(Ime1Constants.IME_ID);
+        shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
 
         assertTrue(runDeviceTestMethod(testUnbind));
     }
@@ -305,13 +357,16 @@
     }
 
     private void cleanUpTestImes() throws Exception {
-        uninstallPackageIfExists(Ime1Constants.PACKAGE);
-        uninstallPackageIfExists(Ime2Constants.PACKAGE);
+        uninstallPackageSyncIfExists(Ime1Constants.PACKAGE);
+        uninstallPackageSyncIfExists(Ime2Constants.PACKAGE);
     }
 
-    private void uninstallPackageIfExists(final String packageName) throws Exception {
+    private void uninstallPackageSyncIfExists(final String packageName) throws Exception {
         if (isPackageInstalled(getDevice(), packageName)) {
             uninstallPackage(getDevice(), packageName);
+            pollingCheck(()-> !isPackageInstalled(getDevice(), packageName),
+                TIMEOUT,
+                packageName + " should be uninstalled.");
         }
     }
 
@@ -334,4 +389,32 @@
             timeout -= POLLING_INTERVAL;
         }
     }
+
+    /**
+     * Wait until IMEs are available in IMMS.
+     * @throws Exception
+     */
+    private void waitUntilImesAreAvailable(String... imeIds) throws Exception {
+        waitUntilImesAreAvailableOrEnabled(false, imeIds);
+    }
+
+    /**
+     * Wait until IMEs are enabled in IMMS.
+     * @throws Exception
+     */
+    private void waitUntilImesAreEnabled(String... imeIds) throws Exception {
+        waitUntilImesAreAvailableOrEnabled(true, imeIds);
+    }
+
+    private void waitUntilImesAreAvailableOrEnabled(
+        boolean shouldBeEnabled, String... imeIds) throws Exception {
+        final String cmd = shouldBeEnabled ?
+                ShellCommandUtils.getEnabledImes() : ShellCommandUtils.getAvailableImes();
+        for (String imeId : imeIds) {
+            pollingCheck(() ->
+                    shell(cmd).contains(imeId),
+                    TIMEOUT,
+                    imeId + " should be " + (shouldBeEnabled? "enabled." : "available."));
+        }
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 83641af..338ef72 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -315,21 +315,20 @@
             @Nullable FieldMatcher.Builder dimension) throws Exception {
         final String atomName = "Atom" + System.nanoTime();
         final String gaugeName = "Gauge" + System.nanoTime();
-        final String predicateName = "SCREEN_IS_ON";
+        final String predicateName = "APP_BREADCRUMB";
         SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
         conf.addAtomMatcher(AtomMatcher.newBuilder()
                 .setId(atomName.hashCode())
                 .setSimpleAtomMatcher(sam));
-        // TODO: change this predicate to something simpler and easier
-        final String predicateTrueName = "SCREEN_TURNED_ON";
-        final String predicateFalseName = "SCREEN_TURNED_OFF";
+        final String predicateTrueName = "APP_BREADCRUMB_1";
+        final String predicateFalseName = "APP_BREADCRUMB_2";
         conf.addAtomMatcher(AtomMatcher.newBuilder()
                 .setId(predicateTrueName.hashCode())
                 .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
-                        .setAtomId(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                        .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
                         .addFieldValueMatcher(FieldValueMatcher.newBuilder()
-                                .setField(ScreenStateChanged.STATE_FIELD_NUMBER)
-                                .setEqInt(DisplayStateEnum.DISPLAY_STATE_ON_VALUE)
+                                .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+                                .setEqInt(1)
                         )
                 )
         )
@@ -337,10 +336,10 @@
                 .addAtomMatcher(AtomMatcher.newBuilder()
                         .setId(predicateFalseName.hashCode())
                         .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
-                                .setAtomId(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                                .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)
                                 .addFieldValueMatcher(FieldValueMatcher.newBuilder()
-                                        .setField(ScreenStateChanged.STATE_FIELD_NUMBER)
-                                        .setEqInt(DisplayStateEnum.DISPLAY_STATE_OFF_VALUE)
+                                        .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)
+                                        .setEqInt(2)
                                 )
                         )
                 );
@@ -507,6 +506,14 @@
         getDevice().executeShellCommand("cmd battery set wireless 1");
     }
 
+    public void setAppBreadcrumbPredicate() throws Exception {
+        doAppBreadcrumbReportedStart(1);
+    }
+
+    public void clearAppBreadcrumbPredicate() throws Exception {
+        doAppBreadcrumbReportedStart(2);
+    }
+
     public void doAppBreadcrumbReportedStart(int label) throws Exception {
         doAppBreadcrumbReported(label, AppBreadcrumbReported.State.START.ordinal());
     }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index c4e47bb..99b300d 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -351,12 +351,10 @@
                 .setField(RemainingBatteryCapacity.CHARGE_UAH_FIELD_NUMBER));
         addGaugeAtom(config, Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
@@ -380,12 +378,10 @@
                         .setField(FullBatteryCapacity.CAPACITY_UAH_FIELD_NUMBER));
         addGaugeAtom(config, Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
@@ -411,12 +407,10 @@
                         .setField(Temperature.SENSOR_NAME_FIELD_NUMBER));
         addGaugeAtom(config, Atom.TEMPERATURE_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
@@ -447,12 +441,10 @@
                         .setField(KernelWakelock.NAME_FIELD_NUMBER));
         addGaugeAtom(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
@@ -477,13 +469,11 @@
                         .setField(CpuTimePerFreq.CLUSTER_FIELD_NUMBER));
         addGaugeAtom(config, Atom.CPU_TIME_PER_FREQ_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
-        Thread.sleep(2000);
-        turnScreenOn();
-        Thread.sleep(2000);
+        Thread.sleep(WAIT_TIME_LONG);
+        setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> data = getGaugeMetricDataList();
 
@@ -504,12 +494,10 @@
                         .setField(SubsystemSleepState.SUBSYSTEM_NAME_FIELD_NUMBER));
         addGaugeAtom(config, Atom.SUBSYSTEM_SLEEP_STATE_FIELD_NUMBER, dimension);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> dataList = getGaugeMetricDataList();
@@ -529,12 +517,10 @@
         StatsdConfig.Builder config = getPulledConfig();
         addGaugeAtom(config, Atom.MODEM_ACTIVITY_INFO_FIELD_NUMBER, null);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> dataList = getGaugeMetricDataList();
@@ -554,12 +540,10 @@
         StatsdConfig.Builder config = getPulledConfig();
         addGaugeAtom(config, Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER, null);
 
-        turnScreenOff();
-
         uploadConfig(config);
 
         Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> dataList = getGaugeMetricDataList();
@@ -574,34 +558,6 @@
         }
     }
 
-    public void testBluetoothActivityInfo() throws Exception {
-        if (statsdDisabled()) {
-            return;
-        }
-        if (!hasFeature(FEATURE_BLUETOOTH, true)) return;
-        StatsdConfig.Builder config = getPulledConfig();
-        addGaugeAtom(config, Atom.BLUETOOTH_ACTIVITY_INFO_FIELD_NUMBER, null);
-
-        turnScreenOff();
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> dataList = getGaugeMetricDataList();
-
-        for (Atom atom: dataList) {
-            assertTrue(atom.getBluetoothActivityInfo().getTimestampMillis() > 0);
-            assertTrue(atom.getBluetoothActivityInfo().getBluetoothStackState() >= 0);
-            assertTrue(atom.getBluetoothActivityInfo().getControllerIdleTimeMillis() > 0);
-            assertTrue(atom.getBluetoothActivityInfo().getControllerTxTimeMillis() >= 0);
-            assertTrue(atom.getBluetoothActivityInfo().getControllerRxTimeMillis() >= 0);
-            assertTrue(atom.getBluetoothActivityInfo().getEnergyUsed() >= 0);
-        }
-    }
-
     // Explicitly tests if the adb command to log a breadcrumb is working.
     public void testBreadcrumbAdb() throws Exception {
         if (statsdDisabled()) {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 3bf8bd8..01fd657 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -224,9 +224,8 @@
 
         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
 
-        turnScreenOff();
         Thread.sleep(WAIT_TIME_SHORT);
-        turnScreenOn();
+        setAppBreadcrumbPredicate();
         Thread.sleep(WAIT_TIME_SHORT);
 
         List<Atom> atomList = getGaugeMetricDataList();
@@ -260,14 +259,11 @@
 
         uploadConfig(config);
 
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_SHORT);
+        Thread.sleep(WAIT_TIME_LONG);
         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
         Thread.sleep(WAIT_TIME_SHORT);
-        turnScreenOff();
-        Thread.sleep(WAIT_TIME_SHORT);
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_SHORT);
+        setAppBreadcrumbPredicate();
+        Thread.sleep(WAIT_TIME_LONG);
 
         List<Atom> atomList = getGaugeMetricDataList();
 
diff --git a/hostsidetests/theme/assets/Q/260dpi.zip b/hostsidetests/theme/assets/Q/260dpi.zip
index 68423a45..ba5d363 100644
--- a/hostsidetests/theme/assets/Q/260dpi.zip
+++ b/hostsidetests/theme/assets/Q/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/280dpi.zip b/hostsidetests/theme/assets/Q/280dpi.zip
index 9a09eb4..34ea14c 100644
--- a/hostsidetests/theme/assets/Q/280dpi.zip
+++ b/hostsidetests/theme/assets/Q/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/300dpi.zip b/hostsidetests/theme/assets/Q/300dpi.zip
index 35390b9d..7595d24 100644
--- a/hostsidetests/theme/assets/Q/300dpi.zip
+++ b/hostsidetests/theme/assets/Q/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/340dpi.zip b/hostsidetests/theme/assets/Q/340dpi.zip
index a65cc9f..8ce960c 100644
--- a/hostsidetests/theme/assets/Q/340dpi.zip
+++ b/hostsidetests/theme/assets/Q/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/360dpi.zip b/hostsidetests/theme/assets/Q/360dpi.zip
index a7be2b6..aae3adc 100644
--- a/hostsidetests/theme/assets/Q/360dpi.zip
+++ b/hostsidetests/theme/assets/Q/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/400dpi.zip b/hostsidetests/theme/assets/Q/400dpi.zip
index f8e62bb..363d602 100644
--- a/hostsidetests/theme/assets/Q/400dpi.zip
+++ b/hostsidetests/theme/assets/Q/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/420dpi.zip b/hostsidetests/theme/assets/Q/420dpi.zip
index 3d70848..0f2ce47 100644
--- a/hostsidetests/theme/assets/Q/420dpi.zip
+++ b/hostsidetests/theme/assets/Q/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/440dpi.zip b/hostsidetests/theme/assets/Q/440dpi.zip
new file mode 100644
index 0000000..2328c61
--- /dev/null
+++ b/hostsidetests/theme/assets/Q/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/560dpi.zip b/hostsidetests/theme/assets/Q/560dpi.zip
index e1527ed..5f1bb0b 100644
--- a/hostsidetests/theme/assets/Q/560dpi.zip
+++ b/hostsidetests/theme/assets/Q/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/hdpi.zip b/hostsidetests/theme/assets/Q/hdpi.zip
index 59c6efc..6d82318 100644
--- a/hostsidetests/theme/assets/Q/hdpi.zip
+++ b/hostsidetests/theme/assets/Q/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/ldpi.zip b/hostsidetests/theme/assets/Q/ldpi.zip
index 8e408af..cc60027 100644
--- a/hostsidetests/theme/assets/Q/ldpi.zip
+++ b/hostsidetests/theme/assets/Q/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/mdpi.zip b/hostsidetests/theme/assets/Q/mdpi.zip
index 9aebe9d..66d41d4 100644
--- a/hostsidetests/theme/assets/Q/mdpi.zip
+++ b/hostsidetests/theme/assets/Q/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/tvdpi.zip b/hostsidetests/theme/assets/Q/tvdpi.zip
index 55525fb..b43032f 100644
--- a/hostsidetests/theme/assets/Q/tvdpi.zip
+++ b/hostsidetests/theme/assets/Q/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xhdpi.zip b/hostsidetests/theme/assets/Q/xhdpi.zip
index 3fecabe..64905f3 100644
--- a/hostsidetests/theme/assets/Q/xhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xxhdpi.zip b/hostsidetests/theme/assets/Q/xxhdpi.zip
index 14d7680..b2cb422 100644
--- a/hostsidetests/theme/assets/Q/xxhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/Q/xxxhdpi.zip b/hostsidetests/theme/assets/Q/xxxhdpi.zip
index aa8d087..d00dbbd 100644
--- a/hostsidetests/theme/assets/Q/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xxxhdpi.zip
Binary files differ
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceStatesTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceStatesTest.java
index 6a60b82..10b84d6 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceStatesTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceStatesTest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.TargetApi;
 import android.app.job.JobInfo;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.UiDevice;
 
@@ -58,12 +59,21 @@
         assertJobNotReady(STATE_JOB_ID);
     }
 
+    static void waitFor(long waitMillis) throws Exception {
+        final long deadline = SystemClock.uptimeMillis() + waitMillis;
+        do {
+             Thread.sleep(500L);
+        } while (SystemClock.uptimeMillis() < deadline);
+    }
+
     /**
      * Toggle device is dock idle or dock active.
      */
     private void toggleFakeDeviceDockState(final boolean idle) throws Exception {
         mUiDevice.executeShellCommand("cmd jobscheduler trigger-dock-state "
                 + (idle ? "idle" : "active"));
+        // Wait a moment to let that happen before proceeding.
+        waitFor(2_000);
     }
 
     /**
@@ -71,12 +81,12 @@
      */
     private void toggleScreenOn(final boolean screenon) throws Exception {
         if (screenon) {
-            mUiDevice.wakeUp();
+            mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
         } else {
-            mUiDevice.sleep();
+            mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
         }
         // Since the screen on/off intent is ordered, they will not be sent right now.
-        Thread.sleep(3000);
+        waitFor(2_000);
     }
 
     /**
@@ -84,6 +94,8 @@
      */
     private void triggerIdleMaintenance() throws Exception {
         mUiDevice.executeShellCommand("cmd activity idle-maintenance");
+        // Wait a moment to let that happen before proceeding.
+        waitFor(2_000);
     }
 
     /**
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
similarity index 85%
rename from tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
rename to tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index e624a62..ffe684d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 
 import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,12 +51,12 @@
 import org.junit.runner.RunWith;
 
 /**
- * Tests that temp whitelisted apps can run jobs if all the other constraints are met
+ * Tests related to job throttling -- device idle, app standby and battery saver.
  */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-public class DeviceIdleJobsTest {
-    private static final String TAG = DeviceIdleJobsTest.class.getSimpleName();
+public class JobThrottlingTest {
+    private static final String TAG = JobThrottlingTest.class.getSimpleName();
     private static final String TEST_APP_PACKAGE = "android.jobscheduler.cts.jobtestapp";
     private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestJobSchedulerReceiver";
     private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestActivity";
@@ -100,7 +101,7 @@
                     break;
                 case ACTION_DEVICE_IDLE_MODE_CHANGED:
                 case ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
-                    synchronized (DeviceIdleJobsTest.this) {
+                    synchronized (JobThrottlingTest.this) {
                         mDeviceInDoze = mPowerManager.isDeviceIdleMode();
                         Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze);
                     }
@@ -151,7 +152,7 @@
         assertFalse("Job started without being tempwhitelisted", awaitJobStart(5_000));
         tempWhitelistTestApp(5_000);
         assertTrue("Job with allow_while_idle flag did not start when the app was tempwhitelisted",
-                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+                awaitJobStart(5_000));
     }
 
     @Test
@@ -190,24 +191,71 @@
     public void testJobsInNeverApp() throws Exception {
         assumeTrue("app standby not enabled", mAppStandbyEnabled);
 
-        enterFakeUnpluggedState();
+        BatteryUtils.runDumpsysBatteryUnplug();
         setTestPackageStandbyBucket(Bucket.NEVER);
         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
         sendScheduleJobBroadcast(false);
         assertFalse("New job started in NEVER standby", awaitJobStart(3_000));
-        resetFakeUnpluggedState();
     }
 
     @Test
     public void testUidActiveBypassesStandby() throws Exception {
-        enterFakeUnpluggedState();
+        BatteryUtils.runDumpsysBatteryUnplug();
         setTestPackageStandbyBucket(Bucket.NEVER);
         tempWhitelistTestApp(6_000);
         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
         sendScheduleJobBroadcast(false);
         assertTrue("New job in uid-active app failed to start in NEVER standby",
                 awaitJobStart(4_000));
-        resetFakeUnpluggedState();
+    }
+
+    @Test
+    public void testBatterySaverOff() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(false);
+        sendScheduleJobBroadcast(false);
+        assertTrue("New job failed to start with battery saver OFF", awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testBatterySaverOn() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        sendScheduleJobBroadcast(false);
+        assertFalse("New job started with battery saver ON", awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testUidActiveBypassesBatterySaverOn() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        tempWhitelistTestApp(6_000);
+        sendScheduleJobBroadcast(false);
+        assertTrue("New job in uid-active app failed to start with battery saver OFF",
+                awaitJobStart(3_000));
+    }
+
+    @Test
+    public void testBatterySaverOnThenUidActive() throws Exception {
+        BatteryUtils.assumeBatterySaverFeature();
+
+        // Enable battery saver, and schedule a job. It shouldn't run.
+        BatteryUtils.runDumpsysBatteryUnplug();
+        BatteryUtils.enableBatterySaver(true);
+        sendScheduleJobBroadcast(false);
+        assertFalse("New job started with battery saver ON", awaitJobStart(3_000));
+
+
+        // Then make the UID active. Now the job should run.
+        tempWhitelistTestApp(120_000);
+        assertTrue("New job in uid-active app failed to start with battery saver OFF",
+                awaitJobStart(120_000));
     }
 
     @After
@@ -221,6 +269,8 @@
         mContext.sendBroadcast(cancelJobsIntent);
         mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
         mContext.unregisterReceiver(mReceiver);
+        BatteryUtils.runDumpsysBatteryReset();
+
         Thread.sleep(500); // To avoid any race between unregister and the next register in setUp
         waitUntilTestAppNotInTempWhitelist();
     }
@@ -255,7 +305,7 @@
         mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce"));
         assertTrue("Could not change device idle state to " + idle,
                 waitUntilTrue(SHELL_TIMEOUT, () -> {
-                    synchronized (DeviceIdleJobsTest.this) {
+                    synchronized (JobThrottlingTest.this) {
                         return mDeviceInDoze == idle;
                     }
                 }));
@@ -286,14 +336,6 @@
                 + " " + bucketName);
     }
 
-    private void enterFakeUnpluggedState() throws Exception {
-        mUiDevice.executeShellCommand("dumpsys battery unplug");
-    }
-
-    private void resetFakeUnpluggedState() throws Exception  {
-        mUiDevice.executeShellCommand("dumpsys battery reset");
-    }
-
     private boolean waitUntilTestAppNotInTempWhitelist() throws Exception {
         long now;
         boolean interrupted = false;
diff --git a/tests/JobSchedulerSharedUid/jobperm/Android.mk b/tests/JobSchedulerSharedUid/jobperm/Android.mk
index 8be235f..a95f0b3 100644
--- a/tests/JobSchedulerSharedUid/jobperm/Android.mk
+++ b/tests/JobSchedulerSharedUid/jobperm/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_MODULE_TAGS := tests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-annotations \
     compatibility-device-util \
 
 LOCAL_SRC_FILES := \
diff --git a/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java b/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
index 5c6b4b2..330ccac 100644
--- a/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
+++ b/tests/JobSchedulerSharedUid/jobperm/src/android/jobscheduler/cts/shareduid/jobperm/JobPermProvider.java
@@ -16,8 +16,6 @@
 
 package android.jobscheduler.cts.shareduid.jobperm;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Intent;
@@ -25,6 +23,8 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/tests/autofillservice/assets/login.html b/tests/autofillservice/assets/login.html
index 60e7cf7..4d8effc 100644
--- a/tests/autofillservice/assets/login.html
+++ b/tests/autofillservice/assets/login.html
@@ -17,8 +17,8 @@
 <html>
 <body>
 <form action="login.html" name="FORM AM I">
-    Username: <input type="text" name="username" autocomplete="username" placeholder="There's no place like a holder"/><br/>
-    Password: <input type="password" name="password" autocomplete="current-password" placeholder="Holder it like it cannnot passer a word"/><br/>
+    Username: <input type="text" name="username" autocomplete="username" placeholder="There's no place like a holder" oninput="javascript:JsHandler.onUsernameChanged(this.value)"/><br/>
+    Password: <input type="password" name="password" autocomplete="current-password" placeholder="Holder it like it cannnot passer a word" oninput="javascript:JsHandler.onPasswordChanged(this.value)"/><br/>
     <br/>
     <input type="submit" value="Login"/>
 </form>
diff --git a/tests/autofillservice/res/layout/fragment_container.xml b/tests/autofillservice/res/layout/fragment_container.xml
index 156efad..f5600e6 100644
--- a/tests/autofillservice/res/layout/fragment_container.xml
+++ b/tests/autofillservice/res/layout/fragment_container.xml
@@ -19,4 +19,6 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
     android:id="@+id/rootContainer" />
diff --git a/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml b/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml
deleted file mode 100644
index 73926a9..0000000
--- a/tests/autofillservice/res/layout/login_with_custom_highlight_activity.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:focusable="true"
-    android:theme="@style/MyAutofilledHighlight"
-    android:focusableInTouchMode="true"
-    android:orientation="vertical" >
-
-    <LinearLayout
-        android:id="@+id/username_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <TextView
-            android:id="@+id/username_label"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@+string/username_string" />
-
-        <EditText
-            android:id="@+id/username"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <TextView
-            android:id="@+id/password_label"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@+string/password_string" />
-
-        <EditText
-            android:id="@+id/password"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:inputType="textPassword"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal" >
-
-        <Button
-            android:id="@+id/clear"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Clear" />
-
-        <Button
-            android:id="@+id/save"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Save" />
-
-        <Button
-            android:id="@+id/login"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Login" />
-
-        <Button
-            android:id="@+id/cancel"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Cancel" />
-    </LinearLayout>
-
-    <TextView
-        android:id="@+id/output"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/login_with_strings_activity.xml b/tests/autofillservice/res/layout/login_with_strings_activity.xml
index 2f90761..972f53f 100644
--- a/tests/autofillservice/res/layout/login_with_strings_activity.xml
+++ b/tests/autofillservice/res/layout/login_with_strings_activity.xml
@@ -33,7 +33,7 @@
             android:id="@+id/username_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@+string/username_string" />
+            android:text="@string/username_string" />
 
         <EditText
             android:id="@+id/username"
@@ -50,7 +50,7 @@
             android:id="@+id/password_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@+string/password_string" />
+            android:text="@string/password_string" />
 
         <EditText
             android:id="@+id/password"
diff --git a/tests/autofillservice/res/layout/view_attribute_test_activity.xml b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
index d03d65c..f2b7c3b 100644
--- a/tests/autofillservice/res/layout/view_attribute_test_activity.xml
+++ b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
@@ -19,6 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
     android:id="@+id/rootContainer">
     <EditText android:id="@+id/editTextNoHint"
         android:layout_width="wrap_content"
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
index a8a8091..3ee3e79 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -22,10 +22,13 @@
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.view.PixelCopy;
 import android.view.View;
 import android.view.autofill.AutofillManager;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.SynchronousPixelCopy;
 
 import java.util.concurrent.CountDownLatch;
@@ -36,6 +39,7 @@
   */
 abstract class AbstractAutoFillActivity extends Activity {
 
+    private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
     private MyAutofillCallback mCallback;
 
     /**
@@ -121,13 +125,46 @@
         unregisterNonNullCallback();
     }
 
+    /**
+     * Waits until {@link #onDestroy()} is called.
+     */
+    public void waintUntilDestroyed(@NonNull Timeout timeout) throws InterruptedException {
+        if (!mDestroyedLatch.await(timeout.ms(), TimeUnit.MILLISECONDS)) {
+            throw new RetryableException(timeout, "activity %s not destroyed", this);
+        }
+    }
+
     private void unregisterNonNullCallback() {
         getAutofillManager().unregisterCallback(mCallback);
         mCallback = null;
     }
 
     @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        AutofillTestWatcher.registerActivity("onCreate()", this);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+
+        // Activitiy is typically unregistered at finish(), but we need to unregister here too
+        // for the cases where it's destroyed due to a config change (like device rotation).
+        AutofillTestWatcher.unregisterActivity("onDestroy()", this);
+        mDestroyedLatch.countDown();
+    }
+
+    @Override
     public void finish() {
+        finishOnly();
+        AutofillTestWatcher.unregisterActivity("finish()", this);
+    }
+
+    /**
+     * Finishes the activity, without unregistering it from {@link AutofillTestWatcher}.
+     */
+    void finishOnly() {
         if (mCallback != null) {
             unregisterNonNullCallback();
         }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
new file mode 100644
index 0000000..b9d631e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractGridActivityTestCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base class for test cases using {@link GridActivity}.
+ */
+abstract class AbstractGridActivityTestCase
+        extends AutoFillServiceTestCase.AutoActivityLaunch<GridActivity> {
+
+    protected GridActivity mActivity;
+
+    @Override
+    protected AutofillActivityTestRule<GridActivity> getActivityRule() {
+        return new AutofillActivityTestRule<GridActivity>(GridActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                postActivityLaunched();
+            }
+        };
+    }
+
+    /**
+     * Hook for subclass to customize activity after it's launched.
+     */
+    protected void postActivityLaunched() {
+    }
+
+    /**
+     * Focus to a cell and expect window event
+     */
+    protected void focusCell(int row, int column) throws TimeoutException {
+        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column));
+    }
+
+    /**
+     * Focus to a cell and expect no window event.
+     */
+    protected void focusCellNoWindowChange(int row, int column) {
+        final AccessibilityEvent event;
+        try {
+            event = mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
+                    Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
+        } catch (TimeoutException ex) {
+            // no window events! looking good
+            return;
+        }
+        throw new IllegalStateException(String.format("Expect no window event when focusing to"
+                + " column %d row %d, but event happened: %s", row, column, event));
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
index 8ec9a3e..13c6e4d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractLoginActivityTestCase.java
@@ -17,61 +17,56 @@
 package android.autofillservice.cts;
 
 import android.view.View;
-
-import org.junit.Before;
-import org.junit.Rule;
+import android.view.accessibility.AccessibilityEvent;
 
 import java.util.concurrent.TimeoutException;
 
 /**
  * Base class for test cases using {@link LoginActivity}.
  */
-abstract class AbstractLoginActivityTestCase extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginActivity>(LoginActivity.class);
-
-    @Rule
-    public final AutofillActivityTestRule<CheckoutActivity> mCheckoutActivityRule =
-            new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class, false);
+abstract class AbstractLoginActivityTestCase
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginActivity> {
 
     protected LoginActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<LoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginActivity>(
+                LoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     /**
      * Requests focus on username and expect Window event happens.
      */
     protected void requestFocusOnUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus));
     }
 
     /**
      * Requests focus on username and expect no Window event happens.
      */
     protected void requestFocusOnUsernameNoWindowChange() {
+        final AccessibilityEvent event;
         try {
-            // TODO: define a small value in Timeout
-            mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                    Timeouts.UI_TIMEOUT.ms());
+            event = mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
+                    Timeouts.WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS);
         } catch (TimeoutException ex) {
             // no window events! looking good
             return;
         }
         throw new IllegalStateException("Expect no window event when focusing to"
-                + " username, but event happened");
+                + " username, but event happened: " + event);
     }
 
     /**
      * Requests focus on password and expect Window event happens.
      */
     protected void requestFocusOnPassword() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onPassword(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onPassword(View::requestFocus));
     }
-
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
index 50526dd..97ed220 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AttachedContextActivityTest.java
@@ -18,24 +18,25 @@
 
 import android.autofillservice.cts.AttachedContextActivity.FillExpectation;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Makes sure activity with attached context can be autofilled.
  */
-public class AttachedContextActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<AttachedContextActivity> mActivityRule =
-            new AutofillActivityTestRule<>(AttachedContextActivity.class);
+public class AttachedContextActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<AttachedContextActivity> {
 
     private AttachedContextActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<AttachedContextActivity> getActivityRule() {
+        return new AutofillActivityTestRule<AttachedContextActivity>(
+                AttachedContextActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 577975f..94fc88c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -17,15 +17,13 @@
 package android.autofillservice.cts;
 
 import static android.autofillservice.cts.Helper.getContext;
-import static android.autofillservice.cts.Helper.getLoggingLevel;
-import static android.autofillservice.cts.Helper.hasAutofillFeature;
-import static android.autofillservice.cts.Helper.setLoggingLevel;
 import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
 import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
 import android.autofillservice.cts.common.SettingsStateKeeperRule;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
@@ -33,179 +31,266 @@
 import android.util.Log;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+
 import com.android.compatibility.common.util.RequiredFeatureRule;
 
-import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
 
 /**
- * Base class for all other tests.
+ * Placeholder for the base class for all integration tests:
+ *
+ * <ul>
+ *   <li>{@link AutoActivityLaunch}
+ *   <li>{@link ManualActivityLaunch}
+ * </ul>
+ *
+ * <p>These classes provide the common infrastructure such as:
+ *
+ * <ul>
+ *   <li>Preserving the autofill service settings.
+ *   <li>Cleaning up test state.
+ *   <li>Wrapping the test under autofill-specific test rules.
+ *   <li>Launching the activity used by the test.
+ * </ul>
  */
-@RunWith(AndroidJUnit4.class)
-// NOTE: @ClassRule requires it to be public
-public abstract class AutoFillServiceTestCase {
-    private static final String TAG = "AutoFillServiceTestCase";
+final class AutoFillServiceTestCase {
 
-    static final UiBot sDefaultUiBot = new UiBot();
+    /**
+     * Base class for all test cases that use an {@link AutofillActivityTestRule} to
+     * launch the activity.
+     */
+    // Must be public becaue of @ClassRule
+    public abstract static class AutoActivityLaunch<A extends AbstractAutoFillActivity>
+            extends BaseTestCase {
 
-    protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
+        @ClassRule
+        public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
+                sTheRealServiceSettingsKeeper;
 
-    private static final Context sContext = InstrumentationRegistry.getTargetContext();
-
-    @ClassRule
-    public static final SettingsStateKeeperRule mServiceSettingsKeeper =
-            new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE);
-
-    @Rule
-    public final TestWatcher watcher = new TestWatcher() {
-        @Override
-        protected void starting(Description description) {
-            JUnitHelper.setCurrentTestName(description.getDisplayName());
+        protected AutoActivityLaunch() {
+            super(sDefaultUiBot);
         }
 
         @Override
-        protected void finished(Description description) {
-            JUnitHelper.setCurrentTestName(null);
+        protected TestRule getMainTestRule() {
+            return getActivityRule();
         }
-    };
 
-    @Rule
-    public final RetryRule mRetryRule = new RetryRule(2);
+        /**
+         * Gets the rule to launch the main activity for this test.
+         *
+         * <p><b>Note: </b>the rule must be either lazily generated or a static singleton, otherwise
+         * this method could return {@code null} when the rule chain that uses it is constructed.
+         *
+         */
+        protected abstract @NonNull AutofillActivityTestRule<A> getActivityRule();
 
-    @Rule
-    public final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
-
-    @Rule
-    public final RequiredFeatureRule mRequiredFeatureRule =
-            new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
-
-    @Rule
-    public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
-            .setDumper(mLoggingRule)
-            .run(() -> sReplier.assertNoUnhandledFillRequests())
-            .run(() -> sReplier.assertNoUnhandledSaveRequests())
-            .add(() -> { return sReplier.getExceptions(); });
-
-    protected final Context mContext = sContext;
-    protected final String mPackageName;
-    protected final UiBot mUiBot;
-
-    /**
-     * Stores the previous logging level so it's restored after the test.
-     */
-    private String mLoggingLevel;
-
-    protected AutoFillServiceTestCase() {
-        this(sDefaultUiBot);
-    }
-
-    protected AutoFillServiceTestCase(UiBot uiBot) {
-        mPackageName = mContext.getPackageName();
-        mUiBot = uiBot;
-        mUiBot.reset();
-    }
-
-    @BeforeClass
-    public static void prepareScreen() throws Exception {
-        if (!hasAutofillFeature()) return;
-
-        // Unlock screen.
-        runShellCommand("input keyevent KEYCODE_WAKEUP");
-
-        // Collapse notifications.
-        runShellCommand("cmd statusbar collapse");
-
-        // Set orientation as portrait, otherwise some tests might fail due to elements not fitting
-        // in, IME orientation, etc...
-        sDefaultUiBot.setScreenOrientation(UiBot.PORTRAIT);
-    }
-
-    @Before
-    public void cleanupStaticState() {
-        Helper.preTestCleanup();
-        sReplier.reset();
-    }
-
-    @Before
-    public void setVerboseLogging() {
-        try {
-            mLoggingLevel = getLoggingLevel();
-        } catch (Exception e) {
-            Log.w(TAG, "Could not get previous logging level: " + e);
-            mLoggingLevel = "debug";
+        protected @NonNull A launchActivity(@NonNull Intent intent) {
+            return getActivityRule().launchActivity(intent);
         }
-        try {
-            setLoggingLevel("verbose");
-        } catch (Exception e) {
-            Log.w(TAG, "Could not change logging level to verbose: " + e);
+
+        protected @NonNull A getActivity() {
+            return getActivityRule().getActivity();
         }
     }
 
     /**
-     * Cleans up activities that might have been left over.
+     * Base class for all test cases that don't require an {@link AutofillActivityTestRule}.
      */
-    @Before
-    @After
-    public void finishActivities() {
-        WelcomeActivity.finishIt(mUiBot);
-    }
+    // Must be public becaue of @ClassRule
+    public abstract static class ManualActivityLaunch extends BaseTestCase {
 
-    @After
-    public void resetVerboseLogging() {
-        try {
-            setLoggingLevel(mLoggingLevel);
-        } catch (Exception e) {
-            Log.w(TAG, "Could not restore logging level to " + mLoggingLevel + ": " + e);
+        @ClassRule
+        public static final SettingsStateKeeperRule sPublicServiceSettingsKeeper =
+                sTheRealServiceSettingsKeeper;
+
+        protected ManualActivityLaunch() {
+            this(sDefaultUiBot);
+        }
+
+        protected ManualActivityLaunch(@NonNull UiBot uiBot) {
+            super(uiBot);
+        }
+
+        @Override
+        protected TestRule getMainTestRule() {
+            return new TestRule() {
+
+                @Override
+                public Statement apply(Statement base, Description description) {
+                    // Returns a no-op statements
+                    return new Statement() {
+                        @Override
+                        public void evaluate() throws Throwable {
+                            base.evaluate();
+                        }
+                    };
+                }
+            };
         }
     }
 
-    @After
-    public void ignoreFurtherRequests() {
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
+    @RunWith(AndroidJUnit4.class)
+    private abstract static class BaseTestCase {
+
+        private static final String TAG = "AutoFillServiceTestCase";
+
+        protected static final Replier sReplier = InstrumentedAutoFillService.getReplier();
+
+        protected static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+        // Hack because JUnit requires that @ClassRule instance belong to a public class.
+        protected static final SettingsStateKeeperRule sTheRealServiceSettingsKeeper =
+                new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE) {
+            @Override
+            protected void preEvaluate(Description description) {
+                JUnitHelper.setCurrentTestClass(description.getClassName());
+            }
+
+            @Override
+            protected void postEvaluate(Description description) {
+                JUnitHelper.setCurrentTestClass(null);
+            }
+        };
+
+        private final TestWatcher mTestWatcher = new AutofillTestWatcher();
+
+        private final RetryRule mRetryRule = new RetryRule(1);
+
+        private final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
+
+        private final RequiredFeatureRule mRequiredFeatureRule =
+                new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
+
+        protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+                .setDumper(mLoggingRule)
+                .run(() -> sReplier.assertNoUnhandledFillRequests())
+                .run(() -> sReplier.assertNoUnhandledSaveRequests())
+                .add(() -> { return sReplier.getExceptions(); });
+
+        @Rule
+        public final RuleChain mLookAllTheseRules = RuleChain
+                //
+                // mRequiredFeatureRule should be first so the test can be skipped right away
+                .outerRule(mRequiredFeatureRule)
+                //
+                // mTestWatcher should always be one the first rules, as it defines the name of the
+                // test being ran and finishes dangling activities at the end
+                .around(mTestWatcher)
+                //
+                // mLoggingRule wraps the test but doesn't interfere with it
+                .around(mLoggingRule)
+                //
+                // mRetryRule will retry test in case of failure
+                .around(mRetryRule)
+                //
+                // mSafeCleanerRule should be closest to the main test as possible.
+                .around(mSafeCleanerRule)
+                //
+                // Finally, let subclasses add their own rules (like ActivityTestRule)
+                .around(getMainTestRule());
+
+
+        protected final Context mContext = sContext;
+        protected final String mPackageName;
+        protected final UiBot mUiBot;
+
+        private BaseTestCase(@NonNull UiBot uiBot) {
+            mPackageName = mContext.getPackageName();
+            mUiBot = uiBot;
+            mUiBot.reset();
+        }
+
+        /**
+         * Gets the test-specific {@link Rule @Rule}.
+         *
+         * <p>Sub-class <b>MUST</b> override this method instead of annotation their own rules,
+         * so the order is preserved.
+         *
+         */
+        @NonNull
+        protected abstract TestRule getMainTestRule();
+
+        @Before
+        public void prepareDevice() throws Exception {
+            Log.v(TAG, "@Before: prepareDevice()");
+
+            // Unlock screen.
+            runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+            // Dismiss keyguard, in case it's set as "Swipe to unlock".
+            runShellCommand("wm dismiss-keyguard");
+
+            // Collapse notifications.
+            runShellCommand("cmd statusbar collapse");
+
+            // Set orientation as portrait, otherwise some tests might fail due to elements not
+            // fitting in, IME orientation, etc...
+            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+
+            // Wait until device is idle to avoid flakiness
+            mUiBot.waitForIdle();
+        }
+
+        @Before
+        public void preTestCleanup() {
+            Log.v(TAG, "@Before: preTestCleanup()");
+
+            disableService();
+
+            InstrumentedAutoFillService.resetStaticState();
+            AuthenticationActivity.resetStaticState();
+            sReplier.reset();
+        }
+
+        /**
+         * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
+         */
+        protected void enableService() {
+            Helper.enableAutofillService(getContext(), SERVICE_NAME);
+        }
+
+        /**
+         * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
+         */
+        protected void disableService() {
+            Helper.disableAutofillService(getContext(), SERVICE_NAME);
+        }
+
+        /**
+         * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
+         */
+        protected void assertServiceEnabled() {
+            Helper.assertAutofillServiceStatus(SERVICE_NAME, true);
+        }
+
+        /**
+         * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
+         */
+        protected void assertServiceDisabled() {
+            Helper.assertAutofillServiceStatus(SERVICE_NAME, false);
+        }
+
+        protected RemoteViews createPresentation(String message) {
+            final RemoteViews presentation = new RemoteViews(getContext()
+                    .getPackageName(), R.layout.list_item);
+            presentation.setTextViewText(R.id.text1, message);
+            return presentation;
+        }
     }
 
-    /**
-     * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
-     */
-    protected void enableService() {
-        Helper.enableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(false);
-    }
+    protected static final UiBot sDefaultUiBot = new UiBot();
 
-    /**
-     * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
-     */
-    protected void disableService() {
-        if (!hasAutofillFeature()) return;
-
-        Helper.disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
-    }
-
-    /**
-     * Asserts that the {@link InstrumentedAutoFillService} is enabled for the default user.
-     */
-    protected void assertServiceEnabled() {
-        Helper.assertAutofillServiceStatus(SERVICE_NAME, true);
-    }
-
-    /**
-     * Asserts that the {@link InstrumentedAutoFillService} is disabled for the default user.
-     */
-    protected void assertServiceDisabled() {
-        Helper.assertAutofillServiceStatus(SERVICE_NAME, false);
-    }
-
-    protected RemoteViews createPresentation(String message) {
-        final RemoteViews presentation = new RemoteViews(getContext()
-                .getPackageName(), R.layout.list_item);
-        presentation.setTextViewText(R.id.text1, message);
-        return presentation;
+    private AutoFillServiceTestCase() {
+        throw new UnsupportedOperationException("Contain static stuff only");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
index 5fae8bf..5f2d476 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
@@ -27,13 +27,12 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.content.Intent;
 import android.service.autofill.SaveInfo;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.view.ViewGroup;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import org.junit.Test;
 
 import java.util.concurrent.atomic.AtomicReference;
@@ -41,22 +40,30 @@
 /**
  * Tests that the session finishes when the views and fragments go away
  */
-public class AutoFinishSessionTest extends AutoFillServiceTestCase {
+public class AutoFinishSessionTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<FragmentContainerActivity> {
 
     private static final String ID_BUTTON = "button";
 
-    @Rule
-    public final AutofillActivityTestRule<FragmentContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<>(FragmentContainerActivity.class);
     private FragmentContainerActivity mActivity;
     private EditText mEditText1;
     private EditText mEditText2;
     private Fragment mFragment;
     private ViewGroup mParent;
 
-    @Before
-    public void initViews() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<FragmentContainerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FragmentContainerActivity>(
+                FragmentContainerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                initViews(getActivity());
+            }
+        };
+    }
+
+    private void initViews(FragmentContainerActivity activitiy) {
+        mActivity = activitiy;
         mEditText1 = mActivity.findViewById(R.id.editText1);
         mEditText2 = mActivity.findViewById(R.id.editText2);
         mFragment = mActivity.getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
@@ -75,16 +82,15 @@
                 .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, viewsToSave).build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
-
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
         sReplier.getNextFillRequest();
-
         mUiBot.assertNoDatasetsEver();
 
+        // Now it's safe to focus on editText1 without triggering a new partition due to race
+        // conditions
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
+
         // remove first set of views
         mActivity.syncRunOnUiThread(() -> {
             mEditText1.setText("editText1-filled");
@@ -205,16 +211,16 @@
                 .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText1").build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
 
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
         sReplier.getNextFillRequest();
-
         mUiBot.assertNoDatasetsEver();
 
+        // Now it's safe to focus on editText1 without triggering a new partition due to race
+        // conditions
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
+
         mActivity.syncRunOnUiThread(() -> {
             mEditText1.setText("editText1-filled");
             mEditText2.setText("editText2-filled");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
index 7e365932..1985ba6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillActivityTestRule.java
@@ -15,13 +15,13 @@
  */
 package android.autofillservice.cts;
 
-import android.app.Activity;
 import android.support.test.rule.ActivityTestRule;
 
 /**
  * Custom {@link ActivityTestRule}.
  */
-public class AutofillActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
+public class AutofillActivityTestRule<T extends AbstractAutoFillActivity>
+        extends ActivityTestRule<T> {
 
     public AutofillActivityTestRule(Class<T> activityClass) {
         super(activityClass);
@@ -30,4 +30,12 @@
     public AutofillActivityTestRule(Class<T> activityClass, boolean launchActivity) {
         super(activityClass, false, launchActivity);
     }
+
+    @Override
+    protected void afterActivityFinished() {
+        // AutofillTestWatcher does not need to watch for this activity as the ActivityTestRule
+        // will take care of finishing it...
+        AutofillTestWatcher.unregisterActivity("AutofillActivityTestRule.afterActivityFinished()",
+                getActivity());
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index 7c5f02e..aacd200 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -18,9 +18,10 @@
 
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 
-import androidx.annotation.NonNull;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import org.junit.AssumptionViolatedException;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -52,7 +53,6 @@
             @Override
             public void evaluate() throws Throwable {
                 final String testName = description.getDisplayName();
-                Log.v(TAG, "@Before " + testName);
                 final String levelBefore = runShellCommand("cmd autofill get log_level");
                 if (!levelBefore.equals("verbose")) {
                     runShellCommand("cmd autofill set log_level verbose");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
new file mode 100644
index 0000000..e9477b8
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.autofillservice.cts;
+
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.util.Set;
+
+/**
+ * Custom {@link TestWatcher} that's the outer rule of all {@link AutoFillServiceTestCase} tests.
+ *
+ * <p>This class is not thread safe, but should be fine...
+ */
+public final class AutofillTestWatcher extends TestWatcher {
+
+    private static final String TAG = "AutofillTestWatcher";
+
+    @GuardedBy("sUnfinishedBusiness")
+    private static final Set<AbstractAutoFillActivity> sUnfinishedBusiness = new ArraySet<>();
+
+    @GuardedBy("sAllActivities")
+    private static final Set<AbstractAutoFillActivity> sAllActivities = new ArraySet<>();
+
+    @Override
+    protected void starting(Description description) {
+        resetStaticState();
+        final String testName = description.getDisplayName();
+        Log.i(TAG, "Starting " + testName);
+        JUnitHelper.setCurrentTestName(testName);
+    }
+
+    @Override
+    protected void finished(Description description) {
+        final String testName = description.getDisplayName();
+        try {
+            finishActivities();
+            waitUntilAllDestroyed();
+        } finally {
+            resetStaticState();
+        }
+        Log.i(TAG, "Finished " + testName);
+        JUnitHelper.setCurrentTestName(null);
+    }
+
+    private void resetStaticState() {
+        synchronized (sUnfinishedBusiness) {
+            sUnfinishedBusiness.clear();
+        }
+        synchronized (sAllActivities) {
+            sAllActivities.clear();
+        }
+    }
+
+    /**
+     * Registers an activity so it's automatically finished (if necessary) after the test.
+     */
+    public static void registerActivity(@NonNull String where,
+            @NonNull AbstractAutoFillActivity activity) {
+        synchronized (sUnfinishedBusiness) {
+            if (sUnfinishedBusiness.contains(activity)) {
+                throw new IllegalStateException("Already registered " + activity);
+            }
+            Log.v(TAG, "registering activity on " + where + ": " + activity);
+            sUnfinishedBusiness.add(activity);
+            sAllActivities.add(activity);
+        }
+        synchronized (sAllActivities) {
+            sAllActivities.add(activity);
+
+        }
+    }
+
+    /**
+     * Unregisters an activity so it's not automatically finished after the test.
+     */
+    public static void unregisterActivity(@NonNull String where,
+            @NonNull AbstractAutoFillActivity activity) {
+        synchronized (sUnfinishedBusiness) {
+            final boolean unregistered = sUnfinishedBusiness.remove(activity);
+            if (unregistered) {
+                Log.d(TAG, "unregistered activity on " + where + ": " + activity);
+            } else {
+                Log.v(TAG, "ignoring already unregistered activity on " + where + ": " + activity);
+            }
+        }
+    }
+
+    private void finishActivities() {
+        synchronized (sUnfinishedBusiness) {
+            if (sUnfinishedBusiness.isEmpty()) {
+                return;
+            }
+            Log.d(TAG, "Manually finishing " + sUnfinishedBusiness.size() + " activities");
+            for (AbstractAutoFillActivity activity : sUnfinishedBusiness) {
+                if (activity.isFinishing()) {
+                    Log.v(TAG, "Ignoring activity that isFinishing(): " + activity);
+                } else {
+                    Log.d(TAG, "Finishing activity: " + activity);
+                    activity.finishOnly();
+                }
+            }
+        }
+    }
+
+    private void waitUntilAllDestroyed() {
+        synchronized (sAllActivities) {
+            if (sAllActivities.isEmpty()) return;
+
+            Log.d(TAG, "Waiting until " + sAllActivities.size() + " activities are destroyed");
+            for (AbstractAutoFillActivity activity : sAllActivities) {
+                Log.d(TAG, "Waiting for " + activity);
+                try {
+                    activity.waintUntilDestroyed(Timeouts.ACTIVITY_RESURRECTION);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "interrupted waiting for " + activity + " to be destroyed");
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index a63afe4..3a0c294 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -35,8 +35,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /*
@@ -54,10 +52,8 @@
  *  CheckoutActivityTest.
  */
 @AppModeFull // Unit test
-public class AutofillValueTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<AllAutofillableViewsActivity> mActivityRule =
-            new AutofillActivityTestRule<>(AllAutofillableViewsActivity.class);
+public class AutofillValueTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<AllAutofillableViewsActivity> {
 
     private AllAutofillableViewsActivity mActivity;
     private EditText mEditText;
@@ -69,9 +65,8 @@
     private DatePicker mDatePicker;
     private TimePicker mTimePicker;
 
-    @Before
-    public void setFields() {
-        mActivity = mActivityRule.getActivity();
+    private void setFields(AllAutofillableViewsActivity activity) {
+        mActivity = activity;
 
         mEditText = (EditText) mActivity.findViewById(R.id.editText);
         mCompoundButton = (CompoundButton) mActivity.findViewById(R.id.compoundButton);
@@ -83,6 +78,17 @@
         mTimePicker = (TimePicker) mActivity.findViewById(R.id.timePicker);
     }
 
+    @Override
+    protected AutofillActivityTestRule<AllAutofillableViewsActivity> getActivityRule() {
+        return new AutofillActivityTestRule<AllAutofillableViewsActivity>(
+                AllAutofillableViewsActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                setFields(getActivity());
+            }
+        };
+    }
+
     @Test
     public void createTextValue() throws Exception {
         assertThat(AutofillValue.forText(null)).isNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
index 141b8d0..f5e7f87 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
@@ -21,7 +21,6 @@
 
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.CheckBox;
@@ -46,7 +45,6 @@
  * </ul>
  */
 public class CheckoutActivity extends AbstractAutoFillActivity {
-    private static final String TAG = "CheckoutActivity";
     private static final long BUY_TIMEOUT_MS = 1000;
 
     static final String ID_CC_NUMBER = "cc_number";
@@ -76,12 +74,6 @@
     private FillExpectation mExpectation;
     private CountDownLatch mBuyLatch;
 
-    private static CheckoutActivity sInstance;
-
-    public CheckoutActivity() {
-        sInstance = this;
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -106,21 +98,6 @@
         mClearButton.setOnClickListener((v) -> resetFields());
     }
 
-    @Override
-    public void finish() {
-        super.finish();
-
-        sInstance = null;
-    }
-
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_CC_NUMBER, Timeouts.ACTIVITY_RESURRECTION);
-        }
-    }
-
     protected int getContentView() {
         return R.layout.checkout_activity;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 6739a73..ac181ec 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -52,8 +52,6 @@
 import android.widget.RemoteViews;
 import android.widget.Spinner;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Arrays;
@@ -62,17 +60,19 @@
 /**
  * Test case for an activity containing non-TextField views.
  */
-public class CheckoutActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<CheckoutActivity> mActivityRule =
-        new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class);
+public class CheckoutActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<CheckoutActivity> {
 
     private CheckoutActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<CheckoutActivity> getActivityRule() {
+        return new AutofillActivityTestRule<CheckoutActivity>(CheckoutActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
index 7858278..49e772d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionDateTest.java
@@ -33,24 +33,25 @@
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Calendar;
 
 @AppModeFull // Service-specific test
-public class CustomDescriptionDateTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<DatePickerSpinnerActivity> mActivityRule = new
-            AutofillActivityTestRule<DatePickerSpinnerActivity>(DatePickerSpinnerActivity.class);
+public class CustomDescriptionDateTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DatePickerSpinnerActivity> {
 
     private DatePickerSpinnerActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<DatePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerSpinnerActivity>(
+                DatePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
index 08050e0..9827fc4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
@@ -40,25 +40,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.function.BiFunction;
 import java.util.regex.Pattern;
 
 @AppModeFull // Service-specific test
-public class CustomDescriptionTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class CustomDescriptionTest extends AbstractLoginActivityTestCase {
 
     /**
      * Base test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 642e372..83fec74 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -25,9 +25,10 @@
 import android.service.autofill.CustomDescription;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiObject2;
-import android.util.Log;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+
 import static org.junit.Assume.assumeTrue;
 
 import org.junit.Test;
@@ -46,11 +47,32 @@
  * <p>The overall behavior should be the same in both cases, although the implementation of the
  * tests per se will be sligthly different.
  */
-abstract class CustomDescriptionWithLinkTestCase extends AutoFillServiceTestCase {
+abstract class CustomDescriptionWithLinkTestCase<A extends AbstractAutoFillActivity> extends
+        AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    private static final String TAG = "CustomDescriptionWithLinkTestCase";
     private static final String ID_LINK = "link";
 
+    private final Class<A> mActivityClass;
+
+    protected A mActivity;
+
+    protected CustomDescriptionWithLinkTestCase(@NonNull Class<A> activityClass) {
+        mActivityClass = activityClass;
+    }
+
+    protected void startActivity() {
+        startActivity(false);
+    }
+
+    protected void startActivity(boolean remainOnRecents) {
+        final Intent intent = new Intent(mContext, mActivityClass);
+        if (remainOnRecents) {
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        mActivity = launchActivity(intent);
+    }
+
     /**
      * Tests scenarios when user taps a link in the custom description and then taps back:
      * the Save UI should have been restored.
@@ -69,13 +91,9 @@
     public final void testTapLink_changeOrientationThenTapBack() throws Exception {
         assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext));
 
-        final int width = mUiBot.getDevice().getDisplayWidth();
-        final int heigth = mUiBot.getDevice().getDisplayHeight();
-        final int min = Math.min(width, heigth);
-
-        assumeTrue("Screen size is too small (" + width + "x" + heigth + ")", min >= 500);
-        Log.d(TAG, "testTapLink_changeOrientationThenTapBack(): screen size is "
-                + width + "x" + heigth);
+        // If the screen is too small and the devices shows an IME, it might not have space for all
+        // UI elements after the device is rotated to landscape.
+        mUiBot.assumeMinimumResolution(500);
 
         mUiBot.setScreenOrientation(UiBot.PORTRAIT);
         try {
@@ -84,9 +102,11 @@
             saveUiRestoredAfterTappingLinkTest(
                     PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON);
         } finally {
-            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
             try {
+                mUiBot.setScreenOrientation(UiBot.PORTRAIT);
                 cleanUpAfterScreenOrientationIsBackToPortrait();
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
             } finally {
                 runShellCommand("wm density reset");
                 runShellCommand("wm size reset");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
index f0c3e04..35f3b73 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
@@ -13,21 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.autofillservice.cts;
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class DatePickerCalendarActivityTest extends DatePickerTestCase<DatePickerCalendarActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<DatePickerCalendarActivity> mActivityRule =
-        new AutofillActivityTestRule<DatePickerCalendarActivity>(DatePickerCalendarActivity.class);
-
     @Override
-    protected DatePickerCalendarActivity getDatePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<DatePickerCalendarActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerCalendarActivity>(
+                DatePickerCalendarActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
index 0fb026a..291d5aa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
@@ -13,21 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package android.autofillservice.cts;
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class DatePickerSpinnerActivityTest extends DatePickerTestCase<DatePickerSpinnerActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<DatePickerSpinnerActivity> mActivityRule =
-        new AutofillActivityTestRule<DatePickerSpinnerActivity>(DatePickerSpinnerActivity.class);
-
     @Override
-    protected DatePickerSpinnerActivity getDatePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<DatePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DatePickerSpinnerActivity>(
+                DatePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index c9afacc..07df4b3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -36,14 +36,14 @@
 /**
  * Base class for {@link AbstractDatePickerActivity} tests.
  */
-abstract class DatePickerTestCase<T extends AbstractDatePickerActivity>
-        extends AutoFillServiceTestCase {
+abstract class DatePickerTestCase<A extends AbstractDatePickerActivity>
+        extends AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    protected abstract T getDatePickerActivity();
+    protected A mActivity;
 
     @Test
     public void testAutoFillAndSave() throws Exception {
-        final T activity = getDatePickerActivity();
+        assertWithMessage("subclass did not set mActivity").that(mActivity).isNotNull();
 
         // Set service.
         enableService();
@@ -62,10 +62,10 @@
                     .build())
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_DATE_PICKER)
                 .build());
-        activity.expectAutoFill("2012/11/20", 2012, Calendar.DECEMBER, 20);
+        mActivity.expectAutoFill("2012/11/20", 2012, Calendar.DECEMBER, 20);
 
         // Trigger auto-fill.
-        activity.onOutput((v) -> v.requestFocus());
+        mActivity.onOutput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
 
         // Assert properties of DatePicker field.
@@ -76,11 +76,11 @@
         mUiBot.selectDataset("The end of the world");
 
         // Check the results.
-        activity.assertAutoFilled();
+        mActivity.assertAutoFilled();
 
         // Trigger save.
-        activity.setDate(2010, Calendar.DECEMBER, 12);
-        activity.tapOk();
+        mActivity.setDate(2010, Calendar.DECEMBER, 12);
+        mActivity.tapOk();
 
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
index 1e9fcf2..175d0cb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
@@ -24,21 +24,21 @@
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class DialogLauncherActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<DialogLauncherActivity> mActivityRule =
-            new AutofillActivityTestRule<DialogLauncherActivity>(DialogLauncherActivity.class);
+public class DialogLauncherActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DialogLauncherActivity> {
 
     private DialogLauncherActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<DialogLauncherActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DialogLauncherActivity>(DialogLauncherActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
index 2f4de15..d4d1ff4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -16,6 +16,9 @@
 
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.Timeouts.ACTIVITY_RESURRECTION;
+import static android.autofillservice.cts.Timeouts.CALLBACK_NOT_CALLED_TIMEOUT_MS;
+
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.content.Intent;
 import android.os.SystemClock;
@@ -28,7 +31,7 @@
 /**
  * Tests for the {@link android.service.autofill.FillResponse.Builder#disableAutofill(long)} API.
  */
-public class DisableAutofillTest extends AutoFillServiceTestCase {
+public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityLaunch {
 
     private static final String TAG = "DisableAutofillTest";
 
@@ -81,7 +84,10 @@
         ASSERT_ENABLED_AND_AUTOFILL
     }
 
-    private void launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
+    /**
+     * Launches and finishes {@link SimpleSaveActivity}, returning how long it took.
+     */
+    private long launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
         Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
         sReplier.assertNoUnhandledFillRequests();
 
@@ -96,6 +102,7 @@
 
         }
 
+        final long before = SystemClock.elapsedRealtime();
         final SimpleSaveActivity activity = startSimpleSaveActivity();
         final MyAutofillCallback callback = activity.registerCallback();
 
@@ -128,9 +135,13 @@
         } finally {
             activity.finish();
         }
+        return SystemClock.elapsedRealtime() - before;
     }
 
-    private void launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
+    /**
+     * Launches and finishes {@link PreSimpleSaveActivity}, returning how long it took.
+     */
+    private long launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
         Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
         sReplier.assertNoUnhandledFillRequests();
 
@@ -143,6 +154,7 @@
                     .build());
         }
 
+        final long before = SystemClock.elapsedRealtime();
         final PreSimpleSaveActivity activity = startPreSimpleSaveActivity();
         final MyAutofillCallback callback = activity.registerCallback();
 
@@ -170,6 +182,7 @@
         } finally {
             activity.finish();
         }
+        return SystemClock.elapsedRealtime() - before;
     }
 
     @Test
@@ -198,19 +211,19 @@
         enableService();
 
         // Need to wait the equivalent of launching 2 activities, plus some extra legging room
-        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+        final long duration = 2 * ACTIVITY_RESURRECTION.ms() + 500;
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder().disableAutofill(duration).build());
 
         // Trigger autofill for the first time.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
 
         // Launch activity again.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+        passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
 
         // Wait for the timeout, then try again, autofilling it this time.
-        SystemClock.sleep(duration + 1);
+        sleep(passedTime, duration);
         launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
 
         // Also try it on another activity.
@@ -268,7 +281,7 @@
         enableService();
 
         // Need to wait the equivalent of launching 2 activities, plus some extra legging room
-        final long duration = 2 * Timeouts.ACTIVITY_RESURRECTION.ms() + 500;
+        final long duration = 2 * ACTIVITY_RESURRECTION.ms() + 500;
 
         // Set expectations.
         sReplier.addResponse(new CannedFillResponse.Builder()
@@ -277,16 +290,16 @@
                 .build());
 
         // Trigger autofill for the first time.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+        long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
 
         // Launch activity again.
-        launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+        passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
 
         // Make sure other app is working.
-        launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+        passedTime += launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
 
         // Wait for the timeout, then try again, autofilling it this time.
-        SystemClock.sleep(duration + 1);
+        sleep(passedTime, duration);
         launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
     }
 
@@ -317,11 +330,26 @@
 
     private void assertAutofillEnabled(AbstractAutoFillActivity activity, boolean expected)
             throws Exception {
-        Timeouts.ACTIVITY_RESURRECTION.run(
+        ACTIVITY_RESURRECTION.run(
                 "assertAutofillEnabled(" + activity.getComponentName().flattenToShortString() + ")",
                 () -> {
                     return activity.getAutofillManager().isEnabled() == expected
                             ? Boolean.TRUE : null;
                 });
     }
+
+    private void sleep(long passedTime, long disableDuration) {
+        final long napTime = disableDuration - passedTime + 500;
+        if (napTime <= 0) {
+            // Throw an exception so ACTIVITY_RESURRECTION is increased
+            throw new RetryableException("took longer than expcted to launch activities: "
+                            + "passedTime=" + passedTime + "ms, disableDuration=" + disableDuration
+                            + ", ACTIVITY_RESURRECTION=" + ACTIVITY_RESURRECTION
+                            + ", CALLBACK_NOT_CALLED_TIMEOUT_MS=" + CALLBACK_NOT_CALLED_TIMEOUT_MS);
+        }
+        Log.v(TAG, "Sleeping for " + napTime + "ms (duration=" + disableDuration + "ms, passedTime="
+                + passedTime + ")");
+
+        SystemClock.sleep(napTime);
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 29ec2b1..420965b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -30,29 +30,26 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class DuplicateIdActivityTest extends AutoFillServiceTestCase {
-    private static final String LOG_TAG = DuplicateIdActivityTest.class.getSimpleName();
-    @Rule
-    public final AutofillActivityTestRule<DuplicateIdActivity> mActivityRule = new AutofillActivityTestRule<>(
-            DuplicateIdActivity.class);
+public class DuplicateIdActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<DuplicateIdActivity> {
 
-    private DuplicateIdActivity mActivity;
+    private static final String TAG = "DuplicateIdActivityTest";
+
+    @Override
+    protected AutofillActivityTestRule<DuplicateIdActivity> getActivityRule() {
+        return new AutofillActivityTestRule<DuplicateIdActivity>(DuplicateIdActivity.class);
+    }
 
     @Before
     public void setup() throws Exception {
         Helper.disableAutoRotation(mUiBot);
         mUiBot.setScreenOrientation(0);
-
-        mActivity = mActivityRule.getActivity();
     }
 
     @After
     public void teardown() {
-        mActivity.finish();
-
         Helper.allowAutoRotation();
     }
 
@@ -97,8 +94,8 @@
         AutofillId id1 = view1.getAutofillId();
         AutofillId id2 = view2.getAutofillId();
 
-        Log.i(LOG_TAG, "view1=" + id1);
-        Log.i(LOG_TAG, "view2=" + id2);
+        Log.v(TAG, "view1=" + id1);
+        Log.v(TAG, "view2=" + id2);
 
         // Both checkboxes use the same id
         assertThat(view1.getId()).isEqualTo(view2.getId());
@@ -128,8 +125,8 @@
         AutofillId recreatedId1 = views[0].getAutofillId();
         AutofillId recreatedId2 = views[1].getAutofillId();
 
-        Log.i(LOG_TAG, "restored view1=" + recreatedId1);
-        Log.i(LOG_TAG, "restored view2=" + recreatedId2);
+        Log.v(TAG, "restored view1=" + recreatedId1);
+        Log.v(TAG, "restored view2=" + recreatedId2);
 
         // For the restoring logic the two views are the same. Hence it might happen that the first
         // view is restored with the id of the second view or the other way round.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
index 28ecdf7..456f5cf 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
@@ -48,25 +48,24 @@
 import android.app.assist.AssistStructure.ViewNode;
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Test case for an activity containing useless auto-fill data that should be optimized out.
  */
-public class FatActivityTest extends AutoFillServiceTestCase {
+public class FatActivityTest extends AutoFillServiceTestCase.AutoActivityLaunch<FatActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<FatActivity> mActivityRule =
-        new AutofillActivityTestRule<FatActivity>(FatActivity.class);
-
-    private FatActivity mFatActivity;
+    private FatActivity mActivity;
     private ViewNode mRoot;
 
-    @Before
-    public void setActivity() {
-        mFatActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<FatActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FatActivity>(FatActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
@@ -78,7 +77,7 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Trigger auto-fill.
-        mFatActivity.onInput((v) -> v.requestFocus());
+        mActivity.onInput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
         mUiBot.assertNoDatasetsEver();
 
@@ -154,7 +153,7 @@
         sReplier.addResponse(NO_RESPONSE);
 
         // Trigger autofill.
-        mFatActivity.onInput((v) -> mFatActivity.getAutofillManager().requestAutofill(v));
+        mActivity.onInput((v) -> mActivity.getAutofillManager().requestAutofill(v));
         final FillRequest fillRequest = sReplier.getNextFillRequest();
         mUiBot.assertNoDatasetsEver();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index 1ba93c4..96e1fa9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -28,26 +28,20 @@
 
 import android.autofillservice.cts.Helper.FieldClassificationResult;
 import android.autofillservice.cts.common.SettingsStateChangerRule;
-import android.content.Context;
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.UserData;
-import android.support.test.InstrumentationRegistry;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.widget.EditText;
 
-import org.junit.Before;
 import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
 
 @AppModeFull // Service-specific test
-public class FieldsClassificationTest extends AutoFillServiceTestCase {
-
-    private static final Context sContext = InstrumentationRegistry.getContext();
+public class FieldsClassificationTest extends AbstractGridActivityTestCase {
 
     @ClassRule
     public static final SettingsStateChangerRule sFeatureEnabler =
@@ -74,17 +68,10 @@
     public static final SettingsStateChangerRule sUserDataMaxCategoryChanger =
             new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, "42");
 
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-
-    private GridActivity mActivity;
     private AutofillManager mAfm;
 
-    @Before
-    public void setFixtures() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected void postActivityLaunched() {
         mAfm = mActivity.getAutofillManager();
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
index 1eab92d..2e1ae0d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -53,8 +53,6 @@
 
 import com.google.common.collect.ImmutableMap;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
@@ -66,18 +64,7 @@
  * Test that uses {@link LoginActivity} to test {@link FillEventHistory}.
  */
 @AppModeFull // Service-specific test
-public class FillEventHistoryTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginActivity>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class FillEventHistoryTest extends AbstractLoginActivityTestCase {
 
     @Test
     public void testDatasetAuthenticationSelected() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
index d33f142..b95fec6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FragmentContainerActivity.java
@@ -17,6 +17,8 @@
 package android.autofillservice.cts;
 
 import android.os.Bundle;
+import android.widget.FrameLayout;
+
 import androidx.annotation.Nullable;
 
 import java.util.concurrent.CountDownLatch;
@@ -30,6 +32,7 @@
             FragmentContainerActivity.class.getName() + "#FRAGMENT_TAG";
     private CountDownLatch mResumed = new CountDownLatch(1);
     private CountDownLatch mStopped = new CountDownLatch(0);
+    private FrameLayout mRootContainer;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -37,6 +40,8 @@
 
         setContentView(R.layout.fragment_container);
 
+        mRootContainer = findViewById(R.id.rootContainer);
+
         // have to manually add fragment as we cannot remove it otherwise
         getFragmentManager().beginTransaction().add(R.id.rootContainer,
                 new FragmentWithEditText(), FRAGMENT_TAG).commitNow();
@@ -70,6 +75,17 @@
         mStopped.countDown();
     }
 
+    /**
+     * Sets whether the root container is focusable or not.
+     *
+     * <p>It's initially set as {@code trye} in the XML layout so autofill is not automatically
+     * triggered in the edit text before the service is prepared to handle it.
+     */
+    public void setRootContainerFocusable(boolean focusable) {
+        mRootContainer.setFocusable(focusable);
+        mRootContainer.setFocusableInTouchMode(focusable);
+    }
+
     public boolean waitUntilResumed() throws InterruptedException {
         return mResumed.await(Timeouts.UI_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
index 65deff56..e1379c3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
@@ -134,11 +134,12 @@
     }
 
     public String getText(int row, int column) throws InterruptedException {
+        final long timeoutMs = 100;
         final BlockingQueue<String> queue = new LinkedBlockingQueue<>(1);
-        onCell(row, column, (c) -> queue.offer(c.getText().toString()));
-        final String text = queue.poll(100, TimeUnit.MILLISECONDS);
+        onCell(row, column, (c) -> Helper.offer(queue, c.getText().toString(), timeoutMs));
+        final String text = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
         if (text == null) {
-            throw new RetryableException("text not set in 100ms");
+            throw new RetryableException("text not set in " + timeoutMs + "ms");
         }
         return text;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 7934730..e6772c4d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -16,7 +16,6 @@
 
 package android.autofillservice.cts;
 
-import static android.autofillservice.cts.InstrumentedAutoFillService.SERVICE_NAME;
 import static android.autofillservice.cts.UiBot.PORTRAIT;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
 import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
@@ -62,13 +61,14 @@
 import androidx.annotation.Nullable;
 
 import com.android.compatibility.common.util.BitmapUtils;
-import com.android.compatibility.common.util.RequiredFeatureRule;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 
 /**
@@ -98,8 +98,6 @@
      */
     public static final String UNUSED_AUTOFILL_VALUE = null;
 
-    private static final String CMD_LIST_SESSIONS = "cmd autofill list sessions";
-
     private static final String ACCELLEROMETER_CHANGE =
             "content insert --uri content://settings/system --bind name:s:accelerometer_rotation "
                     + "--bind value:i:%d";
@@ -737,13 +735,6 @@
     }
 
     /**
-     * Checks if device supports the Autofill feature.
-     */
-    public static boolean hasAutofillFeature() {
-        return RequiredFeatureRule.hasFeature(PackageManager.FEATURE_AUTOFILL);
-    }
-
-    /**
      * Checks if autofill window is fullscreen, see com.android.server.autofill.ui.FillUi.
      */
     public static boolean isAutofillWindowFullScreen(Context context) {
@@ -812,14 +803,6 @@
     }
 
     /**
-     * Asserts that there is a pending session for the given package.
-     */
-    public static void assertHasSessions(String packageName) {
-        final String result = runShellCommand(CMD_LIST_SESSIONS);
-        assertThat(result).contains(packageName);
-    }
-
-    /**
      * Gets the instrumentation context.
      */
     public static Context getContext() {
@@ -827,21 +810,6 @@
     }
 
     /**
-     * Cleans up the autofill state; should be called before pretty much any test.
-     */
-    public static void preTestCleanup() {
-        if (!hasAutofillFeature()) return;
-
-        Log.d(TAG, "preTestCleanup()");
-
-        disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
-
-        InstrumentedAutoFillService.resetStaticState();
-        AuthenticationActivity.resetStaticState();
-    }
-
-    /**
      * Asserts the node has an {@code HTMLInfo} property, with the given tag.
      */
     public static HtmlInfo assertHasHtmlTag(ViewNode node, String expectedTag) {
@@ -1170,10 +1138,8 @@
             return;
         }
 
-        final File dir = new File(LOCAL_DIRECTORY);
-        dir.mkdirs();
-        if (!dir.exists()) {
-            Log.e(TAG, "Could not create directory " + dir);
+        final File dir = getLocalDirectory();
+        if (dir == null) {
             throw new AssertionError("bitmap comparison failed for " + filename
                     + ", and bitmaps could not be dumped on " + dir);
         }
@@ -1184,21 +1150,83 @@
     }
 
     @Nullable
-    private static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File dir,
-            @NonNull String filename) throws IOException {
+    private static File getLocalDirectory() {
+        final File dir = new File(LOCAL_DIRECTORY);
+        dir.mkdirs();
+        if (!dir.exists()) {
+            Log.e(TAG, "Could not create directory " + dir);
+            return null;
+        }
+        return dir;
+    }
+
+    @Nullable
+    private static File createFile(@NonNull File dir, @NonNull String filename) throws IOException {
         final File file = new File(dir, filename);
         if (file.exists()) {
+            Log.v(TAG, "Deleting file " + file);
             file.delete();
         }
         if (!file.createNewFile()) {
             Log.e(TAG, "Could not create file " + file);
             return null;
         }
-        Log.d(TAG, "Dumping bitmap at " + file);
+        return file;
+    }
+
+    @Nullable
+    private static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File dir,
+            @NonNull String filename) throws IOException {
+        final File file = createFile(dir, filename);
+        if (file != null) {
+            dumpBitmap(bitmap, file);
+
+        }
+        return file;
+    }
+
+    @Nullable
+    public static File dumpBitmap(@NonNull Bitmap bitmap, @NonNull File file) throws IOException {
+        Log.i(TAG, "Dumping bitmap at " + file);
         BitmapUtils.saveBitmap(bitmap, file.getParent(), file.getName());
         return file;
     }
 
+    /**
+     * Creates a file in the device, using the name of the current test as a prefix.
+     */
+    @Nullable
+    public static File createTestFile(@NonNull String name) throws IOException {
+        final File dir = getLocalDirectory();
+        if (dir == null) return null;
+
+        final String prefix = JUnitHelper.getCurrentTestName().replaceAll("\\.|\\(|\\/", "_")
+                .replaceAll("\\)", "");
+        final String filename = prefix + "-" + name;
+
+        return createFile(dir, filename);
+    }
+
+    /**
+     * Offers an object to a queue or times out.
+     *
+     * @return {@code true} if the offer was accepted, {$code false} if it timed out or was
+     * interrupted.
+     */
+    public static <T> boolean offer(BlockingQueue<T> queue, T obj, long timeoutMs) {
+        boolean offered = false;
+        try {
+            offered = queue.offer(obj, timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "interrupted offering", e);
+            Thread.currentThread().interrupt();
+        }
+        if (!offered) {
+            Log.e(TAG, "could not offer " + obj + " in " + timeoutMs + "ms");
+        }
+        return offered;
+    }
+
     private Helper() {
         throw new UnsupportedOperationException("contain static methods only");
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
index c96bbbc..974f0cc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
@@ -30,25 +30,27 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Test case for an activity containing non-TextField views with initial values set on XML.
  */
 @AppModeFull // CheckoutActivityTest() is enough to test ephemeral apps support
-public class InitializedCheckoutActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<InitializedCheckoutActivity> mActivityRule =
-        new AutofillActivityTestRule<InitializedCheckoutActivity>(InitializedCheckoutActivity.class);
+public class InitializedCheckoutActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<InitializedCheckoutActivity> {
 
     private InitializedCheckoutActivity mCheckoutActivity;
 
-    @Before
-    public void setActivity() {
-        mCheckoutActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<InitializedCheckoutActivity> getActivityRule() {
+        return new AutofillActivityTestRule<InitializedCheckoutActivity>(
+                InitializedCheckoutActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mCheckoutActivity = getActivity();
+            }
+        };
+
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 42b6b41..ff9a07d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -34,6 +34,8 @@
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
@@ -52,6 +54,7 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -73,19 +76,24 @@
             new AtomicReference<>();
     private static final Replier sReplier = new Replier();
 
-    private static final Object sLock = new Object();
-
-    // @GuardedBy("sLock") // NOTE: not using annotation because of dependencies
-    private static boolean sIgnoreUnexpectedRequests = false;
-
-    // @GuardedBy("sLock") // NOTE: not using annotation because of dependencies
-    private static boolean sConnected;
+    private static AtomicBoolean sConnected = new AtomicBoolean(false);
 
     protected static String sServiceLabel = SERVICE_CLASS;
 
+    // We must handle all requests in a separate thread as the service's main thread is the also
+    // the UI thread of the test process and we don't want to hose it in case of failures here
+    private static final HandlerThread sMyThread = new HandlerThread("MyServiceThread");
+    private final Handler mHandler;
+
+    static {
+        Log.i(TAG, "Starting thread " + sMyThread);
+        sMyThread.start();
+    }
+
     public InstrumentedAutoFillService() {
         sInstance.set(this);
         sServiceLabel = SERVICE_CLASS;
+        mHandler = Handler.createAsync(sMyThread.getLooper());
     }
 
     private static InstrumentedAutoFillService peekInstance() {
@@ -158,59 +166,76 @@
         return sServiceLabel;
     }
 
+    private void handleConnected(boolean connected) {
+        Log.v(TAG, "handleConnected(): from " + sConnected.get() + " to " + connected);
+        sConnected.set(connected);
+    }
+
     @Override
     public void onConnected() {
-        synchronized (sLock) {
-            Log.v(TAG, "onConnected(): connected=" + sConnected);
-            sConnected = true;
-        }
+        mHandler.post(()->handleConnected(true));
     }
 
     @Override
     public void onDisconnected() {
-        synchronized (sLock) {
-            Log.v(TAG, "onDisconnected(): connected=" + sConnected);
-            sConnected = false;
-        }
+        mHandler.post(()->handleConnected(false));
     }
 
     @Override
     public void onFillRequest(android.service.autofill.FillRequest request,
             CancellationSignal cancellationSignal, FillCallback callback) {
-        if (DUMP_FILL_REQUESTS) dumpStructure("onFillRequest()", request.getFillContexts());
-        synchronized (sLock) {
-            if (sIgnoreUnexpectedRequests || !fromSamePackage(request.getFillContexts()))  {
-                Log.w(TAG, "Ignoring onFillRequest()");
-                return;
-            }
+
+        final ComponentName component = getLastActivityComponent(request.getFillContexts());
+        if (!JUnitHelper.isRunningTest()) {
+            Log.e(TAG, "onFillRequest(" + component + ") called after tests finished");
+            return;
         }
-        sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
-                cancellationSignal, callback, request.getFlags());
+        if (!fromSamePackage(component))  {
+            Log.w(TAG, "Ignoring onFillRequest() from different package: " + component);
+            return;
+        }
+        if (DUMP_FILL_REQUESTS) {
+            dumpStructure("onFillRequest()", request.getFillContexts());
+        } else {
+            Log.i(TAG, "onFillRequest() for " + component.toShortString());
+        }
+        mHandler.post(
+                () -> sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
+                        cancellationSignal, callback, request.getFlags()));
     }
 
     @Override
     public void onSaveRequest(android.service.autofill.SaveRequest request,
             SaveCallback callback) {
-        if (DUMP_SAVE_REQUESTS) dumpStructure("onSaveRequest()", request.getFillContexts());
-        synchronized (sLock) {
-            if (sIgnoreUnexpectedRequests || !fromSamePackage(request.getFillContexts())) {
-                Log.w(TAG, "Ignoring onSaveRequest()");
-                return;
-            }
+        mHandler.post(()->handleSaveRequest(request, callback));
+    }
+
+    private void handleSaveRequest(android.service.autofill.SaveRequest request,
+            SaveCallback callback) {
+        final ComponentName component = getLastActivityComponent(request.getFillContexts());
+        if (!JUnitHelper.isRunningTest()) {
+            Log.e(TAG, "onSaveRequest(" + component + ") called after tests finished");
+            return;
         }
-        sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
-                request.getDatasetIds());
+        if (!fromSamePackage(component)) {
+            Log.w(TAG, "Ignoring onSaveRequest() from different package: " + component);
+            return;
+        }
+        if (DUMP_SAVE_REQUESTS) {
+            dumpStructure("onSaveRequest()", request.getFillContexts());
+        } else {
+            Log.i(TAG, "onSaveRequest() for " + component.toShortString());
+        }
+        mHandler.post(() -> sReplier.onSaveRequest(request.getFillContexts(),
+                request.getClientState(), callback,
+                request.getDatasetIds()));
     }
 
     private static boolean isConnected() {
-        synchronized (sLock) {
-            return sConnected;
-        }
+        return sConnected.get();
     }
 
-    private boolean fromSamePackage(List<FillContext> contexts) {
-        final ComponentName component = contexts.get(contexts.size() - 1).getStructure()
-                .getActivityComponent();
+    private boolean fromSamePackage(ComponentName component) {
         final String actualPackage = component.getPackageName();
         if (!actualPackage.equals(getPackageName())
                 && !actualPackage.equals(sReplier.mAcceptedPackageName)) {
@@ -220,15 +245,8 @@
         return true;
     }
 
-    /**
-     * Sets whether unexpected calls to
-     * {@link #onFillRequest(android.service.autofill.FillRequest, CancellationSignal, FillCallback)}
-     * should throw an exception.
-     */
-    public static void setIgnoreUnexpectedRequests(boolean ignore) {
-        synchronized (sLock) {
-            sIgnoreUnexpectedRequests = ignore;
-        }
+    private ComponentName getLastActivityComponent(List<FillContext> contexts) {
+        return contexts.get(contexts.size() - 1).getStructure().getActivityComponent();
     }
 
     /**
@@ -269,7 +287,7 @@
 
     static void resetStaticState() {
         sInstance.set(null);
-        sConnected = false;
+        sConnected.set(false);
         sServiceLabel = SERVICE_CLASS;
     }
 
@@ -406,8 +424,9 @@
          * Sets the {@link IntentSender} that is passed to
          * {@link SaveCallback#onSuccess(IntentSender)}.
          */
-        void setOnSave(IntentSender intentSender) {
+        Replier setOnSave(IntentSender intentSender) {
             mOnSaveIntentSender = intentSender;
+            return this;
         }
 
         /**
@@ -584,19 +603,24 @@
             } catch (Throwable t) {
                 addException(t);
             } finally {
-                mFillRequests.offer(new FillRequest(contexts, data, cancellationSignal, callback,
-                        flags));
+                Helper.offer(mFillRequests, new FillRequest(contexts, data, cancellationSignal,
+                        callback, flags), CONNECTION_TIMEOUT.ms());
             }
         }
 
         private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
                 List<String> datasetIds) {
             Log.d(TAG, "onSaveRequest(): sender=" + mOnSaveIntentSender);
-            mSaveRequests.offer(new SaveRequest(contexts, data, callback, datasetIds));
-            if (mOnSaveIntentSender != null) {
-                callback.onSuccess(mOnSaveIntentSender);
-            } else {
-                callback.onSuccess();
+
+            try {
+                if (mOnSaveIntentSender != null) {
+                    callback.onSuccess(mOnSaveIntentSender);
+                } else {
+                    callback.onSuccess();
+                }
+            } finally {
+                Helper.offer(mSaveRequests, new SaveRequest(contexts, data, callback, datasetIds),
+                        CONNECTION_TIMEOUT.ms());
             }
         }
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
index 28e1fde..2920500 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
@@ -17,21 +17,33 @@
 package android.autofillservice.cts;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 /**
  * Generic helper for JUnit needs.
  */
 public final class JUnitHelper {
 
-    private static String sCurrentTestNamer;
+    private static String sCurrentTestName;
+    private static String sCurrentTestClass;
 
     @NonNull
-    static String getCurrentTestName() {
-        return sCurrentTestNamer != null ? sCurrentTestNamer : "N/A";
+    public static String getCurrentTestName() {
+        if (sCurrentTestName != null) return sCurrentTestName;
+        if (sCurrentTestClass != null) return sCurrentTestClass;
+        return "(Unknown test)";
     }
 
-    public static void setCurrentTestName(String name) {
-        sCurrentTestNamer = name;
+    public static void setCurrentTestName(@Nullable String name) {
+        sCurrentTestName = name;
+    }
+
+    public static void setCurrentTestClass(@Nullable String testClass) {
+        sCurrentTestClass = testClass;
+    }
+
+    public static boolean isRunningTest() {
+        return sCurrentTestName != null;
     }
 
     private JUnitHelper() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index c476c7c..183ca34 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -86,6 +86,7 @@
 import android.view.autofill.AutofillManager;
 import android.widget.RemoteViews;
 
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
@@ -700,8 +701,7 @@
         mUiBot.assertDatasets("The Dude");
 
         // tapping outside autofill window should close it and raise ui hidden event
-        mUiBot.waitForWindowChange(() -> tap(mActivity.getUsernameLabel()),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> tap(mActivity.getUsernameLabel()));
         callback.assertUiHiddenEvent(username);
 
         mUiBot.assertNoDatasets();
@@ -972,13 +972,17 @@
             final String extraValue = saveRequest.data.getString("numbers");
             assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
         } finally {
-            // Make sure we can no longer add overlays
-            runShellCommand("appops set %s SYSTEM_ALERT_WINDOW ignore", mPackageName);
-            // Make sure the overlay is removed
-            mActivity.runOnUiThread(() -> {
-                WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-                windowManager.removeView(overlay[0]);
-            });
+            try {
+                // Make sure we can no longer add overlays
+                runShellCommand("appops set %s SYSTEM_ALERT_WINDOW ignore", mPackageName);
+                // Make sure the overlay is removed
+                mActivity.runOnUiThread(() -> {
+                    WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+                    windowManager.removeView(overlay[0]);
+                });
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
+            }
         }
     }
 
@@ -2154,6 +2158,7 @@
         mActivity.assertAutoFilled();
     }
 
+    @Ignore("blocked on b/4222506") // STOPSHIP: must fix and remove @Ignore before Q is launched
     @Test
     public void testCommitMultipleTimes() throws Throwable {
         // Set service.
@@ -2163,7 +2168,7 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build();
 
-        for (int i = 1; i <= 3; i++) {
+        for (int i = 1; i <= 10; i++) {
             Log.i(TAG, "testCommitMultipleTimes(): step " + i);
             final String username = "user-" + i;
             final String password = "pass-" + i;
@@ -2219,7 +2224,7 @@
         // Set service.
         enableService();
 
-        for (int i = 1; i <= 3; i++) {
+        for (int i = 1; i <= 10; i++) {
             Log.i(TAG, "testCancelMultipleTimes(): step " + i);
             final String username = "user-" + i;
             final String password = "pass-" + i;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
index 3193cc3..cefc58c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithCustomHighlightActivityTest.java
@@ -25,24 +25,24 @@
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
 
-public class LoginWithCustomHighlightActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginWithCustomHighlightActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginWithCustomHighlightActivity>(
-            LoginWithCustomHighlightActivity.class);
+public class LoginWithCustomHighlightActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginWithCustomHighlightActivity> {
 
     private LoginWithCustomHighlightActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<LoginWithCustomHighlightActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginWithCustomHighlightActivity>(
+                LoginWithCustomHighlightActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
@@ -84,7 +84,6 @@
      * Requests focus on username and expect Window event happens.
      */
     protected void requestFocusOnUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.onUsername(View::requestFocus));
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
index 8474d28d..131b40f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
@@ -40,22 +40,24 @@
 import android.platform.test.annotations.AppModeFull;
 import android.view.View;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 @AppModeFull // LoginActivityTest is enough to test ephemeral apps support
-public class LoginWithStringsActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<LoginWithStringsActivity> mActivityRule =
-            new AutofillActivityTestRule<LoginWithStringsActivity>(LoginWithStringsActivity.class);
+public class LoginWithStringsActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<LoginWithStringsActivity> {
 
     private LoginWithStringsActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+
+    @Override
+    protected AutofillActivityTestRule<LoginWithStringsActivity> getActivityRule() {
+        return new AutofillActivityTestRule<LoginWithStringsActivity>(
+                LoginWithStringsActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
index c6cd06a..5aad499 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowEmptyActivity.java
@@ -49,8 +49,11 @@
     }
 
     public static MultiWindowEmptyActivity waitNewInstance() throws InterruptedException {
-        sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
-                TimeUnit.MILLISECONDS);
+        if (!sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
+                TimeUnit.MILLISECONDS)) {
+            throw new RetryableException("New MultiWindowLoginActivity didn't start",
+                    Timeouts.ACTIVITY_RESURRECTION);
+        }
         sLastInstanceLatch = null;
         return sLastInstance;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
index e0b3109..02460050 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivity.java
@@ -49,8 +49,11 @@
     }
 
     public static MultiWindowLoginActivity waitNewInstance() throws InterruptedException {
-        sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
-                TimeUnit.MILLISECONDS);
+        if (!sLastInstanceLatch.await(Timeouts.ACTIVITY_RESURRECTION.getMaxValue(),
+                TimeUnit.MILLISECONDS)) {
+            throw new RetryableException("New MultiWindowLoginActivity didn't start",
+                    Timeouts.ACTIVITY_RESURRECTION);
+        }
         sLastInstanceLatch = null;
         return sLastInstance;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
index 0796028..68d1556 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
@@ -15,7 +15,7 @@
  */
 package android.autofillservice.cts;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.autofillservice.cts.Helper.ID_PASSWORD;
 import static android.autofillservice.cts.Helper.ID_USERNAME;
 import static android.autofillservice.cts.common.ShellHelper.runShellCommand;
@@ -27,46 +27,50 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Intent;
 import android.platform.test.annotations.AppModeFull;
 import android.view.View;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
 
 @AppModeFull // This test requires android.permission.MANAGE_ACTIVITY_STACKS
-public class MultiWindowLoginActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<MultiWindowLoginActivity> mActivityRule =
-            new AutofillActivityTestRule<MultiWindowLoginActivity>(MultiWindowLoginActivity.class);
+public class MultiWindowLoginActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<MultiWindowLoginActivity> {
 
     private LoginActivity mActivity;
-    protected ActivityManager mAm;
+    private ActivityManager mAm;
+    private ActivityTaskManager mAtm;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<MultiWindowLoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<MultiWindowLoginActivity>(
+                MultiWindowLoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                mAm = mContext.getSystemService(ActivityManager.class);
+                mAtm = mContext.getSystemService(ActivityTaskManager.class);
+            }
+        };
     }
 
     @Before
     public void setup() {
         assumeTrue("Skipping test: no split multi-window support",
-                ActivityManager.supportsSplitScreenMultiWindow(mContext));
-        mAm = mContext.getSystemService(ActivityManager.class);
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
     }
 
     /**
      * Touch a view and exepct autofill window change
      */
     protected void tapViewAndExpectWindowEvent(View view) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> tap(view), Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> tap(view));
     }
 
-
     protected String runAmStartActivity(Class<? extends Activity> activityClass, int flags) {
         return runAmStartActivity(activityClass.getName(), flags);
     }
@@ -80,7 +84,7 @@
      * Put activity1 in TOP, will be followed by amStartActivity()
      */
     protected void splitWindow(Activity activity1) {
-        mAm.setTaskWindowingModeSplitScreenPrimary(activity1.getTaskId(),
+        mAtm.setTaskWindowingModeSplitScreenPrimary(activity1.getTaskId(),
                 SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, false, null, true);
 
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
index 8da4add..ad08fd3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleFragmentLoginTest.java
@@ -30,24 +30,28 @@
 import android.view.autofill.AutofillValue;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-public class MultipleFragmentLoginTest extends AutoFillServiceTestCase {
-    private static final String LOG_TAG = MultipleFragmentLoginTest.class.getSimpleName();
-    @Rule
-    public final AutofillActivityTestRule<FragmentContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<>(FragmentContainerActivity.class);
+public class MultipleFragmentLoginTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<FragmentContainerActivity> {
+
+    private static final String LOG_TAG = "MultipleFragmentLoginTest";
+
     private FragmentContainerActivity mActivity;
     private EditText mEditText1;
     private EditText mEditText2;
 
-    @Before
-    public void init() {
-        mActivity = mActivityRule.getActivity();
-        mEditText1 = mActivity.findViewById(R.id.editText1);
-        mEditText2 = mActivity.findViewById(R.id.editText2);
+    @Override
+    protected AutofillActivityTestRule<FragmentContainerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<FragmentContainerActivity>(
+                FragmentContainerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+                mEditText1 = mActivity.findViewById(R.id.editText1);
+                mEditText2 = mActivity.findViewById(R.id.editText2);
+            }
+        };
     }
 
     @Test
@@ -64,21 +68,18 @@
                 .setExtras(clientState)
                 .build());
 
-        final InstrumentedAutoFillService.FillRequest[] fillRequest =
-                new InstrumentedAutoFillService.FillRequest[1];
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
+        final InstrumentedAutoFillService.FillRequest fillRequest1 = sReplier.getNextFillRequest();
+        assertThat(fillRequest1.data).isNull();
 
-        fillRequest[0] = sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver(); // UI is only shown on editText1
 
-        assertThat(fillRequest[0].data).isNull();
+        mActivity.setRootContainerFocusable(false);
 
-        AssistStructure structure = fillRequest[0].contexts.get(0).getStructure();
-        assertThat(fillRequest[0].contexts.size()).isEqualTo(1);
+        final AssistStructure structure = fillRequest1.contexts.get(0).getStructure();
+        assertThat(fillRequest1.contexts.size()).isEqualTo(1);
         assertThat(findNodeByResourceId(structure, "editText1")).isNotNull();
         assertThat(findNodeByResourceId(structure, "editText2")).isNotNull();
         assertThat(findNodeByResourceId(structure, "editText3")).isNull();
@@ -86,6 +87,7 @@
         assertThat(findNodeByResourceId(structure, "editText5")).isNull();
 
         // Wait until autofill has been applied
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
         mUiBot.selectDataset("dataset1");
         mUiBot.assertShownByText("editText1-autofilled");
 
@@ -111,15 +113,15 @@
                         R.id.rootContainer, new FragmentWithMoreEditTexts(),
                         FRAGMENT_TAG).commitNow());
         EditText editText5 = mActivity.findViewById(R.id.editText5);
-        fillRequest[0] = sReplier.getNextFillRequest();
+        final InstrumentedAutoFillService.FillRequest fillRequest2 = sReplier.getNextFillRequest();
 
         // The fillRequest should have a fillContext for each partition. The first partition
         // should be filled in
-        assertThat(fillRequest[0].contexts.size()).isEqualTo(2);
+        assertThat(fillRequest2.contexts.size()).isEqualTo(2);
 
-        assertThat(fillRequest[0].data.getString("key")).isEqualTo("value1");
+        assertThat(fillRequest2.data.getString("key")).isEqualTo("value1");
 
-        AssistStructure structure1 = fillRequest[0].contexts.get(0).getStructure();
+        final AssistStructure structure1 = fillRequest2.contexts.get(0).getStructure();
         ViewNode editText1Node = findNodeByResourceId(structure1, "editText1");
         // The actual value in the structure is not updated in FillRequest-contexts, but the
         // autofill value is. For text views in SaveRequest both are updated, but this is the
@@ -136,7 +138,7 @@
         assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
         assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
 
-        AssistStructure structure2 = fillRequest[0].contexts.get(1).getStructure();
+        final AssistStructure structure2 = fillRequest2.contexts.get(1).getStructure();
 
         assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
         assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
@@ -157,33 +159,33 @@
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
 
         // The saveRequest should have a fillContext for each partition with all the data
-        InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        final InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertThat(saveRequest.contexts.size()).isEqualTo(2);
 
         assertThat(saveRequest.data.getString("key")).isEqualTo("value2");
 
-        structure1 = saveRequest.contexts.get(0).getStructure();
-        editText1Node = findNodeByResourceId(structure1, "editText1");
+        final AssistStructure saveStructure1 = saveRequest.contexts.get(0).getStructure();
+        editText1Node = findNodeByResourceId(saveStructure1, "editText1");
         assertThat(editText1Node.getText().toString()).isEqualTo("editText1-autofilled");
 
-        editText2Node = findNodeByResourceId(structure1, "editText2");
+        editText2Node = findNodeByResourceId(saveStructure1, "editText2");
         assertThat(editText2Node.getText().toString()).isEqualTo("editText2-manually-filled");
 
-        assertThat(findNodeByResourceId(structure1, "editText3")).isNull();
-        assertThat(findNodeByResourceId(structure1, "editText4")).isNull();
-        assertThat(findNodeByResourceId(structure1, "editText5")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText3")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText4")).isNull();
+        assertThat(findNodeByResourceId(saveStructure1, "editText5")).isNull();
 
-        structure2 = saveRequest.contexts.get(1).getStructure();
-        assertThat(findNodeByResourceId(structure2, "editText1")).isNull();
-        assertThat(findNodeByResourceId(structure2, "editText2")).isNull();
+        final AssistStructure saveStructure2 = saveRequest.contexts.get(1).getStructure();
+        assertThat(findNodeByResourceId(saveStructure2, "editText1")).isNull();
+        assertThat(findNodeByResourceId(saveStructure2, "editText2")).isNull();
 
-        ViewNode editText3Node = findNodeByResourceId(structure2, "editText3");
+        ViewNode editText3Node = findNodeByResourceId(saveStructure2, "editText3");
         assertThat(editText3Node.getText().toString()).isEqualTo("editText3-autofilled");
 
-        ViewNode editText4Node = findNodeByResourceId(structure2, "editText4");
+        ViewNode editText4Node = findNodeByResourceId(saveStructure2, "editText4");
         assertThat(editText4Node.getText().toString()).isEqualTo("editText4-autofilled");
 
-        ViewNode editText5Node = findNodeByResourceId(structure2, "editText5");
+        ViewNode editText5Node = findNodeByResourceId(saveStructure2, "editText5");
         assertThat(editText5Node.getText().toString()).isEqualTo("editText5-manually-filled");
     }
 
@@ -213,14 +215,15 @@
 
         sReplier.addResponse(response.build());
 
-        // Trigger autofill
-        mActivity.syncRunOnUiThread(() -> {
-            mEditText2.requestFocus();
-            mEditText1.requestFocus();
-        });
+        // Trigger autofill on editText2
+        mActivity.syncRunOnUiThread(() -> mEditText2.requestFocus());
+        sReplier.getNextFillRequest();
+        mUiBot.assertNoDatasetsEver(); // UI is only shown on editText1
+
+        mActivity.setRootContainerFocusable(false);
 
         // Check UI is shown, but don't select it.
-        sReplier.getNextFillRequest();
+        mActivity.syncRunOnUiThread(() -> mEditText1.requestFocus());
         mUiBot.assertDatasets("dataset1");
 
         // Switch fragments
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
index c04685d..f9b57f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MutableAutofillIdTest.java
@@ -38,31 +38,17 @@
 import android.view.autofill.AutofillId;
 import android.widget.EditText;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.List;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Test cases for the cases where the autofill id of a view is changed by the app.
  */
-public class MutableAutofillIdTest extends AutoFillServiceTestCase {
+public class MutableAutofillIdTest extends AbstractGridActivityTestCase {
 
     private static final String TAG = "MutableAutofillIdTest";
 
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-            new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-    private GridActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
-
     @Test
     public void testDatasetPickerIsNotShownAfterViewIsSwappedOut() throws Exception {
         enableService();
@@ -303,12 +289,4 @@
         assertThat(node2Context2.getIdEntry()).isEqualTo(ID_L1C2);
         assertThat(node2Context2.getText().toString()).isEqualTo("NOD2");
     }
-
-    /**
-     * Focus to a cell and expect window event
-     */
-    void focusCell(int row, int column) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                Timeouts.UI_TIMEOUT.getMaxValue());
-    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 2108a4b..ff3eef9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -17,10 +17,13 @@
 package android.autofillservice.cts;
 
 import static android.autofillservice.cts.Helper.callbackEventAsString;
+import static android.autofillservice.cts.Timeouts.CALLBACK_NOT_CALLED_TIMEOUT_MS;
 import static android.autofillservice.cts.Timeouts.CONNECTION_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillManager.AutofillCallback;
@@ -39,17 +42,33 @@
 
     public static final Timeout MY_TIMEOUT = CONNECTION_TIMEOUT;
 
+    // We must handle all requests in a separate thread as the service's main thread is the also
+    // the UI thread of the test process and we don't want to hose it in case of failures here
+    private static final HandlerThread sMyThread = new HandlerThread("MyCallbackThread");
+    private final Handler mHandler;
+
+    static {
+        Log.i(TAG, "Starting thread " + sMyThread);
+        sMyThread.start();
+    }
+
+    MyAutofillCallback() {
+        mHandler = Handler.createAsync(sMyThread.getLooper());
+    }
+
     @Override
     public void onAutofillEvent(View view, int event) {
-        Log.v(TAG, "onAutofillEvent: view=" + view + ", event=" + callbackEventAsString(event));
-        mEvents.offer(new MyEvent(view, event));
+        mHandler.post(() -> offer(new MyEvent(view, event)));
     }
 
     @Override
     public void onAutofillEvent(View view, int childId, int event) {
-        Log.v(TAG, "onAutofillEvent: view=" + view + ", child=" + childId
-                + ", event=" + callbackEventAsString(event));
-        mEvents.offer(new MyEvent(view, childId, event));
+        mHandler.post(() -> offer(new MyEvent(view, childId, event)));
+    }
+
+    private void offer(MyEvent event) {
+        Log.v(TAG, "offer: " + event);
+        Helper.offer(mEvents, event, MY_TIMEOUT.ms());
     }
 
     /**
@@ -67,7 +86,7 @@
      * Assert no more events were received.
      */
     void assertNotCalled() throws InterruptedException {
-        final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
+        final MyEvent event = mEvents.poll(CALLBACK_NOT_CALLED_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         if (event != null) {
             // Not retryable.
             throw new IllegalStateException("should not have received " + event);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
index 754d5e9..486cf7f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
@@ -21,8 +21,8 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.view.autofill.AutofillValue;
+import android.util.Log;
+import android.webkit.JavascriptInterface;
 import android.webkit.WebView;
 
 import java.util.concurrent.CountDownLatch;
@@ -33,14 +33,18 @@
  */
 public class MyWebView extends WebView {
 
+    private static final String TAG = "MyWebView";
+
     private FillExpectation mExpectation;
 
     public MyWebView(Context context) {
         super(context);
+        setJsHandler();
     }
 
     public MyWebView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        setJsHandler();
     }
 
     public void expectAutofill(String username, String password) {
@@ -49,63 +53,73 @@
 
     public void assertAutofilled() throws Exception {
         assertWithMessage("expectAutofill() not called").that(mExpectation).isNotNull();
-        final boolean set = mExpectation.mLatch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
-        if (mExpectation.mException != null) {
-            throw mExpectation.mException;
-        }
-        assertWithMessage("Timeout (%s ms) expecting autofill()", FILL_TIMEOUT.ms())
-                .that(set).isTrue();
-        assertWithMessage("Wrong value for username").that(mExpectation.mActualUsername)
-                .isEqualTo(mExpectation.mExpectedUsername);
-        assertWithMessage("Wrong value for password").that(mExpectation.mActualPassword)
-                .isEqualTo(mExpectation.mExpectedPassword);
+        mExpectation.assertUsernameCalled();
+        mExpectation.assertPasswordCalled();
     }
 
-    @Override
-    public void autofill(SparseArray<AutofillValue> values) {
-        super.autofill(values);
-
-        if (mExpectation == null) return;
-
-        try {
-            if (values == null || values.size() != 2) {
-                mExpectation.mException =
-                        new IllegalArgumentException("Invalid values on autofill(): " + values);
-            } else {
-                try {
-                    // We don't know the order of the values in the array. As we're just expecting
-                    // 2, it's easy to just check them individually; if we had more, than we would
-                    // need to override onProvideAutofillVirtualStructure() to keep track of the
-                    // nodes added by WebView so we could save their AutofillIds and reuse here.
-                    final String value1 = values.valueAt(0).getTextValue().toString();
-                    final String value2 = values.valueAt(1).getTextValue().toString();
-                    if (mExpectation.mExpectedUsername.equals(value1)) {
-                        mExpectation.mActualUsername = value1;
-                        mExpectation.mActualPassword = value2;
-                    } else {
-                        mExpectation.mActualUsername = value2;
-                        mExpectation.mActualPassword = value1;
-                    }
-                } catch (Exception e) {
-                    mExpectation.mException = e;
-                }
-            }
-        } finally {
-            mExpectation.mLatch.countDown();
-        }
+    private void setJsHandler() {
+        getSettings().setJavaScriptEnabled(true);
+        addJavascriptInterface(new JavascriptHandler(), "JsHandler");
     }
 
     private class FillExpectation {
-        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private final CountDownLatch mUsernameLatch = new CountDownLatch(1);
+        private final CountDownLatch mPasswordLatch = new CountDownLatch(1);
         private final String mExpectedUsername;
         private final String mExpectedPassword;
         private String mActualUsername;
         private String mActualPassword;
-        private Exception mException;
 
         FillExpectation(String username, String password) {
             this.mExpectedUsername = username;
             this.mExpectedPassword = password;
         }
+
+        void setUsername(String username) {
+            mActualUsername = username;
+            mUsernameLatch.countDown();
+        }
+
+        void setPassword(String password) {
+            mActualPassword = password;
+            mPasswordLatch.countDown();
+        }
+
+        void assertUsernameCalled() throws Exception {
+            assertCalled(mUsernameLatch, "username");
+            assertWithMessage("Wrong value for username").that(mExpectation.mActualUsername)
+                .isEqualTo(mExpectation.mExpectedUsername);
+        }
+
+        void assertPasswordCalled() throws Exception {
+            assertCalled(mPasswordLatch, "password");
+            assertWithMessage("Wrong value for password").that(mExpectation.mActualPassword)
+                    .isEqualTo(mExpectation.mExpectedPassword);
+        }
+
+        private void assertCalled(CountDownLatch latch, String field) throws Exception {
+            if (!latch.await(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS)) {
+                throw new RetryableException(FILL_TIMEOUT, "%s not called", field);
+            }
+        }
+    }
+
+    private class JavascriptHandler {
+
+        @JavascriptInterface
+        public void onUsernameChanged(String username) {
+            Log.d(TAG, "onUsernameChanged():" + username);
+            if (mExpectation != null) {
+                mExpectation.setUsername(username);
+            }
+        }
+
+        @JavascriptInterface
+        public void onPasswordChanged(String password) {
+            Log.d(TAG, "onPasswordChanged():" + password);
+            if (mExpectation != null) {
+                mExpectation.setPassword(password);
+            }
+        }
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
index 5aaedde..ac48f28 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
@@ -33,8 +33,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -49,20 +47,22 @@
  * </ul>
  */
 @AppModeFull // Service-specific test
-public class OptionalSaveActivityTest extends AutoFillServiceTestCase {
+public class OptionalSaveActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<OptionalSaveActivity> {
 
     private static final boolean EXPECT_NO_SAVE_UI = false;
     private static final boolean EXPECT_SAVE_UI = false;
 
-    @Rule
-    public final AutofillActivityTestRule<OptionalSaveActivity> mActivityRule =
-        new AutofillActivityTestRule<OptionalSaveActivity>(OptionalSaveActivity.class);
-
     private OptionalSaveActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<OptionalSaveActivity> getActivityRule() {
+        return new AutofillActivityTestRule<OptionalSaveActivity>(OptionalSaveActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d37bd16..767c56d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -50,52 +50,13 @@
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.FillResponse;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
-import java.util.concurrent.TimeoutException;
-
 /**
  * Test case for an activity containing multiple partitions.
  */
 @AppModeFull // Service-specific test
-public class PartitionedActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<GridActivity> mActivityRule =
-        new AutofillActivityTestRule<GridActivity>(GridActivity.class);
-
-    private GridActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
-
-    /**
-     * Focus to a cell and expect window event
-     */
-    void focusCell(int row, int column) throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                Timeouts.UI_TIMEOUT.getMaxValue());
-    }
-
-    /**
-     * Focus to a cell and expect no window event.
-     */
-    void focusCellNoWindowChange(int row, int column) {
-        try {
-            // TODO: define a small value in Timeout
-            mUiBot.waitForWindowChange(() -> mActivity.focusCell(row, column),
-                    Timeouts.UI_TIMEOUT.ms());
-        } catch (TimeoutException ex) {
-            // no window events! looking good
-            return;
-        }
-        throw new IllegalStateException(String.format("Expect no window event when focusing to"
-                + " column %d row %d, but event happened", row, column));
-    }
+public class PartitionedActivityTest extends AbstractGridActivityTestCase {
 
     @Test
     public void testAutofillTwoPartitionsSkipFirst() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index 7ca496b..c476722 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -30,25 +30,25 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Covers scenarios where the behavior is different because some fields were pre-filled.
  */
 @AppModeFull // LoginActivityTest is enough to test ephemeral apps support
-public class PreFilledLoginActivityTest extends AutoFillServiceTestCase {
-
-    @Rule
-    public final AutofillActivityTestRule<PreFilledLoginActivity> mActivityRule =
-            new AutofillActivityTestRule<PreFilledLoginActivity>(PreFilledLoginActivity.class);
+public class PreFilledLoginActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<PreFilledLoginActivity> {
 
     private PreFilledLoginActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<PreFilledLoginActivity> getActivityRule() {
+        return new AutofillActivityTestRule<PreFilledLoginActivity>(PreFilledLoginActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index b3257f0..244d72f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -28,7 +28,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
-import android.content.Intent;
 import android.service.autofill.BatchUpdates;
 import android.service.autofill.CustomDescription;
 import android.service.autofill.RegexValidator;
@@ -38,31 +37,21 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
-import org.junit.After;
-import org.junit.Rule;
-
 import java.util.regex.Pattern;
 
-public class PreSimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
+public class PreSimpleSaveActivityTest
+        extends CustomDescriptionWithLinkTestCase<PreSimpleSaveActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<PreSimpleSaveActivity> mActivityRule =
+    private static final AutofillActivityTestRule<PreSimpleSaveActivity> sActivityRule =
             new AutofillActivityTestRule<PreSimpleSaveActivity>(PreSimpleSaveActivity.class, false);
 
-    private PreSimpleSaveActivity mActivity;
-
-    private void startActivity(boolean remainOnRecents) {
-        final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class);
-        if (remainOnRecents) {
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-        mActivity = mActivityRule.launchActivity(intent);
+    public PreSimpleSaveActivityTest() {
+        super(PreSimpleSaveActivity.class);
     }
 
-    @After
-    public void finishSimpleSaveActivity() {
-        SimpleSaveActivity.finishIt(mUiBot);
+    @Override
+    protected AutofillActivityTestRule<PreSimpleSaveActivity> getActivityRule() {
+        return sActivityRule;
     }
 
     @Override
@@ -81,7 +70,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -110,7 +98,7 @@
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt(mUiBot);
+                WelcomeActivity.finishIt();
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
@@ -140,7 +128,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -200,7 +187,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -234,7 +220,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -292,7 +277,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mPreInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -327,7 +311,6 @@
         newActivty.getAutofillManager().requestAutofill(newActivty.mInput);
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -373,7 +356,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
index 1dbafeb..e190cda 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRule.java
@@ -30,12 +30,18 @@
     private static final String TAG = "RetryRule";
     private final int mMaxAttempts;
 
-    public RetryRule(int maxAttempts) {
-        if (maxAttempts < 2) {
-            throw new IllegalArgumentException(
-                    "Must retry at least once; otherwise, what's the point?");
+    /**
+     * Retries the underlying test when it catches a {@link RetryableException}.
+     *
+     * @param retries number of retries. Use {@code 0} to disable rule.
+     *
+     * @throws IllegalArgumentException if {@code retries} is less than {@code 0}.
+     */
+    public RetryRule(int retries) {
+        if (retries < 0) {
+            throw new IllegalArgumentException("retries must be more than 0");
         }
-        mMaxAttempts = maxAttempts;
+        mMaxAttempts = retries + 1;
     }
 
     @Override
@@ -44,6 +50,13 @@
 
             @Override
             public void evaluate() throws Throwable {
+                if (mMaxAttempts <= 1) {
+                    Log.v(TAG, "Executing " + description.getDisplayName()
+                            + " right away because mMaxAttempts is " + mMaxAttempts);
+                    base.evaluate();
+                    return;
+                }
+
                 final String name = description.getDisplayName();
                 Throwable caught = null;
                 for (int i = 1; i <= mMaxAttempts; i++) {
@@ -53,7 +66,7 @@
                             Log.v(TAG, "Good News, Everyone! " + name + " passed right away");
                         } else {
                             Log.d(TAG,
-                                    "Better late than never: " + name + "passed at attempt #" + i);
+                                    "Better late than never: " + name + " passed at attempt #" + i);
                         }
                         return;
                     } catch (RetryableException e) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
index 3d182ed..0c23f9f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/RetryRuleTest.java
@@ -18,15 +18,22 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
+
 import android.platform.test.annotations.AppModeFull;
-import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
 import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(MockitoJUnitRunner.class)
 @AppModeFull // Unit test
 public class RetryRuleTest {
 
@@ -59,9 +66,16 @@
         }
     }
 
+    private @Mock Statement mMockStatement;
+
+    @Test
+    public void testInvalidConstructor() throws Throwable {
+        assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1));
+    }
+
     @Test
     public void testPassOnRetryableException() throws Throwable {
-        final RetryRule rule = new RetryRule(2);
+        final RetryRule rule = new RetryRule(1);
         rule.apply(new RetryableStatement<RetryableException>(1, sRetryableException), mDescription)
                 .evaluate();
     }
@@ -70,22 +84,58 @@
     public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
         final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
         final RetryableException exception = new RetryableException(timeout, "Y U NO?");
-        final RetryRule rule = new RetryRule(2);
+
+        final RetryRule rule = new RetryRule(1);
         rule.apply(new RetryableStatement<RetryableException>(1, exception), mDescription)
                 .evaluate();
+
         // Assert timeout was increased
         assertThat(timeout.ms()).isEqualTo(2);
     }
 
     @Test
     public void testFailOnRetryableException() throws Throwable {
-        final RetryRule rule = new RetryRule(2);
-        try {
-            rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException),
-                    mDescription).evaluate();
-            throw new AssertionError("2ND CALL, Y U NO FAIL?");
-        } catch (RetryableException e) {
-            assertThat(e).isSameAs(sRetryableException);
-        }
+        final RetryRule rule = new RetryRule(1);
+
+        final RetryableException actualException = expectThrows(RetryableException.class,
+                () -> rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException),
+                        mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(sRetryableException);
+    }
+
+    @Test
+    public void testPassWhenDisabledAndStatementPass() throws Throwable {
+        final RetryRule rule = new RetryRule(0);
+
+        rule.apply(mMockStatement, mDescription).evaluate();
+
+        verify(mMockStatement, times(1)).evaluate();
+    }
+
+    @Test
+    public void testFailWhenDisabledAndStatementThrowsRetryableException() throws Throwable {
+        final RetryableException exception = new RetryableException("Y U NO?");
+        final RetryRule rule = new RetryRule(0);
+        doThrow(exception).when(mMockStatement).evaluate();
+
+        final RetryableException actualException = expectThrows(RetryableException.class,
+                () -> rule.apply(mMockStatement, mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(exception);
+        verify(mMockStatement, times(1)).evaluate();
+    }
+
+    @Test
+    public void testFailWhenDisabledAndStatementThrowsNonRetryableException() throws Throwable {
+        final RuntimeException exception = new RuntimeException("Y U NO?");
+        final RetryRule rule = new RetryRule(0);
+        doThrow(exception).when(mMockStatement).evaluate();
+
+        final RuntimeException actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mMockStatement, mDescription).evaluate());
+
+        assertThat(actualException).isSameAs(exception);
+        verify(mMockStatement, times(1)).evaluate();
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
index bf36138..aefc0de 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
@@ -16,9 +16,10 @@
 
 package android.autofillservice.cts;
 
-import androidx.annotation.NonNull;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -55,7 +56,7 @@
     /**
      * Adds exceptions directly.
      *
-     * <p>Typically used when exceptions were caught asychronously during the test execution.
+     * <p>Typically used for exceptions caught asychronously during the test execution.
      */
     public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
         mExtraThrowables.add(exceptions);
@@ -63,6 +64,17 @@
     }
 
     /**
+     * Adds exceptions directly.
+     *
+     * <p>Typically used for exceptions caught during {@code finally} blocks.
+     */
+    public SafeCleanerRule add(Throwable exception) {
+        Log.w(TAG, "Adding exception directly: " + exception);
+        mThrowables.add(exception);
+        return this;
+    }
+
+    /**
      * Sets a {@link Dumper} used to log errors.
      */
     public SafeCleanerRule setDumper(@NonNull Dumper dumper) {
@@ -79,8 +91,8 @@
                 try {
                     base.evaluate();
                 } catch (Throwable t) {
-                    Log.w(TAG, "Adding exception from main test");
-                    mThrowables.add(t);
+                    Log.w(TAG, "Adding exception from main test at index 0: " + t);
+                    mThrowables.add(0, t);
                 }
 
                 // Then the cleanup runners
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
index f41b001..28af015 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
@@ -149,6 +149,21 @@
     }
 
     @Test
+    public void testTestPass_oneExtraExceptionThrownAsCallable() throws Throwable {
+        final SafeCleanerRule rule = new SafeCleanerRule()
+                .run(mGoodGuyRunner1)
+                .add(mRuntimeException)
+                .add(mGoodGuyExtraExceptions1)
+                .run(mGoodGuyRunner2);
+        final Throwable actualException = expectThrows(RuntimeException.class,
+                () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
+        assertThat(actualException).isSameAs(mRuntimeException);
+        verify(mGoodGuyRunner1).run();
+        verify(mGoodGuyRunner2).run();
+        verify(mGoodGuyExtraExceptions1).call();
+    }
+
+    @Test
     public void testTestPass_oneExtraExceptionThrown() throws Throwable {
         final SafeCleanerRule rule = new SafeCleanerRule()
                 .run(mGoodGuyRunner1)
@@ -193,6 +208,7 @@
         final SafeCleanerRule rule = new SafeCleanerRule()
                 .run(mGoodGuyRunner1)
                 .add(mGoodGuyExtraExceptions1)
+                .add(mRuntimeException)
                 .add(() -> {
                     return ImmutableList.of(extra1, extra2);
                 })
@@ -212,7 +228,8 @@
                 SafeCleanerRule.MultipleExceptions.class,
                 () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
         assertThat(actualException.getThrowables())
-                .containsExactly(testException, error1, error2, extra1, extra2, extra3)
+                .containsExactly(testException, mRuntimeException, error1, error2, extra1, extra2,
+                        extra3)
                 .inOrder();
         verify(mGoodGuyRunner1).run();
         verify(mGoodGuyRunner2).run();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
index 9b03d0a..c6d77c5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -55,7 +55,7 @@
  * Test the lifecycle of a autofill session
  */
 @AppModeFull // This test requires android.permission.WRITE_EXTERNAL_STORAGE
-public class SessionLifecycleTest extends AutoFillServiceTestCase {
+public class SessionLifecycleTest extends AutoFillServiceTestCase.ManualActivityLaunch {
     private static final String ID_BUTTON = "button";
     private static final String ID_CANCEL = "cancel";
 
@@ -66,7 +66,7 @@
     private static final long WAIT_ACTIVITY_MS = 1000;
 
     private static final Timeout SESSION_LIFECYCLE_TIMEOUT = new Timeout(
-            "SESSION_LIFECYCLE_TIMEOUT", 2000, 2F, 5000);
+            "SESSION_LIFECYCLE_TIMEOUT", 5000, 2F, 5000);
 
     /**
      * Runs an {@code assertion}, retrying until {@code timeout} is reached.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
index af582f6..9e471e1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
@@ -27,25 +27,26 @@
 import android.support.test.uiautomator.UiObject2;
 
 import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 @AppModeFull // Service-specific test
-public class SettingsIntentTest extends AutoFillServiceTestCase {
+public class SettingsIntentTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<TrampolineForResultActivity> {
 
     private static final int MY_REQUEST_CODE = 42;
 
-    @Rule
-    public final AutofillActivityTestRule<TrampolineForResultActivity> mActivityRule =
-            new AutofillActivityTestRule<TrampolineForResultActivity>(
-                    TrampolineForResultActivity.class);
 
     protected TrampolineForResultActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<TrampolineForResultActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TrampolineForResultActivity>(
+                TrampolineForResultActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @After
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
index a9924f4..e292c3c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
@@ -50,14 +50,6 @@
         return sInstance;
     }
 
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_LABEL, Timeouts.ACTIVITY_RESURRECTION);
-        }
-    }
-
     public SimpleSaveActivity() {
         sInstance = this;
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index eb59da4..5c3177c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -54,34 +54,32 @@
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
-import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 
 import java.util.regex.Pattern;
 
-public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
+public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase<SimpleSaveActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<SimpleSaveActivity> mActivityRule =
+    private static final AutofillActivityTestRule<SimpleSaveActivity> sActivityRule =
             new AutofillActivityTestRule<SimpleSaveActivity>(SimpleSaveActivity.class, false);
 
-    @Rule
-    public final AutofillActivityTestRule<WelcomeActivity> mWelcomeActivityRule =
+    private static final AutofillActivityTestRule<WelcomeActivity> sWelcomeActivityRule =
             new AutofillActivityTestRule<WelcomeActivity>(WelcomeActivity.class, false);
 
-    private SimpleSaveActivity mActivity;
-
-    private void startActivity() {
-        startActivity(false);
+    public SimpleSaveActivityTest() {
+        super(SimpleSaveActivity.class);
     }
 
-    private void startActivity(boolean remainOnRecents) {
-        final Intent intent = new Intent(mContext, SimpleSaveActivity.class);
-        if (remainOnRecents) {
-            intent.setFlags(
-                    Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK);
-        }
-        mActivity = mActivityRule.launchActivity(intent);
+    @Override
+    protected AutofillActivityTestRule<SimpleSaveActivity> getActivityRule() {
+        return sActivityRule;
+    }
+
+    @Override
+    protected TestRule getMainTestRule() {
+        return RuleChain.outerRule(sActivityRule).around(sWelcomeActivityRule);
     }
 
     private void restartActivity() {
@@ -198,8 +196,12 @@
         try {
             saveTest(true);
         } finally {
-            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
-            cleanUpAfterScreenOrientationIsBackToPortrait();
+            try {
+                mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+                cleanUpAfterScreenOrientationIsBackToPortrait();
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
+            }
         }
     }
 
@@ -217,7 +219,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -250,25 +251,29 @@
         enableService();
 
         // Set expectations.
-        sReplier.setOnSave(WelcomeActivity.createSender(mContext, "Saved by the bell"));
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
-                .build());
+        sReplier.setOnSave(WelcomeActivity.createSender(mContext, "Saved by the bell"))
+                .addResponse(new CannedFillResponse.Builder()
+                        .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+                        .build());
 
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
+
+            // Disable autofill so it's not triggered again after WelcomeActivity finishes
+            // and mActivity is resumed (with focus on mInput) after the session is closed
+            mActivity.mInput.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
         });
 
         // Save it...
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         sReplier.getNextSaveRequest();
+
         // ... and assert activity was launched
         WelcomeActivity.assertShowing(mUiBot, "Saved by the bell");
     }
@@ -288,16 +293,23 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
-        // Trigger save and start a new session right away.
+        // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
             mActivity.mCommit.performClick();
-            mActivity.getAutofillManager().requestAutofill(mActivity.mInput);
         });
 
-        // Make sure Save UI for 1st session was canceled....
+        // Make sure Save UI for 1st session was shown....
+        mUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+        // ...then start the new session right away (without finishing the activity).
+        sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+        mActivity.syncRunOnUiThread(
+                () -> mActivity.getAutofillManager().requestAutofill(mActivity.mInput));
+        sReplier.getNextFillRequest();
+
+        // Make sure Save UI for 1st session was canceled.
         mUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
     }
 
@@ -371,7 +383,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Cancel session.
         mActivity.getAutofillManager().cancel();
@@ -422,7 +433,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -559,7 +569,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -590,7 +599,7 @@
                 break;
             case FINISH_ACTIVITY:
                 // ..then finishes it.
-                WelcomeActivity.finishIt(mUiBot);
+                WelcomeActivity.finishIt();
                 break;
             default:
                 throw new IllegalArgumentException("invalid type: " + type);
@@ -628,7 +637,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -683,7 +691,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -715,7 +722,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -829,7 +835,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -891,7 +896,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -1162,7 +1166,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.setText("108"));
@@ -1223,7 +1226,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
             mActivity.mInput.setText("108");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
index 903011f..c7f34800 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
@@ -17,17 +17,17 @@
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class TimePickerClockActivityTest extends TimePickerTestCase<TimePickerClockActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<TimePickerClockActivity> mActivityRule =
-        new AutofillActivityTestRule<TimePickerClockActivity>(TimePickerClockActivity.class);
-
     @Override
-    protected TimePickerClockActivity getTimePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<TimePickerClockActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TimePickerClockActivity>(
+                TimePickerClockActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
index 07e4592..a6ac44f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
@@ -17,17 +17,17 @@
 
 import android.platform.test.annotations.AppModeFull;
 
-import org.junit.Rule;
-
 @AppModeFull // Unit test
 public class TimePickerSpinnerActivityTest extends TimePickerTestCase<TimePickerSpinnerActivity> {
 
-    @Rule
-    public final AutofillActivityTestRule<TimePickerSpinnerActivity> mActivityRule =
-        new AutofillActivityTestRule<TimePickerSpinnerActivity>(TimePickerSpinnerActivity.class);
-
     @Override
-    protected TimePickerSpinnerActivity getTimePickerActivity() {
-        return mActivityRule.getActivity();
+    protected AutofillActivityTestRule<TimePickerSpinnerActivity> getActivityRule() {
+        return new AutofillActivityTestRule<TimePickerSpinnerActivity>(
+                TimePickerSpinnerActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index 4bd8cf0..22abcd3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -36,14 +36,14 @@
 /**
  * Base class for {@link AbstractTimePickerActivity} tests.
  */
-abstract class TimePickerTestCase<T extends AbstractTimePickerActivity>
-        extends AutoFillServiceTestCase {
+abstract class TimePickerTestCase<A extends AbstractTimePickerActivity>
+        extends AutoFillServiceTestCase.AutoActivityLaunch<A> {
 
-    protected abstract T getTimePickerActivity();
+    protected A mActivity;
 
     @Test
     public void testAutoFillAndSave() throws Exception {
-        final T activity = getTimePickerActivity();
+        assertWithMessage("subclass did not set mActivity").that(mActivity).isNotNull();
 
         // Set service.
         enableService();
@@ -62,10 +62,10 @@
                 .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_OUTPUT, ID_TIME_PICKER)
                 .build());
 
-        activity.expectAutoFill("4:20", 4, 20);
+        mActivity.expectAutoFill("4:20", 4, 20);
 
         // Trigger auto-fill.
-        activity.onOutput((v) -> v.requestFocus());
+        mActivity.onOutput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
 
         // Assert properties of TimePicker field.
@@ -75,11 +75,11 @@
         mUiBot.selectDataset("Adventure Time");
 
         // Check the results.
-        activity.assertAutoFilled();
+        mActivity.assertAutoFilled();
 
         // Trigger save.
-        activity.setTime(10, 40);
-        activity.tapOk();
+        mActivity.setTime(10, 40);
+        mActivity.tapOk();
 
         mUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeout.java b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
index 543a4d2..261cbab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeout.java
@@ -16,10 +16,11 @@
 package android.autofillservice.cts;
 
 import android.os.SystemClock;
-import androidx.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import java.util.concurrent.Callable;
 
 /**
@@ -151,9 +152,9 @@
         if (retryMs < 1) {
             throw new IllegalArgumentException("need to sleep at least 1ms, right?");
         }
-        long startTime = System.currentTimeMillis();
+        long startTime = SystemClock.elapsedRealtime();
         int attempt = 0;
-        while (System.currentTimeMillis() - startTime <= mCurrentValue) {
+        while (SystemClock.elapsedRealtime() - startTime <= mCurrentValue) {
             final T result = job.call();
             if (result != null) {
                 // Good news, everyone: job succeeded on first attempt!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
index d59d1a5..9d367c6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -21,48 +21,76 @@
  */
 final class Timeouts {
 
+    private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 20_000;
+    private static final long ONE_NAPTIME_TO_RULE_THEN_ALL_MS = 2_000;
+
     /**
      * Timeout until framework binds / unbinds from service.
      */
-    static final Timeout CONNECTION_TIMEOUT = new Timeout("CONNECTION_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout CONNECTION_TIMEOUT = new Timeout("CONNECTION_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
+
+    /**
+     * Timeout for {@link MyAutofillCallback#assertNotCalled()} - test will sleep for that amount of
+     * time as there is no callback that be received to assert it's not shown.
+     */
+    static final long CALLBACK_NOT_CALLED_TIMEOUT_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout until framework unbinds from a service.
      */
     // TODO: must be higher than RemoteFillService.TIMEOUT_IDLE_BIND_MILLIS, so we should use a
     // @hidden @Testing constants instead...
-    static final Timeout IDLE_UNBIND_TIMEOUT = new Timeout("IDLE_UNBIND_TIMEOUT", 10000, 2F, 10000);
+    static final Timeout IDLE_UNBIND_TIMEOUT = new Timeout("IDLE_UNBIND_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout to get the expected number of fill events.
      */
-    static final Timeout FILL_EVENTS_TIMEOUT = new Timeout("FILL_EVENTS_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout FILL_EVENTS_TIMEOUT = new Timeout("FILL_EVENTS_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for expected autofill requests.
      */
-    static final Timeout FILL_TIMEOUT = new Timeout("FILL_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout FILL_TIMEOUT = new Timeout("FILL_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS,
+            2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for expected save requests.
      */
-    static final Timeout SAVE_TIMEOUT = new Timeout("SAVE_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout SAVE_TIMEOUT = new Timeout("SAVE_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS,
+            2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout used when save is not expected to be shown - test will sleep for that amount of time
      * as there is no callback that be received to assert it's not shown.
      */
-    static final long SAVE_NOT_SHOWN_NAPTIME_MS = 5000;
+    static final long SAVE_NOT_SHOWN_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout for UI operations. Typically used by {@link UiBot}.
      */
-    static final Timeout UI_TIMEOUT = new Timeout("UI_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_TIMEOUT = new Timeout("UI_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F,
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
+
+    /**
+     * Timeout for a11y window change events.
+     */
+    static final long WINDOW_CHANGE_TIMEOUT_MS = ONE_TIMEOUT_TO_RULE_THEN_ALL_MS;
+
+    /**
+     * Timeout used when an a11y window change events is not expected to be generated - test will
+     * sleep for that amount of time as there is no callback that be received to assert it's not
+     * shown.
+     */
+    static final long WINDOW_CHANGE_NOT_GENERATED_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout for webview operations. Typically used by {@link UiBot}.
      */
-    static final Timeout WEBVIEW_TIMEOUT = new Timeout("WEBVIEW_TIMEOUT", 8000, 2F, 16000);
+    static final Timeout WEBVIEW_TIMEOUT = new Timeout("WEBVIEW_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for showing the autofill dataset picker UI.
@@ -72,26 +100,27 @@
      *
      * <p>Typically used by {@link UiBot}.
      */
-    static final Timeout UI_DATASET_PICKER_TIMEOUT =
-            new Timeout("UI_DATASET_PICKER_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_DATASET_PICKER_TIMEOUT = new Timeout("UI_DATASET_PICKER_TIMEOUT",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout used when the dataset picker is not expected to be shown - test will sleep for that
      * amount of time as there is no callback that be received to assert it's not shown.
      */
-    static final long DATASET_PICKER_NOT_SHOWN_NAPTIME_MS = 5000;
+    static final long DATASET_PICKER_NOT_SHOWN_NAPTIME_MS = ONE_NAPTIME_TO_RULE_THEN_ALL_MS;
 
     /**
      * Timeout (in milliseconds) for an activity to be brought out to top.
      */
-    static final Timeout ACTIVITY_RESURRECTION =
-            new Timeout("ACTIVITY_RESURRECTION", 6000, 3F, 20000);
+    static final Timeout ACTIVITY_RESURRECTION = new Timeout("ACTIVITY_RESURRECTION",
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F, ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     /**
      * Timeout for changing the screen orientation.
      */
-    static final Timeout UI_SCREEN_ORIENTATION_TIMEOUT =
-            new Timeout("UI_SCREEN_ORIENTATION_TIMEOUT", 5000, 2F, 10000);
+    static final Timeout UI_SCREEN_ORIENTATION_TIMEOUT = new Timeout(
+            "UI_SCREEN_ORIENTATION_TIMEOUT", ONE_TIMEOUT_TO_RULE_THEN_ALL_MS, 2F,
+            ONE_TIMEOUT_TO_RULE_THEN_ALL_MS);
 
     private Timeouts() {
         throw new UnsupportedOperationException("contain static methods only");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index c7cca28..2d9dd6d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -32,6 +32,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.Context;
@@ -53,8 +55,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
+import java.io.File;
+import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -128,12 +130,29 @@
         mAutoman = instrumentation.getUiAutomation();
     }
 
+    void waitForIdle() {
+        final long before = SystemClock.elapsedRealtimeNanos();
+        mDevice.waitForIdle();
+        final float delta = ((float) (SystemClock.elapsedRealtimeNanos() - before)) / 1_000_000;
+        Log.v(TAG, "device idle in " + delta + "ms");
+    }
+
     void reset() {
         mOkToCallAssertNoDatasets = false;
     }
 
-    UiDevice getDevice() {
-        return mDevice;
+    /**
+     * Assumes the device has a minimum height and width of {@code minSize}, throwing a
+     * {@code AssumptionViolatedException} if it doesn't (so the test is skiped by the JUnit
+     * Runner).
+     */
+    void assumeMinimumResolution(int minSize) {
+        final int width = mDevice.getDisplayWidth();
+        final int heigth = mDevice.getDisplayHeight();
+        final int min = Math.min(width, heigth);
+        assumeTrue("Screen size is too small (" + width + "x" + heigth + ")", min >= minSize);
+        Log.d(TAG, "assumeMinimumResolution(" + minSize + ") passed: screen size is "
+                + width + "x" + heigth);
     }
 
     /**
@@ -312,7 +331,7 @@
      */
     public void assertNotShowingForSure(String text) throws Exception {
         final UiObject2 object = mDevice.findObject(By.text(text));
-        assertWithMessage("Find node with text '%s'", text).that(object).isNull();
+        assertWithMessage("Found node with text '%s'", text).that(object).isNull();
     }
 
     /**
@@ -677,13 +696,15 @@
             });
         } catch (RetryableException e) {
             if (dumpOnError) {
-                dumpScreen("waitForObject() for " + selector + "failed");
+                dumpScreen("waitForObject() for " + selector + "on "
+                        + (parent == null ? "mDevice" : parent) + " failed");
             }
             throw e;
         }
     }
 
-    private UiObject2 waitForObject(UiObject2 parent, BySelector selector, Timeout timeout)
+    private UiObject2 waitForObject(@Nullable UiObject2 parent, @NonNull BySelector selector,
+            @NonNull Timeout timeout)
             throws Exception {
         return waitForObject(parent, selector, timeout, DUMP_ON_ERROR);
     }
@@ -694,18 +715,18 @@
      * @param selector {@link BySelector} that identifies the object.
      * @param timeout timeout in ms
      */
-    private UiObject2 waitForObject(BySelector selector, Timeout timeout) throws Exception {
+    private UiObject2 waitForObject(@NonNull BySelector selector, @NonNull Timeout timeout)
+            throws Exception {
         return waitForObject(null, selector, timeout);
     }
 
     /**
-     * Execute a Runnable and wait for TYPE_WINDOWS_CHANGED or TYPE_WINDOW_STATE_CHANGED.
-     * TODO: No longer need Retry, Refactoring the Timeout (e.g. we probably need two values:
-     * one large timeout value that expects window event, one small value that expect no window
-     * event)
+     * Execute a Runnable and wait for {@link AccessibilityEvent#TYPE_WINDOWS_CHANGED} or
+     * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
      */
-    public void waitForWindowChange(Runnable runnable, long timeoutMillis) throws TimeoutException {
-        mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
+    public AccessibilityEvent waitForWindowChange(Runnable runnable, long timeoutMillis)
+            throws TimeoutException {
+        return mAutoman.executeAndWaitForEvent(runnable, (AccessibilityEvent event) -> {
             switch (event.getEventType()) {
                 case AccessibilityEvent.TYPE_WINDOWS_CHANGED:
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
@@ -715,6 +736,10 @@
         }, timeoutMillis);
     }
 
+    public AccessibilityEvent waitForWindowChange(Runnable runnable) throws TimeoutException {
+        return waitForWindowChange(runnable, Timeouts.WINDOW_CHANGE_TIMEOUT_MS);
+    }
+
     /**
      * Waits for and returns a list of objects.
      *
@@ -792,25 +817,21 @@
     }
 
     /**
-     * Dumps the current view hierarchy int the output stream.
+     * Dumps the current view hierarchy and take a screenshot and save both locally so they can be
+     * inspected later.
      */
-    public void dumpScreen(String cause) {
-        new Exception("dumpScreen(cause=" + cause + ") stacktrace").printStackTrace(System.out);
+    public void dumpScreen(@NonNull String cause) {
         try {
-            try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-                mDevice.dumpWindowHierarchy(os);
-                os.flush();
-                Log.w(TAG, "Dumping window hierarchy because " + cause);
-                for (String line : os.toString("UTF-8").split("\n")) {
-                    Log.w(TAG, line);
-                    // Sleep a little bit to avoid logs being ignored due to spam
-                    SystemClock.sleep(100);
-                }
+            final File file = Helper.createTestFile("hierarchy.xml");
+            if (file == null) return;
+            Log.w(TAG, "Dumping window hierarchy because " + cause + " on " + file);
+            try (FileInputStream fis = new FileInputStream(file)) {
+                mDevice.dumpWindowHierarchy(file);
             }
-        } catch (IOException e) {
-            // Just ignore it...
-            Log.e(TAG, "exception dumping window hierarchy", e);
-            return;
+        } catch (Exception e) {
+            Log.e(TAG, "error dumping screen on " + cause, e);
+        } finally {
+            takeScreenshotAndSave();
         }
     }
 
@@ -826,6 +847,22 @@
     }
 
     /**
+     * Takes a screenshot and save it in the file system for post-mortem analysis.
+     */
+    public void takeScreenshotAndSave() {
+        File file = null;
+        try {
+            file = Helper.createTestFile("screenshot.png");
+            if (file != null) {
+                final Bitmap screenshot = takeScreenshot();
+                Helper.dumpBitmap(screenshot, file);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error taking screenshot and saving on " + file, e);
+        }
+    }
+
+    /**
      * Asserts the contents of a child element.
      *
      * @param parent parent object
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
index 622e127..85c6737 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -32,25 +32,13 @@
 import android.view.View;
 import android.view.autofill.AutofillId;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 /**
  * Simple integration test to verify that the UI is only shown if the validator passes.
  */
 @AppModeFull // Service-specific test
-public class ValidatorTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<LoginActivity> mActivityRule =
-        new AutofillActivityTestRule<>(LoginActivity.class);
-
-    private LoginActivity mActivity;
-
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-    }
+public class ValidatorTest extends AbstractLoginActivityTestCase {
 
     @Test
     public void testShowUiWhenValidatorPass() throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
index 9f5b69c..bb4561b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
@@ -39,7 +39,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 @AppModeFull // Unit test
-public class ValidatorsTest extends AutoFillServiceTestCase {
+public class ValidatorsTest {
 
     @Mock private Validator mInvalidValidator;
     @Mock private ValueFinder mValueFinder;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 84370ca..8c88928 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -31,8 +31,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,16 +38,20 @@
 
 @RunWith(AndroidJUnit4.class)
 @AppModeFull // Unit test
-public class ViewAttributesTest extends AutoFillServiceTestCase {
-    @Rule
-    public final AutofillActivityTestRule<ViewAttributesTestActivity> mActivityRule =
-            new AutofillActivityTestRule<>(ViewAttributesTestActivity.class);
+public class ViewAttributesTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<ViewAttributesTestActivity> {
 
     private ViewAttributesTestActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<ViewAttributesTestActivity> getActivityRule() {
+        return new AutofillActivityTestRule<ViewAttributesTestActivity>(
+                ViewAttributesTestActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @Nullable private String[] getHintsFromView(@IdRes int resId) {
@@ -178,6 +180,9 @@
 
     @Test
     public void checkViewLocationInAssistStructure() throws Exception {
+        // If screen is not large enough to contain child, the height/weight will be the residual
+        // space instead of the specific size.
+        mUiBot.assumeMinimumResolution(500);
         onAssistStructure(false, (structure) -> {
                     // check size of outerView
                     AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
index b50fb22..d78bbbd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityCompatModeTest.java
@@ -21,7 +21,6 @@
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.Helper.getContext;
-import static android.autofillservice.cts.Helper.hasAutofillFeature;
 import static android.autofillservice.cts.InstrumentedAutoFillServiceCompatMode.SERVICE_NAME;
 import static android.autofillservice.cts.InstrumentedAutoFillServiceCompatMode.SERVICE_PACKAGE;
 import static android.autofillservice.cts.VirtualContainerActivity.INITIAL_URL_BAR_VALUE;
@@ -39,11 +38,9 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
 import android.autofillservice.cts.common.SettingsHelper;
 import android.autofillservice.cts.common.SettingsStateChangerRule;
-import android.content.Context;
 import android.os.SystemClock;
 import android.platform.test.annotations.AppModeFull;
 import android.service.autofill.SaveInfo;
-import android.support.test.InstrumentationRegistry;
 
 import org.junit.After;
 import org.junit.ClassRule;
@@ -54,7 +51,6 @@
  * the Autofill APIs.
  */
 public class VirtualContainerActivityCompatModeTest extends VirtualContainerActivityTest {
-    private static final Context sContext = InstrumentationRegistry.getContext();
 
     @ClassRule
     public static final SettingsStateChangerRule sCompatModeChanger = new SettingsStateChangerRule(
@@ -76,23 +72,19 @@
     }
 
     @Override
-    protected void postActivityLaunched(VirtualContainerActivity activity) {
+    protected void postActivityLaunched() {
         // Set our own compat mode as well..
-        activity.mCustomView.setCompatMode(true);
+        mActivity.mCustomView.setCompatMode(true);
     }
 
     @Override
     protected void enableService() {
         Helper.enableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillServiceCompatMode.setIgnoreUnexpectedRequests(false);
     }
 
     @Override
     protected void disableService() {
-        if (!hasAutofillFeature()) return;
-
         Helper.disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillServiceCompatMode.setIgnoreUnexpectedRequests(true);
     }
 
     @Override
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 84cb1f6..5ad0f73 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -49,9 +49,7 @@
 import android.view.ViewGroup;
 import android.view.autofill.AutofillManager;
 
-import org.junit.Before;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.concurrent.TimeoutException;
@@ -60,21 +58,14 @@
  * Test case for an activity containing virtual children, either using the explicit Autofill APIs
  * or Compat mode.
  */
-public class VirtualContainerActivityTest extends AutoFillServiceTestCase {
+public class VirtualContainerActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<VirtualContainerActivity> {
 
     // TODO(b/74256300): remove when fixed it :-)
     private static final boolean BUG_74256300_FIXED = false;
 
-    @Rule
-    public final AutofillActivityTestRule<VirtualContainerActivity> mActivityRule =
-            new AutofillActivityTestRule<VirtualContainerActivity>(VirtualContainerActivity.class) {
-        @Override
-        protected void beforeActivityLaunched() {
-            preActivityCreated();
-        }
-    };
-
     private final boolean mCompatMode;
+    private AutofillActivityTestRule<VirtualContainerActivity> mActivityRule;
     protected VirtualContainerActivity mActivity;
 
     public VirtualContainerActivityTest() {
@@ -85,12 +76,6 @@
         mCompatMode = compatMode;
     }
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
-        postActivityLaunched(mActivity);
-    }
-
     /**
      * Hook for subclass to customize test before activity is created.
      */
@@ -99,8 +84,27 @@
     /**
      * Hook for subclass to customize activity after it's launched.
      */
-    protected void postActivityLaunched(
-            @SuppressWarnings("unused") VirtualContainerActivity activity) {
+    protected void postActivityLaunched() {}
+
+    @Override
+    protected AutofillActivityTestRule<VirtualContainerActivity> getActivityRule() {
+        if (mActivityRule == null) {
+            mActivityRule = new AutofillActivityTestRule<VirtualContainerActivity>(
+                    VirtualContainerActivity.class) {
+                @Override
+                protected void beforeActivityLaunched() {
+                    preActivityCreated();
+                }
+
+                @Override
+                protected void afterActivityLaunched() {
+                    mActivity = getActivity();
+                    postActivityLaunched();
+                }
+            };
+
+        }
+        return mActivityRule;
     }
 
     @Test
@@ -129,15 +133,14 @@
      * Focus to username and expect window event
      */
     void focusToUsername() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.mUsername.changeFocus(true),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.mUsername.changeFocus(true));
     }
 
     /**
      * Focus to username and expect no autofill window event
      */
     void focusToUsernameExpectNoWindowEvent() throws Throwable {
-        // TODO should use waitForWindowChange() if we can filter out event of app Activity itself.
+        // TODO: should use waitForWindowChange() if we can filter out event of app Activity itself.
         mActivityRule.runOnUiThread(() -> mActivity.mUsername.changeFocus(true));
     }
 
@@ -145,8 +148,7 @@
      * Focus to password and expect window event
      */
     void focusToPassword() throws TimeoutException {
-        mUiBot.waitForWindowChange(() -> mActivity.mPassword.changeFocus(true),
-                Timeouts.UI_TIMEOUT.getMaxValue());
+        mUiBot.waitForWindowChange(() -> mActivity.mPassword.changeFocus(true));
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index a8da1a3..3d609cb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
@@ -109,20 +109,18 @@
                     Log.v(TAG, "onPageFinished(): " + url);
                     latch.countDown();
                 }
-
             });
             mWebView.loadUrl(FAKE_URL);
         });
 
         // Wait until it's loaded.
-
         if (!latch.await(WEBVIEW_TIMEOUT.ms(), TimeUnit.MILLISECONDS)) {
             throw new RetryableException(WEBVIEW_TIMEOUT, "WebView not loaded");
         }
 
         // TODO(b/80317628): re-add check below
         // NOTE: we cannot search by resourceId because WebView does not set them...
-        // uiBot.assertShownByText("Login"); // Login button
+        // uiBot.assertShownByText("Login", WEBVIEW_TIMEOUT); // Login button
 
         return mWebView;
     }
@@ -155,7 +153,7 @@
     }
 
     private UiObject2 getLabel(UiBot uiBot, String label) throws Exception {
-        return uiBot.assertShownByText(label, Timeouts.WEBVIEW_TIMEOUT);
+        return uiBot.assertShownByText(label, WEBVIEW_TIMEOUT);
     }
 
     private UiObject2 getInput(UiBot uiBot, String contentDescription) throws Exception {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index 8623967..e1f4f1e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -34,28 +34,27 @@
 import android.view.autofill.AutofillManager;
 
 import org.junit.AfterClass;
-import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
-import org.junit.Rule;
 import org.junit.Test;
 
-@AppModeFull(reason = "Flaky in instant mode")
-public class WebViewActivityTest extends AutoFillServiceTestCase {
+public class WebViewActivityTest
+        extends AutoFillServiceTestCase.AutoActivityLaunch<WebViewActivity> {
 
     // TODO(b/64951517): WebView currently does not trigger the autofill callbacks when values are
     // set using accessibility.
     private static final boolean INJECT_EVENTS = true;
 
-    @Rule
-    public final AutofillActivityTestRule<WebViewActivity> mActivityRule =
-            new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class);
-
     private WebViewActivity mActivity;
 
-    @Before
-    public void setActivity() {
-        mActivity = mActivityRule.getActivity();
+    @Override
+    protected AutofillActivityTestRule<WebViewActivity> getActivityRule() {
+        return new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class) {
+            @Override
+            protected void afterActivityLaunched() {
+                mActivity = getActivity();
+            }
+        };
     }
 
     @BeforeClass
@@ -139,14 +138,6 @@
         mUiBot.assertNoDatasets();
         callback.assertUiHiddenEvent(myWebView, passwordChildId);
 
-        // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(mUiBot).getText()).isEqualTo("dude");
-        // TODO: proper way to verify text (which is ..... because it's a password) - ideally it
-        // should call passwordInput.isPassword(), but that's not exposed
-        final String password = mActivity.getPasswordInput(mUiBot).getText();
-        assertThat(password).isNotEqualTo("sweet");
-        assertThat(password).hasLength(5);
-
         // Assert structure passed to service.
         try {
             final ViewNode webViewNode =
@@ -276,14 +267,6 @@
         myWebView.assertAutofilled();
         callback.assertUiHiddenEvent(myWebView, usernameChildId);
 
-        // Make sure screen was autofilled.
-        assertThat(mActivity.getUsernameInput(mUiBot).getText()).isEqualTo("dude");
-        // TODO: proper way to verify text (which is ..... because it's a password) - ideally it
-        // should call passwordInput.isPassword(), but that's not exposed
-        final String password = mActivity.getPasswordInput(mUiBot).getText();
-        assertThat(password).isNotEqualTo("sweet");
-        assertThat(password).hasLength(5);
-
         // Now trigger save.
         if (INJECT_EVENTS) {
             mActivity.getUsernameInput(mUiBot).click();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
index e1c726a..9e11da9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
@@ -22,12 +22,13 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
 import android.support.test.uiautomator.UiObject2;
 import android.text.TextUtils;
 import android.util.Log;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 /**
  * Activity that displays a "Welcome USER" message after login.
  */
@@ -75,17 +76,23 @@
         sInstance = null;
     }
 
-    static void finishIt(UiBot uiBot) {
-        if (sInstance != null) {
-            Log.d(TAG, "So long and thanks for all the fish!");
-            sInstance.finish();
-            uiBot.assertGoneByRelativeId(ID_WELCOME, Timeouts.ACTIVITY_RESURRECTION);
-        }
+    @Override
+    public void finish() {
+        super.finish();
+        Log.d(TAG, "So long and thanks for all the finish!");
+
         if (sPendingIntent != null) {
+            Log.v(TAG, " canceling pending intent on finish(): " + sPendingIntent);
             sPendingIntent.cancel();
         }
     }
 
+    static void finishIt() {
+        if (sInstance != null) {
+            sInstance.finish();
+        }
+    }
+
     // TODO: reuse in other places
     static void assertShowingDefaultMessage(UiBot uiBot) throws Exception {
         assertShowing(uiBot, null);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
index 1ca7955..3707eea 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/OneTimeSettingsListener.java
@@ -19,13 +19,17 @@
 import static android.autofillservice.cts.common.SettingsHelper.NAMESPACE_GLOBAL;
 import static android.autofillservice.cts.common.SettingsHelper.NAMESPACE_SECURE;
 
+import android.autofillservice.cts.JUnitHelper;
+import android.autofillservice.cts.RetryableException;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.util.Log;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -34,16 +38,18 @@
  * Helper used to block tests until a secure settings value has been updated.
  */
 public final class OneTimeSettingsListener extends ContentObserver {
+
+    private static final String TAG = "OneTimeSettingsListener";
+    private static final long DEFAULT_TIMEOUT_MS = 30_000;
+
     private final CountDownLatch mLatch = new CountDownLatch(1);
     private final ContentResolver mResolver;
     private final String mKey;
-
-    public OneTimeSettingsListener(Context context, String key) {
-        this(context, NAMESPACE_SECURE, key);
-    }
+    private final long mStarted;
 
     public OneTimeSettingsListener(Context context, String namespace, String key) {
         super(new Handler(Looper.getMainLooper()));
+        mStarted = SystemClock.elapsedRealtime();
         mKey = key;
         mResolver = context.getContentResolver();
         final Uri uri;
@@ -73,10 +79,15 @@
      */
     public void assertCalled() {
         try {
-            final boolean updated = mLatch.await(5, TimeUnit.SECONDS);
+            final boolean updated = mLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
             if (!updated) {
-                throw new IllegalStateException("Settings " + mKey + " not called in 5s");
+                throw new RetryableException(
+                        "Settings " + mKey + " not called in " + DEFAULT_TIMEOUT_MS + "ms");
             }
+            final long delta = SystemClock.elapsedRealtime() - mStarted;
+            // TODO: usually it's notified in ~50-150ms, but for some reason it takes ~10s
+            // on some ViewAttributesTest methods, hence the 30s limit
+            Log.v(TAG, JUnitHelper.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
             throw new IllegalStateException("Interrupted", e);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
index 77963d1..9784db5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/common/StateKeeperRule.java
@@ -44,6 +44,18 @@
         mStateManager = Preconditions.checkNotNull(stateManager);
     }
 
+    /**
+     * Hook for subclasses.
+     */
+    protected void preEvaluate(@SuppressWarnings("unused") Description description) {
+    }
+
+    /**
+     * Hook for subclasses.
+     */
+    protected void postEvaluate(@SuppressWarnings("unused") Description description) {
+    }
+
     @Override
     public Statement apply(Statement base, Description description) {
         return new Statement() {
@@ -51,6 +63,7 @@
             @Override
             public void evaluate() throws Throwable {
                 final T previousValue = mStateManager.get();
+                preEvaluate(description);
                 try {
                     base.evaluate();
                 } finally {
@@ -59,6 +72,7 @@
                         mStateManager.set(previousValue);
                     }
                 }
+                postEvaluate(description);
             }
         };
     }
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index e12f2c4..985f372 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -27,7 +27,12 @@
     android.test.base.stubs \
 
 
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ctstestserver mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner \
+    ctstestserver \
+    mockito-target-minus-junit4 \
+    testng
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 8e0bfa0..5a4a916 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -22,6 +22,7 @@
         <option name="test-file-name" value="CtsFullBackupApp.apk" />
         <option name="test-file-name" value="CtsKeyValueBackupApp.apk" />
         <option name="test-file-name" value="CtsBackupTestCases.apk" />
+        <option name="test-file-name" value="CtsPermissionBackupApp.apk" />
     </target_preparer>
     <target_preparer class="android.cts.backup.BackupPreparer">
         <option name="enable-backup-if-needed" value="true" />
diff --git a/tests/backup/app/permission/Android.mk b/tests/backup/app/permission/Android.mk
new file mode 100644
index 0000000..24ccfbe
--- /dev/null
+++ b/tests/backup/app/permission/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+
+LOCAL_PACKAGE_NAME := CtsPermissionBackupApp
+LOCAL_SDK_VERSION := current
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    compatibility-device-util \
+    ctstestrunner
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/permission/AndroidManifest.xml b/tests/backup/app/permission/AndroidManifest.xml
new file mode 100644
index 0000000..8dd735a
--- /dev/null
+++ b/tests/backup/app/permission/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.backup.permission" >
+
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+    <application
+        android:label="Android Permission Backup CTS App">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/backup/src/android/backup/cts/AgentBindingTest.java b/tests/backup/src/android/backup/cts/AgentBindingTest.java
new file mode 100644
index 0000000..cfd97c7
--- /dev/null
+++ b/tests/backup/src/android/backup/cts/AgentBindingTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.backup.cts;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.expectThrows;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.IBinder;
+
+import java.lang.reflect.Method;
+
+public class AgentBindingTest extends BaseBackupCtsTest {
+    private Context mContext;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    public void testUnbindBackupAgent_isNotCallableFromCts() throws Exception {
+        if (!isBackupSupported()) {
+            return;
+        }
+        expectThrows(Exception.class, () -> unbindBackupAgent(mContext.getApplicationInfo()));
+    }
+
+    public void testBindBackupAgent_isNotCallableFromCts() throws Exception {
+        if (!isBackupSupported()) {
+            return;
+        }
+        expectThrows(Exception.class, () -> bindBackupAgent(mContext.getPackageName(), 0, 0));
+    }
+
+    private static void unbindBackupAgent(ApplicationInfo applicationInfo) throws Exception {
+        callActivityManagerMethod(
+                "unbindBackupAgent",
+                new Class<?>[] {ApplicationInfo.class},
+                new Object[] {applicationInfo});
+    }
+
+    private static void bindBackupAgent(String packageName, int backupRestoreMode, int userId)
+            throws Exception {
+        callActivityManagerMethod(
+                "bindBackupAgent",
+                new Class<?>[] {String.class, int.class, int.class},
+                new Object[] {packageName, backupRestoreMode, userId});
+    }
+
+    private static void callActivityManagerMethod(
+            String methodName, Class<?>[] types, Object[] args) throws Exception {
+        Class<?> activityManagerClass = Class.forName("android.app.IActivityManager");
+        Object activityManager = getActivityManager();
+        Method bindBackupAgentMethod = activityManagerClass.getMethod(methodName, types);
+        bindBackupAgentMethod.invoke(activityManager, args);
+    }
+
+    private static Object getActivityManager() throws Exception {
+        Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager");
+        Class<?> stubClass = Class.forName("android.app.IActivityManager$Stub");
+        Method asInterfaceMethod = stubClass.getMethod("asInterface", IBinder.class);
+        Method getServiceMethod = serviceManagerClass.getMethod("getService", String.class);
+        return asInterfaceMethod.invoke(
+                null, (IBinder) getServiceMethod.invoke(serviceManagerClass, "activity"));
+    }
+}
diff --git a/tests/backup/src/android/backup/cts/PermissionTest.java b/tests/backup/src/android/backup/cts/PermissionTest.java
new file mode 100644
index 0000000..0b7858c
--- /dev/null
+++ b/tests/backup/src/android/backup/cts/PermissionTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.backup.cts;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.ParcelFileDescriptor;
+
+import com.android.compatibility.common.util.BackupUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Verifies that restored permissions are the same with backup value.
+ */
+public class PermissionTest extends BaseBackupCtsTest {
+
+    /** The name of the package of the app under test */
+    private static final String APP_PACKAGE = "android.backup.permission";
+
+    /** The name of the package for backup */
+    private static final String ANDROID_PACKAGE = "android";
+
+    private BackupUtils mBackupUtils =
+            new BackupUtils() {
+                @Override
+                protected InputStream executeShellCommand(String command) throws IOException {
+                    ParcelFileDescriptor pfd =
+                            getInstrumentation().getUiAutomation().executeShellCommand(command);
+                    return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+                }
+            };
+
+    /**
+     * Test backup and restore of grant runtime permission.
+     *
+     * Test logic:
+     * 1. Grant SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
+     * 2. Backup android package, revoke SEND_SMS and WRITE_CONTACTS permissions to APP_PACKAGE.
+     * Then restore android package.
+     * 3. Check restored SEND_SMS and WRITE_CONTACTS permissions.
+     *
+     * @see PackageManagerService#serializeRuntimePermissionGrantsLPr(XmlSerializer, int) and
+     * PackageManagerService#processRestoredPermissionGrantsLPr(XmlPullParser, int)
+     */
+    public void testGrantRuntimePermission() throws Exception {
+        grantRuntimePermission(Manifest.permission.SEND_SMS);
+        grantRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+
+        mBackupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE);
+        revokeRuntimePermission(Manifest.permission.SEND_SMS);
+        revokeRuntimePermission(Manifest.permission.WRITE_CONTACTS);
+        mBackupUtils.restoreAndAssertSuccess(ANDROID_PACKAGE);
+
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                checkPermission(Manifest.permission.SEND_SMS));
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                checkPermission(Manifest.permission.WRITE_CONTACTS));
+    }
+
+    private int checkPermission(String permission) {
+        return getInstrumentation().getContext().getPackageManager().checkPermission(permission,
+                APP_PACKAGE);
+    }
+
+    private void grantRuntimePermission(String permission) {
+        if (checkPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+            getInstrumentation().getUiAutomation().grantRuntimePermission(APP_PACKAGE, permission);
+            assertEquals(PackageManager.PERMISSION_GRANTED, checkPermission(permission));
+        }
+    }
+
+    private void revokeRuntimePermission(String permission) {
+        if (checkPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+            getInstrumentation().getUiAutomation().revokeRuntimePermission(APP_PACKAGE, permission);
+            assertEquals(PackageManager.PERMISSION_DENIED, checkPermission(permission));
+        }
+    }
+}
diff --git a/tests/framework/base/OWNERS b/tests/framework/base/OWNERS
new file mode 100644
index 0000000..fe4ebd7
--- /dev/null
+++ b/tests/framework/base/OWNERS
@@ -0,0 +1,9 @@
+# Windows & Activities
+ogunwale@google.com
+jjaggi@google.com
+racarr@google.com
+chaviw@google.com
+brycelee@google.com
+akulian@google.com
+roosa@google.com
+takaoka@google.com
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 1d2bd3e..09037bb 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
@@ -216,7 +216,7 @@
 
         mAmWmState.computeState(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
-        mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
+        mAmWmState.assertNotExist(BROADCAST_RECEIVER_ACTIVITY);
     }
 
     @Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 0ce9dcf..a00d3eb 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -286,7 +286,7 @@
         // Resize docked stack to fullscreen size. This will trigger activity relaunch with
         // non-empty override configuration corresponding to fullscreen size.
         logSeparator = separateLogs();
-        mAm.resizeStack(stack.mStackId, displayRect);
+        mAtm.resizeStack(stack.mStackId, displayRect);
 
         // Move activity back to fullscreen stack.
         setActivityTaskWindowingMode(activityName,
@@ -333,7 +333,7 @@
         final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
         final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
 
-        mAm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
+        mAtm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
         mAmWmState.waitForValidState(
                 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
                         .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index a873c09..8a04493 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index ae483d8..4f84f0e 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -16,8 +16,8 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -506,7 +506,7 @@
 
         executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
         waitForDockNotMinimized();
-        mAmWmState.assertVisibility(TEST_ACTIVITY, false);
+        mAmWmState.assertNotExist(TEST_ACTIVITY);
         assertDockNotMinimized();
     }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 5d2d82b..ed2a686 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -232,7 +232,7 @@
 
             // Lock the screen and ensure that the fullscreen activity showing over the lockscreen
             // is visible, but not the PiP activity
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertKeyguardShowingAndOccluded();
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index e79c13b..67a773d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -99,7 +99,7 @@
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
@@ -116,7 +116,7 @@
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY, true);
             assertTrue(mAmWmState.getWmState().allWindowsVisible(
@@ -137,7 +137,8 @@
                     SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(
+                    SHOW_WHEN_LOCKED_ACTIVITY, SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
@@ -154,7 +155,7 @@
             launchActivity(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
             assertWallpaperShowing();
@@ -173,7 +174,7 @@
             mAmWmState.computeState(TEST_ACTIVITY, SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.assertVisibility(TEST_ACTIVITY, true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
             mAmWmState.computeState(true);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY, true);
             mAmWmState.assertVisibility(TEST_ACTIVITY, false);
@@ -211,7 +212,7 @@
                             .setMultipleTask(false)
             );
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY);
             mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
             mAmWmState.assertKeyguardShowingAndOccluded();
@@ -333,7 +334,8 @@
             mAmWmState.assertSanity();
             mAmWmState.assertHomeActivityVisible(false);
             mAmWmState.assertKeyguardShowingAndNotOccluded();
-            mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+            // The {@link SHOW_WHEN_LOCKED_ACTIVITY} has gone because of {@link pressBackButton()}.
+            mAmWmState.assertNotExist(SHOW_WHEN_LOCKED_ACTIVITY);
         }
     }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
index 105121b..cdc115a 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -16,6 +16,7 @@
 
 package android.server.am;
 
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ACTIVITY;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_ACTIVITY;
 import static android.server.am.Components.SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY;
@@ -102,7 +103,7 @@
     public void testNewActivityDuringOccluded() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ACTIVITY);
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
@@ -134,10 +135,16 @@
                     mAmWmState.getWmState().getLastTransition());
             assertSingleLaunch(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
 
+            // Waiting for the standard keyguard since
+            // {@link SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY} called
+            // {@link Activity#showWhenLocked(boolean)} and removed the attribute.
             lockScreenSession.gotoKeyguard();
             logSeparator = separateLogs();
-            launchActivity(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
-            mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
+            // Waiting for {@link SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY} stopped since it
+            // already lost show-when-locked attribute.
+            launchActivityNoWait(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY);
+            mAmWmState.waitForActivityState(
+                    SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, STATE_STOPPED);
             assertSingleStartAndStop(SHOW_WHEN_LOCKED_ATTR_REMOVE_ATTR_ACTIVITY, logSeparator);
         }
     }
@@ -146,7 +153,7 @@
     public void testNewActivityDuringOccludedWithAttr() throws Exception {
         try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
             launchActivity(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
-            lockScreenSession.gotoKeyguard();
+            lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_ATTR_ACTIVITY);
             launchActivity(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             mAmWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_ACTIVITY);
             assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index fe1f588..5a733f2 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -211,7 +211,7 @@
         logE("***Waiting for debugger window failed");
     }
 
-    boolean waitForHomeActivityVisible() {
+    void waitForHomeActivityVisible() {
         ComponentName homeActivity = mAmState.getHomeActivityName();
         // Sometimes this function is called before we know what Home Activity is
         if (homeActivity == null) {
@@ -221,20 +221,15 @@
         }
         assertNotNull("homeActivity should not be null", homeActivity);
         waitForValidState(homeActivity);
-        return mAmState.isHomeActivityVisible();
     }
 
-    /**
-     * @return true if recents activity is visible. Devices without recents will return false
-     */
-    boolean waitForRecentsActivityVisible() {
+    void waitForRecentsActivityVisible() {
         if (mAmState.isHomeRecentsComponent()) {
-            return waitForHomeActivityVisible();
+            waitForHomeActivityVisible();
+        } else {
+            waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
+                    "***Waiting for recents activity to be visible...");
         }
-
-        waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
-                "***Waiting for recents activity to be visible...");
-        return mAmState.isRecentsActivityVisible();
     }
 
     void waitForKeyguardShowingAndNotOccluded() {
@@ -583,15 +578,26 @@
         assertEquals(msg, windowName, mWmState.getFrontWindow());
     }
 
+    void assertNotExist(final ComponentName activityName) {
+        final String windowName = getWindowName(activityName);
+        assertFalse("Activity=" + getActivityName(activityName) + " must NOT exist.",
+                mAmState.containsActivity(activityName));
+        assertFalse("Window=" + windowName + " must NOT exits.",
+                mWmState.containsWindow(windowName));
+    }
+
     public void assertVisibility(final ComponentName activityName, final boolean visible) {
         final String windowName = getWindowName(activityName);
-        final boolean activityVisible = mAmState.isActivityVisible(activityName);
-        final boolean windowVisible = mWmState.isWindowVisible(windowName);
+        // Check existence of activity and window.
+        assertTrue("Activity=" + getActivityName(activityName) + " must exist.",
+                mAmState.containsActivity(activityName));
+        assertTrue("Window=" + windowName + " must exist.", mWmState.containsWindow(windowName));
 
+        // Check visibility of activity and window.
         assertEquals("Activity=" + getActivityName(activityName) + " must" + (visible ? "" : " NOT")
-                + " be visible.", visible, activityVisible);
+                + " be visible.", visible, mAmState.isActivityVisible(activityName));
         assertEquals("Window=" + windowName + " must" + (visible ? "" : " NOT") + " be visible.",
-                visible, windowVisible);
+                visible, mWmState.isWindowVisible(windowName));
     }
 
     void assertHomeActivityVisible(boolean visible) {
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
index d50e622..b7ef59a 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index fcca246..13f0c08 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -16,8 +16,8 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -72,16 +72,16 @@
 import android.accessibilityservice.AccessibilityService;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.server.am.settings.SettingsSession;
 import android.support.test.InstrumentationRegistry;
-
 import android.support.test.rule.ActivityTestRule;
 
 import com.android.compatibility.common.util.SystemUtil;
@@ -153,6 +153,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     @Rule
     public final ActivityTestRule<SideActivity> mSideActivityRule =
@@ -227,6 +228,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
                 mContext.getPackageName(), android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
@@ -252,12 +254,12 @@
     }
 
     protected void removeStacksWithActivityTypes(int... activityTypes) {
-        mAm.removeStacksWithActivityTypes(activityTypes);
+        mAtm.removeStacksWithActivityTypes(activityTypes);
         waitForIdle();
     }
 
     protected void removeStacksInWindowingModes(int... windowingModes) {
-        mAm.removeStacksInWindowingModes(windowingModes);
+        mAtm.removeStacksInWindowingModes(windowingModes);
         waitForIdle();
     }
 
@@ -366,7 +368,7 @@
             int createMode) {
         launchActivity(activityName);
         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
-        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
+        mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
                 false /* animate */, null /* initialBounds */, true /* showRecents */);
 
         mAmWmState.waitForValidState(
@@ -388,7 +390,7 @@
      *                                      recents activity is available.
      */
     public void moveTaskToPrimarySplitScreen(int taskId, boolean launchSideActivityIfNeeded) {
-        mAm.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+        mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
                 true /* onTop */, false /* animate */, null /* initialBounds */,
                 true /* showRecents */);
         mAmWmState.waitForRecentsActivityVisible();
@@ -429,7 +431,7 @@
     protected void setActivityTaskWindowingMode(ComponentName activityName, int windowingMode) {
         mAmWmState.computeState(activityName);
         final int taskId = mAmWmState.getAmState().getTaskByActivity(activityName).mTaskId;
-        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAtm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .setWindowingMode(windowingMode)
@@ -548,7 +550,7 @@
     }
 
     protected boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     protected boolean hasHomeScreen() {
@@ -689,13 +691,17 @@
             return this;
         }
 
-        public LockScreenSession gotoKeyguard() {
+        public LockScreenSession gotoKeyguard(ComponentName... showWhenLockedActivities) {
             if (DEBUG && isLockDisabled()) {
                 logE("LockScreenSession.gotoKeyguard() is called without lock enabled.");
             }
             sleepDevice();
             wakeUpDevice();
-            mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            if (showWhenLockedActivities.length == 0) {
+                mAmWmState.waitForKeyguardShowingAndNotOccluded();
+            } else {
+                mAmWmState.waitForValidState(showWhenLockedActivities);
+            }
             return this;
         }
 
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
index 2a9b351..17ca60b 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
index 3ab7c198..4f925e8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
@@ -22,6 +22,12 @@
 import static android.server.wm.alertwindowapp.Components.ALERT_WINDOW_TEST_ACTIVITY;
 import static android.server.wm.alertwindowappsdk25.Components.SDK25_ALERT_WINDOW_TEST_ACTIVITY;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -152,13 +158,12 @@
             // in place for SYSTEM_ALERT_WINDOW, which allows the window
             // to be created, but will be hidden instead.
             if (isUiModeLockedToVrHeadset()) {
-                assertTrue("Should not be empty alertWindows=" + alertWindows,
-                        !alertWindows.isEmpty());
+                assertThat("Should not be empty alertWindows",
+                        alertWindows, hasSize(greaterThan(0)));
                 assertTrue("All alert windows should be hidden",
                         allWindowsHidden(alertWindows));
             } else {
-                assertTrue("Should be empty alertWindows=" + alertWindows,
-                        alertWindows.isEmpty());
+                assertThat("Should be empty alertWindows", alertWindows, empty());
                 assertTrue(AppOpsUtils.rejectedOperationLogged(packageName,
                         OPSTR_SYSTEM_ALERT_WINDOW));
                 return;
@@ -168,8 +173,8 @@
         if (atLeastO) {
             // Assert that only TYPE_APPLICATION_OVERLAY was created.
             for (WindowManagerState.WindowState win : alertWindows) {
-                assertTrue("Can't create win=" + win + " on SDK O or greater",
-                        win.getType() == TYPE_APPLICATION_OVERLAY);
+                assertEquals("Can't create win=" + win + " on SDK O or greater",
+                        win.getType(), TYPE_APPLICATION_OVERLAY);
             }
         }
 
@@ -183,17 +188,17 @@
                 alertWindows.get(alertWindows.size() - 1);
 
         // Assert that the alert windows have higher z-order than the main app window
-        assertTrue("lowestAlertWindow=" + lowestAlertWindow + " less than mainAppWindow="
-                + mainAppWindow,
-                wmState.getZOrder(lowestAlertWindow) > wmState.getZOrder(mainAppWindow));
+        assertThat("lowestAlertWindow has higher z-order than mainAppWindow",
+                wmState.getZOrder(lowestAlertWindow),
+                greaterThan(wmState.getZOrder(mainAppWindow)));
 
         // Assert that legacy alert windows have a lower z-order than the new alert window layer.
         final WindowManagerState.WindowState appOverlayWindow =
                 wmState.getWindowByPackageName(packageName, TYPE_APPLICATION_OVERLAY);
         if (appOverlayWindow != null && highestAlertWindow != appOverlayWindow) {
-            assertTrue("highestAlertWindow=" + highestAlertWindow
-                            + " greater than appOverlayWindow=" + appOverlayWindow,
-                    wmState.getZOrder(highestAlertWindow) < wmState.getZOrder(appOverlayWindow));
+            assertThat("highestAlertWindow has lower z-order than appOverlayWindow",
+                    wmState.getZOrder(highestAlertWindow),
+                    lessThan(wmState.getZOrder(appOverlayWindow)));
         }
 
         // Assert that alert windows are below key system windows.
@@ -201,9 +206,9 @@
                 wmState.getWindowsByPackageName(packageName, SYSTEM_WINDOW_TYPES);
         if (!systemWindows.isEmpty()) {
             final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
-            assertTrue("highestAlertWindow=" + highestAlertWindow
-                            + " greater than lowestSystemWindow=" + lowestSystemWindow,
-                    wmState.getZOrder(highestAlertWindow) < wmState.getZOrder(lowestSystemWindow));
+            assertThat("highestAlertWindow has lower z-order than lowestSystemWindow",
+                    wmState.getZOrder(highestAlertWindow),
+                    lessThan(wmState.getZOrder(lowestSystemWindow)));
         }
         assertTrue(AppOpsUtils.allowedOperationLogged(packageName, OPSTR_SYSTEM_ALERT_WINDOW));
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 5d26234..e3007fd 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -34,6 +34,7 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.os.RemoteException;
@@ -41,6 +42,7 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
 import android.util.Log;
 
 import org.junit.After;
@@ -56,6 +58,7 @@
  */
 @Presubmit
 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_STACKS")
+@FlakyTest(bugId = 109874623)
 public class CrossAppDragAndDropTests {
     private static final String TAG = "CrossAppDragAndDrop";
 
@@ -120,6 +123,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     private Map<String, String> mSourceResults;
     private Map<String, String> mTargetResults;
@@ -142,6 +146,7 @@
 
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         mSourcePackageName = SOURCE_PACKAGE_NAME;
         mTargetPackageName = TARGET_PACKAGE_NAME;
@@ -192,7 +197,7 @@
         clearLogs();
 
         // Remove special stacks.
-        mAm.removeStacksInWindowingModes(new int[] {
+        mAtm.removeStacksInWindowingModes(new int[] {
                 WINDOWING_MODE_PINNED,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
                 WINDOWING_MODE_FREEFORM
@@ -427,11 +432,11 @@
     }
 
     private static boolean supportsDragAndDrop() {
-        return ActivityManager.supportsMultiWindow(InstrumentationRegistry.getContext());
+        return ActivityTaskManager.supportsMultiWindow(InstrumentationRegistry.getContext());
     }
 
     private static boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(InstrumentationRegistry.getContext());
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(InstrumentationRegistry.getContext());
     }
 
     private static boolean supportsFreeformMultiWindow() {
diff --git a/tests/mocking/Android.mk b/tests/mocking/Android.mk
index 2afa4d2..13f74fe 100644
--- a/tests/mocking/Android.mk
+++ b/tests/mocking/Android.mk
@@ -23,10 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target \
     android-support-test \
-    ctstestrunner
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../external/dexmaker/dexmaker-mockito-tests/src/androidTest/java)
+    ctstestrunner \
+    dexmaker-mockmaker-tests
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml
index 9e53939..c6e4ffe 100644
--- a/tests/mocking/AndroidTest.xml
+++ b/tests/mocking/AndroidTest.xml
@@ -25,6 +25,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.mocking.cts" />
-        <option name="runtime-hint" value="5s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 </configuration>
diff --git a/tests/mocking/debuggable/Android.mk b/tests/mocking/debuggable/Android.mk
index 5187811..e72f69f 100644
--- a/tests/mocking/debuggable/Android.mk
+++ b/tests/mocking/debuggable/Android.mk
@@ -23,10 +23,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target \
     android-support-test \
-    ctstestrunner
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../../external/dexmaker/dexmaker-mockito-tests/src/androidTest/java)
+    ctstestrunner \
+    dexmaker-mockmaker-tests
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/debuggable/AndroidTest.xml b/tests/mocking/debuggable/AndroidTest.xml
index 135de46..645f2a2 100644
--- a/tests/mocking/debuggable/AndroidTest.xml
+++ b/tests/mocking/debuggable/AndroidTest.xml
@@ -26,6 +26,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.mocking.cts.debuggable" />
-        <option name="runtime-hint" value="5s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 </configuration>
diff --git a/tests/mocking/extended/Android.mk b/tests/mocking/extended/Android.mk
new file mode 100644
index 0000000..3fee499
--- /dev/null
+++ b/tests/mocking/extended/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := \
+    tests
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner.stubs
+LOCAL_STATIC_JAVA_LIBRARIES = \
+    mockito-target-extended \
+    android-support-test \
+    ctstestrunner \
+    dexmaker-mockmaker-tests \
+    dexmaker-inline-mockmaker-tests \
+    dexmaker-extended-mockmaker-tests \
+    android-support-v4
+LOCAL_MULTILIB := \
+    both
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+    libmultiplejvmtiagentsinterferenceagent \
+    libstaticjvmtiagent
+LOCAL_COMPATIBILITY_SUITE := \
+    cts vts general-tests
+LOCAL_PACKAGE_NAME := \
+    CtsExtendedMockingTestCases
+LOCAL_SDK_VERSION := \
+    current
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/mocking/extended/AndroidManifest.xml b/tests/mocking/extended/AndroidManifest.xml
new file mode 100644
index 0000000..f62d95f
--- /dev/null
+++ b/tests/mocking/extended/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.extended.mocking.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="com.android.dx.mockito.inline.extended.tests.EmptyActivity" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.extended.mocking.cts"
+                     android:label="CTS tests for mockito extended mocking">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/mocking/extended/AndroidTest.xml b/tests/mocking/extended/AndroidTest.xml
new file mode 100644
index 0000000..5fcd077
--- /dev/null
+++ b/tests/mocking/extended/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<configuration description="Config for Mockito extended mocking test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="mocking" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsExtendedMockingTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.extended.mocking.cts" />
+        <option name="runtime-hint" value="120s" />
+    </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/mocking/inline/Android.mk b/tests/mocking/inline/Android.mk
index 9fa873d..67b4fb4 100644
--- a/tests/mocking/inline/Android.mk
+++ b/tests/mocking/inline/Android.mk
@@ -23,15 +23,15 @@
 LOCAL_STATIC_JAVA_LIBRARIES = \
     mockito-target-inline \
     android-support-test \
-    ctstestrunner
+    ctstestrunner \
+    dexmaker-mockmaker-tests \
+    dexmaker-inline-mockmaker-tests \
+    android-support-v4
 LOCAL_MULTILIB := \
     both
 LOCAL_JNI_SHARED_LIBRARIES := \
     libdexmakerjvmtiagent \
     libmultiplejvmtiagentsinterferenceagent
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, \
-            ../../../../external/dexmaker/dexmaker-mockito-inline-tests/src/androidTest/java)
 LOCAL_COMPATIBILITY_SUITE := \
     cts vts general-tests
 LOCAL_PACKAGE_NAME := \
diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml
index 7158dcb..f7b51fb 100644
--- a/tests/mocking/inline/AndroidTest.xml
+++ b/tests/mocking/inline/AndroidTest.xml
@@ -25,7 +25,7 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.inline.mocking.cts" />
-        <option name="runtime-hint" value="30s" />
+        <option name="runtime-hint" value="120s" />
     </test>
 
     <!-- Controller that will skip the module if a native bridge situation is detected -->
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 8a5e974..d4f844f 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -368,6 +368,33 @@
     }
 
     @Test
+    public void testSeekAfterPause() throws Throwable {
+        final AnimatorSet set = new AnimatorSet();
+        ValueAnimator a1 = ValueAnimator.ofFloat(0f, 50f);
+        a1.setDuration(50);
+        ValueAnimator a2 = ValueAnimator.ofFloat(50, 100f);
+        a2.setDuration(50);
+        set.playSequentially(a1, a2);
+        set.setInterpolator(new LinearInterpolator());
+
+        mActivityRule.runOnUiThread(() -> {
+            set.start();
+            set.pause();
+            set.setCurrentPlayTime(60);
+            assertEquals((long) set.getCurrentPlayTime(), 60);
+            assertEquals((float) a1.getAnimatedValue(), 50f, EPSILON);
+            assertEquals((float) a2.getAnimatedValue(), 60f, EPSILON);
+
+            set.setCurrentPlayTime(40);
+            assertEquals((long) set.getCurrentPlayTime(), 40);
+            assertEquals((float) a1.getAnimatedValue(), 40f, EPSILON);
+            assertEquals((float) a2.getAnimatedValue(), 50f, EPSILON);
+
+            set.cancel();
+        });
+    }
+
+    @Test
     public void testDuration() throws Throwable {
         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
         Animator[] animatorArray = { xAnimator, yAnimator };
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
index 4aef998..19a34eb1 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -29,6 +29,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BatteryUtils;
 import com.android.compatibility.common.util.BeforeAfterRule;
 import com.android.compatibility.common.util.OnFailureRule;
 
@@ -60,9 +61,7 @@
     private final BeforeAfterRule mInitializeAndCleanupRule = new BeforeAfterRule() {
         @Override
         protected void onBefore(Statement base, Description description) throws Throwable {
-            // Don't run any battery saver tests on wear.
-            final PackageManager pm = getPackageManager();
-            Assume.assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
+            BatteryUtils.assumeBatterySaverFeature();
 
             turnOnScreen(true);
         }
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 5d59af7..480bb06 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -63,7 +63,8 @@
 	-c tlh \
 	-c xx,xx-rYY
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, BinderPermissionTestService)
 LOCAL_MULTILIB := both
 LOCAL_PACKAGE_NAME := CtsContentTestCases
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 20baa2d..a4e5b1f 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -144,9 +144,9 @@
 
         <uses-library android:name="android.test.runner" />
 
-        <service android:name="android.content.cts.MockContextWrapperService" />
-        <activity android:name=".content.ContextWrapperCtsActivity"
-            android:label="ContextWrapperCtsActivity">
+        <service android:name="android.content.cts.MockContextService" />
+        <activity android:name=".content.ContextCtsActivity"
+            android:label="ContextCtsActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
@@ -167,7 +167,7 @@
             <intent-filter android:priority="1">
                 <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_MOCKTEST" />
                 <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_TESTABORT" />
-                <action android:name="android.content.cts.ContextWrapperTest.BROADCAST_TESTORDER" />
+                <action android:name="android.content.cts.ContextTest.BROADCAST_TESTORDER" />
             </intent-filter>
         </receiver>
 
@@ -189,7 +189,8 @@
 
         <!--Test for PackageManager-->
         <activity android:name="android.content.pm.cts.TestPmActivity"
-                android:icon="@drawable/start">
+                android:icon="@drawable/start"
+                android:launchMode="singleTop">
             <intent-filter>
                 <action android:name="android.intent.action.PMTEST" />
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -203,7 +204,8 @@
             </intent-filter>
         </activity>
         <!--Test for PackageManager-->
-        <service android:name="android.content.pm.cts.TestPmService">
+        <service android:name="android.content.pm.cts.TestPmService"
+            android:permission="android.content.cts.CALL_ABROAD_PERMISSION">
             <intent-filter>
                 <action android:name="android.content.pm.cts.activity.PMTEST_SERVICE" />
             </intent-filter>
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 58305dd..7b7f52e 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -32,6 +32,7 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsContentTestCases.apk" />
         <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
+        <option name="test-file-name" value="CtsBinderPermissionTestService.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/content/BinderPermissionTestService/Android.mk b/tests/tests/content/BinderPermissionTestService/Android.mk
new file mode 100644
index 0000000..697d8f6
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/Android.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, aidl)
+
+LOCAL_PACKAGE_NAME := CtsBinderPermissionTestService
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml b/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml
new file mode 100644
index 0000000..c0e4774
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts">
+    <application>
+        <service
+            android:name=".BinderPermissionTestService"
+            android:exported="true">
+        </service>
+    </application>
+</manifest>
diff --git a/tests/tests/content/BinderPermissionTestService/README.txt b/tests/tests/content/BinderPermissionTestService/README.txt
new file mode 100644
index 0000000..1b7c2bd
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/README.txt
@@ -0,0 +1,7 @@
+A test project that publishes a Binder service. The methods of this service
+check if their caller has certain permissions. This service is used by
+Context tests to verify that methods like enforceCallingPermission()
+work correctly.
+
+This service has to be in a separate package so the permissions of the
+caller (the test) and the callee (this service) are different.
diff --git a/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl b/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl
new file mode 100644
index 0000000..26e6326
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/aidl/com/android/cts/IBinderPermissionTestService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts;
+
+interface IBinderPermissionTestService {
+    // Methods that, when called, invoke one of the permission check methods
+    // to check the caller's permissions.
+    void doEnforceCallingPermission(String permission);
+    int doCheckCallingPermission(String permission);
+    void doEnforceCallingOrSelfPermission(String permission);
+    int doCheckCallingOrSelfPermission(String permission);
+}
diff --git a/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java b/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java
new file mode 100644
index 0000000..955fb30
--- /dev/null
+++ b/tests/tests/content/BinderPermissionTestService/src/com/android/cts/BinderPermissionTestService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+public class BinderPermissionTestService extends Service {
+
+    private static String TEST_NOT_ALLOWED_MESSAGE = "Test: you're not allowed to do this.";
+
+    private final IBinder mBinder = new IBinderPermissionTestService.Stub() {
+        @Override
+        public void doEnforceCallingPermission(String permission) {
+            enforceCallingPermission(permission, TEST_NOT_ALLOWED_MESSAGE);
+        }
+
+        @Override
+        public int doCheckCallingPermission(String permission) {
+            return checkCallingPermission(permission);
+        }
+
+        @Override
+        public void doEnforceCallingOrSelfPermission(String permission) {
+            enforceCallingOrSelfPermission(permission, TEST_NOT_ALLOWED_MESSAGE);
+        }
+
+        @Override
+        public int doCheckCallingOrSelfPermission(String permission) {
+            return checkCallingOrSelfPermission(permission);
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/tests/tests/content/SyncAccountAccessStubs/Android.mk b/tests/tests/content/SyncAccountAccessStubs/Android.mk
index 9f015a3..d176ce7 100644
--- a/tests/tests/content/SyncAccountAccessStubs/Android.mk
+++ b/tests/tests/content/SyncAccountAccessStubs/Android.mk
@@ -20,10 +20,13 @@
 
 LOCAL_MODULE_TAGS := tests
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-annotations
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSyncAccountAccessStubs
-LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
diff --git a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
index 8a21e0d..c97e115 100644
--- a/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
+++ b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
@@ -16,12 +16,12 @@
 
 package com.android.cts.stub;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 public class StubProvider extends ContentProvider {
     @Override
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 4723524..c812e91 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -365,4 +365,18 @@
             assertCanBeHandled(new Intent("android.settings.LOCATION_SCANNING_SETTINGS"));
         }
     }
+
+    public void testChangeDefaultDialer() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            assertCanBeHandled(new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER));
+        }
+    }
+
+    public void testTapAnPaySettings() {
+        PackageManager packageManager = mContext.getPackageManager();
+        if (packageManager.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+            assertCanBeHandled(new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS));
+        }
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
index 4d5378c..5cfd532 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
@@ -16,7 +16,9 @@
 
 package android.content.cts;
 
+import android.database.Cursor;
 import android.database.CursorWindowAllocationException;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.platform.test.annotations.SecurityTest;
 import android.test.AndroidTestCase;
@@ -30,13 +32,33 @@
     private static final String TAG = "ContentProviderCursorWindowTest";
 
     public void testQuery() {
+        // First check if the system has a patch for enforcing protected Parcel data
+        Cursor cursor;
         try {
-            getContext().getContentResolver().query(
+            cursor = getContext().getContentResolver().query(
                     Uri.parse("content://cursorwindow.provider/hello"),
                     null, null, null, null);
-            fail("Reading from malformed Parcel should fail due to invalid offset used");
         } catch (CursorWindowAllocationException expected) {
             Log.i(TAG, "Expected exception: " + expected);
+            return;
+        }
+
+        // If the system has no patch for protected Parcel data,
+        // it should still fail while reading from the cursor
+        try {
+            cursor.moveToFirst();
+
+            int type = cursor.getType(0);
+            if (type != Cursor.FIELD_TYPE_BLOB) {
+                fail("Unexpected type " + type);
+            }
+            byte[] blob = cursor.getBlob(0);
+            Log.i(TAG,  "Blob length " + blob.length);
+            fail("getBlob should fail due to invalid offset used in the field slot");
+        } catch (SQLiteException expected) {
+            Log.i(TAG, "Expected exception: " + expected);
+        } finally {
+            cursor.close();
         }
     }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java b/tests/tests/content/src/android/content/cts/ContextCtsActivity.java
similarity index 94%
rename from tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
rename to tests/tests/content/src/android/content/cts/ContextCtsActivity.java
index 7ea2ab7..a7ea89a 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperCtsActivity.java
+++ b/tests/tests/content/src/android/content/cts/ContextCtsActivity.java
@@ -23,7 +23,7 @@
 
 import android.content.cts.R;
 
-public class ContextWrapperCtsActivity extends Activity {
+public class ContextCtsActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index f84c771..413da63 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -16,39 +16,131 @@
 
 package android.content.cts;
 
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteCursorDriver;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQuery;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
+import android.os.Process;
+import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 import android.view.WindowManager;
 
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.cts.IBinderPermissionTestService;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 public class ContextTest extends AndroidTestCase {
     private static final String TAG = "ContextTest";
+    private static final String ACTUAL_RESULT = "ResultSetByReceiver";
 
-    private Context mContext;
+    private static final String INTIAL_RESULT = "IntialResult";
+
+    private static final String VALUE_ADDED = "ValueAdded";
+    private static final String KEY_ADDED = "AddedByReceiver";
+
+    private static final String VALUE_REMOVED = "ValueWillBeRemove";
+    private static final String KEY_REMOVED = "ToBeRemoved";
+
+    private static final String VALUE_KEPT = "ValueKept";
+    private static final String KEY_KEPT = "ToBeKept";
+
+    private static final String MOCK_STICKY_ACTION = "android.content.cts.ContextTest."
+            + "STICKY_BROADCAST_RESULT";
+
+    private static final String ACTION_BROADCAST_TESTORDER =
+            "android.content.cts.ContextTest.BROADCAST_TESTORDER";
+    private final static String MOCK_ACTION1 = ACTION_BROADCAST_TESTORDER + "1";
+    private final static String MOCK_ACTION2 = ACTION_BROADCAST_TESTORDER + "2";
+
+    // Note: keep these constants in sync with the permissions used by BinderPermissionTestService.
+    //
+    // A permission that's granted to this test package.
+    public static final String GRANTED_PERMISSION = "android.permission.USE_CREDENTIALS";
+    // A permission that's not granted to this test package.
+    public static final String NOT_GRANTED_PERMISSION = "android.permission.HARDWARE_TEST";
+
+    private static final int BROADCAST_TIMEOUT = 10000;
+    private static final int ROOT_UID = 0;
+
+    private Object mLockObj;
+
+    private ArrayList<BroadcastReceiver> mRegisteredReceiverList;
+
+    private boolean mWallpaperChanged;
+    private BitmapDrawable mOriginalWallpaper;
+    private volatile IBinderPermissionTestService mBinderPermissionTestService;
+    private ServiceConnection mBinderPermissionTestConnection;
+
+    protected Context mContext;
+
+    /**
+     * Returns the Context object that's being tested.
+     */
+    protected Context getContextUnderTest() {
+        return getContext();
+    }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mContext = getContext();
+        mContext = getContextUnderTest();
         mContext.setTheme(R.style.Test_Theme);
+
+        mLockObj = new Object();
+
+        mRegisteredReceiverList = new ArrayList<BroadcastReceiver>();
+
+        mOriginalWallpaper = (BitmapDrawable) mContext.getWallpaper();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mWallpaperChanged) {
+            mContext.setWallpaper(mOriginalWallpaper.getBitmap());
+        }
+
+        for (BroadcastReceiver receiver : mRegisteredReceiverList) {
+            mContext.unregisterReceiver(receiver);
+        }
+
+        super.tearDown();
     }
 
     public void testGetString() {
@@ -354,6 +446,10 @@
 
     private void assertValidFile(File file) throws Exception {
         Log.d(TAG, "Checking " + file);
+        if (file.exists()) {
+            assertTrue("File already exists and couldn't be deleted before test: " + file,
+                    file.delete());
+        }
         assertTrue("Failed to create " + file, file.createNewFile());
         assertTrue("Doesn't exist after create " + file, file.exists());
         assertTrue("Failed to delete after create " + file, file.delete());
@@ -382,7 +478,7 @@
     }
 
     private AttributeSet getAttributeSet(int resourceId) {
-        final XmlResourceParser parser = getContext().getResources().getXml(
+        final XmlResourceParser parser = mContext.getResources().getXml(
                 resourceId);
 
         try {
@@ -397,4 +493,842 @@
         assertNotNull(attr);
         return attr;
     }
+
+    private void registerBroadcastReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        mContext.registerReceiver(receiver, filter);
+
+        mRegisteredReceiverList.add(receiver);
+    }
+
+    public void testSendOrderedBroadcast1() throws InterruptedException {
+        final HighPriorityBroadcastReceiver highPriorityReceiver =
+                new HighPriorityBroadcastReceiver();
+        final LowPriorityBroadcastReceiver lowPriorityReceiver =
+                new LowPriorityBroadcastReceiver();
+
+        final IntentFilter filterHighPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
+        filterHighPriority.setPriority(1);
+        final IntentFilter filterLowPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
+        registerBroadcastReceiver(highPriorityReceiver, filterHighPriority);
+        registerBroadcastReceiver(lowPriorityReceiver, filterLowPriority);
+
+        final Intent broadcastIntent = new Intent(ResultReceiver.MOCK_ACTION);
+        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendOrderedBroadcast(broadcastIntent, null);
+        new PollingCheck(BROADCAST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return highPriorityReceiver.hasReceivedBroadCast()
+                        && !lowPriorityReceiver.hasReceivedBroadCast();
+            }
+        }.run();
+
+        synchronized (highPriorityReceiver) {
+            highPriorityReceiver.notify();
+        }
+
+        new PollingCheck(BROADCAST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return highPriorityReceiver.hasReceivedBroadCast()
+                        && lowPriorityReceiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testSendOrderedBroadcast2() throws InterruptedException {
+        final TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
+        broadcastReceiver.mIsOrderedBroadcasts = true;
+
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_KEPT, VALUE_KEPT);
+        bundle.putString(KEY_REMOVED, VALUE_REMOVED);
+        Intent intent = new Intent(ResultReceiver.MOCK_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendOrderedBroadcast(intent, null, broadcastReceiver, null, 1,
+                INTIAL_RESULT, bundle);
+
+        synchronized (mLockObj) {
+            try {
+                mLockObj.wait(BROADCAST_TIMEOUT);
+            } catch (InterruptedException e) {
+                fail("unexpected InterruptedException.");
+            }
+        }
+
+        assertTrue("Receiver didn't make any response.", broadcastReceiver.hadReceivedBroadCast());
+        assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(), 3,
+                broadcastReceiver.getResultCode());
+        assertEquals(ACTUAL_RESULT, broadcastReceiver.getResultData());
+        Bundle resultExtras = broadcastReceiver.getResultExtras(false);
+        assertEquals(VALUE_ADDED, resultExtras.getString(KEY_ADDED));
+        assertEquals(VALUE_KEPT, resultExtras.getString(KEY_KEPT));
+        assertNull(resultExtras.getString(KEY_REMOVED));
+    }
+
+    public void testRegisterReceiver1() throws InterruptedException {
+        final FilteredReceiver broadcastReceiver = new FilteredReceiver();
+        final IntentFilter filter = new IntentFilter(MOCK_ACTION1);
+
+        // Test registerReceiver
+        mContext.registerReceiver(broadcastReceiver, filter);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        mContext.unregisterReceiver(broadcastReceiver);
+
+        // Test unregisterReceiver
+        FilteredReceiver broadcastReceiver2 = new FilteredReceiver();
+        mContext.registerReceiver(broadcastReceiver2, filter);
+        mContext.unregisterReceiver(broadcastReceiver2);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver2.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
+        broadcastReceiver2.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
+    }
+
+    public void testRegisterReceiver2() throws InterruptedException {
+        FilteredReceiver broadcastReceiver = new FilteredReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(MOCK_ACTION1);
+
+        // Test registerReceiver
+        mContext.registerReceiver(broadcastReceiver, filter, null, null);
+
+        // Test unwanted intent(action = MOCK_ACTION2)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION2);
+        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        // Send wanted intent(action = MOCK_ACTION1)
+        broadcastReceiver.reset();
+        waitForFilteredIntent(mContext, MOCK_ACTION1);
+        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
+        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
+
+        mContext.unregisterReceiver(broadcastReceiver);
+    }
+
+    public void testAccessWallpaper() throws IOException, InterruptedException {
+        // set Wallpaper by context#setWallpaper(Bitmap)
+        Bitmap bitmap = Bitmap.createBitmap(20, 30, Bitmap.Config.RGB_565);
+        // Test getWallpaper
+        Drawable testDrawable = mContext.getWallpaper();
+        // Test peekWallpaper
+        Drawable testDrawable2 = mContext.peekWallpaper();
+
+        mContext.setWallpaper(bitmap);
+        mWallpaperChanged = true;
+        synchronized(this) {
+            wait(500);
+        }
+
+        assertNotSame(testDrawable, mContext.peekWallpaper());
+        assertNotNull(mContext.getWallpaper());
+        assertNotSame(testDrawable2, mContext.peekWallpaper());
+        assertNotNull(mContext.peekWallpaper());
+
+        // set Wallpaper by context#setWallpaper(InputStream)
+        mContext.clearWallpaper();
+
+        testDrawable = mContext.getWallpaper();
+        InputStream stream = mContext.getResources().openRawResource(R.drawable.scenery);
+
+        mContext.setWallpaper(stream);
+        synchronized (this) {
+            wait(1000);
+        }
+
+        assertNotSame(testDrawable, mContext.peekWallpaper());
+    }
+
+    public void testAccessDatabase() {
+        String DATABASE_NAME = "databasetest";
+        String DATABASE_NAME1 = DATABASE_NAME + "1";
+        String DATABASE_NAME2 = DATABASE_NAME + "2";
+        SQLiteDatabase mDatabase;
+        File mDatabaseFile;
+
+        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
+            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
+                                    String editTable, SQLiteQuery query) {
+                return new android.database.sqlite.SQLiteCursor(db, masterQuery, editTable, query) {
+                    @Override
+                    public boolean requery() {
+                        setSelectionArguments(new String[] { "2" });
+                        return super.requery();
+                    }
+                };
+            }
+        };
+
+        // FIXME: Move cleanup into tearDown()
+        for (String db : mContext.databaseList()) {
+            File f = mContext.getDatabasePath(db);
+            if (f.exists()) {
+                mContext.deleteDatabase(db);
+            }
+        }
+
+        // Test openOrCreateDatabase with null and actual factory
+        mDatabase = mContext.openOrCreateDatabase(DATABASE_NAME1,
+                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
+        assertNotNull(mDatabase);
+        mDatabase.close();
+        mDatabase = mContext.openOrCreateDatabase(DATABASE_NAME2,
+                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
+        assertNotNull(mDatabase);
+        mDatabase.close();
+
+        // Test getDatabasePath
+        File actualDBPath = mContext.getDatabasePath(DATABASE_NAME1);
+
+        // Test databaseList()
+        List<String> list = Arrays.asList(mContext.databaseList());
+        assertEquals(2, list.size());
+        assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
+        assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
+
+        // Test deleteDatabase()
+        for (int i = 1; i < 3; i++) {
+            mDatabaseFile = mContext.getDatabasePath(DATABASE_NAME + i);
+            assertTrue(mDatabaseFile.exists());
+            mContext.deleteDatabase(DATABASE_NAME + i);
+            mDatabaseFile = new File(actualDBPath, DATABASE_NAME + i);
+            assertFalse(mDatabaseFile.exists());
+        }
+    }
+
+    public void testEnforceUriPermission1() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceUriPermission(uri, Binder.getCallingPid(),
+                    Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceUriPermission is not working without possessing an IPC.");
+            fail("enforceUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testEnforceUriPermission2() {
+        Uri uri = Uri.parse("content://ctstest");
+        try {
+            mContext.enforceUriPermission(uri, NOT_GRANTED_PERMISSION,
+                    NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceUriPermission is not working without possessing an IPC.");
+            fail("enforceUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is ok, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetPackageResourcePath() {
+        assertNotNull(mContext.getPackageResourcePath());
+    }
+
+    public void testStartActivity() {
+        Intent intent = new Intent(mContext, ContextCtsActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        try {
+            mContext.startActivity(intent);
+            fail("Test startActivity should thow a ActivityNotFoundException here.");
+        } catch (ActivityNotFoundException e) {
+            // Because ContextWrapper is a wrapper class, so no need to test
+            // the details of the function's performance. Getting a result
+            // from the wrapped class is enough for testing.
+        }
+    }
+
+    public void testCreatePackageContext() throws PackageManager.NameNotFoundException {
+        Context actualContext = mContext.createPackageContext(getValidPackageName(),
+                Context.CONTEXT_IGNORE_SECURITY);
+
+        assertNotNull(actualContext);
+    }
+
+    /**
+     * Helper method to retrieve a valid application package name to use for tests.
+     */
+    protected String getValidPackageName() {
+        List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackages(
+                PackageManager.GET_ACTIVITIES);
+        assertTrue(packages.size() >= 1);
+        return packages.get(0).packageName;
+    }
+
+    public void testGetMainLooper() {
+        assertNotNull(mContext.getMainLooper());
+    }
+
+    public void testGetApplicationContext() {
+        assertSame(mContext.getApplicationContext(), mContext.getApplicationContext());
+    }
+
+    public void testGetSharedPreferences() {
+        SharedPreferences sp;
+        SharedPreferences localSP;
+
+        sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        String packageName = mContext.getPackageName();
+        localSP = mContext.getSharedPreferences(packageName + "_preferences",
+                Context.MODE_PRIVATE);
+        assertSame(sp, localSP);
+    }
+
+    public void testRevokeUriPermission() {
+        Uri uri = Uri.parse("contents://ctstest");
+        mContext.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    public void testAccessService() throws InterruptedException {
+        MockContextService.reset();
+        bindExpectResult(mContext, new Intent(mContext, MockContextService.class));
+
+        // Check startService
+        assertTrue(MockContextService.hadCalledOnStart());
+        // Check bindService
+        assertTrue(MockContextService.hadCalledOnBind());
+
+        assertTrue(MockContextService.hadCalledOnDestory());
+        // Check unbinService
+        assertTrue(MockContextService.hadCalledOnUnbind());
+    }
+
+    public void testGetPackageCodePath() {
+        assertNotNull(mContext.getPackageCodePath());
+    }
+
+    public void testGetPackageName() {
+        assertEquals("android.content.cts", mContext.getPackageName());
+    }
+
+    public void testGetCacheDir() {
+        assertNotNull(mContext.getCacheDir());
+    }
+
+    public void testGetContentResolver() {
+        assertSame(mContext.getContentResolver(), mContext.getContentResolver());
+    }
+
+    public void testGetFileStreamPath() {
+        String TEST_FILENAME = "TestGetFileStreamPath";
+
+        // Test the path including the input filename
+        String fileStreamPath = mContext.getFileStreamPath(TEST_FILENAME).toString();
+        assertTrue(fileStreamPath.indexOf(TEST_FILENAME) >= 0);
+    }
+
+    public void testGetClassLoader() {
+        assertSame(mContext.getClassLoader(), mContext.getClassLoader());
+    }
+
+    public void testGetWallpaperDesiredMinimumHeightAndWidth() {
+        int height = mContext.getWallpaperDesiredMinimumHeight();
+        int width = mContext.getWallpaperDesiredMinimumWidth();
+
+        // returned value is <= 0, the caller should use the height of the
+        // default display instead.
+        // That is to say, the return values of desired minimumHeight and
+        // minimunWidth are at the same side of 0-dividing line.
+        assertTrue((height > 0 && width > 0) || (height <= 0 && width <= 0));
+    }
+
+    public void testAccessStickyBroadcast() throws InterruptedException {
+        ResultReceiver resultReceiver = new ResultReceiver();
+
+        Intent intent = new Intent(MOCK_STICKY_ACTION);
+        TestBroadcastReceiver stickyReceiver = new TestBroadcastReceiver();
+
+        mContext.sendStickyBroadcast(intent);
+
+        waitForReceiveBroadCast(resultReceiver);
+
+        assertEquals(intent.getAction(), mContext.registerReceiver(stickyReceiver,
+                new IntentFilter(MOCK_STICKY_ACTION)).getAction());
+
+        synchronized (mLockObj) {
+            mLockObj.wait(BROADCAST_TIMEOUT);
+        }
+
+        assertTrue("Receiver didn't make any response.", stickyReceiver.hadReceivedBroadCast());
+
+        mContext.unregisterReceiver(stickyReceiver);
+        mContext.removeStickyBroadcast(intent);
+
+        assertNull(mContext.registerReceiver(stickyReceiver,
+                new IntentFilter(MOCK_STICKY_ACTION)));
+        mContext.unregisterReceiver(stickyReceiver);
+    }
+
+    public void testCheckCallingOrSelfUriPermission() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkCallingOrSelfUriPermission(uri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testGrantUriPermission() {
+        mContext.grantUriPermission("com.android.mms", Uri.parse("contents://ctstest"),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    public void testCheckPermissionGranted() {
+        int returnValue = mContext.checkPermission(
+                GRANTED_PERMISSION, Process.myPid(), Process.myUid());
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckPermissionNotGranted() {
+        int returnValue = mContext.checkPermission(
+                NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid());
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testCheckPermissionRootUser() {
+        // Test with root user, everything will be granted.
+        int returnValue = mContext.checkPermission(NOT_GRANTED_PERMISSION, 1, ROOT_UID);
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckPermissionInvalidRequest() {
+        // Test with null permission.
+        try {
+            int returnValue = mContext.checkPermission(null, 0, ROOT_UID);
+            fail("checkPermission should not accept null permission");
+        } catch (IllegalArgumentException e) {
+        }
+
+        // Test with invalid uid and included granted permission.
+        int returnValue = mContext.checkPermission(GRANTED_PERMISSION, 1, -11);
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testCheckSelfPermissionGranted() {
+        int returnValue = mContext.checkSelfPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+    }
+
+    public void testCheckSelfPermissionNotGranted() {
+        int returnValue = mContext.checkSelfPermission(NOT_GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+    }
+
+    public void testEnforcePermissionGranted() {
+        mContext.enforcePermission(
+                GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
+                "permission isn't granted");
+    }
+
+    public void testEnforcePermissionNotGranted() {
+        try {
+            mContext.enforcePermission(
+                    NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
+                    "permission isn't granted");
+            fail("Permission shouldn't be granted.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testCheckCallingOrSelfPermission_noIpc() {
+        // There's no ongoing Binder call, so this package's permissions are checked.
+        int retValue = mContext.checkCallingOrSelfPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkCallingOrSelfPermission(NOT_GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckCallingOrSelfPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            int retValue = mBinderPermissionTestService.doCheckCallingOrSelfPermission(
+                    GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+            retValue = mBinderPermissionTestService.doCheckCallingOrSelfPermission(
+                    NOT_GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testEnforceCallingOrSelfPermission_noIpc() {
+        // There's no ongoing Binder call, so this package's permissions are checked.
+        mContext.enforceCallingOrSelfPermission(
+                GRANTED_PERMISSION, "permission isn't granted");
+
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    NOT_GRANTED_PERMISSION, "permission isn't granted");
+            fail("Permission shouldn't be granted.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testEnforceCallingOrSelfPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            mBinderPermissionTestService.doEnforceCallingOrSelfPermission(GRANTED_PERMISSION);
+
+            try {
+                mBinderPermissionTestService.doEnforceCallingOrSelfPermission(
+                        NOT_GRANTED_PERMISSION);
+                fail("Permission shouldn't be granted.");
+            } catch (SecurityException expected) {
+            }
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testCheckCallingPermission_noIpc() {
+        // Denied because no IPC is active.
+        int retValue = mContext.checkCallingPermission(GRANTED_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testEnforceCallingPermission_noIpc() {
+        try {
+            mContext.enforceCallingPermission(
+                    GRANTED_PERMISSION,
+                    "enforceCallingPermission is not working without possessing an IPC.");
+            fail("enforceCallingPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // Currently no IPC is handled by this process, this exception is expected
+        }
+    }
+
+    public void testEnforceCallingPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            mBinderPermissionTestService.doEnforceCallingPermission(GRANTED_PERMISSION);
+
+            try {
+                mBinderPermissionTestService.doEnforceCallingPermission(NOT_GRANTED_PERMISSION);
+                fail("Permission shouldn't be granted.");
+            } catch (SecurityException expected) {
+            }
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    public void testCheckCallingPermission_ipc() throws Exception {
+        bindBinderPermissionTestService();
+        try {
+            int returnValue = mBinderPermissionTestService.doCheckCallingPermission(
+                    GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
+
+            returnValue = mBinderPermissionTestService.doCheckCallingPermission(
+                    NOT_GRANTED_PERMISSION);
+            assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
+        } finally {
+            mContext.unbindService(mBinderPermissionTestConnection);
+        }
+    }
+
+    private void bindBinderPermissionTestService() {
+        Intent intent = new Intent(mContext, IBinderPermissionTestService.class);
+        intent.setComponent(new ComponentName(
+                "com.android.cts", "com.android.cts.BinderPermissionTestService"));
+
+        mBinderPermissionTestConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                mBinderPermissionTestService =
+                        IBinderPermissionTestService.Stub.asInterface(iBinder);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName componentName) {
+            }
+        };
+
+        assertTrue("Service not bound", mContext.bindService(
+                intent, mBinderPermissionTestConnection, Context.BIND_AUTO_CREATE));
+
+        new PollingCheck(15 * 1000) {
+            protected boolean check() {
+                return mBinderPermissionTestService != null; // Service was bound.
+            }
+        }.run();
+    }
+
+    public void testCheckUriPermission1() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkUriPermission(uri, Binder.getCallingPid(), 0,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkUriPermission(uri, Binder.getCallingPid(),
+                Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckUriPermission2() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
+                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), 0,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
+
+        retValue = mContext.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
+                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testCheckCallingUriPermission() {
+        Uri uri = Uri.parse("content://ctstest");
+
+        int retValue = mContext.checkCallingUriPermission(uri,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
+    }
+
+    public void testEnforceCallingUriPermission() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceCallingUriPermission is not working without possessing an IPC.");
+            fail("enforceCallingUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetDir() {
+        File dir = mContext.getDir("testpath", Context.MODE_PRIVATE);
+        assertNotNull(dir);
+        dir.delete();
+    }
+
+    public void testGetPackageManager() {
+        assertSame(mContext.getPackageManager(), mContext.getPackageManager());
+    }
+
+    public void testSendBroadcast1() throws InterruptedException {
+        final ResultReceiver receiver = new ResultReceiver();
+
+        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
+
+        mContext.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION));
+
+        new PollingCheck(BROADCAST_TIMEOUT){
+            @Override
+            protected boolean check() {
+                return receiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testSendBroadcast2() throws InterruptedException {
+        final ResultReceiver receiver = new ResultReceiver();
+
+        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
+
+        mContext.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION), null);
+
+        new PollingCheck(BROADCAST_TIMEOUT){
+            @Override
+            protected boolean check() {
+                return receiver.hasReceivedBroadCast();
+            }
+        }.run();
+    }
+
+    public void testEnforceCallingOrSelfUriPermission() {
+        try {
+            Uri uri = Uri.parse("content://ctstest");
+            mContext.enforceCallingOrSelfUriPermission(uri,
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                    "enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
+            fail("enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
+        } catch (SecurityException e) {
+            // If the function is OK, it should throw a SecurityException here because currently no
+            // IPC is handled by this process.
+        }
+    }
+
+    public void testGetAssets() {
+        assertSame(mContext.getAssets(), mContext.getAssets());
+    }
+
+    public void testGetResources() {
+        assertSame(mContext.getResources(), mContext.getResources());
+    }
+
+    public void testStartInstrumentation() {
+        // Use wrong name
+        ComponentName cn = new ComponentName("com.android",
+                "com.android.content.FalseLocalSampleInstrumentation");
+        assertNotNull(cn);
+        assertNotNull(mContext);
+        // If the target instrumentation is wrong, the function should return false.
+        assertFalse(mContext.startInstrumentation(cn, null, null));
+    }
+
+    private void bindExpectResult(Context context, Intent service)
+            throws InterruptedException {
+        if (service == null) {
+            fail("No service created!");
+        }
+        TestConnection conn = new TestConnection(true, false);
+
+        context.bindService(service, conn, Context.BIND_AUTO_CREATE);
+        context.startService(service);
+
+        // Wait for a short time, so the service related operations could be
+        // working.
+        synchronized (this) {
+            wait(2500);
+        }
+        // Test stop Service
+        assertTrue(context.stopService(service));
+        context.unbindService(conn);
+
+        synchronized (this) {
+            wait(1000);
+        }
+    }
+
+    private interface Condition {
+        public boolean onCondition();
+    }
+
+    private synchronized void waitForCondition(Condition con) throws InterruptedException {
+        // check the condition every 1 second until the condition is fulfilled
+        // and wait for 3 seconds at most
+        for (int i = 0; !con.onCondition() && i <= 3; i++) {
+            wait(1000);
+        }
+    }
+
+    private void waitForReceiveBroadCast(final ResultReceiver receiver)
+            throws InterruptedException {
+        Condition con = new Condition() {
+            public boolean onCondition() {
+                return receiver.hasReceivedBroadCast();
+            }
+        };
+        waitForCondition(con);
+    }
+
+    private void waitForFilteredIntent(Context context, final String action)
+            throws InterruptedException {
+        context.sendBroadcast(new Intent(action), null);
+
+        synchronized (mLockObj) {
+            mLockObj.wait(BROADCAST_TIMEOUT);
+        }
+    }
+
+    private final class TestBroadcastReceiver extends BroadcastReceiver {
+        boolean mHadReceivedBroadCast;
+        boolean mIsOrderedBroadcasts;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (this) {
+                if (mIsOrderedBroadcasts) {
+                    setResultCode(3);
+                    setResultData(ACTUAL_RESULT);
+                }
+
+                Bundle map = getResultExtras(false);
+                if (map != null) {
+                    map.remove(KEY_REMOVED);
+                    map.putString(KEY_ADDED, VALUE_ADDED);
+                }
+                mHadReceivedBroadCast = true;
+                this.notifyAll();
+            }
+
+            synchronized (mLockObj) {
+                mLockObj.notify();
+            }
+        }
+
+        boolean hadReceivedBroadCast() {
+            return mHadReceivedBroadCast;
+        }
+
+        void reset(){
+            mHadReceivedBroadCast = false;
+        }
+    }
+
+    private class FilteredReceiver extends BroadcastReceiver {
+        private boolean mHadReceivedBroadCast1 = false;
+        private boolean mHadReceivedBroadCast2 = false;
+
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (MOCK_ACTION1.equals(action)) {
+                mHadReceivedBroadCast1 = true;
+            } else if (MOCK_ACTION2.equals(action)) {
+                mHadReceivedBroadCast2 = true;
+            }
+
+            synchronized (mLockObj) {
+                mLockObj.notify();
+            }
+        }
+
+        public boolean hadReceivedBroadCast1() {
+            return mHadReceivedBroadCast1;
+        }
+
+        public boolean hadReceivedBroadCast2() {
+            return mHadReceivedBroadCast2;
+        }
+
+        public void reset(){
+            mHadReceivedBroadCast1 = false;
+            mHadReceivedBroadCast2 = false;
+        }
+    }
+
+    private class TestConnection implements ServiceConnection {
+        public TestConnection(boolean expectDisconnect, boolean setReporter) {
+        }
+
+        void setMonitor(boolean v) {
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 408855d..abf3506 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -16,117 +16,29 @@
 
 package android.content.cts;
 
-import android.content.cts.R;
-
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteCursorDriver;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQuery;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Process;
-import android.preference.PreferenceManager;
-import android.test.AndroidTestCase;
-import android.view.WindowManager;
-
-import com.android.compatibility.common.util.PollingCheck;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 
 /**
  * Test {@link ContextWrapper}.
+ *
+ * <p>
+ * This class inherits most of its test methods from its parent ContextTest.
+ * Since ContextWrapper delegates its requests to the Context, the same test cases should pass
+ * for both Context and ContextWrapper.
+ *
+ * <p>
+ * There are some tests for ContextWrapper that don't make sense for Context - those are included
+ * in this class.
  */
-public class ContextWrapperTest extends AndroidTestCase {
-    private static final String ACTUAL_RESULT = "ResultSetByReceiver";
+public class ContextWrapperTest extends ContextTest {
 
-    private static final String INTIAL_RESULT = "IntialResult";
-
-    private static final String VALUE_ADDED = "ValueAdded";
-    private static final String KEY_ADDED = "AddedByReceiver";
-
-    private static final String VALUE_REMOVED = "ValueWillBeRemove";
-    private static final String KEY_REMOVED = "ToBeRemoved";
-
-    private static final String VALUE_KEPT = "ValueKept";
-    private static final String KEY_KEPT = "ToBeKept";
-
-    private static final String MOCK_STICKY_ACTION = "android.content.cts.ContextWrapperTest."
-        + "STICKY_BROADCAST_RESULT";
-
-    private static final String ACTION_BROADCAST_TESTORDER =
-        "android.content.cts.ContextWrapperTest.BROADCAST_TESTORDER";
-    private final static String MOCK_ACTION1 = ACTION_BROADCAST_TESTORDER + "1";
-    private final static String MOCK_ACTION2 = ACTION_BROADCAST_TESTORDER + "2";
-
-    // A permission that's granted to this test package.
-    public static final String GRANTED_PERMISSION = "android.permission.USE_CREDENTIALS";
-    // A permission that's not granted to this test package.
-    public static final String NOT_GRANTED_PERMISSION = "android.permission.HARDWARE_TEST";
-
-    private static final int BROADCAST_TIMEOUT = 10000;
-    private static final int ROOT_UID = 0;
-
-    private Context mContext;
-
-    private ContextWrapper mContextWrapper;
-    private Object mLockObj;
-
-    private ArrayList<BroadcastReceiver> mRegisteredReceiverList;
-
-    private boolean mWallpaperChanged;
-    private BitmapDrawable mOriginalWallpaper;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mLockObj = new Object();
-        mContext = getContext();
-        mContextWrapper = new ContextWrapper(mContext);
-
-        mRegisteredReceiverList = new ArrayList<BroadcastReceiver>();
-
-        mOriginalWallpaper = (BitmapDrawable) mContextWrapper.getWallpaper();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (mWallpaperChanged) {
-            mContextWrapper.setWallpaper(mOriginalWallpaper.getBitmap());
-        }
-
-        for (BroadcastReceiver receiver : mRegisteredReceiverList) {
-            mContextWrapper.unregisterReceiver(receiver);
-        }
-
-        super.tearDown();
-    }
-
-    private void registerBroadcastReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-        mContextWrapper.registerReceiver(receiver, filter);
-
-        mRegisteredReceiverList.add(receiver);
+    /**
+     * Returns the ContextWrapper object that's being tested.
+     */
+    protected Context getContextUnderTest() {
+        return new ContextWrapper(getContext());
     }
 
     public void testConstructor() {
@@ -136,344 +48,12 @@
         new ContextWrapper(null);
     }
 
-    public void testSendOrderedBroadcast1() throws InterruptedException {
-        final HighPriorityBroadcastReceiver highPriorityReceiver =
-                new HighPriorityBroadcastReceiver();
-        final LowPriorityBroadcastReceiver lowPriorityReceiver =
-            new LowPriorityBroadcastReceiver();
-
-        final IntentFilter filterHighPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
-        filterHighPriority.setPriority(1);
-        final IntentFilter filterLowPriority = new IntentFilter(ResultReceiver.MOCK_ACTION);
-        registerBroadcastReceiver(highPriorityReceiver, filterHighPriority);
-        registerBroadcastReceiver(lowPriorityReceiver, filterLowPriority);
-
-        final Intent broadcastIntent = new Intent(ResultReceiver.MOCK_ACTION);
-        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContextWrapper.sendOrderedBroadcast(broadcastIntent, null);
-        new PollingCheck(BROADCAST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return highPriorityReceiver.hasReceivedBroadCast()
-                        && !lowPriorityReceiver.hasReceivedBroadCast();
-            }
-        }.run();
-
-        synchronized (highPriorityReceiver) {
-            highPriorityReceiver.notify();
-        }
-
-        new PollingCheck(BROADCAST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return highPriorityReceiver.hasReceivedBroadCast()
-                        && lowPriorityReceiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testSendOrderedBroadcast2() throws InterruptedException {
-        final TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
-        broadcastReceiver.mIsOrderedBroadcasts = true;
-
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_KEPT, VALUE_KEPT);
-        bundle.putString(KEY_REMOVED, VALUE_REMOVED);
-        Intent intent = new Intent(ResultReceiver.MOCK_ACTION);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        mContextWrapper.sendOrderedBroadcast(intent, null, broadcastReceiver, null, 1,
-                INTIAL_RESULT, bundle);
-
-        synchronized (mLockObj) {
-            try {
-                mLockObj.wait(BROADCAST_TIMEOUT);
-            } catch (InterruptedException e) {
-                fail("unexpected InterruptedException.");
-            }
-        }
-
-        assertTrue("Receiver didn't make any response.", broadcastReceiver.hadReceivedBroadCast());
-        assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(), 3,
-                broadcastReceiver.getResultCode());
-        assertEquals(ACTUAL_RESULT, broadcastReceiver.getResultData());
-        Bundle resultExtras = broadcastReceiver.getResultExtras(false);
-        assertEquals(VALUE_ADDED, resultExtras.getString(KEY_ADDED));
-        assertEquals(VALUE_KEPT, resultExtras.getString(KEY_KEPT));
-        assertNull(resultExtras.getString(KEY_REMOVED));
-    }
-
-    public void testRegisterReceiver1() throws InterruptedException {
-        final FilteredReceiver broadcastReceiver = new FilteredReceiver();
-        final IntentFilter filter = new IntentFilter(MOCK_ACTION1);
-
-        // Test registerReceiver
-        mContextWrapper.registerReceiver(broadcastReceiver, filter);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        mContextWrapper.unregisterReceiver(broadcastReceiver);
-
-        // Test unregisterReceiver
-        FilteredReceiver broadcastReceiver2 = new FilteredReceiver();
-        mContextWrapper.registerReceiver(broadcastReceiver2, filter);
-        mContextWrapper.unregisterReceiver(broadcastReceiver2);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver2.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
-        broadcastReceiver2.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
-    }
-
-    public void testRegisterReceiver2() throws InterruptedException {
-        FilteredReceiver broadcastReceiver = new FilteredReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(MOCK_ACTION1);
-
-        // Test registerReceiver
-        mContextWrapper.registerReceiver(broadcastReceiver, filter, null, null);
-
-        // Test unwanted intent(action = MOCK_ACTION2)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
-        assertFalse(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        // Send wanted intent(action = MOCK_ACTION1)
-        broadcastReceiver.reset();
-        waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
-        assertTrue(broadcastReceiver.hadReceivedBroadCast1());
-        assertFalse(broadcastReceiver.hadReceivedBroadCast2());
-
-        mContextWrapper.unregisterReceiver(broadcastReceiver);
-    }
-
-    public void testAccessWallpaper() throws IOException, InterruptedException {
-        // set Wallpaper by contextWrapper#setWallpaper(Bitmap)
-        Bitmap bitmap = Bitmap.createBitmap(20, 30, Bitmap.Config.RGB_565);
-        // Test getWallpaper
-        Drawable testDrawable = mContextWrapper.getWallpaper();
-        // Test peekWallpaper
-        Drawable testDrawable2 = mContextWrapper.peekWallpaper();
-
-        mContextWrapper.setWallpaper(bitmap);
-        mWallpaperChanged = true;
-        synchronized(this) {
-            wait(500);
-        }
-
-        assertNotSame(testDrawable, mContextWrapper.peekWallpaper());
-        assertNotNull(mContextWrapper.getWallpaper());
-        assertNotSame(testDrawable2, mContextWrapper.peekWallpaper());
-        assertNotNull(mContextWrapper.peekWallpaper());
-
-        // set Wallpaper by contextWrapper#setWallpaper(InputStream)
-        mContextWrapper.clearWallpaper();
-
-        testDrawable = mContextWrapper.getWallpaper();
-        InputStream stream = mContextWrapper.getResources().openRawResource(R.drawable.scenery);
-
-        mContextWrapper.setWallpaper(stream);
-        synchronized (this) {
-            wait(1000);
-        }
-
-        assertNotSame(testDrawable, mContextWrapper.peekWallpaper());
-    }
-
-    public void testAccessDatabase() {
-        String DATABASE_NAME = "databasetest";
-        String DATABASE_NAME1 = DATABASE_NAME + "1";
-        String DATABASE_NAME2 = DATABASE_NAME + "2";
-        SQLiteDatabase mDatabase;
-        File mDatabaseFile;
-
-        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
-            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
-                    String editTable, SQLiteQuery query) {
-                return new android.database.sqlite.SQLiteCursor(db, masterQuery, editTable, query) {
-                    @Override
-                    public boolean requery() {
-                        setSelectionArguments(new String[] { "2" });
-                        return super.requery();
-                    }
-                };
-            }
-        };
-
-        // FIXME: Move cleanup into tearDown()
-        for (String db : mContextWrapper.databaseList()) {
-            File f = mContextWrapper.getDatabasePath(db);
-            if (f.exists()) {
-                mContextWrapper.deleteDatabase(db);
-            }
-        }
-
-        // Test openOrCreateDatabase with null and actual factory
-        mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME1,
-                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
-        assertNotNull(mDatabase);
-        mDatabase.close();
-        mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME2,
-                ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
-        assertNotNull(mDatabase);
-        mDatabase.close();
-
-        // Test getDatabasePath
-        File actualDBPath = mContextWrapper.getDatabasePath(DATABASE_NAME1);
-
-        // Test databaseList()
-        List<String> list = Arrays.asList(mContextWrapper.databaseList());
-        assertEquals(2, list.size());
-        assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
-        assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
-
-        // Test deleteDatabase()
-        for (int i = 1; i < 3; i++) {
-            mDatabaseFile = mContextWrapper.getDatabasePath(DATABASE_NAME + i);
-            assertTrue(mDatabaseFile.exists());
-            mContextWrapper.deleteDatabase(DATABASE_NAME + i);
-            mDatabaseFile = new File(actualDBPath, DATABASE_NAME + i);
-            assertFalse(mDatabaseFile.exists());
-        }
-    }
-
-    public void testEnforceUriPermission1() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceUriPermission(uri, Binder.getCallingPid(),
-                    Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceUriPermission is not working without possessing an IPC.");
-            fail("enforceUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testEnforceUriPermission2() {
-        Uri uri = Uri.parse("content://ctstest");
-        try {
-            mContextWrapper.enforceUriPermission(uri, NOT_GRANTED_PERMISSION,
-                    NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
-                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceUriPermission is not working without possessing an IPC.");
-            fail("enforceUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is ok, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetPackageResourcePath() {
-        assertNotNull(mContextWrapper.getPackageResourcePath());
-    }
-
-    public void testStartActivity() {
-        Intent intent = new Intent(mContext, ContextWrapperCtsActivity.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        try {
-            mContextWrapper.startActivity(intent);
-            fail("Test startActivity should thow a ActivityNotFoundException here.");
-        } catch (ActivityNotFoundException e) {
-            // Because ContextWrapper is a wrapper class, so no need to test
-            // the details of the function's performance. Getting a result
-            // from the wrapped class is enough for testing.
-        }
-    }
-
-    public void testCreatePackageContext() throws PackageManager.NameNotFoundException {
-        Context actualContext = mContextWrapper.createPackageContext(getValidPackageName(),
-                Context.CONTEXT_IGNORE_SECURITY);
-
-        assertNotNull(actualContext);
-    }
-
-    /**
-     * Helper method to retrieve a valid application package name to use for tests.
-     */
-    private String getValidPackageName() {
-        List<PackageInfo> packages = mContextWrapper.getPackageManager().getInstalledPackages(
-                PackageManager.GET_ACTIVITIES);
-        assertTrue(packages.size() >= 1);
-        return packages.get(0).packageName;
-    }
-
-    public void testGetMainLooper() {
-        assertNotNull(mContextWrapper.getMainLooper());
-    }
-
-    public void testGetApplicationContext() {
-        assertSame(mContext.getApplicationContext(), mContextWrapper.getApplicationContext());
-    }
-
-    public void testGetSharedPreferences() {
-        SharedPreferences sp;
-        SharedPreferences localSP;
-
-        sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        String packageName = mContextWrapper.getPackageName();
-        localSP = mContextWrapper.getSharedPreferences(packageName + "_preferences",
-                Context.MODE_PRIVATE);
-        assertSame(sp, localSP);
-    }
-
-    public void testRevokeUriPermission() {
-        Uri uri = Uri.parse("contents://ctstest");
-        mContextWrapper.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    public void testAccessService() throws InterruptedException {
-        MockContextWrapperService.reset();
-        bindExpectResult(mContextWrapper, new Intent(mContext, MockContextWrapperService.class));
-
-        // Check startService
-        assertTrue(MockContextWrapperService.hadCalledOnStart());
-        // Check bindService
-        assertTrue(MockContextWrapperService.hadCalledOnBind());
-
-        assertTrue(MockContextWrapperService.hadCalledOnDestory());
-        // Check unbinService
-        assertTrue(MockContextWrapperService.hadCalledOnUnbind());
-    }
-
-    public void testGetPackageCodePath() {
-        assertNotNull(mContextWrapper.getPackageCodePath());
-    }
-
-    public void testGetPackageName() {
-        assertEquals("android.content.cts", mContextWrapper.getPackageName());
-    }
-
-    public void testGetCacheDir() {
-        assertNotNull(mContextWrapper.getCacheDir());
-    }
-
-    public void testGetContentResolver() {
-        assertSame(mContext.getContentResolver(), mContextWrapper.getContentResolver());
-    }
-
     public void testAccessBaseContext() throws PackageManager.NameNotFoundException {
-        MockContextWrapper testContextWrapper = new MockContextWrapper(mContext);
+        Context context = getContext();
+        MockContextWrapper testContextWrapper = new MockContextWrapper(context);
 
         // Test getBaseContext()
-        assertSame(mContext, testContextWrapper.getBaseContext());
+        assertSame(context, testContextWrapper.getBaseContext());
 
         Context secondContext = testContextWrapper.createPackageContext(getValidPackageName(),
                 Context.CONTEXT_IGNORE_SECURITY);
@@ -487,352 +67,7 @@
         }
     }
 
-    public void testGetFileStreamPath() {
-        String TEST_FILENAME = "TestGetFileStreamPath";
-
-        // Test the path including the input filename
-        String fileStreamPath = mContextWrapper.getFileStreamPath(TEST_FILENAME).toString();
-        assertTrue(fileStreamPath.indexOf(TEST_FILENAME) >= 0);
-    }
-
-    public void testGetClassLoader() {
-        assertSame(mContext.getClassLoader(), mContextWrapper.getClassLoader());
-    }
-
-    public void testGetWallpaperDesiredMinimumHeightAndWidth() {
-        int height = mContextWrapper.getWallpaperDesiredMinimumHeight();
-        int width = mContextWrapper.getWallpaperDesiredMinimumWidth();
-
-        // returned value is <= 0, the caller should use the height of the
-        // default display instead.
-        // That is to say, the return values of desired minimumHeight and
-        // minimunWidth are at the same side of 0-dividing line.
-        assertTrue((height > 0 && width > 0) || (height <= 0 && width <= 0));
-    }
-
-    public void testAccessStickyBroadcast() throws InterruptedException {
-        ResultReceiver resultReceiver = new ResultReceiver();
-
-        Intent intent = new Intent(MOCK_STICKY_ACTION);
-        TestBroadcastReceiver stickyReceiver = new TestBroadcastReceiver();
-
-        mContextWrapper.sendStickyBroadcast(intent);
-
-        waitForReceiveBroadCast(resultReceiver);
-
-        assertEquals(intent.getAction(), mContextWrapper.registerReceiver(stickyReceiver,
-                new IntentFilter(MOCK_STICKY_ACTION)).getAction());
-
-        synchronized (mLockObj) {
-            mLockObj.wait(BROADCAST_TIMEOUT);
-        }
-
-        assertTrue("Receiver didn't make any response.", stickyReceiver.hadReceivedBroadCast());
-
-        mContextWrapper.unregisterReceiver(stickyReceiver);
-        mContextWrapper.removeStickyBroadcast(intent);
-
-        assertNull(mContextWrapper.registerReceiver(stickyReceiver,
-                new IntentFilter(MOCK_STICKY_ACTION)));
-        mContextWrapper.unregisterReceiver(stickyReceiver);
-    }
-
-    public void testCheckCallingOrSelfUriPermission() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkCallingOrSelfUriPermission(uri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testGrantUriPermission() {
-        mContextWrapper.grantUriPermission("com.android.mms", Uri.parse("contents://ctstest"),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-    }
-
-    public void testCheckPermissionGranted() {
-        int returnValue = mContextWrapper.checkPermission(
-                GRANTED_PERMISSION, Process.myPid(), Process.myUid());
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckPermissionNotGranted() {
-        int returnValue = mContextWrapper.checkPermission(
-                NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid());
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testCheckPermissionRootUser() {
-        // Test with root user, everything will be granted.
-        int returnValue = mContextWrapper.checkPermission(NOT_GRANTED_PERMISSION, 1, ROOT_UID);
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckPermissionInvalidRequest() {
-        // Test with null permission.
-        try {
-            int returnValue = mContextWrapper.checkPermission(null, 0, ROOT_UID);
-            fail("checkPermission should not accept null permission");
-        } catch (IllegalArgumentException e) {
-        }
-
-        // Test with invalid uid and included granted permission.
-        int returnValue = mContextWrapper.checkPermission(GRANTED_PERMISSION, 1, -11);
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testCheckSelfPermissionGranted() {
-        int returnValue = mContextWrapper.checkSelfPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, returnValue);
-    }
-
-    public void testCheckSelfPermissionNotGranted() {
-        int returnValue = mContextWrapper.checkSelfPermission(NOT_GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, returnValue);
-    }
-
-    public void testEnforcePermissionGranted() {
-        mContextWrapper.enforcePermission(
-                GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
-                "permission isn't granted");
-    }
-
-    public void testEnforcePermissionNotGranted() {
-        try {
-            mContextWrapper.enforcePermission(
-                    NOT_GRANTED_PERMISSION, Process.myPid(), Process.myUid(),
-                    "permission isn't granted");
-            fail("Permission shouldn't be granted.");
-        } catch (SecurityException expected) {
-        }
-    }
-
-    public void testCheckCallingOrSelfPermissionGranted() {
-        int retValue = mContextWrapper.checkCallingOrSelfPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-    }
-
-    public void testEnforceCallingOrSelfPermissionGranted() {
-        mContextWrapper.enforceCallingOrSelfPermission(
-                GRANTED_PERMISSION, "permission isn't granted");
-    }
-
-    public void testEnforceCallingOrSelfPermissionNotGranted() {
-        try {
-            mContextWrapper.enforceCallingOrSelfPermission(
-                    NOT_GRANTED_PERMISSION, "permission isn't granted");
-            fail("Permission shouldn't be granted.");
-        } catch (SecurityException expected) {
-        }
-    }
-
-    public void testCheckCallingPermission() {
-        // Denied because no IPC is active.
-        int retValue = mContextWrapper.checkCallingPermission(GRANTED_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testEnforceCallingPermission() {
-        try {
-            mContextWrapper.enforceCallingPermission(
-                    GRANTED_PERMISSION,
-                    "enforceCallingPermission is not working without possessing an IPC.");
-            fail("enforceCallingPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // Currently no IPC is handled by this process, this exception is expected
-        }
-    }
-
-    public void testCheckUriPermission1() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkUriPermission(uri, Binder.getCallingPid(), 0,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-
-        retValue = mContextWrapper.checkUriPermission(uri, Binder.getCallingPid(),
-                Binder.getCallingUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testCheckUriPermission2() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
-                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), 0,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_GRANTED, retValue);
-
-        retValue = mContextWrapper.checkUriPermission(uri, NOT_GRANTED_PERMISSION,
-                NOT_GRANTED_PERMISSION, Binder.getCallingPid(), Binder.getCallingUid(),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testCheckCallingUriPermission() {
-        Uri uri = Uri.parse("content://ctstest");
-
-        int retValue = mContextWrapper.checkCallingUriPermission(uri,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-        assertEquals(PackageManager.PERMISSION_DENIED, retValue);
-    }
-
-    public void testEnforceCallingUriPermission() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceCallingUriPermission is not working without possessing an IPC.");
-            fail("enforceCallingUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetDir() {
-        File dir = mContextWrapper.getDir("testpath", Context.MODE_PRIVATE);
-        assertNotNull(dir);
-        dir.delete();
-    }
-
-    public void testGetPackageManager() {
-        assertSame(mContext.getPackageManager(), mContextWrapper.getPackageManager());
-    }
-
-    public void testSendBroadcast1() throws InterruptedException {
-        final ResultReceiver receiver = new ResultReceiver();
-
-        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
-
-        mContextWrapper.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION));
-
-        new PollingCheck(BROADCAST_TIMEOUT){
-            @Override
-            protected boolean check() {
-                return receiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testSendBroadcast2() throws InterruptedException {
-        final ResultReceiver receiver = new ResultReceiver();
-
-        registerBroadcastReceiver(receiver, new IntentFilter(ResultReceiver.MOCK_ACTION));
-
-        mContextWrapper.sendBroadcast(new Intent(ResultReceiver.MOCK_ACTION), null);
-
-        new PollingCheck(BROADCAST_TIMEOUT){
-            @Override
-            protected boolean check() {
-                return receiver.hasReceivedBroadCast();
-            }
-        }.run();
-    }
-
-    public void testEnforceCallingOrSelfUriPermission() {
-        try {
-            Uri uri = Uri.parse("content://ctstest");
-            mContextWrapper.enforceCallingOrSelfUriPermission(uri,
-                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                    "enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
-            fail("enforceCallingOrSelfUriPermission is not working without possessing an IPC.");
-        } catch (SecurityException e) {
-            // If the function is OK, it should throw a SecurityException here because currently no
-            // IPC is handled by this process.
-        }
-    }
-
-    public void testGetSystemService() {
-        // Test invalid service name
-        assertNull(mContextWrapper.getSystemService("invalid"));
-
-        // Test valid service name
-        assertNotNull(mContextWrapper.getSystemService(Context.WINDOW_SERVICE));
-    }
-
-    public void testGetSystemServiceByClass() {
-        // Test invalid service class
-        assertNull(mContextWrapper.getSystemService(Object.class));
-
-        // Test valid service name
-        assertNotNull(mContextWrapper.getSystemService(WindowManager.class));
-        assertEquals(mContextWrapper.getSystemService(Context.WINDOW_SERVICE),
-                mContextWrapper.getSystemService(WindowManager.class));
-    }
-
-    public void testGetAssets() {
-        assertSame(mContext.getAssets(), mContextWrapper.getAssets());
-    }
-
-    public void testGetResources() {
-        assertSame(mContext.getResources(), mContextWrapper.getResources());
-    }
-
-    public void testStartInstrumentation() {
-        // Use wrong name
-        ComponentName cn = new ComponentName("com.android",
-                "com.android.content.FalseLocalSampleInstrumentation");
-        assertNotNull(cn);
-        assertNotNull(mContextWrapper);
-        // If the target instrumentation is wrong, the function should return false.
-        assertFalse(mContextWrapper.startInstrumentation(cn, null, null));
-    }
-
-    private void bindExpectResult(Context contextWrapper, Intent service)
-            throws InterruptedException {
-        if (service == null) {
-            fail("No service created!");
-        }
-        TestConnection conn = new TestConnection(true, false);
-
-        contextWrapper.bindService(service, conn, Context.BIND_AUTO_CREATE);
-        contextWrapper.startService(service);
-
-        // Wait for a short time, so the service related operations could be
-        // working.
-        synchronized (this) {
-            wait(2500);
-        }
-        // Test stop Service
-        assertTrue(contextWrapper.stopService(service));
-        contextWrapper.unbindService(conn);
-
-        synchronized (this) {
-            wait(1000);
-        }
-    }
-
-    private interface Condition {
-        public boolean onCondition();
-    }
-
-    private synchronized void waitForCondition(Condition con) throws InterruptedException {
-        // check the condition every 1 second until the condition is fulfilled
-        // and wait for 3 seconds at most
-        for (int i = 0; !con.onCondition() && i <= 3; i++) {
-            wait(1000);
-        }
-    }
-
-    private void waitForReceiveBroadCast(final ResultReceiver receiver)
-            throws InterruptedException {
-        Condition con = new Condition() {
-            public boolean onCondition() {
-                return receiver.hasReceivedBroadCast();
-            }
-        };
-        waitForCondition(con);
-    }
-
-    private void waitForFilteredIntent(ContextWrapper contextWrapper, final String action)
-            throws InterruptedException {
-        contextWrapper.sendBroadcast(new Intent(action), null);
-
-        synchronized (mLockObj) {
-            mLockObj.wait(BROADCAST_TIMEOUT);
-        }
-    }
-
+    // TODO: this mock seems unnecessary. Remove it, and just use ContextWrapper?
     private static final class MockContextWrapper extends ContextWrapper {
         public MockContextWrapper(Context base) {
             super(base);
@@ -843,84 +78,4 @@
             super.attachBaseContext(base);
         }
     }
-
-    private final class TestBroadcastReceiver extends BroadcastReceiver {
-        boolean mHadReceivedBroadCast;
-        boolean mIsOrderedBroadcasts;
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            synchronized (this) {
-                if (mIsOrderedBroadcasts) {
-                    setResultCode(3);
-                    setResultData(ACTUAL_RESULT);
-                }
-
-                Bundle map = getResultExtras(false);
-                if (map != null) {
-                    map.remove(KEY_REMOVED);
-                    map.putString(KEY_ADDED, VALUE_ADDED);
-                }
-                mHadReceivedBroadCast = true;
-                this.notifyAll();
-            }
-
-            synchronized (mLockObj) {
-                mLockObj.notify();
-            }
-        }
-
-        boolean hadReceivedBroadCast() {
-            return mHadReceivedBroadCast;
-        }
-
-        void reset(){
-            mHadReceivedBroadCast = false;
-        }
-    }
-
-    private class FilteredReceiver extends BroadcastReceiver {
-        private boolean mHadReceivedBroadCast1 = false;
-        private boolean mHadReceivedBroadCast2 = false;
-
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (MOCK_ACTION1.equals(action)) {
-                mHadReceivedBroadCast1 = true;
-            } else if (MOCK_ACTION2.equals(action)) {
-                mHadReceivedBroadCast2 = true;
-            }
-
-            synchronized (mLockObj) {
-                mLockObj.notify();
-            }
-        }
-
-        public boolean hadReceivedBroadCast1() {
-            return mHadReceivedBroadCast1;
-        }
-
-        public boolean hadReceivedBroadCast2() {
-            return mHadReceivedBroadCast2;
-        }
-
-        public void reset(){
-            mHadReceivedBroadCast1 = false;
-            mHadReceivedBroadCast2 = false;
-        }
-    }
-
-    private class TestConnection implements ServiceConnection {
-        public TestConnection(boolean expectDisconnect, boolean setReporter) {
-        }
-
-        void setMonitor(boolean v) {
-        }
-
-        public void onServiceConnected(ComponentName name, IBinder service) {
-        }
-
-        public void onServiceDisconnected(ComponentName name) {
-        }
-    }
 }
diff --git a/tests/tests/content/src/android/content/cts/MockContextWrapperService.java b/tests/tests/content/src/android/content/cts/MockContextService.java
similarity index 94%
rename from tests/tests/content/src/android/content/cts/MockContextWrapperService.java
rename to tests/tests/content/src/android/content/cts/MockContextService.java
index ef4b09a..f634f68 100644
--- a/tests/tests/content/src/android/content/cts/MockContextWrapperService.java
+++ b/tests/tests/content/src/android/content/cts/MockContextService.java
@@ -25,11 +25,9 @@
 import android.os.Message;
 
 /**
- * This class is used for {@link ContextWrapper}
- *
- * @see ContextWrapperTest
+ * This class is used for {@link ContextTest}.
  */
-public class MockContextWrapperService extends Service {
+public class MockContextService extends Service {
     private static boolean mHadCalledOnBind = false;
     private static boolean mHadCalledOnUnbind = false;
     private static boolean mHadCalledOnStart = false;
diff --git a/tests/tests/content/src/android/content/cts/ResultReceiver.java b/tests/tests/content/src/android/content/cts/ResultReceiver.java
index 1b80774..8b9cf67 100644
--- a/tests/tests/content/src/android/content/cts/ResultReceiver.java
+++ b/tests/tests/content/src/android/content/cts/ResultReceiver.java
@@ -21,13 +21,11 @@
 import android.content.Intent;
 
 /**
- * This class is used for testing android.content.ContentWrapper.
- *
- * @see ContextWrapperTest
+ * This class is used for {@link ContextTest}.
  */
 public class ResultReceiver extends BroadcastReceiver {
     public static final String MOCK_ACTION =
-        "android.content.cts.ContextWrapperTest.BROADCAST_RESULT";
+        "android.content.cts.ContextTest.BROADCAST_RESULT";
 
     private boolean mReceivedBroadCast;
 
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 60a7abd..4ddafbe 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -18,22 +18,37 @@
 
 import android.content.cts.R;
 
+import static android.content.pm.ApplicationInfo.FLAG_HAS_CODE;
+import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_ACTIVITIES;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
+import static android.content.pm.PackageManager.GET_RECEIVERS;
+import static android.content.pm.PackageManager.GET_SERVICES;
+
+import static com.google.common.truth.Truth.assertThat;
 
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
 import android.test.AndroidTestCase;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
@@ -45,6 +60,8 @@
 public class PackageManagerTest extends AndroidTestCase {
     private PackageManager mPackageManager;
     private static final String PACKAGE_NAME = "android.content.cts";
+    private static final String CONTENT_PKG_NAME = "android.content.cts";
+    private static final String APPLICATION_NAME = "android.content.cts.MockApplication";
     private static final String ACTIVITY_ACTION_NAME = "android.intent.action.PMTEST";
     private static final String MAIN_ACTION_NAME = "android.intent.action.MAIN";
     private static final String SERVICE_ACTION_NAME =
@@ -55,6 +72,8 @@
     private static final String SERVICE_NAME = "android.content.pm.cts.TestPmService";
     private static final String RECEIVER_NAME = "android.content.pm.cts.PmTestReceiver";
     private static final String INSTRUMENT_NAME = "android.content.pm.cts.TestPmInstrumentation";
+    private static final String CALL_ABROAD_PERMISSION_NAME =
+            "android.content.cts.CALL_ABROAD_PERMISSION";
     private static final String PROVIDER_NAME = "android.content.cts.MockContentProvider";
     private static final String PERMISSIONGROUP_NAME = "android.permission-group.COST_MONEY";
 
@@ -110,7 +129,7 @@
         String testPermissionsGroup = "android.permission-group.COST_MONEY";
         List<PermissionInfo> permissions = mPackageManager.queryPermissionsByGroup(
                 testPermissionsGroup, PackageManager.GET_META_DATA);
-        checkPermissionInfoName("android.content.cts.CALL_ABROAD_PERMISSION", permissions);
+        checkPermissionInfoName(CALL_ABROAD_PERMISSION_NAME, permissions);
 
         ApplicationInfo appInfo = mPackageManager.getApplicationInfo(PACKAGE_NAME, 0);
         List<ProviderInfo> providers = mPackageManager.queryContentProviders(PACKAGE_NAME,
@@ -509,4 +528,111 @@
         assertEquals(null, result[1]);
         assertEquals("com.android.cts.ctsshim", result[2]);
     }
+
+    public void testGetInstalledPackages() throws Exception {
+        List<PackageInfo> pkgs = mPackageManager.getInstalledPackages(GET_META_DATA
+                | GET_PERMISSIONS | GET_ACTIVITIES | GET_PROVIDERS | GET_SERVICES | GET_RECEIVERS);
+
+        PackageInfo pkgInfo = findPackageOrFail(pkgs, PACKAGE_NAME);
+
+        // Check metadata
+        ApplicationInfo appInfo = pkgInfo.applicationInfo;
+        assertEquals(APPLICATION_NAME, appInfo.name);
+        assertEquals("Android TestCase", appInfo.loadLabel(mPackageManager));
+        assertEquals(PACKAGE_NAME, appInfo.packageName);
+        assertTrue(appInfo.enabled);
+        // The process name defaults to the package name when not set.
+        assertEquals(PACKAGE_NAME, appInfo.processName);
+        assertEquals(0, appInfo.flags & FLAG_SYSTEM);
+        assertEquals(FLAG_INSTALLED, appInfo.flags & FLAG_INSTALLED);
+        assertEquals(FLAG_HAS_CODE, appInfo.flags & FLAG_HAS_CODE);
+
+        // Check required permissions
+        List<String> requestedPermissions = Arrays.asList(pkgInfo.requestedPermissions);
+        assertThat(requestedPermissions).containsAllOf(
+                "android.permission.MANAGE_ACCOUNTS",
+                "android.permission.ACCESS_NETWORK_STATE",
+                "android.content.cts.permission.TEST_GRANTED");
+
+        // Check declared permissions
+        PermissionInfo declaredPermission = (PermissionInfo) findPackageItemOrFail(
+                pkgInfo.permissions, CALL_ABROAD_PERMISSION_NAME);
+        assertEquals("Call abroad", declaredPermission.loadLabel(mPackageManager));
+        assertEquals(PERMISSIONGROUP_NAME, declaredPermission.group);
+        assertEquals(PermissionInfo.PROTECTION_NORMAL, declaredPermission.protectionLevel);
+
+        // Check activities
+        ActivityInfo activity = findPackageItemOrFail(pkgInfo.activities, ACTIVITY_NAME);
+        assertTrue(activity.enabled);
+        assertTrue(activity.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, activity.taskAffinity);
+        assertEquals(ActivityInfo.LAUNCH_SINGLE_TOP, activity.launchMode);
+
+        // Check services
+        ServiceInfo service = findPackageItemOrFail(pkgInfo.services, SERVICE_NAME);
+        assertTrue(service.enabled);
+        assertTrue(service.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, service.packageName);
+        assertEquals(CALL_ABROAD_PERMISSION_NAME, service.permission);
+
+        // Check ContentProviders
+        ProviderInfo provider = findPackageItemOrFail(pkgInfo.providers, PROVIDER_NAME);
+        assertTrue(provider.enabled);
+        assertFalse(provider.exported); // Don't export by default.
+        assertEquals(PACKAGE_NAME, provider.packageName);
+        assertEquals("ctstest", provider.authority);
+
+        // Check Receivers
+        ActivityInfo receiver = findPackageItemOrFail(pkgInfo.receivers, RECEIVER_NAME);
+        assertTrue(receiver.enabled);
+        assertTrue(receiver.exported); // Has intent filters - export by default.
+        assertEquals(PACKAGE_NAME, receiver.packageName);
+    }
+
+    // Tests that other packages can be queried.
+    public void testGetInstalledPackages_OtherPackages() throws Exception {
+        List<PackageInfo> pkgInfos = mPackageManager.getInstalledPackages(0);
+
+        // Check a normal package.
+        PackageInfo pkgInfo = findPackageOrFail(pkgInfos, "com.android.cts.stub"); // A test package
+        assertEquals(0, pkgInfo.applicationInfo.flags & FLAG_SYSTEM);
+
+        // Check a system package.
+        pkgInfo = findPackageOrFail(pkgInfos, "android");
+        assertEquals(FLAG_SYSTEM, pkgInfo.applicationInfo.flags & FLAG_SYSTEM);
+    }
+
+    public void testGetInstalledApplications() throws Exception {
+        List<ApplicationInfo> apps = mPackageManager.getInstalledApplications(GET_META_DATA);
+
+        ApplicationInfo app = findPackageItemOrFail(
+                apps.toArray(new ApplicationInfo[] {}), APPLICATION_NAME);
+
+        assertEquals(APPLICATION_NAME, app.name);
+        assertEquals("Android TestCase", app.loadLabel(mPackageManager));
+        assertEquals(PACKAGE_NAME, app.packageName);
+        assertTrue(app.enabled);
+        // The process name defaults to the package name when not set.
+        assertEquals(PACKAGE_NAME, app.processName);
+    }
+
+    private PackageInfo findPackageOrFail(List<PackageInfo> pkgInfos, String pkgName) {
+        for (PackageInfo pkgInfo : pkgInfos) {
+            if (pkgName.equals(pkgInfo.packageName)) {
+                return pkgInfo;
+            }
+        }
+        fail("Package not found with name " + pkgName);
+        return null;
+    }
+
+    private <T extends PackageItemInfo> T findPackageItemOrFail(T[] items, String name) {
+        for (T item : items) {
+            if (name.equals(item.name)) {
+                return item;
+            }
+        }
+        fail("Package item not found with name " + name);
+        return null;
+    }
 }
diff --git a/tests/tests/database/AndroidTest.xml b/tests/tests/database/AndroidTest.xml
index a1db9c0..c82a05a 100644
--- a/tests/tests/database/AndroidTest.xml
+++ b/tests/tests/database/AndroidTest.xml
@@ -23,6 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.database.cts" />
         <option name="runtime-hint" value="11m" />
-        <option name="hidden-api-checks" value="false"/>
+        <option name="hidden-api-checks" value="true"/>
     </test>
 </configuration>
diff --git a/tests/tests/graphics/jni/Android.mk b/tests/tests/graphics/jni/Android.mk
index b72fde7..ed90679 100644
--- a/tests/tests/graphics/jni/Android.mk
+++ b/tests/tests/graphics/jni/Android.mk
@@ -32,6 +32,7 @@
 	android_graphics_cts_MediaVulkanGpuTest.cpp \
 	android_graphics_cts_VulkanFeaturesTest.cpp \
 	android_graphics_cts_VulkanPreTransformCtsActivity.cpp \
+	android_graphics_cts_VulkanSurfaceSupportTest.cpp \
 	CameraTestHelpers.cpp \
 	ImageReaderTestHelpers.cpp \
 	MediaTestHelpers.cpp \
diff --git a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
index bb46671..a435ab4 100644
--- a/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
+++ b/tests/tests/graphics/jni/CtsGraphicsJniOnLoad.cpp
@@ -26,6 +26,7 @@
 extern int register_android_graphics_cts_MediaVulkanGpuTest(JNIEnv*);
 extern int register_android_graphics_cts_VulkanFeaturesTest(JNIEnv*);
 extern int register_android_graphics_cts_VulkanPreTransformCtsActivity(JNIEnv*);
+extern int register_android_graphics_cts_VulkanSurfaceSupportTest(JNIEnv*);
 
 jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
     JNIEnv* env = nullptr;
@@ -47,6 +48,8 @@
         return JNI_ERR;
     if (register_android_graphics_cts_VulkanPreTransformCtsActivity(env))
         return JNI_ERR;
+    if (register_android_graphics_cts_VulkanSurfaceSupportTest(env))
+        return JNI_ERR;
     if (register_android_graphics_cts_BasicVulkanGpuTest(env))
         return JNI_ERR;
     if (register_android_graphics_cts_CameraVulkanGpuTest(env))
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
index bdcb7b3..c401473 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.cpp
@@ -15,6 +15,12 @@
  *
  */
 
+#define LOG_TAG "VulkanPreTransformTestHelpers"
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+
 #include <android/log.h>
 #include <cstring>
 
@@ -25,7 +31,7 @@
 #define ASSERT(a)                                              \
     if (!(a)) {                                                \
         ALOGE("Failure: " #a " at " __FILE__ ":%d", __LINE__); \
-        return -1;                                             \
+        return VK_TEST_ERROR;                                  \
     }
 #define VK_CALL(a) ASSERT(VK_SUCCESS == (a))
 
@@ -124,7 +130,7 @@
     }
 }
 
-int32_t DeviceInfo::init(JNIEnv* env, jobject jSurface) {
+VkTestResult DeviceInfo::init(JNIEnv* env, jobject jSurface) {
     ASSERT(jSurface);
 
     mWindow = ANativeWindow_fromSurface(env, jSurface);
@@ -164,7 +170,7 @@
     VK_CALL(vkEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
     if (gpuCount == 0) {
         ALOGD("No physical device available");
-        return 1;
+        return VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED;
     }
 
     std::vector<VkPhysicalDevice> gpus(gpuCount, VK_NULL_HANDLE);
@@ -205,6 +211,13 @@
     ASSERT(queueFamilyIndex < queueFamilyCount);
     mQueueFamilyIndex = queueFamilyIndex;
 
+    VkBool32 supported = VK_FALSE;
+    VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(mGpu, mQueueFamilyIndex, mSurface, &supported));
+    if (supported == VK_FALSE) {
+        ALOGD("Surface format not supported");
+        return VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED;
+    }
+
     const float priority = 1.0f;
     const VkDeviceQueueCreateInfo queueCreateInfo = {
             .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
@@ -229,7 +242,7 @@
 
     vkGetDeviceQueue(mDevice, mQueueFamilyIndex, 0, &mQueue);
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
 SwapchainInfo::SwapchainInfo(const DeviceInfo* const deviceInfo)
@@ -246,7 +259,7 @@
     }
 }
 
-int32_t SwapchainInfo::init(bool setPreTransform) {
+VkTestResult SwapchainInfo::init(bool setPreTransform) {
     VkSurfaceCapabilitiesKHR surfaceCapabilities;
     VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mDeviceInfo->gpu(), mDeviceInfo->surface(),
                                                       &surfaceCapabilities));
@@ -316,7 +329,7 @@
     VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchain, &mSwapchainLength, nullptr));
     ALOGD("Swapchain length = %u", mSwapchainLength);
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
 Renderer::Renderer(const DeviceInfo* const deviceInfo, const SwapchainInfo* const swapchainInfo)
@@ -359,7 +372,7 @@
     }
 }
 
-int32_t Renderer::createRenderPass() {
+VkTestResult Renderer::createRenderPass() {
     const VkAttachmentDescription attachmentDescription = {
             .flags = 0,
             .format = mSwapchainInfo->format(),
@@ -401,10 +414,10 @@
     VK_CALL(vkCreateRenderPass(mDeviceInfo->device(), &renderPassCreateInfo, nullptr,
                                &mRenderPass));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createFrameBuffers() {
+VkTestResult Renderer::createFrameBuffers() {
     uint32_t swapchainLength = mSwapchainInfo->swapchainLength();
     std::vector<VkImage> images(swapchainLength, VK_NULL_HANDLE);
     VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(),
@@ -456,10 +469,10 @@
                                     &mFramebuffers[i]));
     }
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createVertexBuffers() {
+VkTestResult Renderer::createVertexBuffers() {
     const uint32_t queueFamilyIndex = mDeviceInfo->queueFamilyIndex();
     const VkBufferCreateInfo bufferCreateInfo = {
             .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -508,17 +521,15 @@
 
     VK_CALL(vkBindBufferMemory(mDeviceInfo->device(), mVertexBuffer, mDeviceMemory, 0));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* const outShader) {
+VkTestResult Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* const outShader) {
     ASSERT(filePath);
 
     AAsset* file = AAssetManager_open(mAssetManager, filePath, AASSET_MODE_BUFFER);
-    if (!file) {
-        ALOGE("Failed to open shader file");
-        return -1;
-    }
+    ASSERT(file);
+
     size_t fileLength = AAsset_getLength(file);
     std::vector<char> fileContent(fileLength);
     AAsset_read(file, fileContent.data(), fileLength);
@@ -534,10 +545,10 @@
     VK_CALL(vkCreateShaderModule(mDeviceInfo->device(), &shaderModuleCreateInfo, nullptr,
                                  outShader));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::createGraphicsPipeline() {
+VkTestResult Renderer::createGraphicsPipeline() {
     const VkPushConstantRange pushConstantRange = {
             .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
             .offset = 0,
@@ -706,10 +717,10 @@
     mVertexShader = VK_NULL_HANDLE;
     mFragmentShader = VK_NULL_HANDLE;
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::init(JNIEnv* env, jobject jAssetManager) {
+VkTestResult Renderer::init(JNIEnv* env, jobject jAssetManager) {
     mAssetManager = AAssetManager_fromJava(env, jAssetManager);
     ASSERT(mAssetManager);
 
@@ -816,10 +827,10 @@
     };
     VK_CALL(vkCreateSemaphore(mDeviceInfo->device(), &semaphoreCreateInfo, nullptr, &mSemaphore));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
 
-int32_t Renderer::drawFrame() {
+VkTestResult Renderer::drawFrame() {
     uint32_t nextIndex;
     VK_CALL(vkAcquireNextImageKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(), UINT64_MAX,
                                   mSemaphore, VK_NULL_HANDLE, &nextIndex));
@@ -855,5 +866,5 @@
     };
     VK_CALL(vkQueuePresentKHR(mDeviceInfo->queue(), &presentInfo));
 
-    return 0;
+    return VK_TEST_SUCCESS;
 }
diff --git a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
index f876aad..027ce74 100644
--- a/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
+++ b/tests/tests/graphics/jni/VulkanPreTransformTestHelpers.h
@@ -17,23 +17,24 @@
 #ifndef VULKAN_PRE_TRANSFORM_TEST_HELPERS_H
 #define VULKAN_PRE_TRANSFORM_TEST_HELPERS_H
 
-#define LOG_TAG "vulkan"
-
-#ifndef VK_USE_PLATFORM_ANDROID_KHR
-#define VK_USE_PLATFORM_ANDROID_KHR
-#endif
-
 #include <android/asset_manager_jni.h>
 #include <android/native_window_jni.h>
 #include <jni.h>
 #include <vulkan/vulkan.h>
 #include <vector>
 
+typedef enum VkTestResult {
+    VK_TEST_ERROR = -1,
+    VK_TEST_SUCCESS = 0,
+    VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED = 1,
+    VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED = 2,
+} VkTestResult;
+
 class DeviceInfo {
 public:
     DeviceInfo();
     ~DeviceInfo();
-    int32_t init(JNIEnv* env, jobject jSurface);
+    VkTestResult init(JNIEnv* env, jobject jSurface);
     VkPhysicalDevice gpu() const { return mGpu; }
     VkSurfaceKHR surface() const { return mSurface; }
     uint32_t queueFamilyIndex() const { return mQueueFamilyIndex; }
@@ -54,7 +55,7 @@
 public:
     SwapchainInfo(const DeviceInfo* const deviceInfo);
     ~SwapchainInfo();
-    int32_t init(bool setPreTransform);
+    VkTestResult init(bool setPreTransform);
     VkFormat format() const { return mFormat; }
     VkExtent2D displaySize() const { return mDisplaySize; }
     VkSwapchainKHR swapchain() const { return mSwapchain; }
@@ -73,15 +74,15 @@
 public:
     Renderer(const DeviceInfo* const deviceInfo, const SwapchainInfo* const swapchainInfo);
     ~Renderer();
-    int32_t init(JNIEnv* env, jobject assetManager);
-    int32_t drawFrame();
+    VkTestResult init(JNIEnv* env, jobject assetManager);
+    VkTestResult drawFrame();
 
 private:
-    int32_t createRenderPass();
-    int32_t createFrameBuffers();
-    int32_t createVertexBuffers();
-    int32_t loadShaderFromFile(const char* filePath, VkShaderModule* const outShader);
-    int32_t createGraphicsPipeline();
+    VkTestResult createRenderPass();
+    VkTestResult createFrameBuffers();
+    VkTestResult createVertexBuffers();
+    VkTestResult loadShaderFromFile(const char* filePath, VkShaderModule* const outShader);
+    VkTestResult createGraphicsPipeline();
 
     const DeviceInfo* const mDeviceInfo;
     const SwapchainInfo* const mSwapchainInfo;
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
index 7d1a428..e868870 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
@@ -15,11 +15,7 @@
  *
  */
 
-#define LOG_TAG "vulkan"
-
-#ifndef VK_USE_PLATFORM_ANDROID_KHR
-#define VK_USE_PLATFORM_ANDROID_KHR
-#endif
+#define LOG_TAG "VulkanPreTransformCtsActivity"
 
 #include <android/log.h>
 #include <jni.h>
@@ -51,21 +47,23 @@
     ASSERT(jSurface, "jSurface is NULL");
 
     DeviceInfo deviceInfo;
-    int ret = deviceInfo.init(env, jSurface);
-    ASSERT(ret >= 0, "Failed to initialize Vulkan device");
-    if (ret > 0) {
+    VkTestResult ret = deviceInfo.init(env, jSurface);
+    if (ret == VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED) {
         ALOGD("Hardware not supported for this test");
         return;
     }
+    ASSERT(ret == VK_TEST_SUCCESS, "Failed to initialize Vulkan device");
 
     SwapchainInfo swapchainInfo(&deviceInfo);
-    ASSERT(!swapchainInfo.init(setPreTransform), "Failed to initialize Vulkan swapchain");
+    ASSERT(swapchainInfo.init(setPreTransform) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan swapchain");
 
     Renderer renderer(&deviceInfo, &swapchainInfo);
-    ASSERT(!renderer.init(env, jAssetManager), "Failed to initialize Vulkan renderer");
+    ASSERT(renderer.init(env, jAssetManager) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan renderer");
 
     for (uint32_t i = 0; i < 120; ++i) {
-        ASSERT(!renderer.drawFrame(), "Failed to draw frame");
+        ASSERT(renderer.drawFrame() == VK_TEST_SUCCESS, "Failed to draw frame");
     }
 
     ASSERT(validatePixelValues(env, setPreTransform), "Not properly rotated");
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp
new file mode 100644
index 0000000..8f3cf22
--- /dev/null
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanSurfaceSupportTest.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "VulkanSurfaceSupportTest"
+
+#ifndef VK_USE_PLATFORM_ANDROID_KHR
+#define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+
+#include <android/log.h>
+#include <jni.h>
+#include <array>
+
+#include "NativeTestHelpers.h"
+#include "VulkanPreTransformTestHelpers.h"
+
+#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+namespace {
+
+void createNativeTest(JNIEnv* env, jclass /*clazz*/, jobject jAssetManager, jobject jSurface,
+                      jboolean jSupported) {
+    ASSERT(jAssetManager, "jAssetManager is NULL");
+    ASSERT(jSurface, "jSurface is NULL");
+
+    DeviceInfo deviceInfo;
+    VkTestResult ret = deviceInfo.init(env, jSurface);
+    if (ret == VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED) {
+        ALOGD("Hardware not supported for this test");
+        return;
+    }
+    if (ret == VK_TEST_SURFACE_FORMAT_NOT_SUPPORTED) {
+        ASSERT(jSupported == false, "Surface format should not be supported");
+        return;
+    }
+    ASSERT(ret == VK_TEST_SUCCESS, "Failed to initialize Vulkan device");
+    ASSERT(jSupported == true, "Surface format should be supported");
+
+    SwapchainInfo swapchainInfo(&deviceInfo);
+    ASSERT(swapchainInfo.init(false) == VK_TEST_SUCCESS, "Failed to initialize Vulkan swapchain");
+
+    Renderer renderer(&deviceInfo, &swapchainInfo);
+    ASSERT(renderer.init(env, jAssetManager) == VK_TEST_SUCCESS,
+           "Failed to initialize Vulkan renderer");
+
+    for (uint32_t i = 0; i < 3; ++i) {
+        ASSERT(renderer.drawFrame() == VK_TEST_SUCCESS, "Failed to draw frame");
+    }
+}
+
+const std::array<JNINativeMethod, 1> JNI_METHODS = {{
+        {"nCreateNativeTest", "(Landroid/content/res/AssetManager;Landroid/view/Surface;Z)V",
+         (void*)createNativeTest},
+}};
+
+} // anonymous namespace
+
+int register_android_graphics_cts_VulkanSurfaceSupportTest(JNIEnv* env) {
+    jclass clazz = env->FindClass("android/graphics/cts/VulkanSurfaceSupportTest");
+    return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
+}
diff --git a/tests/tests/graphics/res/drawable/gradientdrawable.xml b/tests/tests/graphics/res/drawable/gradientdrawable.xml
index ed8ff96..3d147dd 100644
--- a/tests/tests/graphics/res/drawable/gradientdrawable.xml
+++ b/tests/tests/graphics/res/drawable/gradientdrawable.xml
@@ -15,7 +15,11 @@
  * limitations under the License.
  -->
 
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:opticalInsetLeft="1px"
+    android:opticalInsetTop="2px"
+    android:opticalInsetRight="3px"
+    android:opticalInsetBottom="4px">
     <gradient android:startColor="#ffffffff" android:centerColor="#ffff0000"
               android:endColor="#0000ffff" />
     <corners android:radius="8px" />
diff --git a/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml b/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
index 7839ad1..1010ce3 100644
--- a/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
+++ b/tests/tests/graphics/res/drawable/vector_drawable_grouping_1.xml
@@ -16,6 +16,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="64dp"
         android:width="64dp"
+        android:opticalInsetLeft="10px"
+        android:opticalInsetTop="20px"
+        android:opticalInsetRight="30px"
+        android:opticalInsetBottom="40px"
         android:viewportHeight="256"
         android:viewportWidth="256" >
 
diff --git a/tests/tests/graphics/res/drawable/vector_icon_create.xml b/tests/tests/graphics/res/drawable/vector_icon_create.xml
index 7db4ad5..55113f3 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_create.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_create.xml
@@ -19,6 +19,10 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:height="64dp"
         android:width="64dp"
+        android:opticalInsetLeft="1px"
+        android:opticalInsetTop="2px"
+        android:opticalInsetRight="3px"
+        android:opticalInsetBottom="4px"
         android:viewportHeight="24"
         android:viewportWidth="24" >
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 7645e25..2ea6e16 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -355,13 +355,15 @@
             ColorSpace cs = b.getColorSpace();
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
-
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
 
             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
 
             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            assertTrue(b.isMutable());
             verifySetPixel(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
@@ -370,6 +372,7 @@
 
     private static void verifySetPixel(@NonNull Bitmap b,
             @ColorInt int newColor, @ColorInt int expectedColor) {
+        assertTrue(b.isMutable());
         b.setPixel(0, 0, newColor);
 
         ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
@@ -399,9 +402,11 @@
             verifySetPixels(b, newColor, expectedColor);
 
             b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            assertTrue(b.isMutable());
             verifySetPixels(b, newColor, expectedColor);
 
             b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            assertTrue(b.isMutable());
             verifySetPixels(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
@@ -410,6 +415,7 @@
 
     private static void verifySetPixels(@NonNull Bitmap b,
             @ColorInt int newColor, @ColorInt int expectedColor) {
+        assertTrue(b.isMutable());
         int[] pixels = new int[b.getWidth() * b.getHeight()];
         Arrays.fill(pixels, newColor);
         b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
index 02c9425..6b9eb5a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
@@ -25,6 +25,7 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -107,9 +108,9 @@
     @Test
     public void testGetPixel() {
         // Opaque pixels from opaque bitmap
-        assertEquals(0xff0f131f, mOpaqueBitmap.getPixel(0, 0));
-        assertEquals(0xff0f1421, mOpaqueBitmap.getPixel(1, 0));
-        assertEquals(0xff101523, mOpaqueBitmap.getPixel(2, 0));
+        validatePixel(0xff0f131f, mOpaqueBitmap.getPixel(0, 0));
+        validatePixel(0xff0f1421, mOpaqueBitmap.getPixel(1, 0));
+        validatePixel(0xff101523, mOpaqueBitmap.getPixel(2, 0));
 
         // Opaque pixels from transparent bitmap
         assertEquals(0xffff0000, mTransparentBitmap.getPixel(0, 0));
@@ -148,4 +149,11 @@
         Bitmap b = mask.copy(Config.RGBA_F16, false);
         assertNotNull(b);
     }
+
+    private void validatePixel(int expected, int actual) {
+        assertEquals(Color.alpha(expected), Color.alpha(actual));
+        assertEquals(Color.red(expected), Color.red(actual), 1);
+        assertEquals(Color.green(expected), Color.green(actual), 1);
+        assertEquals(Color.blue(expected), Color.blue(actual), 1);
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 1aec304..5289726 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -30,8 +30,10 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorSpace;
+import android.graphics.ColorSpace.Named;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Picture;
 import android.os.Debug;
 import android.os.Parcel;
 import android.os.StrictMode;
@@ -207,8 +209,10 @@
     public void testCreateBitmap1() {
         int[] colors = createColors(100);
         Bitmap bitmap = Bitmap.createBitmap(colors, 10, 10, Config.RGB_565);
+        assertFalse(bitmap.isMutable());
         Bitmap ret = Bitmap.createBitmap(bitmap);
         assertNotNull(ret);
+        assertFalse(ret.isMutable());
         assertEquals(10, ret.getWidth());
         assertEquals(10, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -223,8 +227,10 @@
     public void testCreateBitmap2() {
         // special case: output bitmap is equal to the input bitmap
         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
+        assertFalse(mBitmap.isMutable()); // createBitmap w/ colors should be immutable
         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100);
         assertNotNull(ret);
+        assertFalse(ret.isMutable()); // createBitmap from subset should be immutable
         assertTrue(mBitmap.equals(ret));
 
         //normal case
@@ -277,19 +283,33 @@
         mBitmap = Bitmap.createBitmap(new int[100 * 100], 100, 100, Config.ARGB_8888);
         Bitmap ret = Bitmap.createBitmap(mBitmap, 0, 0, 100, 100, null, false);
         assertNotNull(ret);
+        assertFalse(ret.isMutable()); // subset should be immutable
         assertTrue(mBitmap.equals(ret));
 
         // normal case
         mBitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
         ret = Bitmap.createBitmap(mBitmap, 10, 10, 50, 50, new Matrix(), true);
+        assertTrue(ret.isMutable());
         assertNotNull(ret);
         assertFalse(mBitmap.equals(ret));
     }
 
     @Test
+    public void testCreateBitmapFromHardwareBitmap() {
+        Bitmap hardwareBitmap = BitmapFactory.decodeResource(mRes, R.drawable.robot,
+                HARDWARE_OPTIONS);
+        assertEquals(Config.HARDWARE, hardwareBitmap.getConfig());
+
+        Bitmap ret = Bitmap.createBitmap(hardwareBitmap, 0, 0, 100, 100, null, false);
+        assertEquals(Config.HARDWARE, ret.getConfig());
+        assertFalse(ret.isMutable());
+    }
+
+    @Test
     public void testCreateBitmap4() {
         Bitmap ret = Bitmap.createBitmap(100, 200, Config.RGB_565);
         assertNotNull(ret);
+        assertTrue(ret.isMutable());
         assertEquals(100, ret.getWidth());
         assertEquals(200, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -306,6 +326,7 @@
     public void testCreateBitmap_matrix() {
         int[] colorArray = new int[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK };
         Bitmap src = Bitmap.createBitmap(2, 2, Config.ARGB_8888);
+        assertTrue(src.isMutable());
         src.setPixels(colorArray,0, 2, 0, 0, 2, 2);
 
         // baseline
@@ -313,21 +334,25 @@
 
         // null
         Bitmap dst = Bitmap.createBitmap(src, 0, 0, 2, 2, null, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(colorArray, dst);
 
         // identity matrix
         Matrix matrix = new Matrix();
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(colorArray, dst);
 
         // big scale - only red visible
         matrix.setScale(10, 10);
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(new int[] { Color.RED, Color.RED, Color.RED, Color.RED }, dst);
 
         // rotation
         matrix.setRotate(90);
         dst = Bitmap.createBitmap(src, 0, 0, 2, 2, matrix, false);
+        assertTrue(dst.isMutable());
         verify2x2BitmapContents(
                 new int[] { Color.BLUE, Color.RED, Color.BLACK, Color.GREEN }, dst);
     }
@@ -379,6 +404,7 @@
         // normal case
         Bitmap ret = Bitmap.createBitmap(colors, 5, 10, 10, 5, Config.RGB_565);
         assertNotNull(ret);
+        assertFalse(ret.isMutable());
         assertEquals(10, ret.getWidth());
         assertEquals(5, ret.getHeight());
         assertEquals(Config.RGB_565, ret.getConfig());
@@ -409,8 +435,26 @@
         assertEquals(metrics.densityDpi, bitmap.getDensity());
 
         int[] colors = createColors(100);
-        assertNotNull(Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888));
-        assertNotNull(Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888));
+        bitmap = Bitmap.createBitmap(metrics, colors, 0, 10, 10, 10, Config.ARGB_8888);
+        assertNotNull(bitmap);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(metrics, colors, 10, 10, Config.ARGB_8888);
+        assertNotNull(bitmap);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
+    public void testCreateBitmap_noDisplayMetrics_mutable() {
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
+        assertTrue(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true);
+        assertTrue(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(10, 10, Config.ARGB_8888, true, ColorSpace.get(Named.SRGB));
+        assertTrue(bitmap.isMutable());
     }
 
     @Test
@@ -430,12 +474,51 @@
     }
 
     @Test
+    public void testCreateBitmap_noDisplayMetrics_immutable() {
+        int[] colors = createColors(100);
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(colors, 0, 10, 10, 10, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(colors, 10, 10, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
+    public void testCreateBitmap_Picture_immutable() {
+        Picture picture = new Picture();
+        Canvas canvas = picture.beginRecording(200, 100);
+
+        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        p.setColor(0x88FF0000);
+        canvas.drawCircle(50, 50, 40, p);
+
+        p.setColor(Color.GREEN);
+        p.setTextSize(30);
+        canvas.drawText("Pictures", 60, 60, p);
+        picture.endRecording();
+
+        Bitmap bitmap;
+        bitmap = Bitmap.createBitmap(picture);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(picture, 100, 100, Config.HARDWARE);
+        assertFalse(bitmap.isMutable());
+
+        bitmap = Bitmap.createBitmap(picture, 100, 100, Config.ARGB_8888);
+        assertFalse(bitmap.isMutable());
+    }
+
+    @Test
     public void testCreateScaledBitmap() {
         mBitmap = Bitmap.createBitmap(100, 200, Config.RGB_565);
+        assertTrue(mBitmap.isMutable());
         Bitmap ret = Bitmap.createScaledBitmap(mBitmap, 50, 100, false);
         assertNotNull(ret);
         assertEquals(50, ret.getWidth());
         assertEquals(100, ret.getHeight());
+        assertTrue(ret.isMutable());
     }
 
     @Test
@@ -1457,6 +1540,14 @@
         nValidateNdkAccessAfterRecycle(bitmap);
     }
 
+    @Test
+    public void bitmapIsMutable() {
+        Bitmap b = Bitmap.createBitmap(10, 10, Config.ARGB_8888);
+        assertTrue("CreateBitmap w/ params should be mutable", b.isMutable());
+        assertTrue("CreateBitmap from bitmap should be mutable",
+                Bitmap.createBitmap(b).isMutable());
+    }
+
     private void runGcAndFinalizersSync() {
         final CountDownLatch fence = new CountDownLatch(1);
         new Object() {
diff --git a/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java b/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java
new file mode 100644
index 0000000..caaddf2
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/InsetsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InsetsTest {
+
+    @Test
+    public void testEmptyInsets() {
+        assertEquals(Insets.NONE, Insets.of(0, 0, 0, 0));
+    }
+
+    @Test
+    public void testInsetsAppliedInOrder() {
+        Insets insets = Insets.of(1, 2, 3, 4);
+        assertEquals(1, insets.left);
+        assertEquals(2, insets.top);
+        assertEquals(3, insets.right);
+        assertEquals(4, insets.bottom);
+    }
+
+    @Test
+    public void testInsetsFromNullRect() {
+        assertEquals(Insets.NONE, Insets.of(null));
+    }
+
+    @Test
+    public void testInsetsFromRect() {
+        Rect rect = new Rect(1, 2, 3, 4);
+        Insets insets = Insets.of(rect);
+        assertEquals(1, insets.left);
+        assertEquals(2, insets.top);
+        assertEquals(3, insets.right);
+        assertEquals(4, insets.bottom);
+    }
+
+    @Test
+    public void testInsetsEquality() {
+        Rect rect = new Rect(10, 20, 30, 40);
+        Insets insets1 = Insets.of(rect);
+        Insets insets2 = Insets.of(10, 20, 30, 40);
+        assertEquals(insets1, insets2);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
index d83eecf..d566b41 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
@@ -34,9 +34,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class PictureTest {
@@ -49,7 +46,6 @@
     // In particular, this test verifies that, in the following situations,
     // the created picture (effectively) has balanced saves and restores:
     //   - copy constructed picture from actively recording picture
-    //   - writeToStream/createFromStream created picture from actively recording picture
     //   - actively recording picture after draw call
     @Test
     public void testSaveRestoreBalance() {
@@ -65,17 +61,6 @@
 
         assertEquals(expectedSaveCount, canvas.getSaveCount());
 
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        original.writeToStream(bout);
-
-        assertEquals(expectedSaveCount, canvas.getSaveCount());
-
-        Picture serialized = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
-        // The serialization/deserialization process will balance the saves and restores
-        verifyBalance(serialized);
-
-        assertEquals(expectedSaveCount, canvas.getSaveCount());
-
         Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
         Canvas drawDest = new Canvas(bitmap);
         original.draw(drawDest);
@@ -118,7 +103,6 @@
     @Test
     public void testPicture() {
         Picture picture = new Picture();
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
 
         Canvas canvas = picture.beginRecording(TEST_WIDTH, TEST_HEIGHT);
         assertNotNull(canvas);
@@ -131,16 +115,6 @@
         verifySize(picture);
         verifyBitmap(bitmap);
 
-        picture.writeToStream(bout);
-        picture = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
-
-        // create a new Canvas with a new bitmap
-        bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
-        canvas = new Canvas(bitmap);
-        picture.draw(canvas);
-        verifySize(picture);
-        verifyBitmap(bitmap);
-
         Picture pic = new Picture(picture);
         bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
         canvas = new Canvas(bitmap);
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
index 0d426d4..591c280 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformCtsActivity.java
@@ -31,7 +31,7 @@
         System.loadLibrary("ctsgraphics_jni");
     }
 
-    private static final String TAG = "vulkan";
+    private static final String TAG = VulkanPreTransformCtsActivity.class.getSimpleName();
 
     protected Surface mSurface;
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
index cc2259a..c220867 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
@@ -62,7 +62,7 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class VulkanPreTransformTest {
-    private static final String TAG = "vulkan";
+    private static final String TAG = VulkanPreTransformTest.class.getSimpleName();
     private static final boolean DEBUG = false;
     private static VulkanPreTransformCtsActivity sActivity = null;
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java
new file mode 100644
index 0000000..bd144c7
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanSurfaceSupportTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.cts;
+
+import android.content.res.AssetManager;
+import android.graphics.ImageFormat;
+import android.media.ImageReader;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Surface;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class VulkanSurfaceSupportTest {
+
+    static {
+        System.loadLibrary("ctsgraphics_jni");
+    }
+
+    private static final String TAG = VulkanSurfaceSupportTest.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    @Test
+    public void testVulkanUnsupportedFormat() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 3);
+        nCreateNativeTest(
+                InstrumentationRegistry.getContext().getAssets(), reader.getSurface(), false);
+    }
+
+    @Test
+    public void testVulkanSupportedFormat() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.RGB_565, 3);
+        nCreateNativeTest(
+                InstrumentationRegistry.getContext().getAssets(), reader.getSurface(), true);
+    }
+
+    private static native void nCreateNativeTest(
+            AssetManager manager, Surface surface, boolean supported);
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
index 422f06d..4b98f22 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableParameterizedTest.java
@@ -29,7 +29,6 @@
 import android.graphics.Rect;
 import android.graphics.cts.R;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
@@ -226,7 +225,7 @@
         });
     }
 
-    @MediumTest
+    @LargeTest
     @Test
     public void testEmptyAnimatorSet() throws Throwable {
         int resId = R.drawable.avd_empty_animator;
@@ -246,7 +245,7 @@
         AnimatedVectorDrawableTest.waitForAVDStop(callback, MAX_TIMEOUT_MS);
         // Check that the AVD with empty AnimatorSet has finished
         callback.assertEnded(true);
-        callback.assertAVDRuntime(0, TimeUnit.MILLISECONDS.toNanos(64)); // 4 frames
+        callback.assertAVDRuntime(0, TimeUnit.MILLISECONDS.toNanos(300));
     }
 
     // Does a fuzzy comparison between two images.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
index f399cf6..2cd8bc0 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
@@ -28,13 +28,12 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.cts.R;
-import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.SmallTest;
@@ -108,6 +107,17 @@
         }
     }
 
+    @SmallTest
+    @Test
+    public void testGetOpticalInsets() throws Exception {
+        XmlPullParser parser = mResources.getXml(mResId);
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        AnimatedVectorDrawable drawable = new AnimatedVectorDrawable();
+        drawable.inflate(mResources, parser, attrs);
+
+        assertEquals(Insets.of(10, 20, 30, 40), drawable.getOpticalInsets());
+    }
+
     @Test
     public void testGetChangingConfigurations() {
         AnimatedVectorDrawable avd = new AnimatedVectorDrawable();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index 536c16f..fbafcf4 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -25,43 +25,36 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.content.res.Resources.Theme;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
-import android.graphics.Rect;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
 import android.graphics.cts.R;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.view.Gravity;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Shader;
-import android.graphics.Bitmap.Config;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Shader.TileMode;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Drawable.ConstantState;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.AttributeSet;
-import android.util.LayoutDirection;
-import android.util.Xml;
-import android.view.Gravity;
-
 import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -143,6 +136,23 @@
     }
 
     @Test
+    public void testBitmapDrawableOpticalInset() {
+        InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+        BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
+
+        int intrinsicWidth = bitmapDrawable.getIntrinsicWidth();
+        int intrinsicHeight = bitmapDrawable.getIntrinsicHeight();
+
+        bitmapDrawable.setGravity(Gravity.CENTER);
+        bitmapDrawable.setBounds(0, 0, intrinsicWidth * 3, intrinsicHeight * 3);
+
+        Insets opticalInsets = bitmapDrawable.getOpticalInsets();
+        Insets expected = Insets.of(intrinsicWidth, intrinsicHeight, intrinsicWidth,
+                    intrinsicHeight);
+        assertEquals(expected, opticalInsets);
+    }
+
+    @Test
     public void testAccessMipMap() {
         Bitmap source = BitmapFactory.decodeResource(mContext.getResources(), R.raw.testimage);
         BitmapDrawable bitmapDrawable = new BitmapDrawable(source);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
index ee34254..675f15c 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.fail;
 
 import android.content.res.Resources;
@@ -148,6 +149,15 @@
     }
 
     @Test
+    public void testGetColorFilter() {
+        final ColorDrawable d = new ColorDrawable(Color.WHITE);
+        PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(Color.BLACK, Mode.SRC_OVER);
+        d.setColorFilter(colorFilter);
+
+        assertSame(colorFilter, d.getColorFilter());
+    }
+
+    @Test
     public void testSetTint() {
         final ColorDrawable d = new ColorDrawable(Color.WHITE);
         assertEquals(Color.WHITE, DrawableTestUtils.getPixel(d, 0, 0));
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 9b93a48..b8f5cbb 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -41,6 +41,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuff.Mode;
@@ -53,6 +54,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -825,6 +828,22 @@
         assertEquals(true, mDrawableContainer.isStateful());
     }
 
+    @Test
+    public void testGetOpticalBoundsWithNoInternalDrawable() {
+        DrawableContainer container = new DrawableContainer();
+        assertEquals(Insets.NONE, container.getOpticalInsets());
+    }
+
+    @Test
+    public void testGetOpticalBoundsFromInternalDrawable() {
+        mMockDrawableContainer.setConstantState(mDrawableContainerState);
+        MockDrawable mockDrawable = new MockDrawable();
+        mockDrawable.setInsets(Insets.of(20, 40, 60, 100));
+
+        addAndSelectDrawable(mockDrawable);
+        assertEquals(Insets.of(20, 40, 60, 100), mDrawableContainer.getOpticalInsets());
+    }
+
     private void addAndSelectDrawable(Drawable drawable) {
         int pos = mDrawableContainerState.addChild(drawable);
         mDrawableContainer.selectDrawable(pos);
@@ -861,6 +880,8 @@
         private boolean mHasCalledOnStateChanged;
         private boolean mHasCalledOnLevelChanged;
 
+        private Insets mInsets = null;
+
         @Override
         public int getOpacity() {
             return PixelFormat.OPAQUE;
@@ -878,6 +899,15 @@
         public void setColorFilter(ColorFilter colorFilter) {
         }
 
+        public void setInsets(@Nullable Insets insets) {
+            mInsets = insets;
+        }
+
+        @Override
+        public Insets getOpticalInsets() {
+            return mInsets != null ? mInsets : Insets.NONE;
+        }
+
         public boolean hasOnBoundsChangedCalled() {
             return mHasCalledOnBoundsChanged;
         }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
index 3c26f39..4fd64b8 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTest.java
@@ -16,44 +16,6 @@
 
 package android.graphics.drawable.cts;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.cts.R;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Drawable.Callback;
-import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.AndroidTestCase;
-import android.util.AttributeSet;
-import android.util.StateSet;
-import android.util.TypedValue;
-import android.util.Xml;
-import android.view.View;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -66,6 +28,43 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Insets;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.cts.R;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.Callback;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DrawableTest {
@@ -722,6 +721,12 @@
         assertSame(mockDrawable, mockDrawable.mutate());
     }
 
+    @Test
+    public void testDefaultOpticalInsetsIsNone() {
+        Drawable mockDrawable = new MockDrawable();
+        assertEquals(Insets.NONE, mockDrawable.getOpticalInsets());
+    }
+
     // Since Mockito can't mock or spy on protected methods, we have a custom extension
     // of Drawable to track calls to protected methods. This class also has empty implementations
     // of the base abstract methods.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
index c7acccc..6987b01 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableWrapperTest.java
@@ -35,6 +35,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -49,6 +50,8 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.util.StateSet;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -377,6 +380,21 @@
         verify(mockDrawable, times(1)).getIntrinsicHeight();
     }
 
+    @Test
+    public void testGetOpticalInsetsNoInternalDrawable() {
+        DrawableWrapper wrapper = new MockDrawableWrapper(null);
+        assertEquals(Insets.NONE, wrapper.getOpticalInsets());
+    }
+
+    @Test
+    public void testGetOpticalInsetsFromInternalDrawable() {
+        MockDrawable drawable = new MockDrawable();
+        drawable.setInsets(Insets.of(30, 60, 90, 120));
+        DrawableWrapper wrapper = new MockDrawableWrapper(drawable);
+
+        assertEquals(Insets.of(30, 60, 90, 120), wrapper.getOpticalInsets());
+    }
+
     @SuppressWarnings("deprecation")
     @Test
     public void testGetConstantState() {
@@ -390,6 +408,7 @@
     private static class MockDrawable extends Drawable {
         private boolean mCalledOnLevelChange = false;
         private ColorFilter mColorFilter;
+        private Insets mInsets = null;
 
         @Override
         public void draw(Canvas canvas) {
@@ -414,6 +433,15 @@
             return mColorFilter;
         }
 
+        public void setInsets(@Nullable Insets insets) {
+            mInsets = insets;
+        }
+
+        @Override
+        public Insets getOpticalInsets() {
+            return mInsets != null ? mInsets : Insets.NONE;
+        }
+
         @Override
         protected boolean onLevelChange(int level) {
             mCalledOnLevelChange = true;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index 4a05376..bf91296 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -30,6 +30,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.cts.R;
@@ -367,9 +368,9 @@
         gradientDrawable.setColor(color);
         assertEquals("Color was set to RED", color, gradientDrawable.getColor());
 
-        color = null;
-        gradientDrawable.setColor(color);
-        assertEquals("Color was set to null (TRANSPARENT)", color, gradientDrawable.getColor());
+        gradientDrawable.setColor(null);
+        assertEquals("Color was set to null (TRANSPARENT)",
+                ColorStateList.valueOf(Color.TRANSPARENT), gradientDrawable.getColor());
     }
 
     @Test
@@ -557,6 +558,13 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        GradientDrawable drawable =
+                (GradientDrawable) mResources.getDrawable(R.drawable.gradientdrawable);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         final Rect tempPadding = new Rect();
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
index b1c1f03..01685a8 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/InsetDrawableTest.java
@@ -26,15 +26,13 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.cts.R;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.InsetDrawable;
 import android.support.test.InstrumentationRegistry;
@@ -459,6 +457,12 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        InsetDrawable drawable = new InsetDrawable(mPassDrawable, 1, 2, 3, 4);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         // Capture initial state at default density.
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
index d563102..11f8fc5 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -373,7 +373,7 @@
         a.setShape(new OvalShape());
 
         ShapeDrawable b = (ShapeDrawable) a.getConstantState().newDrawable();
-        assertSame(a.getShape(), b.getShape());
+        assertEquals(a.getShape(), b.getShape());
         a.mutate();
 
         assertNotNull(a.getShape());
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
index c96a1ca..a7b20c6 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/StateListDrawableTest.java
@@ -30,6 +30,7 @@
 import android.graphics.cts.R;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.DrawableContainer.DrawableContainerState;
 import android.graphics.drawable.StateListDrawable;
@@ -332,6 +333,66 @@
     }
 
     @Test
+    public void testGetStateCount() {
+        StateListDrawable stateList = new StateListDrawable();
+        stateList.addState(new int[]{0}, new ColorDrawable(Color.RED));
+
+        assertEquals(1, stateList.getStateCount());
+
+        stateList.addState(new int[]{1}, new ColorDrawable(Color.GREEN));
+
+        assertEquals(2, stateList.getStateCount());
+
+        stateList.addState(new int[]{2}, new ColorDrawable(Color.BLUE));
+
+        assertEquals(3, stateList.getStateCount());
+    }
+
+    @Test
+    public void testGetStateDrawable() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable colorDrawable = new ColorDrawable(Color.RED);
+        int[] stateSet = new int[]{1};
+        stateList.addState(stateSet, colorDrawable);
+
+        Drawable drawable = stateList.getStateDrawable(0);
+        assertSame(colorDrawable, drawable);
+    }
+
+    @Test
+    public void testGetStateSet() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable colorDrawable = new ColorDrawable(Color.GREEN);
+        int[] stateSet = new int[]{0};
+
+        stateList.addState(stateSet, colorDrawable);
+        int[] resolvedStateSet = stateList.getStateSet(0);
+        assertEquals(stateSet, resolvedStateSet);
+    }
+
+    @Test
+    public void testGetStateDrawableIndex() {
+        StateListDrawable stateList = new StateListDrawable();
+
+        ColorDrawable drawable1 = new ColorDrawable(Color.CYAN);
+        ColorDrawable drawable2 = new ColorDrawable(Color.YELLOW);
+        ColorDrawable drawable3 = new ColorDrawable(Color.GREEN);
+        int[] stateSet1 = new int[]{42};
+        int[] stateSet2 = new int[]{27};
+        int[] stateSet3 = new int[]{57};
+
+        stateList.addState(stateSet1, drawable1);
+        stateList.addState(stateSet2, drawable2);
+        stateList.addState(stateSet3, drawable3);
+
+        assertEquals(0, stateList.getStateDrawableIndex(stateSet1));
+        assertEquals(1, stateList.getStateDrawableIndex(stateSet2));
+        assertEquals(2, stateList.getStateDrawableIndex(stateSet3));
+    }
+
+    @Test
     public void testMutate() {
         StateListDrawable d1 =
             (StateListDrawable) mResources.getDrawable(R.drawable.statelistdrawable);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
index 76b6e2c..673a1b1 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -27,28 +27,27 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.cts.R;
 import android.graphics.drawable.Drawable.ConstantState;
 import android.graphics.drawable.VectorDrawable;
-import androidx.annotation.Nullable;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Xml;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 
 @SmallTest
@@ -425,6 +424,13 @@
         }
     }
 
+    @Test
+    public void testOpticalInsets() {
+        VectorDrawable drawable =
+                (VectorDrawable) mResources.getDrawable(R.drawable.vector_icon_create);
+        assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets());
+    }
+
     private void verifyPreloadDensityInner(Resources res, int densityDpi)
             throws XmlPullParserException, IOException {
         // Capture initial state at default density.
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 4e8aaa7..f247e177 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -41,7 +41,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsHardwareTestCases
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/hardware/jni/Android.mk b/tests/tests/hardware/jni/Android.mk
index 0cd95e7..6df6667 100644
--- a/tests/tests/hardware/jni/Android.mk
+++ b/tests/tests/hardware/jni/Android.mk
@@ -30,7 +30,9 @@
 
 LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
 
-LOCAL_CXX_STL := libc++_static
+LOCAL_NDK_STL_VARIANT := none
+
+LOCAL_SDK_VERSION := current
 
 LOCAL_CLANG := true
 
diff --git a/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
index c5b5c35..8df230f 100644
--- a/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
+++ b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
@@ -20,7 +20,6 @@
 #include <jni.h>
 
 #include <android/hardware_buffer_jni.h>
-#include <utils/Errors.h>
 
 #define LOG_TAG "HardwareBufferTest"
 
@@ -35,7 +34,9 @@
     desc.usage = usage;
     desc.format = format;
     int res = AHardwareBuffer_allocate(&desc, &buffer);
-    if (res == android::NO_ERROR) {
+
+    // TODO: Change this to res == NO_ERROR after b/77153085 is fixed
+    if (res == 0) {
         return AHardwareBuffer_toHardwareBuffer(env, buffer);
     } else {
         return 0;
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
index 9089529..0b7f7a0 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -24,7 +24,6 @@
 import android.hardware.input.cts.InputCallback;
 import android.hardware.input.cts.InputCtsActivity;
 import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
 import android.view.KeyEvent;
@@ -40,8 +39,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
-import libcore.io.IoUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -72,7 +69,7 @@
         new ActivityTestRule<>(InputCtsActivity.class);
 
     @Before
-    public void setUp() throws Exception {
+    public void setUp() {
         clearKeys();
         clearMotions();
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -81,8 +78,10 @@
     }
 
     @After
-    public void tearDown() throws Exception {
-        IoUtils.closeQuietly(mOutputStream);
+    public void tearDown() {
+        try {
+            mOutputStream.close();
+        } catch (IOException ignored) {}
     }
 
     /**
@@ -104,7 +103,6 @@
         } catch (InterruptedException ex) {
             fail("Unexpectedly interrupted while waiting for device added notification.");
         }
-        SystemClock.sleep(100);
     }
 
     /**
@@ -179,12 +177,14 @@
         mMotions.clear();
     }
 
-    private void setupPipes() throws IOException {
+    private void setupPipes() {
         UiAutomation ui = mInstrumentation.getUiAutomation();
         ParcelFileDescriptor[] pipes = ui.executeShellCommandRw(HID_COMMAND);
 
         mOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pipes[1]);
-        IoUtils.closeQuietly(pipes[0]); // hid command is write-only
+        try {
+          pipes[0].close(); // hid command is write-only
+        } catch (IOException ignored) {}
     }
 
     private String getEvents(int id) throws IOException {
@@ -203,6 +203,7 @@
         return baos.toString();
     }
 
+
     private class InputListener implements InputCallback {
         @Override
         public void onKeyEvent(KeyEvent ev) {
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 4346c34..31d9844 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -47,6 +47,7 @@
     private final static String VENDOR_CONFIG_FILE = "/vendor/etc/public.libraries.txt";
     private final static String[] PUBLIC_SYSTEM_LIBRARIES = {
         "libaaudio.so",
+        "libamidi.so",
         "libandroid.so",
         "libc.so",
         "libcamera2ndk.so",
diff --git a/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java b/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
index be769cf..c52b59d 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityAndWindowManagersState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
diff --git a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
index 87f80b0..8e419a4 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerTestBase.java
@@ -34,6 +34,7 @@
 import static android.server.am.UiDeviceUtils.waitForDeviceIdle;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.SystemClock;
@@ -76,6 +77,7 @@
 
     protected Context mContext;
     protected ActivityManager mAm;
+    protected ActivityTaskManager mAtm;
 
     /**
      * @return the am command to start the given activity with the following extra key/value pairs.
@@ -116,6 +118,7 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getContext();
         mAm = mContext.getSystemService(ActivityManager.class);
+        mAtm = mContext.getSystemService(ActivityTaskManager.class);
 
         pressWakeupButton();
         pressUnlockButton();
@@ -136,7 +139,7 @@
     }
 
     protected void removeStacksWithActivityTypes(int... activityTypes) {
-        mAm.removeStacksWithActivityTypes(activityTypes);
+        mAtm.removeStacksWithActivityTypes(activityTypes);
         waitForIdle();
     }
 
@@ -177,7 +180,7 @@
 
     protected void setActivityTaskWindowingMode(ComponentName activityName, int windowingMode) {
         final int taskId = getActivityTaskId(activityName);
-        mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+        mAtm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
         mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
                 .setActivityType(ACTIVITY_TYPE_STANDARD)
                 .setWindowingMode(windowingMode)
@@ -237,7 +240,7 @@
     }
 
     protected boolean supportsSplitScreenMultiWindow() {
-        return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+        return ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     protected boolean hasDeviceFeature(final String requiredFeature) {
diff --git a/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java b/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
index 05806a7..5fac1c0 100644
--- a/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
+++ b/tests/tests/keystore/src/android/server/am/WaitForValidActivityState.java
@@ -16,7 +16,7 @@
 
 package android.server.am;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
diff --git a/tests/tests/libcoreapievolution/Android.mk b/tests/tests/libcoreapievolution/Android.mk
index 439e30d..84b8279 100644
--- a/tests/tests/libcoreapievolution/Android.mk
+++ b/tests/tests/libcoreapievolution/Android.mk
@@ -21,7 +21,9 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner junit
+
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
index 2538cea..6217df4 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayer2Test.java
@@ -1247,7 +1247,7 @@
         assertEquals("MediaPlayer2 had error in clockRate " + ts1.getMediaClockRate(),
                 playbackRate, ts1.getMediaClockRate(), 0.001f);
         assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
-                nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
+                nt1 <= ts1.getAnchorSystemNanoTime() && ts1.getAnchorSystemNanoTime() <= nt2);
 
         onPauseCalled.reset();
         mPlayer.pause();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 91bc3ee..83e43dd 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1333,7 +1333,7 @@
         assertEquals("MediaPlayer had error in clockRate " + ts1.getMediaClockRate(),
                 playbackRate, ts1.getMediaClockRate(), 0.001f);
         assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
-                nt1 <= ts1.getAnchorSytemNanoTime() && ts1.getAnchorSytemNanoTime() <= nt2);
+                nt1 <= ts1.getAnchorSystemNanoTime() && ts1.getAnchorSystemNanoTime() <= nt2);
 
         mMediaPlayer.pause();
         ts1 = mMediaPlayer.getTimestamp();
diff --git a/tests/tests/nativemidi/Android.mk b/tests/tests/nativemidi/Android.mk
new file mode 100755
index 0000000..5da14e4
--- /dev/null
+++ b/tests/tests/nativemidi/Android.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# NativeMidiEchoTest
+#
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+LOCAL_JNI_SHARED_LIBRARIES := libnativemidi_jni
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_PACKAGE_NAME := CtsNativeMidiTestCases
+LOCAL_MULTILIB := both
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/nativemidi/AndroidManifest.xml b/tests/tests/nativemidi/AndroidManifest.xml
new file mode 100755
index 0000000..5c42c52
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.nativemidi.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <uses-feature android:name="android.software.midi" android:required="true"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="NativeMidiEchoTestService"
+                android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService" />
+            </intent-filter>
+            <meta-data android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/echo_device_info" />
+        </service>
+
+        <activity android:name="android.nativemidi.cts.NativeMidiEchoTest"
+                  android:label="NativeMidiEchoTest"/>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS Native MIDI tests"
+        android:targetPackage="android.nativemidi.cts" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/nativemidi/AndroidTest.xml b/tests/tests/nativemidi/AndroidTest.xml
new file mode 100644
index 0000000..3a4774a
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS MIDI test cases">
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="test-suite-tag" value="cts" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMidiTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.nativemidi.cts" />
+        <option name="runtime-hint" value="8m" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
new file mode 100644
index 0000000..1de9703
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nativemidi.cts;
+
+import android.app.Activity;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
+
+import android.os.Bundle;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.test.AndroidTestCase;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+/*
+ * Test Class
+ */
+@RunWith(AndroidJUnit4.class)
+public class NativeMidiEchoTest {
+    private static final String TAG = "NativeMidiEchoTest";
+
+    public static final String TEST_MANUFACTURER = "AndroidCTS";
+    public static final String ECHO_PRODUCT = "NativeMidiEcho";
+
+    private static final long NANOS_PER_MSEC = 1000L * 1000L;
+
+    // This number seems excessively large and it is not clear if there is a linear
+    // relationship between the number of messages sent and the time required to send them
+    private static final int TIMEOUT_PER_MESSAGE_MS = 10;
+
+    // This timeout value is very generous.
+    private static final int TIMEOUT_OPEN_MSEC = 2000; // arbitrary
+
+    private Context mContext = InstrumentationRegistry.getContext();
+    private MidiManager mMidiManager;
+
+    private MidiDevice mEchoDevice;
+
+    private Random mRandom = new Random(1972941337);
+
+    // (Native code) attributes associated with a test/EchoServer instance.
+    private long mTestContext;
+
+    static {
+        System.loadLibrary("nativemidi_jni");
+    }
+
+    /*
+     * Helpers
+     */
+    private boolean hasMidiSupport() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private byte[] generateRandomMessage(int len) {
+        byte[] buffer = new byte[len];
+        for(int index = 0; index < len; index++) {
+            buffer[index] = (byte)(mRandom.nextInt() & 0xFF);
+        }
+        return buffer;
+    }
+
+    private void generateRandomBufers(byte[][] buffers, long timestamps[], int numMessages) {
+        int messageLen;
+        int maxMessageLen = 128;
+        for(int buffIndex = 0; buffIndex < numMessages; buffIndex++) {
+            messageLen = (int)(mRandom.nextFloat() * (maxMessageLen-1)) + 1;
+            buffers[buffIndex] = generateRandomMessage(messageLen);
+            timestamps[buffIndex] = mRandom.nextLong();
+        }
+    }
+
+    private void compareMessages(byte[] buffer, long timestamp, NativeMidiMessage nativeMsg) {
+        Assert.assertEquals("byte count of message", buffer.length, nativeMsg.len);
+        Assert.assertEquals("timestamp in message", timestamp, nativeMsg.timestamp);
+
+        for (int index = 0; index < buffer.length; index++) {
+            Assert.assertEquals("message byte[" + index + "]", buffer[index] & 0x0FF,
+                    nativeMsg.buffer[index] & 0x0FF);
+        }
+    }
+
+    /*
+     * Echo Server
+     */
+    // Listens for an asynchronous device open and notifies waiting foreground
+    // test.
+    class MyTestOpenCallback implements MidiManager.OnDeviceOpenedListener {
+        private volatile MidiDevice mDevice;
+
+        @Override
+        public synchronized void onDeviceOpened(MidiDevice device) {
+            mDevice = device;
+            notifyAll();
+        }
+
+        public synchronized MidiDevice waitForOpen(int msec)
+              throws InterruptedException {
+            long deadline = System.currentTimeMillis() + msec;
+            long timeRemaining = msec;
+            while (mDevice == null && timeRemaining > 0) {
+                wait(timeRemaining);
+                timeRemaining = deadline - System.currentTimeMillis();
+            }
+            return mDevice;
+        }
+    }
+
+     protected void setUpEchoServer() throws Exception {
+        Log.i(TAG, "++ setUpEchoServer()");
+        MidiDeviceInfo echoInfo = findEchoDevice();
+
+        // Open device.
+        MyTestOpenCallback callback = new MyTestOpenCallback();
+        mMidiManager.openDevice(echoInfo, callback, null);
+        mEchoDevice = callback.waitForOpen(TIMEOUT_OPEN_MSEC);
+        Assert.assertNotNull("could not open " + ECHO_PRODUCT, mEchoDevice);
+
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        mTestContext = allocTestContext();
+        Assert.assertTrue("couldn't allocate test context.", mTestContext != 0);
+
+        // Open Device
+        int result = openNativeMidiDevice(mTestContext, mEchoDevice);
+        Assert.assertEquals("Bad open native MIDI device", 0, result);
+
+        // Open Input
+        result = startWritingMidi(mTestContext, 0/*mPortNumber*/);
+        Assert.assertEquals("Bad start writing (native) MIDI", 0, result);
+
+        // Open Output
+        result = startReadingMidi(mTestContext, 0/*mPortNumber*/);
+        Assert.assertEquals("Bad start Reading (native) MIDI", 0, result);
+    }
+
+    protected void tearDownEchoServer() throws IOException {
+        Log.i(TAG, "++ tearDownEchoServer()");
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        int result;
+
+        // Stop inputs
+        result = stopReadingMidi(mTestContext);
+        Assert.assertEquals("Bad stop reading (native) MIDI", 0, result);
+
+        // Stop outputs
+        result = stopWritingMidi(mTestContext);
+        Assert.assertEquals("Bad stop writing (native) MIDI", 0, result);
+
+        // Close Device
+        result = closeNativeMidiDevice(mTestContext);
+        Assert.assertEquals("Bad close native MIDI device", 0, result);
+
+        freeTestContext(mTestContext);
+        mTestContext = 0;
+
+        mEchoDevice.close();
+    }
+
+    // Search through the available devices for the ECHO loop-back device.
+    protected MidiDeviceInfo findEchoDevice() {
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        MidiDeviceInfo echoInfo = null;
+        for (MidiDeviceInfo info : infos) {
+            Bundle properties = info.getProperties();
+            String manufacturer = (String) properties.get(
+                    MidiDeviceInfo.PROPERTY_MANUFACTURER);
+
+            if (TEST_MANUFACTURER.equals(manufacturer)) {
+                String product = (String) properties.get(
+                        MidiDeviceInfo.PROPERTY_PRODUCT);
+                if (ECHO_PRODUCT.equals(product)) {
+                    echoInfo = info;
+                    break;
+                }
+            }
+        }
+        Assert.assertNotNull("could not find " + ECHO_PRODUCT, echoInfo);
+        return echoInfo;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        Log.i(TAG, "++ setUp() mContext:" + mContext);
+        if (!hasMidiSupport()) {
+            Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
+            return; // Not supported so don't test it.
+        }
+        mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
+        Assert.assertNotNull("Could not get the MidiManger.", mMidiManager);
+
+        setUpEchoServer();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        tearDownEchoServer();
+
+        Log.i(TAG, "++ tearDown()");
+        mMidiManager = null;
+    }
+
+    @Test
+    public void test_A_MidiManager() throws Exception {
+        Log.i(TAG, "++++ test_A_MidiManager() this:" + System.identityHashCode(this));
+
+        if (!hasMidiSupport()) {
+            return; // Nothing to test
+        }
+
+        Assert.assertNotNull("MidiManager not supported.", mMidiManager);
+
+        // There should be at least one device for the Echo server.
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        Assert.assertNotNull("device list was null", infos);
+        Assert.assertTrue("device list was empty", infos.length >= 1);
+
+        Log.i(TAG, "++++ test_A_MidiManager() - DONE");
+    }
+
+    @Test
+    public void test_B_SendData() throws Exception {
+        Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
+
+        Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
+        Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+        writeMidi(mTestContext, buffer, 0, buffer.length);
+
+        Assert.assertTrue("Didn't get 1 send", getNumBytesSent(mTestContext) == buffer.length);
+        Assert.assertEquals("Didn't get right number of bytes sent",
+                buffer.length, getNumBytesSent(mTestContext));
+
+        Log.i(TAG, "++++ test_B_SendData() - DONE");
+    }
+
+    @Test
+    public void test_C_EchoSmallMessage() throws Exception {
+        Log.i(TAG, "++++ test_C_EchoSmallMessage() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+
+        writeMidiWithTimestamp(mTestContext, buffer, 0, 0, timestamp); // should be a NOOP
+        writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, timestamp);
+        writeMidiWithTimestamp(mTestContext, buffer, 0, 0, timestamp); // should be a NOOP
+
+        // Wait for message to pass quickly through echo service.
+        final int numMessages = 1;
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        NativeMidiMessage message = getReceivedMessageAt(mTestContext, 0);
+        compareMessages(buffer, timestamp, message);
+
+        Log.i(TAG, "++++ test_C_EchoSmallMessage() - DONE");
+    }
+
+    @Test
+    public void test_D_EchoNMessages() throws Exception {
+        Log.i(TAG, "++++ test_D_EchoNMessages() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        int numMessages = 100;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        // correct number of messages
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
+            compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
+        }
+
+        Log.i(TAG, "++++ test_D_EchoNMessages() - DONE");
+    }
+
+    @Test
+    public void test_E_FlushMessages() throws Exception {
+        Log.i(TAG, "++++ test_E_FlushMessages() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        int numMessages = 7;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+              timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        int result = flushSentMessages(mTestContext);
+        Assert.assertEquals("flush messages failed", 0, result);
+
+        // correct number of messages
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
+            compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
+        }
+
+        Log.i(TAG, "++++ test_E_FlushMessages() - DONE");
+    }
+
+    @Test
+    public void test_F_HugeMessage() throws Exception {
+        Log.i(TAG, "++++ test_F_HugeMessage() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        // Arbitrarily large message.
+        int hugeMessageLen = 1024 * 10;
+        byte[] buffer = generateRandomMessage(hugeMessageLen);
+        int result = writeMidi(mTestContext, buffer, 0, buffer.length);
+        Assert.assertEquals("Huge write failed.", hugeMessageLen, result);
+
+        int kindaHugeMessageLen = 1024 * 2;
+        buffer = generateRandomMessage(kindaHugeMessageLen);
+        result = writeMidi(mTestContext, buffer, 0, buffer.length);
+        Assert.assertEquals("Kinda big write failed.", kindaHugeMessageLen, result);
+
+        Log.i(TAG, "++++ test_F_HugeMessage() - DONE");
+    }
+
+    /**
+     * Check a large timeout for the echoed messages to come through. If they exceed this
+     * or don't come through at all, something is wrong.
+     */
+    @Test
+    public void test_G_NativeEchoTime() throws Exception {
+        Log.i(TAG, "++++ test_G_NativeEchoTime() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
+            Assert.assertEquals("message index", (byte) (60 + msgIndex), message.buffer[1]);
+            long elapsedNanos = message.timeReceived - message.timestamp;
+            // If this test fails then there may be a problem with the thread scheduler
+            // or there may be kernel activity that is blocking execution at the user level.
+            Assert.assertTrue("MIDI round trip latency index:" + msgIndex
+                    + " too large, " + elapsedNanos
+                    + " nanoseconds " +
+                    "timestamp:" + message.timestamp + " received:" + message.timeReceived,
+                    (elapsedNanos < maxLatencyNanos));
+        }
+
+        Log.i(TAG, "++++ test_G_NativeEchoTime() - DONE");
+    }
+
+    @Test
+    public void test_H_EchoNMessages_PureNative() throws Exception {
+        Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() this:" + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        int numMessages = 2;
+        byte[][] buffers = new byte[numMessages][];
+        long timestamps[] = new long[numMessages];
+        generateRandomBufers(buffers, timestamps, numMessages);
+
+        for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+
+        int result = matchNativeMessages(mTestContext);
+        Assert.assertEquals("Native Compare Test Failed", result, 0);
+
+        Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() - DONE");
+    }
+
+    /**
+     * Check a large timeout for the echoed messages to come through. If they exceed this
+     * or don't come through at all, something is wrong.
+     */
+    @Test
+    public void test_I_NativeEchoTime_PureNative() throws Exception {
+        Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() this:"
+                + System.identityHashCode(this));
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(mTestContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass through echo service.
+        final int timeoutMs = TIMEOUT_PER_MESSAGE_MS * numMessages;
+        Thread.sleep(timeoutMs);
+        Assert.assertEquals("number of messages.",
+                numMessages, getNumReceivedMessages(mTestContext));
+
+        int result = checkNativeLatency(mTestContext, maxLatencyNanos);
+        Assert.assertEquals("failed pure native latency test.", 0, result);
+
+        Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() - DONE");
+    }
+
+    // Native Routines
+    public static native void initN();
+
+    public static native long allocTestContext();
+    public static native void freeTestContext(long context);
+
+    public native int openNativeMidiDevice(long ctx, MidiDevice device);
+    public native int closeNativeMidiDevice(long ctx);
+
+    public native int startReadingMidi(long ctx, int portNumber);
+    public native int stopReadingMidi(long ctx);
+
+    public native int startWritingMidi(long ctx, int portNumber);
+    public native int stopWritingMidi(long ctx);
+
+    public native int writeMidi(long ctx, byte[] data, int offset, int length);
+    public native int writeMidiWithTimestamp(long ctx, byte[] data, int offset, int length,
+            long timestamp);
+    public native int flushSentMessages(long ctx);
+
+    // Status - Counters
+    public native int getNumSends(long ctx);
+    public native int getNumBytesSent(long ctx);
+    public native int getNumReceives(long ctx);
+    public native int getNumBytesReceived(long ctx);
+
+    // Status - Received Messages
+    public native int getNumReceivedMessages(long ctx);
+    public native NativeMidiMessage getReceivedMessageAt(long ctx, int index);
+
+    // Pure Native Checks
+    public native int matchNativeMessages(long ctx);
+    public native int checkNativeLatency(long ctx, long maxLatencyNanos);
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
new file mode 100644
index 0000000..1984419
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nativemidi.cts;
+
+import android.content.Context;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Virtual MIDI Device that copies its input to its output.
+ * This is a Java Service that is used to test the Native MIDI API.
+ */
+
+public class NativeMidiEchoTestService extends MidiDeviceService {
+    private static final String TAG = "NativeMidiEchoTestService";
+
+    // Other apps will write to this port.
+    private MidiReceiver mInputReceiver = new MyReceiver();
+    // This app will copy the data to this port.
+    private MidiReceiver mOutputReceiver;
+    private static NativeMidiEchoTestService mInstance;
+
+    // These are public so we can easily read them from CTS test.
+    public int statusChangeCount;
+    public boolean inputOpened;
+    public int outputOpenCount;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mInstance = this;
+        MidiManager midiManager = (MidiManager)getSystemService(Context.MIDI_SERVICE);
+        if (midiManager == null) {
+            Log.e(TAG, "No MIDI Manager!");
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    // For CTS testing, so I can read test fields.
+    public static NativeMidiEchoTestService getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        return new MidiReceiver[] { mInputReceiver };
+    }
+
+    class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            if (mOutputReceiver == null) {
+                mOutputReceiver = getOutputPortReceivers()[0];
+            }
+            // Copy input to output.
+            mOutputReceiver.send(data, offset, count, timestamp);
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        statusChangeCount++;
+        inputOpened = status.isInputPortOpen(0);
+        outputOpenCount = status.getOutputPortOpenCount(0);
+    }
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
new file mode 100644
index 0000000..d8186fe
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nativemidi.cts;
+
+public class NativeMidiMessage {
+    public static final int AMIDI_PACKET_SIZE = 1024;
+    public static final int AMIDI_PACKET_OVERHEAD = 9;
+    public static final int AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD;
+
+    public int opcode;
+    public byte[] buffer = new byte[AMIDI_BUFFER_SIZE];
+    public int len;
+    public long timestamp;
+    public long timeReceived;
+
+    public NativeMidiMessage() {}
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{opcode:" + opcode + " [len:" + len + "|");
+        for(int index = 0; index < len; index++) {
+            sb.append("0x" + Integer.toHexString(buffer[index] & 0x00FF));
+            if (index < len - 1) {
+                sb.append(" ");
+            }
+        }
+        sb.append("] timestamp:" + Long.toHexString(timestamp));
+
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/nativemidi/jni/Android.mk b/tests/tests/nativemidi/jni/Android.mk
new file mode 100644
index 0000000..bb62dec
--- /dev/null
+++ b/tests/tests/nativemidi/jni/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MODULE := libnativemidi_jni
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := native-lib.cpp
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -O0
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+LOCAL_SHARED_LIBRARIES := libamidi liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/nativemidi/jni/native-lib.cpp b/tests/tests/nativemidi/jni/native-lib.cpp
new file mode 100644
index 0000000..e68859c
--- /dev/null
+++ b/tests/tests/nativemidi/jni/native-lib.cpp
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string>
+#include <thread>
+#include <time.h>
+#include <vector>
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMidiManager-JNI"
+#include <android/log.h>
+
+#include <jni.h>
+
+#include <amidi/midi.h>
+
+extern "C" {
+
+/*
+ * Structures for storing data flowing through the echo server.
+ */
+#define SIZE_DATABUFFER 256
+/*
+ * Received Messages
+ */
+typedef struct {
+    std::unique_ptr<uint8_t[]> dataBuff;
+    size_t numDataBytes;
+    int32_t opCode;
+    int64_t timestamp;
+    int64_t timeReceived;
+} ReceivedMessageRecord;
+
+/*
+ * Sent Messages
+ */
+typedef struct {
+    std::unique_ptr<uint8_t[]> dataBuff;
+    size_t numDataBytes;
+    int64_t timestamp;
+    long timeSent;
+} SentMessageRecord;
+
+/*
+ * Context
+ * Holds the state of a given test and native MIDI I/O setup for that test.
+ * NOTE: There is one of these per test (and therefore unique to each test thread).
+ */
+class TestContext {
+private:
+    // counters
+    std::atomic<int> mNumSends;
+    std::atomic<int> mNumBytesSent;
+    std::atomic<int> mNumReceives;
+    std::atomic<int> mNumBytesReceived;
+
+    std::vector<ReceivedMessageRecord> mReceivedMsgs;
+    std::vector<SentMessageRecord> mSentMsgs;
+
+    // Java NativeMidiMessage class stuff, for passing messages back out to the Java client.
+    jclass mClsNativeMidiMessage;
+    jmethodID mMidNativeMidiMessage_ctor;
+    jfieldID mFid_opcode;
+    jfieldID mFid_buffer;
+    jfieldID mFid_len;
+    jfieldID mFid_timestamp;
+    jfieldID mFid_timeReceived;
+
+    std::mutex lock;
+
+public:
+    // read Thread
+    std::unique_ptr<std::thread> mReadThread;
+    std::atomic<bool> mReading;
+
+    AMidiDevice* nativeDevice;
+    std::atomic<AMidiOutputPort*> midiOutputPort;
+    std::atomic<AMidiInputPort*> midiInputPort;
+
+    TestContext() :
+        mNumSends(0),
+        mNumBytesSent(0),
+        mNumReceives(0),
+        mNumBytesReceived(0),
+        mClsNativeMidiMessage(0),
+        mMidNativeMidiMessage_ctor(0),
+        mFid_opcode(0),
+        mFid_buffer(0),
+        mFid_len(0),
+        mFid_timestamp(0),
+        mFid_timeReceived(0),
+        mReading(false),
+        nativeDevice(nullptr),
+        midiOutputPort(nullptr),
+        midiInputPort(nullptr)
+    {}
+
+    bool initN(JNIEnv* env);
+
+    int getNumSends() { return mNumSends; }
+    void incNumSends() { mNumSends++; }
+
+    int getNumBytesSent() { return mNumBytesSent; }
+    void incNumBytesSent(int numBytes) { mNumBytesSent += numBytes; }
+
+    int getNumReceives() { return mNumReceives; }
+    void incNumReceives() { mNumReceives++; }
+
+    int getNumBytesReceived() { return mNumBytesReceived; }
+    void incNumBytesReceived(int numBytes) { mNumBytesReceived += numBytes; }
+
+    void addSent(SentMessageRecord&& msg) { mSentMsgs.push_back(std::move(msg)); }
+    size_t getNumSentMsgs() { return mSentMsgs.size(); }
+
+    void addReceived(ReceivedMessageRecord&& msg) { mReceivedMsgs.push_back(std::move(msg)); }
+    size_t getNumReceivedMsgs() { return mReceivedMsgs.size(); }
+
+    jobject transferReceiveMsgAt(JNIEnv* env, int index);
+
+    static const int COMPARE_SUCCESS = 0;
+    static const int COMPARE_COUNTMISSMATCH = 1;
+    static const int COMPARE_DATALENMISMATCH = 2;
+    static const int COMPARE_DATAMISMATCH = 3;
+    static const int COMPARE_TIMESTAMPMISMATCH = 4;
+    int compareInsAndOuts();
+
+    static const int CHECKLATENCY_SUCCESS = 0;
+    static const int CHECKLATENCY_COUNTMISSMATCH = 1;
+    static const int CHECKLATENCY_LATENCYEXCEEDED = 2;
+    int checkInOutLatency(long maxLatencyNanos);
+};
+
+//
+// Helpers
+//
+static long System_nanoTime() {
+    // this code is the implementation of System.nanoTime()
+    // from system/code/ojluni/src/main/native/System.
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+bool TestContext::initN(JNIEnv* env) {
+    static const char* clsSigNativeMidiMessage = "android/nativemidi/cts/NativeMidiMessage";
+
+    jclass cls = env->FindClass(clsSigNativeMidiMessage);
+    if (cls == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't find NativeMidiMessage class");
+        return false; // we are doomed, so bail.
+    }
+    mClsNativeMidiMessage = (jclass)env->NewGlobalRef(cls);
+    if (mClsNativeMidiMessage == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't allocate NativeMidiMessage");
+        return false; // we are doomed, so bail.
+    }
+
+    mMidNativeMidiMessage_ctor = env->GetMethodID(mClsNativeMidiMessage, "<init>", "()V");
+    mFid_opcode = env->GetFieldID(mClsNativeMidiMessage, "opcode", "I");
+    mFid_buffer = env->GetFieldID(mClsNativeMidiMessage, "buffer", "[B");
+    mFid_len = env->GetFieldID( mClsNativeMidiMessage, "len", "I");
+    mFid_timestamp = env->GetFieldID(mClsNativeMidiMessage, "timestamp", "J");
+    mFid_timeReceived = env->GetFieldID(mClsNativeMidiMessage, "timeReceived", "J");
+    if (mMidNativeMidiMessage_ctor == NULL ||
+        mFid_opcode == NULL ||
+        mFid_buffer == NULL ||
+        mFid_len == NULL ||
+        mFid_timestamp == NULL ||
+        mFid_timeReceived == NULL) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "JNI Error - couldn't load Field IDs");
+        return false; // we are doomed, so bail.
+    }
+
+    return true;
+}
+
+jobject TestContext::transferReceiveMsgAt(JNIEnv* env, int index) {
+    jobject msg = NULL;
+
+    if (index < (int)mReceivedMsgs.size()) {
+        ReceivedMessageRecord receiveRec = std::move(mReceivedMsgs.at(index));
+        msg = env->NewObject(mClsNativeMidiMessage, mMidNativeMidiMessage_ctor);
+
+        env->SetIntField(msg, mFid_opcode, receiveRec.opCode);
+        env->SetIntField(msg, mFid_len, receiveRec.numDataBytes);
+        jobject buffer_array = env->GetObjectField(msg, mFid_buffer);
+        env->SetByteArrayRegion(reinterpret_cast<jbyteArray>(buffer_array), 0,
+                receiveRec.numDataBytes, (jbyte*)receiveRec.dataBuff.get());
+        env->SetLongField(msg, mFid_timestamp, receiveRec.timestamp);
+        env->SetLongField(msg, mFid_timeReceived, receiveRec.timeReceived);
+    }
+
+    return msg;
+}
+
+int TestContext::compareInsAndOuts() {
+    // Number of messages sent/received
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_COUNTMISSMATCH r:%zu s:%zu",
+                mReceivedMsgs.size(), mSentMsgs.size());
+       return COMPARE_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages from the test above.
+    size_t numMessages = mSentMsgs.size();
+    for (size_t msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        // Data Length?
+        if (mReceivedMsgs[msgIndex].numDataBytes != mSentMsgs[msgIndex].numDataBytes) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "---- COMPARE_DATALENMISMATCH r:%zu s:%zu",
+                    mReceivedMsgs[msgIndex].numDataBytes, mSentMsgs[msgIndex].numDataBytes);
+            return COMPARE_DATALENMISMATCH;
+        }
+
+        // Timestamps
+        if (mReceivedMsgs[msgIndex].timestamp != mSentMsgs[msgIndex].timestamp) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- COMPARE_TIMESTAMPMISMATCH");
+            return COMPARE_TIMESTAMPMISMATCH;
+        }
+
+        // we know that the data in both messages have the same number of bytes from the test above.
+        int dataLen = mReceivedMsgs[msgIndex].numDataBytes;
+        for (int dataIndex = 0; dataIndex < dataLen; dataIndex++) {
+            // Data Values?
+            if (mReceivedMsgs[msgIndex].dataBuff[dataIndex] !=
+                    mSentMsgs[msgIndex].dataBuff[dataIndex]) {
+                __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                        "---- COMPARE_DATAMISMATCH r:%d s:%d",
+                        (int)mReceivedMsgs[msgIndex].dataBuff[dataIndex],
+                        (int)mSentMsgs[msgIndex].dataBuff[dataIndex]);
+                return COMPARE_DATAMISMATCH;
+            }
+        }
+    }
+
+    return COMPARE_SUCCESS;
+}
+
+int TestContext::checkInOutLatency(long maxLatencyNanos) {
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "  ---- CHECKLATENCY_COUNTMISSMATCH");
+        return CHECKLATENCY_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages
+    // from the test above.
+    int numMessages = mSentMsgs.size();
+    for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        long timeDelta =  mSentMsgs[msgIndex].timeSent - mReceivedMsgs[msgIndex].timestamp;
+        if (timeDelta > maxLatencyNanos) {
+            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                    "  ---- CHECKLATENCY_LATENCYEXCEEDED %ld", timeDelta);
+            return CHECKLATENCY_LATENCYEXCEEDED;
+        }
+    }
+
+    return CHECKLATENCY_SUCCESS;
+}
+
+JNIEXPORT jlong JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_allocTestContext(
+        JNIEnv* env, jclass) {
+    TestContext* context = new TestContext;
+    if (!context->initN(env)) {
+        delete context;
+        context = NULL;
+    }
+
+    return (jlong)context;
+}
+
+JNIEXPORT void JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_freeTestContext(
+        JNIEnv*, jclass, jlong context) {
+    delete (TestContext*)context;
+}
+
+/*
+ * Receiving API
+ */
+//static void DumpDataMessage(ReceivedMessageRecord* msg) {
+//    char midiDumpBuffer[SIZE_DATABUFFER * 4]; // more than enough
+//    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
+//    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
+//            "%" PRIx64 " ", msg->timestamp);
+//    for (uint8_t *b = msg->buffer, *e = b + msg->numDataBytes; b < e; ++b) {
+//        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
+//                "%02x ", *b);
+//    }
+//    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "---- DUMP %s", midiDumpBuffer);
+//}
+
+void readThreadRoutine(TestContext* context) {
+    int32_t opCode;
+    uint8_t inDataBuffer[SIZE_DATABUFFER];
+    size_t numDataBytes;
+    int64_t timestamp;
+
+    while (context->mReading) {
+        AMidiOutputPort* outputPort = context->midiOutputPort.load();
+        if (outputPort != nullptr) {
+            ssize_t numMessages =
+                AMidiOutputPort_receive(outputPort, &opCode,
+                    inDataBuffer, sizeof(inDataBuffer), &numDataBytes, &timestamp);
+
+            if (numMessages > 0) {
+                context->incNumReceives();
+                context->incNumBytesReceived(numDataBytes);
+                ReceivedMessageRecord receiveRec;
+                receiveRec.timeReceived = System_nanoTime();
+                receiveRec.numDataBytes = numDataBytes;
+                receiveRec.dataBuff.reset(new uint8_t[receiveRec.numDataBytes]);
+                memcpy(receiveRec.dataBuff.get(), inDataBuffer, receiveRec.numDataBytes);
+                receiveRec.opCode = opCode;
+                receiveRec.timestamp = timestamp;
+                context->addReceived(std::move(receiveRec));
+            }
+        }
+    }
+}
+
+static media_status_t commonDeviceOpen(JNIEnv *env, jobject midiDeviceObj, AMidiDevice** device) {
+    media_status_t status = AMidiDevice_fromJava(env, midiDeviceObj, device);
+    if (status == AMEDIA_OK) {
+        // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+        //      "---- Obtained device token for obj %p: dev %p", midiDeviceObj, device);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "---- Could not obtain device token for obj %p: status:%d", midiDeviceObj, status);
+    }
+
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_openNativeMidiDevice(
+        JNIEnv* env, jobject, jlong ctx, jobject deviceObj) {
+    TestContext* context = (TestContext*)ctx;
+    media_status_t status = commonDeviceOpen(env, deviceObj, &context->nativeDevice);
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_closeNativeMidiDevice(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    media_status_t status = AMidiDevice_release(context->nativeDevice);
+    return status;
+}
+
+/*
+ * Sending API
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startWritingMidi(
+        JNIEnv*, jobject, jlong ctx, jint portNumber) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiInputPort* inputPort;
+    media_status_t status = AMidiInputPort_open(context->nativeDevice, portNumber, &inputPort);
+    if (status == AMEDIA_OK) {
+        // __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+        //      "---- Opened INPUT port %d: token %p", portNumber, inputPort);
+        context->midiInputPort.store(inputPort);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "---- Could not open INPUT port %p:%d %d",
+                context->nativeDevice, portNumber, status);
+        return status;
+    }
+
+    return AMEDIA_OK;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopWritingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiInputPort* inputPort = context->midiInputPort.exchange(nullptr);
+    if (inputPort == nullptr) {
+        return -1;
+    }
+
+    AMidiInputPort_close(inputPort);
+
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+        JNIEnv* env, jobject,
+        jlong ctx, jbyteArray data, jint offset, jint numBytes, jlong timestamp) {
+
+    TestContext* context = (TestContext*)ctx;
+    context->incNumSends();
+    context->incNumBytesSent(numBytes);
+
+    jbyte* bufferPtr = env->GetByteArrayElements(data, NULL);
+    if (bufferPtr == NULL) {
+        return -1;
+    }
+
+    int numWritten =  AMidiInputPort_sendWithTimestamp(
+            context->midiInputPort, (uint8_t*)bufferPtr + offset, numBytes, timestamp);
+    if (numWritten > 0) {
+        // Don't save a send record if we didn't send!
+        SentMessageRecord sendRec;
+        sendRec.numDataBytes = numBytes;
+        sendRec.dataBuff.reset(new uint8_t[sendRec.numDataBytes]);
+        memcpy(sendRec.dataBuff.get(), (uint8_t*)bufferPtr + offset, numBytes);
+        sendRec.timestamp = timestamp;
+        sendRec.timeSent = System_nanoTime();
+        context->addSent(std::move(sendRec));
+    }
+
+    env->ReleaseByteArrayElements(data, bufferPtr, JNI_ABORT);
+
+    return numWritten;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidi(
+        JNIEnv* env, jobject j_object, jlong ctx, jbyteArray data, jint offset, jint numBytes) {
+    return Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+            env, j_object, ctx, data, offset, numBytes, 0L);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_flushSentMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    return AMidiInputPort_sendFlush(context->midiInputPort);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumSends(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumSends();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesSent(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesSent();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceives(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceives();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesReceived(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesReceived();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startReadingMidi(
+        JNIEnv*, jobject, jlong ctx, jint portNumber) {
+
+    // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ startReadingMidi()");
+    TestContext* context = (TestContext*)ctx;
+
+    AMidiOutputPort* outputPort;
+    media_status_t status = AMidiOutputPort_open(context->nativeDevice, portNumber, &outputPort);
+    if (status == AMEDIA_OK) {
+        context->midiOutputPort.store(outputPort);
+    } else {
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+                "---- Could not open OUTPUT port %p : %d %d",
+                context->nativeDevice, portNumber, status);
+        return status;
+    }
+
+    // Start read thread
+    context->mReading = true;
+    context->mReadThread.reset(new std::thread(readThreadRoutine, context));
+    std::this_thread::yield(); // let the read thread startup.
+
+    return status;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopReadingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    // __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "++++ stopReadingMidi()");
+    TestContext* context = (TestContext*)ctx;
+    context->mReading = false;
+
+    context->mReadThread->join();
+
+    AMidiOutputPort* outputPort = context->midiOutputPort.exchange(nullptr);
+    if (outputPort == nullptr) {
+        return -1;
+    }
+
+    AMidiOutputPort_close(outputPort);
+
+    return 0;
+}
+
+/*
+ * Messages
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceivedMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceivedMsgs();
+}
+
+JNIEXPORT jobject JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getReceivedMessageAt(
+        JNIEnv* env, jobject, jlong ctx, jint index) {
+    return ((TestContext*)ctx)->transferReceiveMsgAt(env, index);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_matchNativeMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->compareInsAndOuts();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_checkNativeLatency(
+        JNIEnv*, jobject, jlong ctx, jlong maxLatencyNanos) {
+    return ((TestContext*)ctx)->checkInOutLatency(maxLatencyNanos);
+}
+
+} // extern "C"
diff --git a/tests/tests/nativemidi/res/xml/echo_device_info.xml b/tests/tests/nativemidi/res/xml/echo_device_info.xml
new file mode 100644
index 0000000..9f2ebee
--- /dev/null
+++ b/tests/tests/nativemidi/res/xml/echo_device_info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<devices>
+    <device manufacturer="AndroidCTS" product="NativeMidiEcho" tags="echo,test">
+        <input-port name="input" />
+        <output-port name="output" />
+    </device>
+</devices>
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index b467b44..3d6505a 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -47,7 +47,7 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
 
-LOCAL_CFLAGS := -Werror -Wall
+LOCAL_CFLAGS := -Werror -Wall -DNNTEST_ONLY_PUBLIC_API
 
 LOCAL_SDK_VERSION := current
 LOCAL_NDK_STL_VARIANT := c++_static
diff --git a/tests/tests/os/src/android/os/cts/LocaleListTest.java b/tests/tests/os/src/android/os/cts/LocaleListTest.java
index bc30e5c..89780b2 100644
--- a/tests/tests/os/src/android/os/cts/LocaleListTest.java
+++ b/tests/tests/os/src/android/os/cts/LocaleListTest.java
@@ -20,6 +20,8 @@
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 
 public class LocaleListTest extends AndroidTestCase {
@@ -247,7 +249,7 @@
             assertEquals("ae,en,ja", LocaleList.getAdjustedDefault().toLanguageTags());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -281,7 +283,7 @@
             assertEquals(locales, LocaleList.getAdjustedDefault());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -297,7 +299,7 @@
             assertEquals(locales, LocaleList.getAdjustedDefault());
         } finally {
             // restore the original values
-            LocaleList.setDefault(originalLocaleList, originalLocaleList.indexOf(originalLocale));
+            resetDefaultLocaleList(originalLocale, originalLocaleList);
         }
     }
 
@@ -319,6 +321,26 @@
         LocaleList.getEmptyLocaleList().describeContents();
     }
 
+    private static void resetDefaultLocaleList(Locale topLocale, LocaleList localeList) {
+        final List<Locale> newLocales = new ArrayList<>();
+
+        if (topLocale != null) {
+            newLocales.add(topLocale);
+        }
+
+        if (localeList != null) {
+            for (int index = 0; index < localeList.size(); index++) {
+                final Locale locale = localeList.get(index);
+                if (topLocale != null && !topLocale.equals(locale)) {
+                    newLocales.add(locale);
+                }
+            }
+        }
+
+        final LocaleList result = new LocaleList(newLocales.toArray(new Locale[newLocales.size()]));
+        LocaleList.setDefault(result);
+    }
+
     private static LocaleList cloneViaParcel(final LocaleList original) {
         Parcel parcel = null;
         try {
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index da72966..ccc1f1a 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -29,6 +29,8 @@
 public class SecurityPatchTest extends InstrumentationTestCase {
 
     private static final String TAG = SecurityPatchTest.class.getSimpleName();
+    private static final String MISSING_BOOT_SECURITY_PATCH_LEVEL =
+            "ro.boot.security_patch should be defined in the device specific BoardConfig.mk on the BOARD_KERNEL_COMMANDLINE";
     private static final String SECURITY_PATCH_ERROR =
             "security_patch should be in the format \"YYYY-MM-DD\". Found \"%s\"";
     private static final String SECURITY_PATCH_DATE_ERROR =
@@ -39,12 +41,14 @@
     private boolean mSkipTests = false;
     private String mVendorSecurityPatch;
     private String mBuildSecurityPatch;
+    private String mBootSecurityPatch;
 
     @Override
     protected void setUp() {
         mSkipTests = (ApiLevelUtil.isBefore(Build.VERSION_CODES.M));
         mVendorSecurityPatch = SystemProperties.get("ro.vendor.build.security_patch", "");
         mBuildSecurityPatch = Build.VERSION.SECURITY_PATCH;
+        mBootSecurityPatch = SystemProperties.get("ro.boot.security_patch", "");
     }
 
     /** Security patch string must exist in M or higher **/
@@ -57,6 +61,7 @@
         assertTrue(error, !mBuildSecurityPatch.isEmpty());
     }
 
+    /** Vendor security patch string must exist in P or higher **/
     public void testVendorSecurityPatchFound() {
         if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
             Log.w(TAG, "Skipping P+ Test");
@@ -65,6 +70,15 @@
         assertTrue(!mVendorSecurityPatch.isEmpty());
     }
 
+    /** Boot security patch string must exist in P or higher **/
+    public void testBootSecurityPatchFound() {
+        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O) {
+            Log.w(TAG, "Skipping P+ Test");
+            return;
+        }
+        assertTrue(MISSING_BOOT_SECURITY_PATCH_LEVEL, !mBootSecurityPatch.isEmpty());
+    }
+
     public void testSecurityPatchesFormat() {
         if (mSkipTests) {
             Log.w(TAG, "Skipping M+ Test.");
@@ -79,6 +93,9 @@
         }
         error = String.format(SECURITY_PATCH_ERROR, mVendorSecurityPatch);
         testSecurityPatchFormat(mVendorSecurityPatch, error);
+
+        error = String.format(SECURITY_PATCH_ERROR, mBootSecurityPatch);
+        testSecurityPatchFormat(mBootSecurityPatch, error);
     }
 
     /** Security patch should be of the form YYYY-MM-DD in M or higher */
@@ -117,7 +134,14 @@
                                      SECURITY_PATCH_MONTH,
                                      mVendorSecurityPatch);
         testSecurityPatchDate(mVendorSecurityPatch, error);
+
+        error = String.format(SECURITY_PATCH_DATE_ERROR,
+                                     SECURITY_PATCH_YEAR,
+                                     SECURITY_PATCH_MONTH,
+                                     mBootSecurityPatch);
+        testSecurityPatchDate(mBootSecurityPatch, error);
     }
+
     /** Security patch should no older than the month this test was updated in M or higher **/
     private void testSecurityPatchDate(String patch, String error) {
         int declaredYear = 0;
diff --git a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
index 50237f0..0f2a1c2 100644
--- a/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
+++ b/tests/tests/os/src/android/os/cts/VibrationEffectTest.java
@@ -17,6 +17,7 @@
 package android.os.cts;
 
 import android.content.Context;
+import android.hardware.vibrator.V1_0.EffectStrength;
 import android.media.AudioAttributes;
 import android.os.Parcel;
 import android.os.VibrationEffect;
@@ -31,8 +32,10 @@
 
 import java.util.Arrays;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 @SmallTest
@@ -51,13 +54,22 @@
             VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
     private static final VibrationEffect TEST_WAVEFORM_NO_AMPLITUDES =
             VibrationEffect.createWaveform(TEST_TIMINGS, -1);
+    private static final VibrationEffect TEST_PREBAKED =
+            VibrationEffect.get(VibrationEffect.EFFECT_CLICK, true);
 
 
     @Test
     public void testCreateOneShot() {
-        VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
-        VibrationEffect.createOneShot(1, 1);
-        VibrationEffect.createOneShot(1000, 255);
+        VibrationEffect e = VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
+        assertEquals(100, e.getDuration());
+        assertEquals(VibrationEffect.DEFAULT_AMPLITUDE,
+                ((VibrationEffect.OneShot)e).getAmplitude());
+        e = VibrationEffect.createOneShot(1, 1);
+        assertEquals(1, e.getDuration());
+        assertEquals(1, ((VibrationEffect.OneShot)e).getAmplitude());
+        e = VibrationEffect.createOneShot(1000, 255);
+        assertEquals(1000, e.getDuration());
+        assertEquals(255, ((VibrationEffect.OneShot)e).getAmplitude());
     }
 
     @Test
@@ -76,6 +88,11 @@
         } catch (IllegalArgumentException expected) { }
 
         try {
+            VibrationEffect.createOneShot(TEST_TIMING, 0);
+            fail("Invalid amplitude, should throw IllegalArgumentException");
+        } catch (IllegalArgumentException expected) { }
+
+        try {
             VibrationEffect.createOneShot(TEST_TIMING, 256);
             fail("Invalid amplitude, should throw IllegalArgumentException");
         } catch (IllegalArgumentException expected) { }
@@ -113,10 +130,42 @@
     }
 
     @Test
+    public void testCreatePrebaked() {
+        int[] ids = { VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK,
+                VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_THUD,
+                VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_HEAVY_CLICK };
+        boolean[] fallbacks = { false, true };
+        for (int id : ids) {
+            for (boolean fallback : fallbacks) {
+                VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)
+                        VibrationEffect.get(id, fallback);
+                assertEquals(id, effect.getId());
+                assertEquals(fallback, effect.shouldFallback());
+                assertEquals(-1, effect.getDuration());
+            }
+        }
+    }
+
+    @Test
     public void testCreateWaveform() {
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0);
-        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, TEST_AMPLITUDES.length - 1);
+        VibrationEffect.Waveform effect = (VibrationEffect.Waveform)
+                VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(-1, effect.getRepeatIndex());
+        assertEquals(400, effect.getDuration());
+        effect = (VibrationEffect.Waveform)
+            VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(0, effect.getRepeatIndex());
+        assertEquals(Long.MAX_VALUE, effect.getDuration());
+        effect = (VibrationEffect.Waveform)VibrationEffect.createWaveform(TEST_TIMINGS,
+                TEST_AMPLITUDES, TEST_AMPLITUDES.length - 1);
+        assertArrayEquals(TEST_TIMINGS, effect.getTimings());
+        assertArrayEquals(TEST_AMPLITUDES, effect.getAmplitudes());
+        assertEquals(TEST_AMPLITUDES.length - 1, effect.getRepeatIndex());
+        assertEquals(Long.MAX_VALUE, effect.getDuration());
     }
 
     @Test
@@ -278,17 +327,72 @@
     }
 
     @Test
-    public void testParceling() {
+    public void testParcelingOneShot() {
         Parcel p = Parcel.obtain();
         TEST_ONE_SHOT.writeToParcel(p, 0);
         p.setDataPosition(0);
         VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
         assertEquals(TEST_ONE_SHOT, parceledEffect);
+    }
 
-        p.setDataPosition(0);
+    @Test
+    public void testParcelingWaveForm() {
+        Parcel p = Parcel.obtain();
         TEST_WAVEFORM.writeToParcel(p, 0);
         p.setDataPosition(0);
-        parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
+        VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
         assertEquals(TEST_WAVEFORM, parceledEffect);
     }
+
+    @Test
+    public void testParcelingPrebaked() {
+        Parcel p = Parcel.obtain();
+        TEST_PREBAKED.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        VibrationEffect parceledEffect = VibrationEffect.CREATOR.createFromParcel(p);
+        assertEquals(TEST_PREBAKED, parceledEffect);
+    }
+
+    @Test
+    public void testDescribeContents() {
+        TEST_ONE_SHOT.describeContents();
+        TEST_WAVEFORM.describeContents();
+        TEST_WAVEFORM_NO_AMPLITUDES.describeContents();
+        TEST_PREBAKED.describeContents();
+    }
+
+    @Test
+    public void testSetStrength() {
+        VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
+                VibrationEffect.EFFECT_CLICK, true);
+        int[] strengths = { EffectStrength.LIGHT, EffectStrength.MEDIUM, EffectStrength.STRONG};
+        for (int strength : strengths) {
+            effect.setEffectStrength(strength);
+            assertEquals(strength, effect.getEffectStrength());
+        }
+    }
+
+    @Test
+    public void testSetStrengthInvalid() {
+        VibrationEffect.Prebaked effect = (VibrationEffect.Prebaked)VibrationEffect.get(
+                VibrationEffect.EFFECT_CLICK, true);
+        try {
+            effect.setEffectStrength(239017);
+            fail("Illegal strength, should throw IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {}
+    }
+
+    @Test
+    public void testPrebakedEquals() {
+        VibrationEffect otherEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK, true);
+        assertEquals(TEST_PREBAKED, otherEffect);
+        assertEquals(TEST_PREBAKED.hashCode(), otherEffect.hashCode());
+    }
+
+    @Test
+    public void testToString() {
+        TEST_ONE_SHOT.toString();
+        TEST_WAVEFORM.toString();
+        TEST_PREBAKED.toString();
+    }
 }
diff --git a/tests/tests/packageinstaller/externalsources/Android.bp b/tests/tests/packageinstaller/externalsources/Android.bp
new file mode 100644
index 0000000..3b0598b
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library_static {
+    name: "CtsExternalSourcesTestCases-lib",
+    sdk_version: "test_current",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "junit",
+        "android-support-test",
+        "platform-test-annotations",
+        "compatibility-device-util",
+    ]
+}
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/externalsources/Android.mk b/tests/tests/packageinstaller/externalsources/Android.mk
index b7346b5..d8c062d 100755
--- a/tests/tests/packageinstaller/externalsources/Android.mk
+++ b/tests/tests/packageinstaller/externalsources/Android.mk
@@ -22,12 +22,7 @@
 
 LOCAL_PACKAGE_NAME := CtsExternalSourcesTestCases
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator \
-                               android-support-test \
-                               androidx.legacy_legacy-support-v4 \
-                               compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalSourcesTestCases-lib
 
 LOCAL_SDK_VERSION := current
 
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java
deleted file mode 100644
index 7aa6a57..0000000
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.packageinstaller.externalsources.cts;
-
-import static org.junit.Assert.assertFalse;
-
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.platform.test.annotations.AppModeInstant;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.AppOpsUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@AppModeInstant
-public class ExternalSourcesInstantAppsTest {
-
-    private Context mContext;
-    private PackageManager mPm;
-    private String mPackageName;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mPm = mContext.getPackageManager();
-        mPackageName = mContext.getPackageName();
-    }
-
-    private void setAppOpsMode(int mode) throws IOException {
-        AppOpsUtils.setOpMode(mPackageName, "REQUEST_INSTALL_PACKAGES", mode);
-    }
-
-    @Test
-    public void blockedSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_ERRORED);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void allowedSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_ALLOWED);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void defaultSourceTest() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_DEFAULT);
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        assertFalse("Instant app " + mPackageName + " allowed to install packages", isTrusted);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        setAppOpsMode(AppOpsManager.MODE_DEFAULT);
-    }
-}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt
new file mode 100644
index 0000000..67bed56
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesInstantAppsTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.packageinstaller.externalsources.cts
+
+import android.app.AppOpsManager.*
+import android.platform.test.annotations.AppModeInstant
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.AppOpsUtils
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@AppModeInstant
+class ExternalSourcesInstantAppsTest {
+    private val pm = InstrumentationRegistry.getTargetContext().packageManager
+    private val packageName = InstrumentationRegistry.getTargetContext().packageName
+
+    private fun setAppOpsMode(mode: Int) {
+        AppOpsUtils.setOpMode(packageName, "REQUEST_INSTALL_PACKAGES", mode)
+    }
+
+    @Test
+    fun blockedSourceTest() {
+        setAppOpsMode(MODE_ERRORED)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun allowedSourceTest() {
+        setAppOpsMode(MODE_ALLOWED)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun defaultSourceTest() {
+        setAppOpsMode(MODE_DEFAULT)
+        assertFalse("Instant app $packageName allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @After
+    fun resetAppOpsMode() {
+        setAppOpsMode(MODE_DEFAULT)
+    }
+}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
deleted file mode 100644
index a29264b..0000000
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.packageinstaller.externalsources.cts;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.UiDevice;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@AppModeFull
-public class ExternalSourcesTest {
-
-    private Context mContext;
-    private PackageManager mPm;
-    private String mPackageName;
-    private UiDevice mUiDevice;
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mPm = mContext.getPackageManager();
-        mPackageName = mContext.getPackageName();
-        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-    }
-
-    private void setAppOpsMode(String mode) throws IOException {
-        final StringBuilder commandBuilder = new StringBuilder("appops set");
-        commandBuilder.append(" " + mPackageName);
-        commandBuilder.append(" REQUEST_INSTALL_PACKAGES");
-        commandBuilder.append(" " + mode);
-        mUiDevice.executeShellCommand(commandBuilder.toString());
-    }
-
-    @Test
-    public void blockedSourceTest() throws Exception {
-        setAppOpsMode("deny");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertFalse("Package " + mPackageName
-                + " allowed to install packages after setting app op to errored", isTrusted);
-    }
-
-    @Test
-    public void allowedSourceTest() throws Exception {
-        setAppOpsMode("allow");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertTrue("Package " + mPackageName
-                + " blocked from installing packages after setting app op to allowed", isTrusted);
-    }
-
-    @Test
-    public void defaultSourceTest() throws Exception {
-        setAppOpsMode("default");
-        final boolean isTrusted = mPm.canRequestPackageInstalls();
-        Assert.assertFalse("Package " + mPackageName
-                + " with default app ops state allowed to install packages", isTrusted);
-    }
-
-    @Test
-    public void testManageUnknownSourcesExists() {
-        Intent manageUnknownSources = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
-        ResolveInfo info = mPm.resolveActivity(manageUnknownSources, 0);
-        Assert.assertNotNull("No activity found for " + manageUnknownSources.getAction(), info);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        setAppOpsMode("default");
-    }
-}
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt
new file mode 100644
index 0000000..5edfa38
--- /dev/null
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.packageinstaller.externalsources.cts
+
+import android.app.AppOpsManager.*
+import android.content.Intent
+import android.platform.test.annotations.AppModeFull
+import android.provider.Settings
+import android.support.test.InstrumentationRegistry
+import android.support.test.filters.SmallTest
+import android.support.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.AppOpsUtils
+import org.junit.After
+import org.junit.Assert.*
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@AppModeFull
+class ExternalSourcesTest {
+    private val pm = InstrumentationRegistry.getTargetContext().packageManager
+    private val packageName = InstrumentationRegistry.getTargetContext().packageName
+
+    private fun setAppOpsMode(mode: Int) {
+        AppOpsUtils.setOpMode(packageName, "REQUEST_INSTALL_PACKAGES", mode)
+    }
+
+    @Test
+    fun blockedSourceTest() {
+        setAppOpsMode(MODE_ERRORED)
+        assertFalse("Package $packageName allowed to install packages after setting app op to "
+                + "errored", pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun allowedSourceTest() {
+        setAppOpsMode(MODE_ALLOWED)
+        assertTrue("Package $packageName blocked from installing packages after setting app op to "
+                + "allowed", pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun defaultSourceTest() {
+        setAppOpsMode(MODE_DEFAULT)
+        assertFalse("Package $packageName with default app ops state allowed to install packages",
+                pm.canRequestPackageInstalls())
+    }
+
+    @Test
+    fun testManageUnknownSourcesExists() {
+        val manageUnknownSources = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
+        assertNotNull("No activity found for ${manageUnknownSources.action}",
+                pm.resolveActivity(manageUnknownSources, 0))
+    }
+
+    @After
+    fun resetAppOpsMode() {
+        setAppOpsMode(MODE_DEFAULT)
+    }
+}
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 2075137..0172716 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -27,8 +27,6 @@
 # Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
 
-LOCAL_JAVA_LIBRARIES := telephony-common
-
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
     guava \
@@ -41,9 +39,8 @@
 
 LOCAL_PACKAGE_NAME := CtsPermissionTestCases
 
-# uncomment when b/13249777 is fixed
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SDK_VERSION := test_current
+
 LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
 LOCAL_JAVA_LIBRARIES += android.test.base.stubs
 
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index c516a60..79deeba 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -43,6 +43,5 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.permission.cts" />
         <option name="runtime-hint" value="13m" />
-        <option name="hidden-api-checks" value="false" />
     </test>
 </configuration>
diff --git a/tests/tests/permission/jni/Android.mk b/tests/tests/permission/jni/Android.mk
index 96b7506..b0d16a8 100644
--- a/tests/tests/permission/jni/Android.mk
+++ b/tests/tests/permission/jni/Android.mk
@@ -17,6 +17,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libctspermission_jni
+LOCAL_SDK_VERSION := current
 
 # Don't include this package in any configuration by default.
 LOCAL_MODULE_TAGS := optional
@@ -29,7 +30,7 @@
 
 LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
 LOCAL_CPPFLAGS := -std=gnu++11
-LOCAL_CXX_STL := libc++_static
+LOCAL_NDK_STL_VARIANT := c++_static
 
 LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
 
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index 547af7e..1c8e772 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -241,7 +241,7 @@
             assertNotNull("Each app op must have an operation string defined", opStr);
             opStrs.add(opStr);
         }
-        assertEquals("Not all op strings are unique", AppOpsManager._NUM_OP, opStrs.size());
+        assertEquals("Not all op strings are unique", AppOpsManager.getNumOps(), opStrs.size());
     }
 
     @SmallTest
diff --git a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
deleted file mode 100644
index 006fb6d..0000000
--- a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission.cts;
-
-import android.content.Context;
-import android.os.PowerManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-
-/**
- * Verify that various PowerManagement functionality requires Permission.
- */
-public class DevicePowerPermissionTest extends AndroidTestCase {
-    PowerManager mPowerManager;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-    }
-
-    /**
-     * Verify that going to sleep requires Permission.
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#DEVICE_POWER}.
-     */
-    @LargeTest
-    public void testGoToSleep() {
-        try {
-            mPowerManager.goToSleep(0);
-            fail("Was able to call PowerManager.goToSleep without DEVICE_POWER Permission.");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-}
diff --git a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
index ec0de49..83c8287 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -23,12 +23,17 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
 import android.provider.CallLog;
 import android.provider.Contacts;
+import android.provider.ContactsContract;
 import android.provider.Settings;
+import android.provider.Telephony;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
@@ -37,18 +42,36 @@
  */
 @MediumTest
 public class ProviderPermissionTest extends AndroidTestCase {
+
+    private static final String TAG = ProviderPermissionTest.class.getSimpleName();
+
+    private static final List<Uri> CONTACT_URIS = new ArrayList<Uri>() {{
+        add(Contacts.People.CONTENT_URI); // Deprecated.
+        add(ContactsContract.Contacts.CONTENT_FILTER_URI);
+        add(ContactsContract.Contacts.CONTENT_GROUP_URI);
+        add(ContactsContract.Contacts.CONTENT_LOOKUP_URI);
+        add(ContactsContract.CommonDataKinds.Email.CONTENT_URI);
+        add(ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI);
+        add(ContactsContract.Directory.CONTENT_URI);
+        add(ContactsContract.Directory.ENTERPRISE_CONTENT_URI);
+        add(ContactsContract.Profile.CONTENT_URI);
+    }};
+
     /**
-     * Verify that read and write to contact requires permissions.
+     * Verify that reading contacts requires permissions.
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#READ_CONTACTS}
      */
     public void testReadContacts() {
-        assertReadingContentUriRequiresPermission(Contacts.People.CONTENT_URI,
-                android.Manifest.permission.READ_CONTACTS);
+        for (Uri uri : CONTACT_URIS) {
+            Log.d(TAG, "Checking contacts URI " + uri);
+            assertReadingContentUriRequiresPermission(uri,
+                    android.Manifest.permission.READ_CONTACTS);
+        }
     }
 
     /**
-     * Verify that write to contact requires permissions.
+     * Verify that writing contacts requires permissions.
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#WRITE_CONTACTS}
      */
@@ -57,22 +80,6 @@
                 android.Manifest.permission.WRITE_CONTACTS);
     }
 
-    public void assertWritingContentUriRequiresPermission(Uri uri, String permission,
-            boolean allowIAE) {
-        try {
-            getContext().getContentResolver().insert(uri, new ContentValues());
-            fail("expected SecurityException requiring " + permission);
-        } catch (IllegalArgumentException e) {
-            if (!allowIAE) {
-                fail("expected SecurityException requiring " + permission + " got " + e);
-            }
-        } catch (SecurityException expected) {
-            assertNotNull("security exception's error message.", expected.getMessage());
-            assertTrue("error message should contain " + permission + ".",
-                    expected.getMessage().contains(permission));
-        }
-    }
-
     /**
      * Verify that reading call logs requires permissions.
      * <p>Tests Permission:
@@ -89,10 +96,66 @@
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#WRITE_CALL_LOG}
      */
+    @AppModeFull
     public void testWriteCallLog() {
         assertWritingContentUriRequiresPermission(CallLog.CONTENT_URI,
-                android.Manifest.permission.WRITE_CALL_LOG,
-                getContext().getPackageManager().isInstantApp());
+                android.Manifest.permission.WRITE_CALL_LOG);
+    }
+
+    /**
+     * Verify that reading from call-log (a content provider that is not accessible to instant apps)
+     * returns null
+     */
+    @AppModeInstant
+    public void testReadCallLogInstant() {
+        assertNull(getContext().getContentResolver().query(CallLog.CONTENT_URI, null, null, null,
+                null));
+    }
+
+    /**
+     * Verify that writing to call-log (a content provider that is not accessible to instant apps)
+     * yields an IAE.
+     */
+    @AppModeInstant
+    public void testWriteCallLogInstant() {
+        try {
+            getContext().getContentResolver().insert(CallLog.CONTENT_URI, new ContentValues());
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    /**
+     * Verify that reading already received SMS messages requires permissions.
+     * <p>Tests Permission:
+     *   {@link android.Manifest.permission#READ_SMS}
+     *
+     * <p>Note: The WRITE_SMS permission has been removed.
+     */
+    @AppModeFull
+    public void testReadSms() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertReadingContentUriRequiresPermission(Telephony.Sms.CONTENT_URI,
+                android.Manifest.permission.READ_SMS);
+    }
+
+    /**
+     * Verify that reading from 'sms' (a content provider that is not accessible to instant apps)
+     * returns null
+     */
+    @AppModeInstant
+    public void testReadSmsInstant() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertNull(getContext().getContentResolver().query(Telephony.Sms.CONTENT_URI, null, null,
+                null, null));
     }
 
     /**
diff --git a/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java
new file mode 100644
index 0000000..ab099ee
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/SmsManagerPermissionTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.SmsManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Test that sending SMS and MMS messages requires permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsManagerPermissionTest {
+
+    private static final String SOURCE_ADDRESS = "+15550000000";
+    private static final String DESTINATION_ADDRESS = "+15550000001";
+
+    private boolean mHasTelephony;
+    private SmsManager mSmsManager;
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mHasTelephony = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY);
+        assumeTrue(mHasTelephony); // Don't run these tests if FEATURE_TELEPHONY is not available.
+
+        mSmsManager = SmsManager.getDefault();
+        assertNotNull(mSmsManager);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendTextMessage() {
+        mSmsManager.sendTextMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendTextMessageWithoutPersisting() {
+        mSmsManager.sendTextMessageWithoutPersisting(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendMultipartTextMessage() {
+        ArrayList<String> messageParts = new ArrayList<>();
+        messageParts.add("Message text");
+        mSmsManager.sendMultipartTextMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, messageParts, null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendDataMessage() {
+        mSmsManager.sendDataMessage(
+                DESTINATION_ADDRESS, SOURCE_ADDRESS, (short) 1, new byte[]{0, 0, 0}, null, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSendMultimediaMessage() {
+        // Ideally we would provide an Uri to an existing resource, to make sure the
+        // SecurityException is not due to the invalid Uri.
+        Uri uri = Uri.parse("android.resource://android.permission.cts/some-image.png");
+        mSmsManager.sendMultimediaMessage(mContext, uri, "", null, null);
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java
deleted file mode 100644
index 2346d4a..0000000
--- a/tests/tests/permission/src/android/permission/cts/SuspendAppsPermissionTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.permission.cts;
-
-import static android.Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
-import static android.Manifest.permission.SUSPEND_APPS;
-import static android.content.Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SuspendAppsPermissionTest {
-
-    private PackageManager mPackageManager;
-
-    @Before
-    public void setUp() {
-        mPackageManager = InstrumentationRegistry.getTargetContext().getPackageManager();
-    }
-
-    @Test
-    public void testNumberOfAppsWithPermission() {
-        final List<PackageInfo> packagesWithPerm = mPackageManager.getPackagesHoldingPermissions(
-                new String[]{SUSPEND_APPS}, 0);
-        assertTrue("At most one app can hold the permission " + SUSPEND_APPS + ", but found more: "
-                + packagesWithPerm, packagesWithPerm.size() <= 1);
-    }
-
-    @Test
-    public void testShowSuspendedAppDetailsDeclared() {
-        final List<PackageInfo> packagesWithPerm = mPackageManager.getPackagesHoldingPermissions(
-                new String[]{SUSPEND_APPS}, 0);
-        final Intent showDetailsIntent = new Intent(ACTION_SHOW_SUSPENDED_APP_DETAILS);
-        boolean success = true;
-        StringBuilder errorString = new StringBuilder();
-        for (PackageInfo packageInfo : packagesWithPerm) {
-            showDetailsIntent.setPackage(packageInfo.packageName);
-            final ResolveInfo resolveInfo = mPackageManager.resolveActivity(showDetailsIntent, 0);
-            if (resolveInfo == null || resolveInfo.activityInfo == null) {
-                errorString.append("No activity found for " + ACTION_SHOW_SUSPENDED_APP_DETAILS
-                        + " inside package " + packageInfo.packageName);
-                success = false;
-            }
-            else if (!SEND_SHOW_SUSPENDED_APP_DETAILS.equals(resolveInfo.activityInfo.permission)) {
-                errorString.append("Activity handling " + ACTION_SHOW_SUSPENDED_APP_DETAILS
-                        + " not protected with permission " + SEND_SHOW_SUSPENDED_APP_DETAILS);
-                success = false;
-            }
-        }
-        if (!success) {
-            fail(errorString.toString());
-        }
-    }
-}
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index d117e19..dcc9cdb 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -275,48 +275,6 @@
         }
     }
 
-    /**
-     * Verify that TelephonyManager.setAllowedCarriers requires Permission.
-     * <p>
-     * Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
-     */
-    @Test
-    public void testSetAllowedCarriers() {
-        if (!mHasTelephony
-                || !getContext().getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
-            return;
-        }
-        try {
-            mTelephonyManager.setAllowedCarriers(0, Collections.emptyList());
-            fail("Able to set allowed carriers");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
-    /**
-     * Verify that TelephonyManager.getAllowedCarriers requires Permission.
-     * <p>
-     * Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
-     */
-    @Test
-    public void testGetAllowedCarriers() {
-        if (!mHasTelephony
-                || !getContext().getPackageManager().hasSystemFeature(
-                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
-            return;
-        }
-        try {
-            mTelephonyManager.getAllowedCarriers(0);
-            fail("Able to get allowed carriers");
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index b20bd67..9eeabaf 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -4283,16 +4283,6 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.server.stats.StatsCompanionService$AnomalyAlarmReceiver"
-                  android:permission="android.permission.STATSCOMPANION"
-                  android:exported="false">
-        </receiver>
-
-        <receiver android:name="com.android.server.stats.StatsCompanionService$PullingAlarmReceiver"
-                  android:permission="android.permission.STATSCOMPANION"
-                  android:exported="false">
-        </receiver>
-
         <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
                   android:exported="false">
             <intent-filter>
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
index e0f387b..8e042d7 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowLandscapeTest.java
@@ -101,14 +101,6 @@
     }
 
     /**
-     * Landscape setup of {@link #startWithFragmentAndInitTitleMultiWindowInner}.
-     */
-    @Test
-    public void startWithFragmentAndInitTitleMultiWindowLandscapeTest() {
-        startWithFragmentAndInitTitleMultiWindowInner();
-    }
-
-    /**
      * Landscape setup of {@link #startWithFragmentNoHeadersInner}.
      */
     @Test
@@ -125,14 +117,6 @@
     }
 
     /**
-     * Landscape setup of {@link #startWithFragmentNoHeadersMultiWindowTest}.
-     */
-    @Test
-    public void startWithFragmentNoHeadersMultiWindowLandscapeTest() {
-        startWithFragmentNoHeadersMultiWindowTest();
-    }
-
-    /**
      * Landscape setup of {@link #listDialogTest}.
      */
     @Test
@@ -156,38 +140,6 @@
         recreateInnerFragmentTest();
     }
 
-    /**
-     * Landscape setup of {@link #multiWindowInOutTest}.
-     */
-    @Test
-    public void multiWindowInOutLandscapeTest() {
-        multiWindowInOutTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowInnerFragmentInOutTest}.
-     */
-    @Test
-    public void multiWindowInnerFragmentInOutLandscapeTest() {
-        multiWindowInnerFragmentInOutTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowInitialHeaderOnBackTest}.
-     */
-    @Test
-    public void multiWindowInitialHeaderOnBackLandscapeTest() {
-        multiWindowInitialHeaderOnBackTest();
-    }
-
-    /**
-     * Landscape setup of {@link #multiWindowHistoryPreserveTest}.
-     */
-    @Test
-    public void multiWindowHistoryPreserveLandscapeTest() {
-        multiWindowHistoryPreserveTest();
-    }
-
     @Override
     protected PreferenceWithHeaders launchActivity(Intent intent) {
         if (intent != null) {
@@ -200,9 +152,7 @@
     @Override
     protected void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(runnable);
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
index 1caed6f..ab3922e 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowPortraitTest.java
@@ -101,14 +101,6 @@
     }
 
     /**
-     * Portrait setup of {@link #startWithFragmentAndInitTitleMultiWindowInner}.
-     */
-    @Test
-    public void startWithFragmentAndInitTitleMultiWindowPortraitTest() {
-        startWithFragmentAndInitTitleMultiWindowInner();
-    }
-
-    /**
      * Portrait setup of {@link #startWithFragmentNoHeadersInner}.
      */
     @Test
@@ -125,14 +117,6 @@
     }
 
     /**
-     * Portrait setup of {@link #startWithFragmentNoHeadersMultiWindowTest}.
-     */
-    @Test
-    public void startWithFragmentNoHeadersMultiWindowPortraitTest() {
-        startWithFragmentNoHeadersMultiWindowTest();
-    }
-
-    /**
      * Portrait setup of {@link #listDialogTest}.
      */
     @Test
@@ -156,38 +140,6 @@
         recreateInnerFragmentTest();
     }
 
-    /**
-     * Portrait setup of {@link #multiWindowInOutTest}.
-     */
-    @Test
-    public void multiWindowInOutPortraitTest() {
-        multiWindowInOutTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowInnerFragmentInOutTest}.
-     */
-    @Test
-    public void multiWindowInnerFragmentInOutPortraitTest() {
-        multiWindowInnerFragmentInOutTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowInitialHeaderOnBackTest}.
-     */
-    @Test
-    public void multiWindowInitialHeaderOnBackPortraitTest() {
-        multiWindowInitialHeaderOnBackTest();
-    }
-
-    /**
-     * Portrait setup of {@link #multiWindowHistoryPreserveTest}.
-     */
-    @Test
-    public void multiWindowHistoryPreservePortraitTest() {
-        multiWindowHistoryPreserveTest();
-    }
-
     @Override
     protected PreferenceWithHeaders launchActivity(Intent intent) {
         if (intent != null) {
@@ -200,9 +152,7 @@
     @Override
     protected void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(runnable);
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
index 253f8f6..28071ac 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityFlowTest.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.SystemClock;
-import android.preference2.cts.R;
 import android.util.Log;
 
 import com.android.compatibility.common.util.BitmapUtils;
@@ -325,36 +324,6 @@
     }
 
     /**
-     * For: Large screen (multi-pane).
-     * Scenario: Tests that initial title is displayed or hidden properly when transitioning in and
-     * out of the multi-window mode.
-     */
-    void startWithFragmentAndInitTitleMultiWindowInner() {
-        launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class,
-                false /* noHeaders */, INITIAL_TITLE_RES_ID);
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialStateForFragment();
-        String testTitle = mActivity.getResources().getString(INITIAL_TITLE_RES_ID);
-
-        // Title should not be shown (we are in multi-pane).
-        assertFalse(mTestUtils.isTextShown(testTitle));
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Title should be shown (we are in single-pane).
-        assertTextShown(testTitle);
-
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Title should not be shown (we are back in multi-pane).
-        assertTextHidden(testTitle);
-    }
-
-    /**
      * For: Any screen (single or multi-pane).
      * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers in multi-pane
      * is applied correctly.
@@ -390,34 +359,6 @@
 
     /**
      * For: Any screen (single or multi-pane).
-     * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers survives
-     * correctly multi-window changes. Tested via screenshots.
-     */
-    void startWithFragmentNoHeadersMultiWindowTest() {
-        launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class,
-                true /* noHeaders */, -1 /* initialTitle */);
-
-        assertInitialStateForFragment();
-
-        // Workaround for some focus bug in the framework
-        mTestUtils.tapOnViewWithText(PREFS2_PANEL_TITLE);
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        assertInitialStateForFragment();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Any screen (single or multi-pane).
      * Scenario: Tests that list preference opens correctly and that back press correctly closes it.
      */
     void listDialogTest()  {
@@ -515,163 +456,6 @@
         assertScreenshotsAreEqual(before, after);
     }
 
-    /**
-     * For: Any screen (single or multi-pane).
-     * Scenario: Tests that the PreferenceActivity properly restores its state after going to
-     * multi-window and back. Test done via screenshots.
-     */
-    void multiWindowInOutTest() {
-        launchActivity();
-
-        assertInitialState();
-        // Tap on Prefs2 header.
-        tapOnPrefs2Header();
-
-        assertPanelPrefs2Shown();
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        assertPanelPrefs2Shown();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Any screen (single or multi-pane).
-     * Scenario: Tests that the PreferenceActivity properly restores its state after going to
-     * multi-window and back while an inner fragment is shown. Test done via screenshots.
-     */
-    void multiWindowInnerFragmentInOutTest() {
-        launchActivity();
-
-        assertInitialState();
-        if (!mIsMultiPane) {
-            tapOnPrefs1Header();
-        }
-
-        // Go to preferences inner fragment.
-        mTestUtils.tapOnViewWithText(INNER_FRAGMENT_PREF_BUTTON);
-
-        // We don't need to check that correct panel is displayed that is already covered by
-        // smallScreenGoToFragmentInner and largeScreenGoToFragmentInner
-
-        // Take screenshot
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter and leave multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Large screen (single or multi-pane).
-     * Scenario: Goes to single-pane by entering multi-window and tests that back press ends up with
-     * a list of headers and nothing else. Then leaves multi-window back to single-pane and tests if
-     * the proper default header was opened (screenshot test).
-     */
-    void multiWindowInitialHeaderOnBackTest() {
-        launchActivity();
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialState();
-
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        // Enter multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-
-        // Get window focus (otherwise back press would close multi-window instead of firing to the
-        // Activity.
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        pressBack();
-
-        // Only headers should be shown (also checks that we have correct focus).
-        assertHeadersShown();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Hidden();
-
-        // Leave multi-window
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Headers and Prefs1 should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
-    /**
-     * For: Large screen (multi-pane).
-     * Scenario: Tests that history is preserved correctly while transitioning to multi-window.
-     * Navigates to Prefs2 pane and then goes to single-pane mode via multi-window. Test that back
-     * press navigates to the headers list. Then tests that restoring multi-pane by leaving
-     * multi-window opens the same screen with which was the activity started before (screenshot
-     * test).
-     */
-    void multiWindowHistoryPreserveTest() {
-        launchActivity();
-        if (!shouldRunLargeDeviceTest()) {
-            return;
-        }
-
-        assertInitialState();
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        tapOnPrefs2Header();
-
-        // Enter multi-window.
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Only Prefs2 should be shown (also checks that we have correct focus).
-        assertHeadersHidden();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Shown();
-
-        pressBack();
-
-        // Only headers should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Hidden();
-        assertPanelPrefs2Hidden();
-
-        tapOnPrefs1Header();
-
-        // Only Prefs1 should be shown.
-        assertHeadersHidden();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Leave multi-window
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Headers and Prefs1 should be shown.
-        assertHeadersShown();
-        assertPanelPrefs1Shown();
-        assertPanelPrefs2Hidden();
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
     private void assertScreenshotsAreEqual(Bitmap before, Bitmap after) {
         assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after));
     }
@@ -690,7 +474,6 @@
             assertPanelPrefs1Hidden();
             assertPanelPrefs2Hidden();
         }
-
         assertHeadersAreLoaded();
     }
 
@@ -708,8 +491,6 @@
             assertPanelPrefs1Hidden();
             assertPanelPrefs2Shown();
         }
-
-
     }
 
     public boolean shouldRunLargeDeviceTest() {
@@ -739,12 +520,10 @@
     }
 
     private void assertHeadersAreLoaded() {
-        runOnUiThread(() -> {
-            assertEquals(EXPECTED_HEADERS_COUNT,
-                    mActivity.loadedHeaders == null
-                            ? 0
-                            : mActivity.loadedHeaders.size());
-        });
+        runOnUiThread(() -> assertEquals(EXPECTED_HEADERS_COUNT,
+                mActivity.loadedHeaders == null
+                        ? 0
+                        : mActivity.loadedHeaders.size()));
     }
 
     private void assertHeadersShown() {
@@ -822,9 +601,7 @@
     private void launchActivity() {
         mActivity = launchActivity(null);
         mTestUtils.device.waitForIdle();
-        runOnUiThread(() -> {
-            mIsMultiPane = mActivity.isMultiPane();
-        });
+        runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane());
     }
 
     private void launchActivityWithExtras(Class extraFragment, boolean noHeaders,
@@ -843,9 +620,7 @@
 
         mActivity = launchActivity(intent);
         mTestUtils.device.waitForIdle();
-        runOnUiThread(() -> {
-            mIsMultiPane = mActivity.isMultiPane();
-        });
+        runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane());
     }
 
     protected abstract PreferenceWithHeaders launchActivity(Intent intent);
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
index b3d8b8d..46863cb 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceActivityLegacyFlowTest.java
@@ -84,44 +84,6 @@
         assertScreenshotsAreEqual(before, after);
     }
 
-    /**
-     * Scenario: Tests that the activity still shows the preference screen even after multi-window
-     * is entered.
-     */
-    @Test
-    public void legacyActivityMultiWindowTest() {
-        waitForIdle();
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.getMultiWindowFocus(mActivity);
-
-        // Prefs list should be shown.
-        assertTextShown(LEGACY_SCREEN_TEXT);
-
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Prefs list should be shown.
-        assertTextShown(LEGACY_SCREEN_TEXT);
-    }
-
-    /**
-     * Scenario: Tests that the activity correctly restores its state after multi-window changes
-     * in legacy mode.
-     */
-    @Test
-    public void legacyActivityMultiWindowToggleTest() {
-        waitForIdle();
-
-        Bitmap before = mTestUtils.takeScreenshot();
-
-        mTestUtils.enterMultiWindow(mActivity);
-        mTestUtils.leaveMultiWindow(mActivity);
-
-        // Compare screenshots
-        Bitmap after = mTestUtils.takeScreenshot();
-        assertScreenshotsAreEqual(before, after);
-    }
-
     private void recreate() {
         runOnUiThread(() -> mActivity.recreate());
         SystemClock.sleep(1000);
@@ -130,9 +92,7 @@
 
     private void runOnUiThread(final Runnable runnable) {
         try {
-            mActivityRule.runOnUiThread(() -> {
-                runnable.run();
-            });
+            mActivityRule.runOnUiThread(() -> runnable.run());
         } catch (Throwable ex) {
             throw new RuntimeException("Failure on the UI thread", ex);
         }
diff --git a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
index e00e658..568b6f9 100644
--- a/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
+++ b/tests/tests/preference2/src/android/preference2/cts/TestUtils.java
@@ -16,26 +16,17 @@
 
 package android.preference2.cts;
 
-import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.app.UiModeManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
-import android.support.test.uiautomator.UiSelector;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import java.io.IOException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 
 /**
  * Collection of helper utils for testing preferences.
@@ -43,19 +34,24 @@
 public class TestUtils {
 
     final UiDevice device;
+
+    private final Context mContext;
     private final Instrumentation mInstrumentation;
+    private final String mPackageName;
     private final UiAutomation mAutomation;
     private int mStatusBarHeight = -1;
     private int mNavigationBarHeight = -1;
 
     TestUtils() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getTargetContext();
+        mPackageName = mContext.getPackageName();
         device = UiDevice.getInstance(mInstrumentation);
         mAutomation = mInstrumentation.getUiAutomation();
     }
 
     Bitmap takeScreenshot() {
-        // Only take screenshot once the screen is stable enough.
+        // Only take a screenshot once the screen is stable enough.
         device.waitForIdle();
 
         Bitmap bt = mAutomation.takeScreenshot();
@@ -66,8 +62,8 @@
         // Crop-out the navigation bar to avoid flakiness with button animations.
         int navigationBarHeight = getNavigationBarHeight();
 
-        // Crop the right side for scrollbar which might or might not be visible. On wearable
-        // devices the scroll bar is a curve and occupies 20% of the right side.
+        // Crop-out the right side for the scrollbar which may or may not be visible.
+        // On wearable devices the scroll bar is a curve and occupies 20% of the right side.
         int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20;
 
         bt = Bitmap.createBitmap(
@@ -77,142 +73,45 @@
         return bt;
     }
 
-    void tapOnViewWithText(String searchText) {
-        if (searchText == null) {
-            return;
+    void tapOnViewWithText(String text) {
+        UiObject2 object = getTextObject(text);
+        if (object == null) {
+            throw new AssertionError("View with text '" + text + "' was not found!");
         }
-
-        try {
-            // If the current UI has shown text, just click on it.
-            UiObject text = new UiObject(new UiSelector().text(searchText));
-            if (text.exists() || text.waitForExists(1000)) {
-                text.click();
-                return;
-            }
-
-            // Otherwise, if it is scrollable, scroll to where the text is and tap.
-            UiScrollable textScroll = new UiScrollable(new UiSelector().scrollable(true));
-
-            textScroll.scrollIntoView(new UiSelector().text(searchText));
-            text = new UiObject(new UiSelector().text(searchText));
-            text.click();
-        } catch (UiObjectNotFoundException e) {
-            throw new AssertionError("View with text '" + searchText + "' was not found!", e);
-        }
+        object.click();
     }
 
-    boolean isTextShown(String searchText) {
-        if (searchText == null) {
-            return false;
-        }
-
-        UiObject text = new UiObject(new UiSelector().text(searchText));
-        if (text.exists() || text.waitForExists(1000)) {
-            return true;
-        }
-
-        UiScrollable textScroll = new UiScrollable(new UiSelector().scrollable(true));
-        try {
-            return textScroll.scrollIntoView(new UiSelector().text(searchText));
-        } catch (UiObjectNotFoundException e) {
-            return false;
-        }
+    boolean isTextShown(String text) {
+        return getTextObject(text) != null;
     }
 
     boolean isTextHidden(String text) {
-        UiObject obj = device.findObject(new UiSelector().textMatches(text));
-        if (!obj.exists()) {
-            return true;
-        }
-        return obj.waitUntilGone(1000);
+        return getTextObject(text) == null;
     }
 
     boolean isTextFocused(String text) {
-        UiObject obj = device.findObject(new UiSelector().textMatches(text));
-        try {
-            return obj.isFocused();
-        } catch (UiObjectNotFoundException e) {
-            return false;
-        }
+        UiObject2 object = getTextObject(text);
+        return object != null && object.isFocused();
     }
 
     boolean isOnWatchUiMode() {
-        Context context = mInstrumentation.getTargetContext();
-        UiModeManager uiModeManager = context.getSystemService(UiModeManager.class);
+        UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
         return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_WATCH;
     }
 
-    void getMultiWindowFocus(Context context) {
-        // Get window focus (otherwise back press would close multi-window instead of firing to the
-        // Activity and also the automator would fail to find objects on the screen.
-        // We want to click slightly below status bar in the 1/3 of width of the screen.
-        int x = device.getDisplayWidth() / 3;
-        int resourceId =
-                context.getResources().getIdentifier("status_bar_height", "dimen", "android");
-        int statusBarHeight =
-                (resourceId > 0) ? context.getResources().getDimensionPixelSize(resourceId) : 0;
-        device.click(x, 2 * statusBarHeight);
-    }
-
-    // Multi-window helpers taken from ActivityManagerDockedStackTests.java
-
-    void enterMultiWindow(Activity activity) {
-        try {
-            int id = getActivityTaskId(activity);
-            runShellCommand("am stack move-task " + id + " 3 true");
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to get activity task id!", e);
-        }
-        SystemClock.sleep(5000);
-    }
-
-    void leaveMultiWindow(Activity activity) {
-        try {
-            int id = getActivityTaskId(activity);
-            runShellCommand("am stack move-task " + id + " 1 true");
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to get activity task id!", e);
-        }
-        SystemClock.sleep(5000);
-    }
-
-    private int getActivityTaskId(Activity activity) throws IOException {
-        // Taken from ActivityManagerTestBase.java
-        final String output = runShellCommand("am stack list");
-        final Pattern activityPattern =
-                Pattern.compile("(.*) " + getWindowName(activity) + " (.*)");
-        for (String line : output.split("\\n")) {
-            Matcher matcher = activityPattern.matcher(line);
-            if (matcher.matches()) {
-                for (String word : line.split("\\s+")) {
-                    if (word.startsWith("taskId")) {
-                        final String withColon = word.split("=")[1];
-                        return Integer.parseInt(withColon.substring(0, withColon.length() - 1));
-                    }
-                }
-            }
-        }
-        return -1;
-    }
-
-    private String getWindowName(Activity activity) {
-        String componentName = activity.getPackageName();
-        String baseWindowName = componentName + "/" + componentName + ".";
-        return baseWindowName + activity.getClass().getSimpleName();
-    }
-
     private int getStatusBarHeight() {
         // Cache the result to keep it fast.
         if (mStatusBarHeight >= 0) {
             return mStatusBarHeight;
         }
 
-        mStatusBarHeight = 0;
         int resourceId = mInstrumentation.getTargetContext().getResources()
                 .getIdentifier("status_bar_height", "dimen", "android");
         if (resourceId > 0) {
             mStatusBarHeight = mInstrumentation.getTargetContext().getResources()
                     .getDimensionPixelSize(resourceId);
+        } else {
+            mStatusBarHeight = 0;
         }
         return mStatusBarHeight;
     }
@@ -223,21 +122,19 @@
             return mNavigationBarHeight;
         }
 
-        mNavigationBarHeight = 0;
         int resourceId = mInstrumentation.getTargetContext().getResources()
                 .getIdentifier("navigation_bar_height", "dimen", "android");
         if (resourceId > 0) {
             mNavigationBarHeight = mInstrumentation.getTargetContext().getResources()
                     .getDimensionPixelSize(resourceId);
+        } else {
+            mNavigationBarHeight = 0;
         }
         return mNavigationBarHeight;
     }
 
-    private String runShellCommand(String cmd) {
-        try {
-            return SystemUtil.runShellCommand(mInstrumentation, cmd);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to run command: " + cmd, e);
-        }
+    private UiObject2 getTextObject(String text) {
+        // Wait for up to 1 second to find the object. Returns null if the object cannot be found.
+        return device.wait(Until.findObject(By.text(text).pkg(mPackageName)), 1000);
     }
 }
diff --git a/tests/tests/print/AndroidTest.xml b/tests/tests/print/AndroidTest.xml
index ffcaf65..82679a1 100644
--- a/tests/tests/print/AndroidTest.xml
+++ b/tests/tests/print/AndroidTest.xml
@@ -23,6 +23,17 @@
         <option name="teardown-command" value="cmd print set-bind-instant-service-allowed false" />
     </target_preparer>
 
+    <!-- Create place to store apks -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/print" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+    </target_preparer>
+
+    <!-- Load external print service APK onto device -->
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="push" value="CtsExternalPrintService.apk->/data/local/tmp/cts/print/CtsExternalPrintService.apk" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsPrintTestCases.apk" />
diff --git a/tests/tests/print/ExternalPrintService/Android.mk b/tests/tests/print/ExternalPrintService/Android.mk
new file mode 100644
index 0000000..4cee3b7
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+
+LOCAL_PACKAGE_NAME := CtsExternalPrintService
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/print/ExternalPrintService/AndroidManifest.xml b/tests/tests/print/ExternalPrintService/AndroidManifest.xml
new file mode 100644
index 0000000..ff480c1
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.print.cts.externalservice" >
+
+    <application android:allowBackup="false" >
+        <service
+            android:name=".ExternalService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+          <intent-filter>
+              <action android:name="android.printservice.PrintService" />
+          </intent-filter>
+
+          <meta-data
+              android:name="android.printservice"
+              android:resource="@xml/printservice">
+          </meta-data>
+        </service>
+    </application>
+</manifest>
diff --git a/tests/tests/print/ExternalPrintService/res/xml/printservice.xml b/tests/tests/print/ExternalPrintService/res/xml/printservice.xml
new file mode 100644
index 0000000..a470cc7
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/res/xml/printservice.xml
@@ -0,0 +1,17 @@
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<print-service />
diff --git a/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java b/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java
new file mode 100644
index 0000000..d50f15e
--- /dev/null
+++ b/tests/tests/print/ExternalPrintService/src/android/print/cts/externalservice/ExternalService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.cts.externalservice;
+
+import static android.print.PrinterInfo.STATUS_IDLE;
+
+import static org.junit.Assert.fail;
+
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ExternalService extends PrintService {
+    private static final String PRINTER_NAME = "ExternalServicePrinter";
+
+    @Override
+    protected PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        return new PrinterDiscoverySession() {
+            @Override
+            public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+                addPrinters(Collections.singletonList(
+                        (new PrinterInfo.Builder(generatePrinterId(PRINTER_NAME), PRINTER_NAME,
+                                STATUS_IDLE)).build()));
+            }
+
+            @Override
+            public void onStopPrinterDiscovery() {
+                // empty
+            }
+
+            @Override
+            public void onValidatePrinters(List<PrinterId> printerIds) {
+                // empty
+            }
+
+            @Override
+            public void onStartPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onDestroy() {
+                // empty
+            }
+        };
+    }
+
+    @Override
+    protected void onRequestCancelPrintJob(PrintJob printJob) {
+        fail("This service does not support printing");
+    }
+
+    @Override
+    protected void onPrintJobQueued(PrintJob printJob) {
+        fail("This service does not support printing");
+    }
+}
diff --git a/tests/tests/print/printTestUtilLib/Android.mk b/tests/tests/print/printTestUtilLib/Android.mk
index 358861b..4ed8ab6 100644
--- a/tests/tests/print/printTestUtilLib/Android.mk
+++ b/tests/tests/print/printTestUtilLib/Android.mk
@@ -22,7 +22,12 @@
 
 LOCAL_MODULE := print-test-util-lib
 
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 \
+                               ctstestrunner \
+                               ub-uiautomator \
+                               compatibility-device-util \
+                               android-support-test \
+                               platformprotosnano
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
index 5c40dfc..048768f 100755
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
@@ -64,8 +64,10 @@
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -99,6 +101,7 @@
 import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -189,7 +192,7 @@
         sEnabledImes = null;
     }
 
-    protected static Instrumentation getInstrumentation() {
+    public static Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
 
@@ -205,7 +208,8 @@
         Log.d(LOG_TAG, "disableImes()");
         disableImes();
         Log.d(LOG_TAG, "disablePrintServices()");
-        disablePrintServices(instrumentation.getTargetContext().getPackageName());
+        sDisabledPrintServicesBefore = disablePrintServices(instrumentation.getTargetContext()
+                .getPackageName());
 
         // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
         // Dexmaker is used by mockito.
@@ -219,12 +223,14 @@
      * Disable all print services beside the ones we want to leave enabled.
      *
      * @param packageToLeaveEnabled The package of the services to leave enabled.
+     *
+     * @return Services that were enabled before this method was called
      */
-    private static void disablePrintServices(@NonNull String packageToLeaveEnabled)
+    protected static @NonNull String disablePrintServices(@Nullable String packageToLeaveEnabled)
             throws IOException {
         Instrumentation instrumentation = getInstrumentation();
 
-        sDisabledPrintServicesBefore = SystemUtil.runShellCommand(instrumentation,
+        String previousEnabledServices = SystemUtil.runShellCommand(instrumentation,
                 "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
 
         Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
@@ -233,7 +239,8 @@
 
         StringBuilder builder = new StringBuilder();
         for (ResolveInfo service : installedServices) {
-            if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
+            if (packageToLeaveEnabled != null
+                    && packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
                 continue;
             }
             if (builder.length() > 0) {
@@ -245,15 +252,19 @@
 
         SystemUtil.runShellCommand(instrumentation, "settings put secure "
                 + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
+
+        return previousEnabledServices;
     }
 
     /**
      * Revert {@link #disablePrintServices(String)}
+     *
+     * @param servicesToEnable Services that should be enabled
      */
-    private static  void enablePrintServices() throws IOException {
+    protected static void enablePrintServices(@NonNull String servicesToEnable) throws IOException {
         SystemUtil.runShellCommand(getInstrumentation(),
                 "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
-                        + sDisabledPrintServicesBefore);
+                        + servicesToEnable);
     }
 
     @Before
@@ -315,7 +326,7 @@
         Instrumentation instrumentation = getInstrumentation();
 
         Log.d(LOG_TAG, "enablePrintServices()");
-        enablePrintServices();
+        enablePrintServices(sDisabledPrintServicesBefore);
 
         Log.d(LOG_TAG, "enableImes()");
         enableImes();
@@ -551,62 +562,55 @@
     protected void waitForPrinterUnavailable() throws Exception {
         final String printerUnavailableMessage = "This printer isn\'t available right now.";
 
-        UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/message"));
+        UiObject2 message = getUiDevice().wait(Until.findObject(
+                By.res("com.android.printspooler:id/message")), OPERATION_TIMEOUT_MILLIS);
+
+        if (message == null) {
+            dumpWindowHierarchy();
+            throw new UiObjectNotFoundException("Cannot find " + printerUnavailableMessage);
+        }
         if (!message.getText().equals(printerUnavailableMessage)) {
             throw new Exception("Wrong message: " + message.getText() + " instead of "
                     + printerUnavailableMessage);
         }
     }
 
-    protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
+    protected void selectPrinter(String printerName) throws IOException, UiObjectNotFoundException {
+        selectPrinter(printerName, OPERATION_TIMEOUT_MILLIS);
+    }
+
+    protected void selectPrinter(String printerName, long timeout) throws IOException,
+            UiObjectNotFoundException {
         try {
-            long delay = 1;
-            while (true) {
-                try {
-                    UiDevice uiDevice = getUiDevice();
-                    UiObject destinationSpinner = uiDevice.findObject(new UiSelector()
-                            .resourceId("com.android.printspooler:id/destination_spinner"));
+            UiDevice uiDevice = getUiDevice();
+            UiObject2 destinationSpinner = uiDevice.wait(Until.findObject(
+                    By.res("com.android.printspooler:id/destination_spinner")), timeout);
 
-                    destinationSpinner.click();
-                    getUiDevice().waitForIdle();
-
-                    // Give spinner some time to expand
-                    try {
-                        Thread.sleep(delay);
-                    } catch (InterruptedException e) {
-                        // ignore
-                    }
-
-                    // try to select printer
-                    UiObject printerOption = uiDevice.findObject(
-                            new UiSelector().text(printerName));
-                    printerOption.click();
-                } catch (UiObjectNotFoundException e) {
-                    Log.e(LOG_TAG, "Could not select printer " + printerName, e);
-                }
-
+            if (destinationSpinner != null) {
+                destinationSpinner.click();
                 getUiDevice().waitForIdle();
-
-                if (!printerName.equals("All printers…")) {
-                    // Make sure printer is selected
-                    if (getUiDevice().hasObject(By.text(printerName))) {
-                        break;
-                    } else {
-                        if (delay <= OPERATION_TIMEOUT_MILLIS) {
-                            Log.w(LOG_TAG, "Cannot find printer " + printerName + ", retrying.");
-                            delay *= 2;
-                        } else {
-                            throw new UiObjectNotFoundException(
-                                    "Could find printer " + printerName
-                                            + " even though we retried");
-                        }
-                    }
-                } else {
-                    break;
-                }
             }
-        } catch (UiObjectNotFoundException e) {
+
+            selectPrinterSpinnerOpen(printerName, timeout);
+        } catch (Exception e) {
+            dumpWindowHierarchy();
+            throw e;
+        }
+    }
+
+    protected void selectPrinterSpinnerOpen(String printerName, long timeout)
+            throws IOException, UiObjectNotFoundException {
+        try {
+            UiDevice uiDevice = getUiDevice();
+            UiObject2 printerOption = uiDevice.wait(Until.findObject(By.text(printerName)),
+                    timeout);
+            if (printerOption == null) {
+                throw new UiObjectNotFoundException(printerName + " not found");
+            }
+
+            printerOption.click();
+            getUiDevice().waitForIdle();
+        } catch (Exception e) {
             dumpWindowHierarchy();
             throw e;
         }
@@ -728,14 +732,15 @@
     }
 
     public void clickPrintButton() throws UiObjectNotFoundException, IOException {
-        try {
-            UiObject printButton = getUiDevice().findObject(new UiSelector().resourceId(
-                    "com.android.printspooler:id/print_button"));
-            printButton.click();
-        } catch (UiObjectNotFoundException e) {
+        getUiDevice().waitForIdle();
+
+        UiObject2 printButton = getUiDevice().wait(Until.findObject(By.res(
+                "com.android.printspooler:id/print_button")), OPERATION_TIMEOUT_MILLIS);
+        if (printButton == null) {
             dumpWindowHierarchy();
-            throw e;
+            throw new UiObjectNotFoundException("print button not found");
         }
+        printButton.click();
     }
 
     protected void clickRetryButton() throws UiObjectNotFoundException, IOException {
@@ -864,9 +869,7 @@
 
                     callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
                             .setPageCount(numPages).build(),
-                            !oldAttributes.equals(printAttributes[0]));
-
-                    oldAttributes = printAttributes[0];
+                            !Objects.equals(oldAttributes, printAttributes[0]));
 
                     onLayoutCalled();
                     return null;
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
index 7e40bad..b7f9fe6 100644
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
@@ -16,14 +16,23 @@
 
 package android.print.test;
 
+import static android.print.test.BasePrintTest.getInstrumentation;
+
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor;
 import android.print.PrintJob;
 import android.print.PrintManager;
-import androidx.annotation.NonNull;
+import android.service.print.nano.PrintServiceDumpProto;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Utilities for print tests
  */
@@ -97,16 +106,18 @@
      * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
      *
      * @param r The {@link Invokable} to run.
+     * @param timeout the maximum time to wait
      */
-    public static void eventually(@NonNull Invokable r) throws Throwable {
-        long start = System.currentTimeMillis();
+    public static void eventually(@NonNull Invokable r, long timeout) throws Throwable {
+        long start = System.nanoTime();
 
         while (true) {
             try {
                 r.run();
                 break;
             } catch (Throwable e) {
-                if (System.currentTimeMillis() - start < BasePrintTest.OPERATION_TIMEOUT_MILLIS) {
+                if (System.nanoTime() - start < TimeUnit.NANOSECONDS.convert(timeout,
+                        TimeUnit.MILLISECONDS)) {
                     Log.e(LOG_TAG, "Ignoring exception", e);
 
                     try {
@@ -122,6 +133,15 @@
     }
 
     /**
+     * Make sure that a {@link Invokable} eventually finishes without throwing a {@link Throwable}.
+     *
+     * @param r The {@link Invokable} to run.
+     */
+    public static void eventually(@NonNull Invokable r) throws Throwable {
+        eventually(r, BasePrintTest.OPERATION_TIMEOUT_MILLIS);
+    }
+
+    /**
      * @param name Name of print job
      *
      * @return The print job for the name
@@ -145,4 +165,30 @@
     public static @NonNull PrintManager getPrintManager(@NonNull Context context) {
         return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
     }
+
+    /**
+     * Get the {@link PrintServiceDumpProto}
+     */
+    public static PrintServiceDumpProto getProtoDump() throws Exception {
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand("dumpsys print --proto");
+
+        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+                byte[] buffer = new byte[16384];
+
+                while (true) {
+                    int numRead = is.read(buffer);
+
+                    if (numRead == -1) {
+                        break;
+                    } else {
+                        os.write(buffer, 0, numRead);
+                    }
+                }
+            }
+
+            return PrintServiceDumpProto.parseFrom(os.toByteArray());
+        }
+    }
 }
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
index 09d1f78..cc82d04 100644
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
@@ -17,13 +17,16 @@
 package android.print.test.services;
 
 import android.content.Context;
+import android.print.PrinterId;
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.printservice.PrinterDiscoverySession;
+import android.util.Log;
 
 import java.util.List;
 
 public abstract class StubbablePrintService extends PrintService {
+    private static final String LOG_TAG = StubbablePrintService.class.getSimpleName();
 
     @Override
     public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
@@ -32,7 +35,39 @@
             return new StubbablePrinterDiscoverySession(this,
                     getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
         }
-        return null;
+
+        Log.w(LOG_TAG, "onCreatePrinterDiscoverySession called but no callbacks are set up");
+        return new PrinterDiscoverySession() {
+            @Override
+            public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterDiscovery() {
+                // empty
+            }
+
+            @Override
+            public void onValidatePrinters(List<PrinterId> printerIds) {
+                // empty
+            }
+
+            @Override
+            public void onStartPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onStopPrinterStateTracking(PrinterId printerId) {
+                // empty
+            }
+
+            @Override
+            public void onDestroy() {
+                // empty
+            }
+        };
     }
 
     @Override
diff --git a/tests/tests/print/src/android/print/cts/InstallBehavior.java b/tests/tests/print/src/android/print/cts/InstallBehavior.java
new file mode 100644
index 0000000..ea0f399
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/InstallBehavior.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.cts;
+
+import static org.junit.Assert.fail;
+
+import android.print.PrintManager;
+import android.print.test.BasePrintTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Behavior of Android when a print service is installed
+ */
+@RunWith(AndroidJUnit4.class)
+public class InstallBehavior extends BasePrintTest {
+    private static String sPreviousEnabledServices;
+
+    @BeforeClass
+    public static void disableAllPrintServices() throws Exception {
+        sPreviousEnabledServices = disablePrintServices(null);
+    }
+
+    @Before
+    @After
+    public void uninstallExternalPrintService() throws Exception {
+        SystemUtil.runShellCommand(getInstrumentation(),
+                "pm uninstall android.print.cts.externalservice");
+    }
+
+    @AfterClass
+    public static void reenablePrintServices() throws Exception {
+        enablePrintServices(sPreviousEnabledServices);
+    }
+
+    /**
+     * Printers from a newly installed print service should show up immediately.
+     *
+     * <p>This tests:
+     * <ul>
+     *     <li>Print services are enabled by default</li>
+     *     <li>Print services get added to already running printer discovery sessions</li>
+     * </ul>
+     */
+    @Test
+    public void installedServiceIsEnabled() throws Exception {
+        getActivity().getSystemService(PrintManager.class).print("printjob",
+                createDefaultPrintDocumentAdapter(1), null);
+
+        waitForWriteAdapterCallback(1);
+
+        // Printer should not be available
+        try {
+            selectPrinter("ExternalServicePrinter", 500);
+            fail();
+        } catch (UiObjectNotFoundException expected) {
+            // expected
+        }
+
+        SystemUtil.runShellCommand(getInstrumentation(),
+                "pm install /data/local/tmp/cts/print/CtsExternalPrintService.apk");
+
+        selectPrinterSpinnerOpen("ExternalServicePrinter", OPERATION_TIMEOUT_MILLIS);
+
+        // Exit print preview and thereby end printing
+        getUiDevice().pressBack();
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index 706f971..4b45fea 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -1757,7 +1757,13 @@
                     // Mark layout called.
                     onLayoutCalled();
                     return null;
-                }, null, invocation -> {
+                }, invocation -> {
+                    WriteResultCallback callback = (WriteResultCallback) invocation
+                            .getArguments()[3];
+                    callback.onWriteFailed(null);
+                    onWriteCalled();
+                    return null;
+                }, invocation -> {
                     // Mark finish was called.
                     onFinishCalled();
                     return null;
@@ -1768,6 +1774,7 @@
 
         // Wait for layout.
         waitForLayoutAdapterCallbackCount(1);
+        waitForWriteAdapterCallback(1);
 
         // Cancel printing.
         getUiDevice().pressBack(); // wakes up the device.
@@ -1855,6 +1862,7 @@
 
         // Wait for layout.
         waitForLayoutAdapterCallbackCount(1);
+        waitForWriteAdapterCallback(1);
 
         // Cancel printing.
         getUiDevice().pressBack(); // wakes up the device.
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
index 63e83e3..c8849fa 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
@@ -107,9 +107,12 @@
                 invocation -> printerDiscoverySessionCallbacks,
                 invocation -> {
                     PrintJob printJob = (PrintJob) invocation.getArguments()[0];
-                    queuedInfo[0] = printJob.getDocument().getInfo();
-                    queuedData[0] = printJob.getDocument().getData();
+                    synchronized (queuedInfo) {
+                        queuedInfo[0] = printJob.getDocument().getInfo();
+                        queuedData[0] = printJob.getDocument().getData();
+                    }
                     printJob.complete();
+                    onPrintJobQueuedCalled();
                     return null;
                 }, null);
 
@@ -127,12 +130,15 @@
                     return null;
                 }, invocation -> {
                     Object[] args = invocation.getArguments();
-                    PageRange[] pages = (PageRange[]) args[0];
                     ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
                     WriteResultCallback callback = (WriteResultCallback) args[3];
                     writeBlankPages(printAttributes[0], fd, 0, 1);
                     fd.close();
-                    callback.onWriteFinished(pages);
+                    if (pageCount != null && pageCount > 0) {
+                        callback.onWriteFinished(new PageRange[]{new PageRange(0, pageCount - 1)});
+                    } else {
+                        callback.onWriteFinished(new PageRange[]{new PageRange(0, 1)});
+                    }
                     onWriteCalled();
                     return null;
                 }, invocation -> null);
@@ -148,9 +154,14 @@
 
         // Wait for the session to be destroyed to isolate tests.
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        waitForServiceOnPrintJobQueuedCallbackCalled(1);
 
         // Check that the document name was carried over 1:1
-        eventually(() -> assertEquals(name, queuedInfo[0].getName()));
+        eventually(() -> {
+            synchronized (queuedInfo) {
+                assertEquals(name, queuedInfo[0].getName());
+            }
+        });
 
         // Content type is set to document by default, but is otherwise unrestricted
         if (contentType != null) {
@@ -191,7 +202,7 @@
      */
     @Test
     public void documentInfoNothingSet() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, null);
+        printDocumentBaseTest("documentInfoNothingSet", null, null);
     }
 
     /**
@@ -201,7 +212,8 @@
      */
     @Test
     public void documentInfoUnknownPageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
+        printDocumentBaseTest("documentInfoUnknownPageCount", null,
+                PrintDocumentInfo.PAGE_COUNT_UNKNOWN);
     }
 
     /**
@@ -211,7 +223,7 @@
      */
     @Test
     public void documentInfoZeroPageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 0);
+        printDocumentBaseTest("documentInfoZeroPageCount", null, 0);
     }
 
     /**
@@ -221,7 +233,7 @@
      */
     @Test
     public void documentInfoOnePageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 1);
+        printDocumentBaseTest("documentInfoOnePageCount", null, 1);
     }
 
     /**
@@ -231,7 +243,7 @@
      */
     @Test
     public void documentInfoThreePageCount() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, null, 3);
+        printDocumentBaseTest("documentInfoThreePageCount", null, 3);
     }
 
     /**
@@ -241,7 +253,8 @@
      */
     @Test
     public void documentInfoContentTypePhoto() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_PHOTO, null);
+        printDocumentBaseTest("documentInfoContentTypePhoto", PrintDocumentInfo.CONTENT_TYPE_PHOTO,
+                null);
     }
 
     /**
@@ -251,7 +264,8 @@
      */
     @Test
     public void documentInfoContentTypeUnknown() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, null);
+        printDocumentBaseTest("documentInfoContentTypeUnknown",
+                PrintDocumentInfo.CONTENT_TYPE_UNKNOWN, null);
     }
 
     /**
@@ -261,7 +275,7 @@
      */
     @Test
     public void documentInfoContentTypeNonDefined() throws Throwable {
-        printDocumentBaseTest(PRINT_JOB_NAME, -23, null);
+        printDocumentBaseTest("documentInfoContentTypeNonDefined", -23, null);
     }
 
     private PrinterDiscoverySessionCallbacks createFirstMockDiscoverySessionCallbacks() {
diff --git a/tests/tests/print/src/android/print/cts/PrintJobTest.java b/tests/tests/print/src/android/print/cts/PrintJobTest.java
index 2ab52b3..4f99f7a 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobTest.java
@@ -147,8 +147,7 @@
      *
      * @throws Exception If anything is unexpected.
      */
-    private void baseTest(PrintJobTestFn testFn)
-            throws Exception {
+    private void baseTest(PrintJobTestFn testFn) throws Throwable {
         testSuccess[0] = false;
 
         // Create the session of the printers that we will be checking.
@@ -168,12 +167,17 @@
         // Create a print adapter that respects the print contract.
         PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
 
+
         // Start printing.
         print(adapter);
-        clickPrintButton();
+        waitForWriteAdapterCallback(1);
 
-        // Wait for print job to be queued
-        waitForServiceOnPrintJobQueuedCallbackCalled(1);
+        eventually(() -> {
+            clickPrintButton();
+
+            // Wait for print job to be queued
+            waitForServiceOnPrintJobQueuedCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
 
         // Wait for discovery session to be destroyed to isolate tests from each other
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
@@ -239,7 +243,7 @@
     }
 
     @Test
-    public void blockWithReason() throws Exception {
+    public void blockWithReason() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
             checkState(printJob, PrintJobInfo.STATE_STARTED);
@@ -267,7 +271,7 @@
     }
 
     @Test
-    public void failWithReason() throws Exception {
+    public void failWithReason() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
             checkState(printJob, PrintJobInfo.STATE_STARTED);
@@ -287,7 +291,7 @@
     }
 
     @Test
-    public void tag() throws Exception {
+    public void tag() throws Throwable {
         baseTest(printJob -> {
             // Default value should be null
             assertNull(printJob.getTag());
@@ -451,7 +455,7 @@
     }
 
     @Test
-    public void other() throws Exception {
+    public void other() throws Throwable {
         baseTest(printJob -> {
             assertNotNull(printJob.getDocument());
             assertNotNull(printJob.getId());
@@ -459,7 +463,7 @@
     }
 
     @Test
-    public void setStatus() throws Exception {
+    public void setStatus() throws Throwable {
         baseTest(printJob -> {
             printJob.start();
 
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index b5a23e2..86a4e3f 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -60,9 +60,13 @@
 import android.printservice.PrintJob;
 import android.printservice.PrintService;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+
+import androidx.annotation.NonNull;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -86,6 +90,16 @@
     /** The custom printer icon to use */
     private Icon mIcon;
 
+    private @NonNull PrinterCapabilitiesInfo getDefaultOptionPrinterCapabilites(
+            @NonNull PrinterId printerId) {
+        return new PrinterCapabilitiesInfo.Builder(printerId)
+                .setMinMargins(new Margins(200, 200, 200, 200))
+                .addMediaSize(MediaSize.ISO_A4, true)
+                .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                        PrintAttributes.COLOR_MODE_COLOR).build();
+    }
+
     /**
      * Create a mock {@link PrinterDiscoverySessionCallbacks} that discovers a single printer with
      * minimal capabilities.
@@ -93,7 +107,7 @@
      * @return The mock session callbacks
      */
     private PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
-            String printerName, ArrayList<String> trackedPrinters) {
+            String printerName) {
         return createMockPrinterDiscoverySessionCallbacks(invocation -> {
             // Get the session.
             StubbablePrinterDiscoverySession session =
@@ -106,25 +120,16 @@
                 PrinterId printerId = session.getService()
                         .generatePrinterId(printerName);
 
-                PrinterCapabilitiesInfo capabilities = new PrinterCapabilitiesInfo.Builder(
-                        printerId)
-                        .setMinMargins(new Margins(200, 200, 200, 200))
-                        .addMediaSize(MediaSize.ISO_A4, true)
-                        .addResolution(new Resolution("300x300", "300x300", 300, 300),
-                                true)
-                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
-                                PrintAttributes.COLOR_MODE_COLOR)
-                        .build();
-
                 Intent infoIntent = new Intent(getActivity(), InfoActivity.class);
                 infoIntent.putExtra("PRINTER_NAME", PRINTER_NAME);
 
-                PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
+                PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(),
+                        0,
                         infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
                 sPrinter = new PrinterInfo.Builder(printerId, printerName,
                         PrinterInfo.STATUS_IDLE)
-                        .setCapabilities(capabilities)
+                        .setCapabilities(getDefaultOptionPrinterCapabilites(printerId))
                         .setDescription("Minimal capabilities")
                         .setInfoIntent(infoPendingIntent)
                         .build();
@@ -136,16 +141,7 @@
             onPrinterDiscoverySessionCreateCalled();
 
             return null;
-        }, null, null, invocation -> {
-            if (trackedPrinters != null) {
-                synchronized (trackedPrinters) {
-                    trackedPrinters
-                            .add(((PrinterId) invocation.getArguments()[0]).getLocalId());
-                    trackedPrinters.notifyAll();
-                }
-            }
-            return null;
-        }, invocation -> {
+        }, null, null, null, invocation -> {
             CustomPrinterIconCallback callback = (CustomPrinterIconCallback) invocation
                     .getArguments()[2];
 
@@ -153,16 +149,7 @@
                 callback.onCustomPrinterIconLoaded(mIcon);
             }
             return null;
-        }, invocation -> {
-            if (trackedPrinters != null) {
-                synchronized (trackedPrinters) {
-                    trackedPrinters.remove(((PrinterId) invocation.getArguments()[0]).getLocalId());
-                    trackedPrinters.notifyAll();
-                }
-            }
-
-            return null;
-        }, invocation -> {
+        }, null, invocation -> {
             // Take a note onDestroy was called.
             onPrinterDiscoverySessionDestroyCalled();
             return null;
@@ -276,8 +263,8 @@
     @Test
     public void progress() throws Throwable {
         // Create the session callbacks that we will be checking.
-        PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -382,8 +369,8 @@
     @Test
     public void updateIcon() throws Throwable {
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -446,8 +433,8 @@
     @Test
     public void cannotUseAttachBaseContext() throws Throwable {
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, null);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME);
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -490,15 +477,15 @@
             PrintManager pm = (PrintManager) getActivity().getSystemService(Context.PRINT_SERVICE);
 
             // Configure first print service
-            PrinterDiscoverySessionCallbacks sessionCallbacks1
-                    = createMockPrinterDiscoverySessionCallbacks("Printer1", null);
+            PrinterDiscoverySessionCallbacks sessionCallbacks1 =
+                    createMockPrinterDiscoverySessionCallbacks("Printer1");
             PrintServiceCallbacks serviceCallbacks1 = createMockPrinterServiceCallbacks(
                     sessionCallbacks1);
             FirstPrintService.setCallbacks(serviceCallbacks1);
 
             // Configure second print service
-            PrinterDiscoverySessionCallbacks sessionCallbacks2
-                    = createMockPrinterDiscoverySessionCallbacks("Printer2", null);
+            PrinterDiscoverySessionCallbacks sessionCallbacks2 =
+                    createMockPrinterDiscoverySessionCallbacks("Printer2");
             PrintServiceCallbacks serviceCallbacks2 = createMockPrinterServiceCallbacks(
                     sessionCallbacks2);
             SecondPrintService.setCallbacks(serviceCallbacks2);
@@ -587,8 +574,60 @@
         ArrayList<String> trackedPrinters = new ArrayList<>();
 
         // Create the session callbacks that we will be checking.
-        final PrinterDiscoverySessionCallbacks sessionCallbacks
-                = createMockPrinterDiscoverySessionCallbacks(PRINTER_NAME, trackedPrinters);
+        final PrinterDiscoverySessionCallbacks sessionCallbacks =
+                createMockPrinterDiscoverySessionCallbacks(invocation -> {
+                    // Get the session.
+                    StubbablePrinterDiscoverySession session =
+                            ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+                    PrinterId printer1Id = session.getService().generatePrinterId("Printer1");
+
+                    PrinterInfo printer1 = new PrinterInfo.Builder(printer1Id, "Printer1",
+                            PrinterInfo.STATUS_IDLE).setCapabilities(
+                            getDefaultOptionPrinterCapabilites(printer1Id)).build();
+
+                    PrinterId printer2Id = session.getService().generatePrinterId("Printer2");
+
+                    Intent infoIntent = new Intent(getActivity(), InfoActivity.class);
+                    infoIntent.putExtra("PRINTER_NAME", "Printer2");
+
+                    PendingIntent infoPendingIntent = PendingIntent.getActivity(getActivity(), 0,
+                            infoIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                    PrinterInfo printer2 = new PrinterInfo.Builder(printer2Id, "Printer2",
+                            PrinterInfo.STATUS_IDLE)
+                            .setInfoIntent(infoPendingIntent)
+                            .setCapabilities(getDefaultOptionPrinterCapabilites(printer2Id))
+                            .build();
+
+                    List<PrinterInfo> printers = new ArrayList<>();
+                    printers.add(printer1);
+                    printers.add(printer2);
+                    session.addPrinters(printers);
+
+                    onPrinterDiscoverySessionCreateCalled();
+
+                    return null;
+                }, null, null, invocation -> {
+                    synchronized (trackedPrinters) {
+                        trackedPrinters.add(
+                                ((PrinterId) invocation.getArguments()[0]).getLocalId());
+                        trackedPrinters.notifyAll();
+                    }
+
+                    return null;
+                }, null, invocation -> {
+                    synchronized (trackedPrinters) {
+                        trackedPrinters.remove(
+                                ((PrinterId) invocation.getArguments()[0]).getLocalId());
+                        trackedPrinters.notifyAll();
+                    }
+
+                    return null;
+                }, invocation -> {
+                    onPrinterDiscoverySessionDestroyCalled();
+                    return null;
+                });
 
         // Create the service callbacks for the first print service.
         PrintServiceCallbacks serviceCallbacks = createMockPrinterServiceCallbacks(
@@ -606,34 +645,52 @@
         // Start printing.
         print(adapter);
 
+        selectPrinter("Printer1");
+
+        eventually(() -> {
+            synchronized (trackedPrinters) {
+                assertFalse(trackedPrinters.contains("Printer2"));
+            }
+        });
+
         // Enter select printer activity
         selectPrinter("All printers…");
 
-        assertFalse(trackedPrinters.contains(PRINTER_NAME));
+        try {
+            InfoActivity.addObserver(activity -> {
+                Intent intent = activity.getIntent();
 
-        InfoActivity.addObserver(activity -> {
-            Intent intent = activity.getIntent();
+                assertEquals("Printer2", intent.getStringExtra("PRINTER_NAME"));
+                assertTrue(intent.getBooleanExtra(PrintService.EXTRA_CAN_SELECT_PRINTER,
+                        false));
 
-            assertEquals(PRINTER_NAME, intent.getStringExtra("PRINTER_NAME"));
-            assertTrue(intent.getBooleanExtra(PrintService.EXTRA_CAN_SELECT_PRINTER,
-                            false));
+                activity.setResult(Activity.RESULT_OK,
+                        (new Intent()).putExtra(PrintService.EXTRA_SELECT_PRINTER, true));
+                activity.finish();
+            });
 
-            activity.setResult(Activity.RESULT_OK,
-                    (new Intent()).putExtra(PrintService.EXTRA_SELECT_PRINTER, true));
-            activity.finish();
-        });
+            try {
+                // Wait until printer is selected and thereby tracked
+                eventually(() -> {
+                    getUiDevice().waitForIdle();
+                    // Open info activity which executes the code above
+                    getUiDevice().wait(
+                            Until.findObject(By.res("com.android.printspooler:id/more_info")),
+                            OPERATION_TIMEOUT_MILLIS).click();
 
-        // Open info activity which executed the code above
-        UiObject moreInfoButton = getUiDevice().findObject(new UiSelector().resourceId(
-                "com.android.printspooler:id/more_info"));
-        moreInfoButton.click();
+                    eventually(() -> {
+                        synchronized (trackedPrinters) {
+                            assertTrue(trackedPrinters.contains("Printer2"));
+                        }
+                    }, OPERATION_TIMEOUT_MILLIS  / 2);
+                }, OPERATION_TIMEOUT_MILLIS * 2);
+            } finally {
+                InfoActivity.clearObservers();
+            }
+        } finally {
+            getUiDevice().pressBack();
+        }
 
-        // Wait until printer is selected and thereby tracked
-        eventually(() -> assertTrue(trackedPrinters.contains(PRINTER_NAME)));
-
-        InfoActivity.clearObservers();
-
-        getUiDevice().pressBack();
         getUiDevice().pressBack();
         waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
     }
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 2531f80..3ace7b7 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -41,11 +41,12 @@
 import android.print.test.services.StubbablePrinterDiscoverySession;
 import android.printservice.PrintJob;
 import android.printservice.PrinterDiscoverySession;
-import androidx.annotation.NonNull;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
 
+import androidx.annotation.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -185,8 +186,11 @@
 
         // Wait for preview to load and finish print
         waitForWriteAdapterCallback(1);
-        clickPrintButton();
-        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+        eventually(() -> {
+            clickPrintButton();
+            waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
     }
 
     @Test
@@ -234,8 +238,11 @@
 
         // Wait for preview to load and finish print
         waitForWriteAdapterCallback(1);
-        clickPrintButton();
-        waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+
+        eventually(() -> {
+            clickPrintButton();
+            waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+        }, OPERATION_TIMEOUT_MILLIS * 2);
     }
 
     @Test
diff --git a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
index d2cc04a..cd30602 100755
--- a/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
+++ b/tests/tests/shortcutmanager/packages/packagemanifest_nonshared/AndroidManifest.xml
@@ -23,6 +23,14 @@
 
         <activity android:name="Launcher" android:enabled="true" android:exported="false">
         </activity>
+
+        <activity-alias android:name="HomeActivity"
+            android:targetActivity="Launcher">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
 
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerFakingPublisherTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerFakingPublisherTest.java
new file mode 100644
index 0000000..baa47b0
--- /dev/null
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerFakingPublisherTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.cts.shortcutmanager;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.runCommand;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ShortcutInfo;
+import android.platform.test.annotations.SecurityTest;
+import android.support.test.InstrumentationRegistry;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Assume;
+
+/**
+ * CTS for b/109824443.
+ */
+@SmallTest
+@SecurityTest
+public class ShortcutManagerFakingPublisherTest extends ShortcutManagerCtsTestsBase {
+    private static final String ANOTHER_PACKAGE =
+            "android.content.pm.cts.shortcutmanager.packages.package4";
+
+    private static final ComponentName ANOTHER_HOME_ACTIVITY = new ComponentName(
+            ANOTHER_PACKAGE, "android.content.pm.cts.shortcutmanager.packages.HomeActivity");
+
+    private static final String INVALID_ID =
+            "[ShortcutManagerFakingPublisherTest.shortcut_that_should_not_be_created]";
+
+    @Override
+    protected String getOverrideConfig() {
+        return "reset_interval_sec=999999,"
+                + "max_updates_per_interval=999999,"
+                + "max_shortcuts=10"
+                + "max_icon_dimension_dp=96,"
+                + "max_icon_dimension_dp_lowram=96,"
+                + "icon_format=PNG,"
+                + "icon_quality=100";
+    }
+
+    public void testSpoofingPublisher() {
+        final Context myContext = getTestContext();
+        final Context anotherContext;
+        try {
+            anotherContext = getTestContext().createPackageContext(ANOTHER_PACKAGE, 0);
+        } catch (NameNotFoundException e) {
+            fail("Unable to create package context for " + ANOTHER_PACKAGE);
+            return;
+        }
+        final ShortcutInfo invalid = new ShortcutInfo.Builder(anotherContext, INVALID_ID)
+                .setShortLabel(INVALID_ID)
+                .setIntent(new Intent(Intent.ACTION_VIEW))
+                .setActivity(ANOTHER_HOME_ACTIVITY)
+                .build();
+
+        // Check set.
+        runWithCaller(mPackageContext1, () -> {
+            getManager().removeAllDynamicShortcuts();
+
+            assertShortcutPackageMismatch("setDynamicShortcuts1", mPackageContext1, () -> {
+                getManager().setDynamicShortcuts(list(
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("setDynamicShortcuts2A", mPackageContext1, () -> {
+                getManager().setDynamicShortcuts(list(
+                        invalid,
+                        makeShortcut("s1", "title1")));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("setDynamicShortcuts2B", mPackageContext1, () -> {
+                getManager().setDynamicShortcuts(list(
+                        makeShortcut("s1", "title1"),
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+        });
+
+        // Check add.
+        runWithCaller(mPackageContext1, () -> {
+            getManager().removeAllDynamicShortcuts();
+
+            assertShortcutPackageMismatch("addDynamicShortcuts1", mPackageContext1, () -> {
+                getManager().addDynamicShortcuts(list(
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("addDynamicShortcuts2A", mPackageContext1, () -> {
+                getManager().addDynamicShortcuts(list(
+                        invalid,
+                        makeShortcut("s1", "title1")));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("addDynamicShortcuts2B", mPackageContext1, () -> {
+                getManager().addDynamicShortcuts(list(
+                        makeShortcut("s1", "title1"),
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+        });
+
+        // Check update.
+        runWithCaller(mPackageContext1, () -> {
+            getManager().removeAllDynamicShortcuts();
+
+            assertShortcutPackageMismatch("updateShortcuts1", mPackageContext1, () -> {
+                getManager().updateShortcuts(list(
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("updateShortcuts2A", mPackageContext1, () -> {
+                getManager().updateShortcuts(list(
+                        invalid,
+                        makeShortcut("s1", "title1")));
+            });
+            assertInvalidShortcutNotCreated();
+            assertShortcutPackageMismatch("updateShortcuts2B", mPackageContext1, () -> {
+                getManager().updateShortcuts(list(
+                        makeShortcut("s1", "title1"),
+                        invalid));
+            });
+            assertInvalidShortcutNotCreated();
+        });
+
+        // requestPin (API26 and above)
+        runWithCaller(mPackageContext1, () -> {
+            getManager().removeAllDynamicShortcuts();
+
+            assertShortcutPackageMismatch("requestPinShortcut", mPackageContext1, () -> {
+                getManager().requestPinShortcut(invalid, null);
+            });
+            assertInvalidShortcutNotCreated();
+        });
+
+        // createShortcutResultIntent (API26 and above)
+        runWithCaller(mPackageContext1, () -> {
+            getManager().removeAllDynamicShortcuts();
+
+            assertShortcutPackageMismatch("createShortcutResultIntent", mPackageContext1, () -> {
+                getManager().createShortcutResultIntent(invalid);
+            });
+            assertInvalidShortcutNotCreated();
+        });
+    }
+
+    private void assertInvalidShortcutNotCreated() {
+        for (String s : runCommand(InstrumentationRegistry.getInstrumentation(),
+                "dumpsys shortcut")) {
+            assertFalse("dumpsys shortcut contained invalid ID", s.contains(INVALID_ID));
+        }
+    }
+
+    private void assertShortcutPackageMismatch(String method, Context callerContext, Runnable r) {
+        assertExpectException(
+                "Caller=" + callerContext.getPackageName() + ", method=" + method,
+                SecurityException.class, "Shortcut package name mismatch",
+                () -> runWithCaller(callerContext, () -> r.run())
+        );
+    }
+}
diff --git a/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java b/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
index 53b13cb..b6c162b 100644
--- a/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
+++ b/tests/tests/slice/src/android/slice/cts/LocalSliceProvider.java
@@ -47,7 +47,7 @@
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
         mSliceService = mock(SliceManager.class, withSettings()
-                .spiedInstance(context.getSystemService(Context.SLICE_SERVICE))
+                .spiedInstance(context.getSystemService(SliceManager.class))
                 .defaultAnswer(invocation -> {
                     Answer s = sAnswer != null ? sAnswer : Answers.CALLS_REAL_METHODS;
                     return s.answer(invocation);
@@ -55,7 +55,7 @@
         Context wrapped = new ContextWrapper(context) {
             @Override
             public Object getSystemService(String name) {
-                if (Context.SLICE_SERVICE.equals(name)) {
+                if (getSystemServiceName(SliceManager.class).equals(name)) {
                     return mSliceService;
                 }
                 return super.getSystemService(name);
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
index 7323f30..901b47d 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -16,14 +16,23 @@
 
 package android.telephony.cts;
 
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OPSTR_READ_PHONE_STATE;
+
+import static com.android.compatibility.common.util.AppOpsUtils.setOpMode;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.os.PersistableBundle;
+import android.platform.test.annotations.SecurityTest;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 
+import java.io.IOException;
+
 public class CarrierConfigManagerTest extends AndroidTestCase {
     private CarrierConfigManager mConfigManager;
     private TelephonyManager mTelephonyManager;
@@ -37,6 +46,16 @@
                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_ALLOWED);
+        } catch (IOException e) {
+            fail();
+        }
+        super.tearDown();
+    }
+
     /**
      * Checks whether the telephony stack should be running on this device.
      *
@@ -87,6 +106,29 @@
         checkConfig(config);
     }
 
+    @SecurityTest
+    public void testRevokePermission() {
+        PersistableBundle config;
+
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_IGNORED);
+        } catch (IOException e) {
+            fail();
+        }
+
+        config = mConfigManager.getConfig();
+        assertTrue(config.isEmptyParcel());
+
+        try {
+            setOpMode("android.telephony.cts", OPSTR_READ_PHONE_STATE, MODE_ALLOWED);
+        } catch (IOException e) {
+            fail();
+        }
+
+        config = mConfigManager.getConfig();
+        checkConfig(config);
+    }
+
     public void testGetConfigForSubId() {
         PersistableBundle config =
                 mConfigManager.getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
diff --git a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
index 9491eff..7b42687 100644
--- a/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
+++ b/tests/tests/text/src/android/text/cts/PrecomputedTextTest.java
@@ -26,6 +26,8 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.Typeface;
@@ -43,6 +45,10 @@
 import android.text.style.BackgroundColorSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -620,4 +626,82 @@
         PrecomputedText.create("a\nb", param).getBounds(0, 3, rect);
     }
 
+    private static Bitmap drawToBitmap(@NonNull CharSequence cs,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @IntRange(from = 0) int ctxStart, @IntRange(from = 0) int ctxEnd,
+            @NonNull TextPaint paint) {
+
+        Rect rect = new Rect();
+        paint.getTextBounds(cs.toString(), start, end, rect);
+        final Bitmap bmp = Bitmap.createBitmap(rect.width(),
+                rect.height(), Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(bmp);
+        c.save();
+        c.translate(0, 0);
+        c.drawTextRun(cs, start, end, ctxStart, ctxEnd, 0, 0, false /* isRtl */, paint);
+        c.restore();
+        return bmp;
+    }
+
+    private static void assertSameOutput(@NonNull CharSequence cs,
+            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @IntRange(from = 0) int ctxStart, @IntRange(from = 0) int ctxEnd,
+            @NonNull TextPaint paint) {
+        final Params params = new Params.Builder(paint).build();
+        final PrecomputedText pt = PrecomputedText.create(cs, params);
+
+        final Bitmap originalDrawOutput = drawToBitmap(cs, start, end, ctxStart, ctxEnd, paint);
+        final Bitmap precomputedDrawOutput = drawToBitmap(pt, start, end, ctxStart, ctxEnd, paint);
+        assertTrue(originalDrawOutput.sameAs(precomputedDrawOutput));
+    }
+
+    @Test
+    public void testDrawText() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World");
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, ssb.length() - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 5, ssb.length() - 5, 2, ssb.length() - 2, paint);
+    }
+
+    @Test
+    public void testDrawText_MultiStyle() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World");
+        ssb.setSpan(new TypefaceSpan("serif"), 0, 6, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, ssb.length() - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 5, ssb.length() - 5, 2, ssb.length() - 2, paint);
+    }
+
+    @Test
+    public void testDrawText_MultiParagraph() {
+        final TextPaint paint = new TextPaint();
+        paint.setTextSize(32.0f);
+
+        final SpannableStringBuilder ssb = new SpannableStringBuilder(
+                "Hello, World\nHello, Android");
+
+        // The first line
+        final int firstLineLen = "Hello, World\n".length();
+        assertSameOutput(ssb, 0, firstLineLen, 0, firstLineLen, paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 0, firstLineLen, paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 2, firstLineLen - 2, paint);
+
+        // The second line.
+        assertSameOutput(ssb, firstLineLen, ssb.length(), firstLineLen, ssb.length(), paint);
+        assertSameOutput(ssb, firstLineLen + 3, ssb.length() - 3,
+                firstLineLen, ssb.length(), paint);
+        assertSameOutput(ssb, firstLineLen + 5, ssb.length() - 5,
+                firstLineLen + 2, ssb.length() - 2, paint);
+
+        // Across the paragraph
+        assertSameOutput(ssb, 0, ssb.length(), 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 0, ssb.length(), paint);
+        assertSameOutput(ssb, 3, firstLineLen - 3, 2, ssb.length() - 2, paint);
+    }
 }
diff --git a/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java b/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
index 489fb2d..9fed9ee 100644
--- a/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
+++ b/tests/tests/transition/src/android/transition/cts/TransitionSetTest.java
@@ -18,18 +18,28 @@
 import static com.android.compatibility.common.util.CtsMockitoUtils.within;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.animation.TimeInterpolator;
+import android.graphics.Rect;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.transition.ArcMotion;
 import android.transition.ChangeBounds;
 import android.transition.Fade;
+import android.transition.PathMotion;
 import android.transition.Transition;
+import android.transition.TransitionPropagation;
 import android.transition.TransitionSet;
+import android.transition.TransitionValues;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -123,5 +133,119 @@
         assertEquals(1, transitionSet.getTransitionCount());
         assertSame(changeBounds, transitionSet.getTransitionAt(0));
     }
+
+    @Test
+    public void testSetTransferValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        fade.setDuration(500);
+        fade.setPropagation(new TestPropagation());
+        fade.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        });
+        fade.setInterpolator(new AccelerateDecelerateInterpolator());
+        fade.setPathMotion(new ArcMotion());
+
+        TransitionSet transitionSet = new TransitionSet();
+        int duration = 100;
+        TestPropagation propagation = new TestPropagation();
+        TimeInterpolator interpolator = new DecelerateInterpolator();
+        PathMotion pathMotion = new ArcMotion();
+        Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        };
+        transitionSet.setDuration(duration);
+        transitionSet.setPropagation(propagation);
+        transitionSet.setInterpolator(interpolator);
+        transitionSet.setPathMotion(pathMotion);
+        transitionSet.setEpicenterCallback(epicenterCallback);
+
+        transitionSet.addTransition(fade);
+        assertEquals(duration, fade.getDuration());
+        assertSame(propagation, fade.getPropagation());
+        assertSame(interpolator, fade.getInterpolator());
+        assertSame(pathMotion, fade.getPathMotion());
+        assertSame(epicenterCallback, fade.getEpicenterCallback());
+    }
+
+    @Test
+    public void testSetTransferNullValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        fade.setDuration(500);
+        fade.setPropagation(new TestPropagation());
+        fade.setEpicenterCallback(new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        });
+        fade.setInterpolator(new AccelerateDecelerateInterpolator());
+        fade.setPathMotion(new ArcMotion());
+
+        TransitionSet transitionSet = new TransitionSet();
+        transitionSet.setDuration(0);
+        transitionSet.setPropagation(null);
+        transitionSet.setInterpolator(null);
+        transitionSet.setPathMotion(null);
+        transitionSet.setEpicenterCallback(null);
+
+        transitionSet.addTransition(fade);
+        assertEquals(0, fade.getDuration());
+        assertNull(fade.getPropagation());
+        assertNull(fade.getInterpolator());
+        assertSame(transitionSet.getPathMotion(), fade.getPathMotion());
+        assertNull(fade.getEpicenterCallback());
+    }
+
+    @Test
+    public void testSetNoTransferValuesDuringAdd() throws Throwable {
+        Fade fade = new Fade();
+        int duration = 100;
+        TestPropagation propagation = new TestPropagation();
+        TimeInterpolator interpolator = new DecelerateInterpolator();
+        PathMotion pathMotion = new ArcMotion();
+        Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
+            @Override
+            public Rect onGetEpicenter(Transition transition) {
+                return null;
+            }
+        };
+        fade.setDuration(duration);
+        fade.setPropagation(propagation);
+        fade.setInterpolator(interpolator);
+        fade.setPathMotion(pathMotion);
+        fade.setEpicenterCallback(epicenterCallback);
+
+        TransitionSet transitionSet = new TransitionSet();
+
+        transitionSet.addTransition(fade);
+        assertEquals(duration, fade.getDuration());
+        assertSame(propagation, fade.getPropagation());
+        assertSame(interpolator, fade.getInterpolator());
+        assertSame(pathMotion, fade.getPathMotion());
+        assertSame(epicenterCallback, fade.getEpicenterCallback());
+    }
+
+    private static class TestPropagation extends TransitionPropagation {
+        @Override
+        public long getStartDelay(ViewGroup sceneRoot, Transition transition,
+                TransitionValues startValues, TransitionValues endValues) {
+            return 0;
+        }
+
+        @Override
+        public void captureValues(TransitionValues transitionValues) {
+        }
+
+        @Override
+        public String[] getPropagationProperties() {
+            return new String[] { };
+        }
+    }
 }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index c9e0e42..422201c 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -49,7 +49,6 @@
 
 import androidx.annotation.ColorInt;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -538,7 +537,6 @@
 
     @LargeTest
     @Test
-    @Ignore // b/109839751
     public void testWebViewWithUnclippedLayer() {
         if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
             return; // no WebView to run test on
@@ -566,14 +564,13 @@
                         new int[] {
                                 Color.BLUE,
                                 Color.WHITE,
-                                0xffb3b3ff, // white blended with blue
+                                0xffc5c5ff, // white blended with blue
                                 0xffdbdbff  // white blended with blue
-                        }));
+                        }, 50));
     }
 
     @LargeTest
     @Test
-    @Ignore // b/109839751
     public void testWebViewWithUnclippedLayerAndComplexClip() {
         if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
             return; // no WebView to run test on
@@ -604,9 +601,9 @@
                                 Color.WHITE,
                                 Color.WHITE,
                                 Color.BLUE,
-                                0xffb3b3ff, // white blended with blue
+                                0xffc5c5ff, // white blended with blue
                                 0xffdbdbff  // white blended with blue
-                        }));
+                        }, 50));
     }
 
     @LargeTest
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
index ad20dd7..e25260b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/TextureViewTests.java
@@ -25,8 +25,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.SurfaceTexture;
-import androidx.annotation.ColorInt;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.uirendering.cts.R;
 import android.uirendering.cts.bitmapverifiers.ColorVerifier;
@@ -38,6 +37,8 @@
 import android.view.TextureView.SurfaceTextureListener;
 import android.view.ViewGroup;
 
+import androidx.annotation.ColorInt;
+
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -47,7 +48,8 @@
 
 import java.util.concurrent.CountDownLatch;
 
-@MediumTest
+// Temporarily mark @LargeTest to surpress it from presubmit b/37773896
+@LargeTest
 @RunWith(AndroidJUnit4.class)
 public class TextureViewTests extends ActivityTestBase {
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
index f4db101..1368420 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
@@ -16,9 +16,6 @@
 
 package android.uirendering.cts.testclasses;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,14 +28,15 @@
 import android.uirendering.cts.bitmapverifiers.RectVerifier;
 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
-
-import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.Gravity;
 import android.view.View;
+import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
 import java.util.concurrent.CountDownLatch;
-import android.animation.Animator;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -82,68 +80,51 @@
      */
     @Test
     public void testInvalidateCache() {
-        CountDownLatch testFinishedFence = new CountDownLatch(1);
-
-        ViewInitializer initializer = new ViewInitializer() {
-            ValueAnimator mAnimator;
-
-            @Override
-            public void initializeView(View view) {
-                FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
-                root.setBackgroundColor(Color.BLUE);
-
-                final VectorDrawableView child = new VectorDrawableView(view.getContext());
-
-                child.setLayoutParams(new FrameLayout.LayoutParams(ActivityTestBase.TEST_WIDTH,
-                    ActivityTestBase.TEST_HEIGHT));
-                // VectorDrawable is a red circle drawn on top of a blue background.
-                // The first frame has VectorDrawable size set to 1x1 pixels, which deforms
-                // the red circle into a 1x1 red-ish square.
-                // An animation grows VectorDrawable bounds from 0x0 to 90x90. If VD cache is
-                // refreshed, then we should see a red circle on top of a blue background.
-                // If VD cache is stale, then VD will upscale the original 1x1 cached image to
-                // 90x90 red-ish square.
-                // At the end of the animation, we verify the color of top left pixel, which should
-                // be a blue background pixel.
-                child.setVDSize(new Rect(0, 0, 2, 2)); //first draw with VD size set to 1x1 pixels.
-                root.addView(child);
-
-                mAnimator = ValueAnimator.ofFloat(0, 1);
-                mAnimator.setRepeatCount(0);
-                mAnimator.setDuration(400);
-                mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        float progress = (float) mAnimator.getAnimatedValue();
-                        child.setVDSize(new Rect(0, 0, (int)(progress*child.getWidth()),
-                                (int)(progress*child.getHeight())));
-                        child.invalidate();
-                    }
-                });
-                mAnimator.addListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            super.onAnimationEnd(animation);
-                            testFinishedFence.countDown();
-                        }
-                    });
-
-                mAnimator.start();
-            }
-
-            @Override
-            public void teardownView() {
-              mAnimator.cancel();
-            }
-        };
-
+        final CountDownLatch fence = new CountDownLatch(1);
         createTest()
-            .addLayout(R.layout.frame_layout, initializer, true, testFinishedFence)
-            .runWithVerifier(new SamplePointVerifier(
-                new Point[] { new Point(0, 0) },
-                new int[] { 0xff0000ff }
-            ));
+                .addLayout(R.layout.frame_layout, view -> {
+                    FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+                    root.setBackgroundColor(Color.BLUE);
+                    final VectorDrawableView child = new VectorDrawableView(view.getContext());
+                    // VectorDrawable is a red circle drawn on top of a blue background.
+                    // The first frame has VectorDrawable size set to 1x1 pixels, which deforms
+                    // the red circle into a 1x1 red-ish square.
+                    // After first draw we grow VectorDrawable bounds from 0x0 to 90x90. If VD cache
+                    // is refreshed, then we should see a red circle on top of a blue background.
+                    // If VD cache is stale, then VD will upscale the original 1x1 cached image to
+                    // 90x90 red-ish square.
+                    // At the end we verify the color of top left pixel, which should be a blue
+                    // background pixel.
+                    child.setVDSize(new Rect(0, 0, 2, 2));
+
+                    root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT,
+                              Gravity.TOP | Gravity.LEFT));
+
+                    // Post a new VD size a few frames in, so that the initial draw completes.
+                    root.getViewTreeObserver().addOnPreDrawListener(
+                            new ViewTreeObserver.OnPreDrawListener() {
+                                int mDrawCount = 0;
+                                @Override
+                                public boolean onPreDraw() {
+                                    if (mDrawCount++ == 5) {
+                                        child.setVDSize(new Rect(0, 0,
+                                                (int) (child.getWidth()),
+                                                (int) (child.getHeight())));
+                                        child.invalidate();
+
+                                        root.getViewTreeObserver().removeOnPreDrawListener(this);
+                                        root.post(fence::countDown);
+                                    } else {
+                                        root.postInvalidate();
+                                    }
+                                    return true;
+                                }
+                            });
+                }, true, fence)
+                .runWithVerifier(new SamplePointVerifier(
+                    new Point[] { new Point(0, 0) },
+                    new int[] { 0xff0000ff }
+                ));
     }
 }
 
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 598552b..3045d1f2 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -16,6 +16,7 @@
 
 package android.view.cts;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
@@ -34,16 +35,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.TimeUnit;
-
 
 /**
  * Certain KeyEvents should never be delivered to apps. These keys are:
  *      KEYCODE_ASSIST
  *      KEYCODE_VOICE_ASSIST
  *      KEYCODE_HOME
- * This test launches an Activity and inject KeyEvents with the corresponding key codes.
+ * This test launches an Activity and injects KeyEvents with the corresponding key codes.
  * The test will fail if any of these keys are received by the activity.
+ *
+ * Certain combinations of keys should be treated as shortcuts. Those are:
+ *      KEYCODE_META_* + KEYCODE_ENTER --> KEYCODE_BACK
+ *      KEYCODE_META_* + KEYCODE_DEL --> KEYCODE_HOME
+ * For those combinations, we make sure that they are either delivered to the app
+ * as the desired key (KEYCODE_BACK), or not delivered to the app (KEYCODE_HOME).
  */
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -77,6 +82,48 @@
         testKey(KeyEvent.KEYCODE_HOME);
     }
 
+    @Test
+    public void testKeyCodeHomeShortcutLeftMeta() {
+        testKeyCodeHomeShortcut(KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeHomeShortcutRightMeta() {
+        testKeyCodeHomeShortcut(KeyEvent.META_META_RIGHT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeBackShortcutLeftMeta() {
+        testKeyCodeBackShortcut(KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_ON);
+    }
+
+    @Test
+    public void testKeyCodeBackShortcutRightMeta() {
+        testKeyCodeBackShortcut(KeyEvent.META_META_RIGHT_ON | KeyEvent.META_META_ON);
+    }
+
+    private void testKeyCodeHomeShortcut(int metaState) {
+        long downTime = SystemClock.uptimeMillis();
+        injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_ENTER, 0, metaState));
+        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_ENTER, 0, metaState));
+
+        assertKeyNotReceived();
+    }
+
+    private void testKeyCodeBackShortcut(int metaState) {
+        long downTime = SystemClock.uptimeMillis();
+        injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_DEL, 0, metaState));
+        injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+                KeyEvent.KEYCODE_DEL, 0, metaState));
+
+        assertKeyReceived(KeyEvent.KEYCODE_BACK, KeyEvent.ACTION_DOWN);
+        assertKeyReceived(KeyEvent.KEYCODE_BACK, KeyEvent.ACTION_UP);
+        assertKeyNotReceived();
+    }
+
     private void testKey(int keyCode) {
         sendKey(keyCode);
         assertKeyNotReceived();
@@ -97,14 +144,19 @@
     }
 
     private void assertKeyNotReceived() {
-        try {
-            KeyEvent keyEvent = mActivity.mKeyEvents.poll(1, TimeUnit.SECONDS);
-            if (keyEvent == null) {
-                return;
-            }
-            fail("Should not have received " + KeyEvent.keyCodeToString(keyEvent.getKeyCode()));
-        } catch (InterruptedException ex) {
-            fail("BlockingQueue.poll(..) was unexpectedly interrupted");
+        KeyEvent keyEvent = mActivity.mKeyEvents.poll();
+        if (keyEvent == null) {
+            return;
         }
+        fail("Should not have received " + KeyEvent.keyCodeToString(keyEvent.getKeyCode()));
+    }
+
+    private void assertKeyReceived(int keyCode, int action) {
+        KeyEvent keyEvent = mActivity.mKeyEvents.poll();
+        if (keyEvent == null) {
+            fail("Did not receive " + KeyEvent.keyCodeToString(keyCode) + ", queue is empty");
+        }
+        assertEquals(keyCode, keyEvent.getKeyCode());
+        assertEquals(action, keyEvent.getAction());
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
index 18e0713..2422a61 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
@@ -19,21 +19,14 @@
 import android.app.Activity;
 import android.view.KeyEvent;
 
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
+import java.util.LinkedList;
+import java.util.Queue;
 
 public class KeyEventInterceptTestActivity extends Activity {
-    final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingDeque<>();
+    final Queue<KeyEvent> mKeyEvents = new LinkedList<>();
 
     @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        mKeyEvents.add(event);
-        return true;
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        // Check this in case some spurious event with ACTION_UP is received
+    public boolean dispatchKeyEvent(KeyEvent event) {
         mKeyEvents.add(event);
         return true;
     }
diff --git a/tests/tests/view/src/android/view/cts/KeyEventTest.java b/tests/tests/view/src/android/view/cts/KeyEventTest.java
index fde3f81..1d33865 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventTest.java
@@ -760,4 +760,37 @@
                 1, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_TOUCHSCREEN);
         assertFalse(mKeyEvent.isLongPress());
     }
+
+    @Test
+    public void testKeyCodeFromString() {
+        assertEquals(KeyEvent.KEYCODE_A, KeyEvent.keyCodeFromString("KEYCODE_A"));
+        assertEquals(KeyEvent.KEYCODE_A, KeyEvent.keyCodeFromString("A"));
+        assertEquals(KeyEvent.KEYCODE_A,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_A)));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("keycode_a"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("a"));
+        assertEquals(0, KeyEvent.keyCodeFromString("0"));
+        assertEquals(1, KeyEvent.keyCodeFromString("1"));
+        assertEquals(KeyEvent.KEYCODE_HOME, KeyEvent.keyCodeFromString("3"));
+        assertEquals(KeyEvent.KEYCODE_POWER,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_POWER)));
+        assertEquals(KeyEvent.KEYCODE_MENU,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.KEYCODE_MENU)));
+        assertEquals(KeyEvent.KEYCODE_BACK, KeyEvent.keyCodeFromString("BACK"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("back"));
+
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN,
+                KeyEvent.keyCodeFromString("KEYCODE_NOT_A_REAL_KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("NOT_A_REAL_KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("-1"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("1001"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE_123"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString("KEYCODE_"));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN, KeyEvent.keyCodeFromString(""));
+        assertEquals(KeyEvent.LAST_KEYCODE,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.LAST_KEYCODE)));
+        assertEquals(KeyEvent.KEYCODE_UNKNOWN,
+                KeyEvent.keyCodeFromString(Integer.toString(KeyEvent.LAST_KEYCODE + 1)));
+    }
 }
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 21e116d..6e3ba4e 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -20,6 +20,8 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+    <!-- Note: we must provide INTERNET permission for
+     ServiceWorkerWebSettingsTest#testBlockNetworkLoads -->
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <application android:maxRecents="1" android:usesCleartextTraffic="true">
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index 0fa239d..6a879f4 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -16,6 +16,9 @@
 
 package android.webkit.cts;
 
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.net.Uri;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -166,7 +169,7 @@
             }
         });
         // Wait for all the responses to arrive.
-        boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
     }
 
     // Test that a message port that is closed cannot used to send a message
@@ -234,4 +237,67 @@
         });
         waitForTitle(hello);
     }
+
+    // Ensure the callback is invoked on the correct Handler.
+    public void testWebMessageHandler() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        loadPage(CHANNEL_MESSAGE);
+        final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+        WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+        mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+        final int messageCount = 1;
+        final CountDownLatch latch = new CountDownLatch(messageCount);
+
+        // Create a new thread for the WebMessageCallback.
+        final HandlerThread messageHandlerThread = new HandlerThread("POST_MESSAGE_THREAD");
+        messageHandlerThread.start();
+        final Handler messageHandler = new Handler(messageHandlerThread.getLooper());
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE));
+                channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+                    @Override
+                    public void onMessage(WebMessagePort port, WebMessage message) {
+                        assertTrue(messageHandlerThread.getLooper().isCurrentThread());
+                        latch.countDown();
+                    }
+                }, messageHandler);
+            }
+        });
+        // Wait for all the responses to arrive.
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+    }
+
+    // Ensure the callback is invoked on the MainLooper by default.
+    public void testWebMessageDefaultHandler() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        loadPage(CHANNEL_MESSAGE);
+        final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+        WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+        mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+        final int messageCount = 1;
+        final CountDownLatch latch = new CountDownLatch(messageCount);
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE));
+                channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+                    @Override
+                    public void onMessage(WebMessagePort port, WebMessage message) {
+                        assertTrue(Looper.getMainLooper().isCurrentThread());
+                        latch.countDown();
+                    }
+                });
+            }
+        });
+        // Wait for all the responses to arrive.
+        assertTrue(latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS));
+    }
 }
diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java
new file mode 100644
index 0000000..6569a4f
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerWebSettingsTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.test.ActivityInstrumentationTestCase2;
+import android.webkit.ServiceWorkerController;
+import android.webkit.ServiceWorkerWebSettings;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.android.compatibility.common.util.NullWebViewUtils;
+
+
+public class ServiceWorkerWebSettingsTest extends
+        ActivityInstrumentationTestCase2<WebViewCtsActivity> {
+
+    private ServiceWorkerWebSettings mSettings;
+    private WebViewOnUiThread mOnUiThread;
+
+    public ServiceWorkerWebSettingsTest() {
+        super("android.webkit.cts", WebViewCtsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        WebView webview = getActivity().getWebView();
+        if (webview != null) {
+            mOnUiThread = new WebViewOnUiThread(this, webview);
+            mSettings = ServiceWorkerController.getInstance().getServiceWorkerWebSettings();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOnUiThread != null) {
+            mOnUiThread.cleanUp();
+        }
+        super.tearDown();
+    }
+
+    public void testCacheMode() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        int i = WebSettings.LOAD_DEFAULT;
+        assertEquals(i, mSettings.getCacheMode());
+        for (; i <= WebSettings.LOAD_CACHE_ONLY; i++) {
+            mSettings.setCacheMode(i);
+            assertEquals(i, mSettings.getCacheMode());
+        }
+    }
+
+    public void testAllowContentAccess() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(mSettings.getAllowContentAccess(), true);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setAllowContentAccess(b);
+            assertEquals(b, mSettings.getAllowContentAccess());
+        }
+    }
+
+    public void testAllowFileAccess() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(mSettings.getAllowFileAccess(), true);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setAllowFileAccess(b);
+            assertEquals(b, mSettings.getAllowFileAccess());
+        }
+    }
+
+    public void testBlockNetworkLoads() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        // Note: we cannot test this setter unless we provide the INTERNET permission, otherwise we
+        // get a SecurityException when we pass 'false'.
+        final boolean hasInternetPermission = true;
+
+        assertEquals(mSettings.getBlockNetworkLoads(), !hasInternetPermission);
+        for (boolean b : new boolean[]{false, true}) {
+            mSettings.setBlockNetworkLoads(b);
+            assertEquals(b, mSettings.getBlockNetworkLoads());
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index ae875a5..099952f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -741,6 +741,21 @@
         assertEquals("No database", mOnUiThread.getTitle());
     }
 
+    public void testDisabledActionModeMenuItems() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        assertEquals(WebSettings.MENU_ITEM_NONE, mSettings.getDisabledActionModeMenuItems());
+
+        int allDisabledFlags = WebSettings.MENU_ITEM_NONE | WebSettings.MENU_ITEM_SHARE |
+                WebSettings.MENU_ITEM_WEB_SEARCH | WebSettings.MENU_ITEM_PROCESS_TEXT;
+        for (int i = WebSettings.MENU_ITEM_NONE; i <= allDisabledFlags; i++) {
+            mSettings.setDisabledActionModeMenuItems(i);
+            assertEquals(i, mSettings.getDisabledActionModeMenuItems());
+        }
+    }
+
     public void testLoadsImagesAutomatically() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index 58e59e5..f66b371 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -43,6 +43,8 @@
 
 import java.io.ByteArrayInputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.List;
@@ -604,20 +606,23 @@
         assertFalse(webViewClient.didRenderProcessCrash());
     }
 
-    public void testOnSafeBrowsingHit() throws Throwable {
+    public void testOnSafeBrowsingHitBackToSafety() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
-                new SafeBrowsingBackToSafetyClient();
-        mOnUiThread.setWebViewClient(backToSafetyWebViewClient);
-        mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
-
         mWebServer = new CtsTestServer(getActivity());
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         final String ORIGINAL_URL = mOnUiThread.getUrl();
 
+        final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
+                new SafeBrowsingBackToSafetyClient();
+        mOnUiThread.setWebViewClient(backToSafetyWebViewClient);
+        mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
+
+        // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
+        // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
+        // enabled.
         if (mOnUiThread.getSettings().getSafeBrowsingEnabled()) {
             assertEquals(0, backToSafetyWebViewClient.hasOnReceivedErrorCode());
             mOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_URL);
@@ -633,11 +638,25 @@
             // Check that we actually navigated backward
             assertEquals(ORIGINAL_URL, mOnUiThread.getUrl());
         }
+    }
 
-        final SafeBrowsingProceedClient proceedWebViewClient = new SafeBrowsingProceedClient();
+    public void testOnSafeBrowsingHitProceed() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        mWebServer = new CtsTestServer(getActivity());
+        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        final String ORIGINAL_URL = mOnUiThread.getUrl();
+
+        final SafeBrowsingProceedClient proceedWebViewClient =
+                new SafeBrowsingProceedClient();
         mOnUiThread.setWebViewClient(proceedWebViewClient);
-
         mOnUiThread.getSettings().setSafeBrowsingEnabled(true);
+
+        // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
+        // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
+        // enabled.
         if (mOnUiThread.getSettings().getSafeBrowsingEnabled()) {
             assertEquals(0, proceedWebViewClient.hasOnReceivedErrorCode());
             mOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_URL);
@@ -658,6 +677,26 @@
         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
     }
 
+    public void testOnPageCommitVisibleCalled() throws Exception {
+        // Check that the onPageCommitVisible callback is called
+        // correctly.
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final CountDownLatch callbackLatch = new CountDownLatch(1);
+
+        mOnUiThread.setWebViewClient(new WebViewClient() {
+                public void onPageCommitVisible(WebView view, String url) {
+                    assertEquals(url, "about:blank");
+                    callbackLatch.countDown();
+                }
+            });
+
+        mOnUiThread.loadUrl("about:blank");
+        assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
     private class MockWebViewClient extends WaitForLoadedClient {
         private boolean mOnPageStartedCalled;
         private boolean mOnPageFinishedCalled;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 4bedbb1..e6ff32b 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -1361,7 +1361,7 @@
         assertNull(mWebView.getUrl());
         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
         // Snippet of HTML that will prevent favicon requests to the test server.
-        final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"#\" /></head>";
+        final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"%23\" /></head>";
 
         // Trying to resolve a relative URL against a data URL without a base URL
         // will fail and we won't make a request to the test web server.
@@ -1968,8 +1968,8 @@
         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
         mOnUiThread.loadDataAndWaitForCompletion(
                 "<html><head><title>Title</title><style type=\"text/css\">"
-                + "#imgElement { -webkit-transform: translate3d(0,0,1); }"
-                + "#imgElement.finish { -webkit-transform: translate3d(0,0,0);"
+                + "%23imgElement { -webkit-transform: translate3d(0,0,1); }"
+                + "%23imgElement.finish { -webkit-transform: translate3d(0,0,0);"
                 + " -webkit-transition-duration: 1ms; }</style>"
                 + "<script type=\"text/javascript\">function imgLoad() {"
                 + "imgElement = document.getElementById('imgElement');"
@@ -2656,26 +2656,6 @@
         assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
-    public void testOnPageCommitVisibleCalled() throws Exception {
-        // Check that the onPageCommitVisible callback is called
-        // correctly.
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-
-        final CountDownLatch callbackLatch = new CountDownLatch(1);
-
-        mOnUiThread.setWebViewClient(new WebViewClient() {
-                public void onPageCommitVisible(WebView view, String url) {
-                    assertEquals(url, "about:blank");
-                    callbackLatch.countDown();
-                }
-            });
-
-        mOnUiThread.loadUrl("about:blank");
-        assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
-    }
-
     public void testSetSafeBrowsingWhitelistWithMalformedList() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
diff --git a/tests/tests/widget/res/values/strings.xml b/tests/tests/widget/res/values/strings.xml
index 78f0f7c..9e36cc0 100644
--- a/tests/tests/widget/res/values/strings.xml
+++ b/tests/tests/widget/res/values/strings.xml
@@ -197,6 +197,7 @@
     <string name="toolbar_subtitle">Toolbar subtitle</string>
     <string name="toolbar_navigation">Toolbar navigation</string>
     <string name="toolbar_logo">Toolbar logo</string>
+    <string name="toolbar_collapse">Toolbar collapse</string>
 
     <string name="search_query_hint">query hint</string>
 
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index 5476b22..d59fe97 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -42,13 +43,19 @@
 import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
+import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Xfermode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.graphics.drawable.PaintDrawable;
 import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
@@ -500,6 +507,46 @@
         verify(mockImageView, times(1)).onSizeChanged(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
+    @Test
+    public void testSetColorFilterPreservesDrawableProperties() {
+        ImageView imageView = new ImageView(InstrumentationRegistry.getTargetContext());
+
+        int colorAlpha = 128;
+        MockDrawable mockDrawable = new MockDrawable();
+        mockDrawable.setAlpha(colorAlpha);
+        mockDrawable.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+
+        imageView.setImageDrawable(mockDrawable);
+
+        imageView.setColorFilter(Color.RED);
+        assertEquals(colorAlpha, mockDrawable.getAlpha());
+        assertNotNull(mockDrawable.getXfermode());
+    }
+
+    @Test
+    public void testImageViewSetColorFilterPropagatedToDrawable() {
+        ImageView imageView = new ImageView(InstrumentationRegistry.getTargetContext());
+
+        MockDrawable mockDrawable = new MockDrawable();
+        imageView.setImageDrawable(mockDrawable);
+        imageView.setColorFilter(Color.RED);
+
+        ColorFilter imageViewColorFilter = imageView.getColorFilter();
+        assertTrue(imageViewColorFilter instanceof PorterDuffColorFilter);
+
+        PorterDuffColorFilter imageViewPorterDuffFilter =
+                (PorterDuffColorFilter) imageViewColorFilter;
+        assertEquals(Color.RED, imageViewPorterDuffFilter.getColor());
+        assertEquals(Mode.SRC_ATOP, imageViewPorterDuffFilter.getMode());
+
+        ColorFilter colorFilter = mockDrawable.getColorFilter();
+        assertTrue(colorFilter instanceof PorterDuffColorFilter);
+
+        PorterDuffColorFilter porterDuffColorFilter = (PorterDuffColorFilter) colorFilter;
+        assertEquals(Color.RED, porterDuffColorFilter.getColor());
+        assertEquals(PorterDuff.Mode.SRC_ATOP, porterDuffColorFilter.getMode());
+    }
+
     @UiThreadTest
     @Test
     public void testVerifyDrawable() {
@@ -674,4 +721,49 @@
             super.onSizeChanged(w, h, oldw, oldh);
         }
     }
+
+    public static class MockDrawable extends Drawable {
+
+        private ColorFilter mFilter;
+        private int mAlpha;
+        private Xfermode mXfermode;
+
+        @Override
+        public void draw(Canvas canvas) {
+            // NO-OP
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            mAlpha = alpha;
+        }
+
+        public int getAlpha() {
+            return mAlpha;
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+            mFilter = colorFilter;
+        }
+
+        @Override
+        public void setXfermode(Xfermode mode) {
+            mXfermode = mode;
+        }
+
+        public @Nullable Xfermode getXfermode() {
+            return mXfermode;
+        }
+
+        @Override
+        public @Nullable ColorFilter getColorFilter() {
+            return mFilter;
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/TextClockTest.java b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
index acb6796..0f55bbc 100644
--- a/tests/tests/widget/src/android/widget/cts/TextClockTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
@@ -30,13 +30,13 @@
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.text.format.DateFormat;
 import android.util.MutableBoolean;
 import android.widget.TextClock;
 
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.SystemUtil;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -56,7 +56,7 @@
 public class TextClockTest {
     private Activity mActivity;
     private TextClock mTextClock;
-    private boolean mStartedAs24;
+    private String mDefaultTime1224;
 
     @Rule
     public ActivityTestRule<TextClockCtsActivity> mActivityRule =
@@ -66,12 +66,14 @@
     public void setup() throws Throwable {
         mActivity = mActivityRule.getActivity();
         mTextClock = mActivity.findViewById(R.id.textclock);
-        mStartedAs24 = DateFormat.is24HourFormat(mActivity);
+        mDefaultTime1224 = Settings.System.getString(mActivity.getContentResolver(),
+                Settings.System.TIME_12_24);
     }
 
+    @After
     public void teardown() throws Throwable {
-        int base = mStartedAs24 ? 24 : 12;
-        Settings.System.putInt(mActivity.getContentResolver(), Settings.System.TIME_12_24, base);
+        Settings.System.putString(mActivity.getContentResolver(), Settings.System.TIME_12_24,
+                mDefaultTime1224);
     }
 
     @Test
diff --git a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
index ff4da10..8a70859 100644
--- a/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToolbarTest.java
@@ -287,6 +287,41 @@
     }
 
     @Test
+    public void testCollapseConfiguration() throws Throwable {
+        // Inflate menu and expand action view to display collapse button
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.inflateMenu(R.menu.toolbar_menu_search));
+        final MenuItem searchMenuItem = mMainToolbar.getMenu().findItem(R.id.action_search);
+        mActivityRule.runOnUiThread(searchMenuItem::expandActionView);
+        mInstrumentation.waitForIdleSync();
+
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.setCollapseIcon(R.drawable.icon_green));
+        Drawable toolbarCollapseIcon = mMainToolbar.getCollapseIcon();
+        TestUtils.assertAllPixelsOfColor("Collapse icon is green", toolbarCollapseIcon,
+                toolbarCollapseIcon.getIntrinsicWidth(),
+                toolbarCollapseIcon.getIntrinsicHeight(),
+                true, Color.GREEN, 1, false);
+
+        WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
+                () -> mMainToolbar.setCollapseIcon(mActivity.getDrawable(R.drawable.icon_blue)));
+        toolbarCollapseIcon = mMainToolbar.getCollapseIcon();
+        TestUtils.assertAllPixelsOfColor("Collapse icon is blue", toolbarCollapseIcon,
+                toolbarCollapseIcon.getIntrinsicWidth(),
+                toolbarCollapseIcon.getIntrinsicHeight(),
+                true, Color.BLUE, 1, false);
+
+        mActivityRule.runOnUiThread(
+                () -> mMainToolbar.setCollapseContentDescription(R.string.toolbar_collapse));
+        assertEquals(mActivity.getResources().getString(R.string.toolbar_collapse),
+                mMainToolbar.getCollapseContentDescription());
+
+        mActivityRule.runOnUiThread(
+                () -> mMainToolbar.setCollapseContentDescription("Collapse legend"));
+        assertEquals("Collapse legend", mMainToolbar.getCollapseContentDescription());
+    }
+
+    @Test
     public void testLogoConfiguration() throws Throwable {
         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mMainToolbar,
                 () -> mMainToolbar.setLogo(R.drawable.icon_yellow));
diff --git a/tools/release-parser/Android.mk b/tools/release-parser/Android.mk
new file mode 100644
index 0000000..aed8583
--- /dev/null
+++ b/tools/release-parser/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# cts release-parser java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(call all-subdir-java-files) \
+    $(call all-proto-files-under, proto)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
+
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_MODULE := release-parser
+
+# This tool is not checking any dependencies or metadata, so all of the
+# dependencies of all of the tests must be on its classpath. This is
+# super fragile.
+LOCAL_STATIC_JAVA_LIBRARIES += \
+  compatibility-host-util \
+  hosttestlib \
+  dexlib2 \
+  tradefed
+
+include $(BUILD_HOST_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tools/release-parser/MANIFEST.mf b/tools/release-parser/MANIFEST.mf
new file mode 100644
index 0000000..8acac32
--- /dev/null
+++ b/tools/release-parser/MANIFEST.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.android.cts.releaseparser.Main
+Implementation-Version: %BUILD_NUMBER%
\ No newline at end of file
diff --git a/tools/release-parser/proto/release.proto b/tools/release-parser/proto/release.proto
new file mode 100644
index 0000000..2be9538
--- /dev/null
+++ b/tools/release-parser/proto/release.proto
@@ -0,0 +1,187 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// [START declaration]
+syntax = "proto3";
+package com_android_cts_releaseparser;
+// [END declaration]
+
+// [START java_declaration]
+option java_package = "com.android.cts.releaseparser";
+option java_outer_classname = "ReleaseProto";
+// [END java_declaration]
+
+// [START messages]
+message Option {
+    string name = 1;
+    string key = 2;
+    string value =3;
+}
+
+message TestModuleConfig {
+    string module_name = 1;
+    string component = 2;
+    string description = 3;
+    repeated Option options = 4;
+
+    message TargetPreparer {
+        string test_class = 1;
+        repeated Option options = 2;
+    }
+    repeated TargetPreparer target_preparers = 5;
+    repeated string test_file_names = 6;
+
+    message TestClass {
+        string test_class = 1;
+        string package = 2;
+        repeated Option options = 3;
+    }
+    repeated TestClass test_classes = 7;
+    repeated string test_jars = 8;
+}
+
+// An entry in a release
+message Entry {
+    // Name
+    string name = 1;
+
+    enum EntryType {
+        FOLDER = 0;
+        FILE = 1;
+        CONFIG = 2;
+        JAR = 3;
+        APK = 4;
+        EXE = 5;
+        SO = 6;
+    }
+    // Type
+    EntryType type = 2;
+
+    // Size
+    int64 size = 3;
+    // Content ID
+    string content_id = 4;
+    // Parent folder
+    string parent_folder = 5;
+    // Relative path
+    string relative_path = 6;
+
+    // TestModule.config message
+    TestModuleConfig test_module_config = 7;
+}
+
+message ReleaseContent {
+    // Name
+    string name = 1;
+    // Version
+    string version = 2;
+    // Build Number
+    string build_number = 3;
+    // Content ID
+    string content_id = 4;
+    string fullname = 5;
+    string target_arch = 6;
+    string test_suite_tradefed = 7;
+    // File Entries
+    map<string, Entry> entries = 8;
+    repeated string known_failures = 9;
+}
+
+message Annotation {
+    int32 visibility = 1;
+    string type = 2;
+
+    message Element {
+        string name = 1;
+        string value = 2;
+    }
+    repeated Element elements = 3;
+}
+
+enum TestClassType {
+    UNKNOWN = 0;
+    JUNIT3 = 1;
+    JUNIT4 = 2;
+    PARAMETERIZED = 3;
+    JAVAHOST = 4;
+}
+
+message TestSuite {
+    string name = 1;
+    // Version
+    string version = 2;
+    // Build Number
+    string build_number = 3;
+    // Content ID
+    string content_id = 4;
+
+    enum TestType {
+        UNKNOWN = 0;
+        ANDROIDJUNIT = 1;
+        JAVAHOST = 2;
+        GTEST = 3;
+        LIBCORE = 4;
+        DALVIK = 5;
+        DEQP = 6;
+    }
+
+    message Module {
+        string name = 1;
+        string config_file = 2;
+        TestType test_type = 3;
+        string test_class = 4;
+
+        message Package {
+            string name = 1;
+            string package_file = 2;
+            string content_id = 3;
+            string op_codes = 4;
+
+            message Class {
+                string name = 1;
+                string type = 2;
+                string super_class = 3;
+                string interface = 4;
+                TestClassType test_class_type = 5;
+                repeated Annotation annotations = 6;
+
+                message Method {
+                    string defining_class = 1;
+                    string name = 2;
+                    string parameters = 3;
+                    string return_type = 4;
+                    int32 access_flags = 5;
+                    string known_failure_filter = 6;
+                    repeated Annotation annotations = 7;
+                }
+                repeated Method methods = 7;
+
+                message Field {
+                    string defining_class = 1;
+                    string name = 2;
+                    string type = 3;
+                    int32 access_flags = 4;
+                    string initial_value = 5;
+                    repeated Annotation annotations = 6;
+                }
+                repeated Field fields = 8;
+            }
+            repeated Class classes = 5;
+        }
+        repeated Package packages = 5;
+    }
+    repeated Module modules = 5;
+}
+
+// [END messages]
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java b/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java
new file mode 100644
index 0000000..b2b693d
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/JarTestFinder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Representation of the entire CDD. */
+class JarTestFinder {
+
+    public static Collection<Class<?>> getClasses(File jarTestFile)
+            throws IllegalArgumentException {
+        List<Class<?>> classes = new ArrayList<>();
+
+        try (JarFile jarFile = new JarFile(jarTestFile)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+
+            URL[] urls = {new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath()))};
+            URLClassLoader cl =
+                    URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
+
+            while (e.hasMoreElements()) {
+                JarEntry je = e.nextElement();
+                if (je.isDirectory()
+                        || !je.getName().endsWith(".class")
+                        || je.getName().contains("$")) {
+                    continue;
+                }
+                String className = getClassName(je.getName());
+                if (!className.endsWith("Test")) {
+                    continue;
+                }
+                try {
+                    Class<?> cls = cl.loadClass(className);
+                    int modifiers = cls.getModifiers();
+                    if (!Modifier.isStatic(modifiers)
+                            && !Modifier.isPrivate(modifiers)
+                            && !Modifier.isProtected(modifiers)
+                            && !Modifier.isInterface(modifiers)
+                            && !Modifier.isAbstract(modifiers)) {
+
+                        classes.add(cls);
+                    }
+                } catch (ClassNotFoundException | Error x) {
+                    System.err.println(
+                            String.format(
+                                    "Cannot find test class %s from %s",
+                                    className, jarTestFile.getName()));
+                    x.printStackTrace();
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return classes;
+    }
+
+    private static String getClassName(String name) {
+        // -6 because of .class
+        return name.substring(0, name.length() - 6).replace('/', '.');
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/Main.java b/tools/release-parser/src/com/android/cts/releaseparser/Main.java
new file mode 100644
index 0000000..e017036
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/Main.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import java.nio.file.Paths;
+
+/** Main of release parser */
+public class Main {
+
+    private static final String USAGE_MESSAGE =
+            "Usage: java -jar releaseparser.jar [-options] <folder> [args...]\n"
+                    + "           to prase a release content in the folder\n"
+                    + "Options:\n"
+                    + "\t-a API#\t API Level, e.g. 27 \n"
+                    + "\t-i PATH\t path to a release folder \n"
+                    + "\t-o PATH\t path to output files \n";
+
+    private Main() {}
+
+    /** Get the argument or print out the usage and exit. */
+    private static void printUsage() {
+        System.out.printf(USAGE_MESSAGE);
+        System.exit(1);
+    }
+
+    /** Get the argument or print out the usage and exit. */
+    private static String getExpectedArg(String[] args, int index) {
+        if (index < args.length) {
+            return args[index];
+        } else {
+            printUsage();
+            return null; // Never will happen because printUsage will call exit(1)
+        }
+    }
+
+    public static void main(final String[] args) {
+        String relNameVer;
+        String relFolder = "";
+        String outputPath = "";
+        int apiL = 27;
+
+        for (int i = 0; i < args.length; i++) {
+            if (args[i].startsWith("-")) {
+                if ("-o".equals(args[i])) {
+                    outputPath = getExpectedArg(args, ++i);
+                    File file = new File(outputPath);
+                    // Only acception a folder
+                    if (!file.isDirectory()) {
+                        printUsage();
+                    }
+                } else if ("-i".equals(args[i])) {
+                    relFolder = getExpectedArg(args, ++i);
+                    File file = new File(relFolder);
+                    // Only acception a folder
+                    if (!file.isDirectory()) {
+                        printUsage();
+                    }
+                } else if ("-a".equals(args[i])) {
+                    apiL = Integer.parseInt(getExpectedArg(args, ++i));
+                } else {
+                    printUsage();
+                }
+            }
+        }
+
+        if ("".equals(relFolder) || "".equals(outputPath)) {
+            printUsage();
+        }
+
+        ReleaseParser relParser = new ReleaseParser(relFolder);
+        relNameVer = relParser.getRelNameVer();
+        relParser.writeRelesaeContentCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-ReleaseContent.csv", relNameVer))
+                        .toString());
+        relParser.writeKnownFailureCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-KnownFailure.csv", relNameVer)).toString());
+
+        // Write release content message to disk.
+        ReleaseContent relContent = relParser.getReleaseContent();
+        try {
+            FileOutputStream output =
+                    new FileOutputStream(
+                            Paths.get(outputPath, String.format("%s-ReleaseContent.pb", relNameVer))
+                                    .toString());
+            try {
+                relContent.writeTo(output);
+            } finally {
+                output.close();
+            }
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+
+        TestSuiteParser tsParser = new TestSuiteParser(relContent, relFolder, apiL);
+        tsParser.writeCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-TestCase.csv", relNameVer)).toString());
+        tsParser.writeModuleCsvFile(
+                relNameVer,
+                Paths.get(outputPath, String.format("%s-TestModule.csv", relNameVer)).toString());
+
+        // Write test suite content message to disk.
+        TestSuite testSuite = tsParser.getTestSuite();
+        try {
+            FileOutputStream output =
+                    new FileOutputStream(
+                            Paths.get(outputPath, String.format("%s-TestSuite.pb", relNameVer))
+                                    .toString());
+            try {
+                testSuite.writeTo(output);
+            } finally {
+                output.close();
+            }
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
new file mode 100644
index 0000000..97e0c5c
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class ReleaseParser {
+    // configuration option
+    private static final String NOT_SHARDABLE_TAG = "not-shardable";
+    // test class option
+    private static final String RUNTIME_HIT_TAG = "runtime-hint";
+    // com.android.tradefed.testtype.AndroidJUnitTest option
+    private static final String PACKAGE_TAG = "package";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_NAME_TAG = "jar";
+    // com.android.tradefed.testtype.GTest option
+    private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+    private static final String MODULE_TAG = "module-name";
+
+    private static final String SUITE_API_INSTALLER_TAG =
+            "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+    private static final String JAR_HOST_TEST_TAG =
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+    // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+    private static final String PUSH_TAG = "push";
+
+    // test class
+    private static final String ANDROID_JUNIT_TEST_TAG =
+            "com.android.tradefed.testtype.AndroidJUnitTest";
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".config";
+    private static final String JAR_EXT_TAG = ".jar";
+    private static final String APK_EXT_TAG = ".apk";
+    private static final String SO_EXT_TAG = ".so";
+    private static final String TEST_SUITE_TRADEFED_TAG = "-tradefed.jar";
+    private static final String TESTCASES_FOLDER_FORMAT = "testcases/%s";
+
+    private final String mFolderPath;
+    private Path mRootPath;
+    private ReleaseContent.Builder mRelContentBuilder;
+    private Map<String, Entry> mEntries;
+
+    ReleaseParser(String folder) {
+        mFolderPath = folder;
+        File fFile = new File(mFolderPath);
+        mRootPath = Paths.get(fFile.getAbsolutePath());
+        mEntries = new HashMap<String, Entry>();
+    }
+
+    public String getRelNameVer() {
+        ReleaseContent relContent = getReleaseContent();
+        return String.format(
+                "%s-%s-%s",
+                relContent.getName(), relContent.getVersion(), relContent.getBuildNumber());
+    }
+
+    public ReleaseContent getReleaseContent() {
+        if (mRelContentBuilder == null) {
+            mRelContentBuilder = ReleaseContent.newBuilder();
+            // also add the root folder entry
+            Entry.Builder fBuilder = parseFolder(mFolderPath);
+            fBuilder.setName(
+                    String.format(
+                            "%s-%s-%s",
+                            mRelContentBuilder.getName(),
+                            mRelContentBuilder.getVersion(),
+                            mRelContentBuilder.getBuildNumber()));
+            fBuilder.setParentFolder(mFolderPath);
+            Entry fEntry = fBuilder.build();
+            mEntries.put(fEntry.getRelativePath(), fEntry);
+            mRelContentBuilder.putAllEntries(mEntries);
+        }
+        return mRelContentBuilder.build();
+    }
+
+    // Parse all files in a folder and return the foler entry builder
+    private Entry.Builder parseFolder(String fPath) {
+        Entry.Builder folderEntry = Entry.newBuilder();
+        File folder = new File(fPath);
+        Path folderPath = Paths.get(folder.getAbsolutePath());
+        String folderRelativePath = mRootPath.relativize(folderPath).toString();
+        File[] fileList = folder.listFiles();
+        Long folderSize = 0L;
+        List<Entry> entryList = new ArrayList<Entry>();
+
+        // walks through all files
+        for (File file : fileList) {
+            if (file.isFile()) {
+                String fileRelativePath =
+                        mRootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
+                Entry.Builder fileEntry = Entry.newBuilder();
+                fileEntry.setName(file.getName());
+                fileEntry.setSize(file.length());
+                fileEntry.setContentId(getFileContentId(file));
+                fileEntry.setRelativePath(fileRelativePath);
+                fileEntry.setParentFolder(folderRelativePath);
+                try {
+                    TestModuleConfig tmConfig = parseTestModuleConfig(fileEntry, file);
+                    if (null != tmConfig) {
+                        fileEntry.setTestModuleConfig(tmConfig);
+                    }
+                    // get [cts]-known-failures.xml
+                    if (file.getName().endsWith(TEST_SUITE_TRADEFED_TAG)) {
+                        mRelContentBuilder.setTestSuiteTradefed(fileRelativePath);
+                        TestSuiteTradefedParser tstParser = new TestSuiteTradefedParser(file);
+                        mRelContentBuilder.addAllKnownFailures(tstParser.getKnownFailureList());
+                        mRelContentBuilder.setName(tstParser.getName());
+                        mRelContentBuilder.setFullname(tstParser.getFullname());
+                        mRelContentBuilder.setBuildNumber(tstParser.getBuildNumber());
+                        mRelContentBuilder.setTargetArch(tstParser.getTargetArch());
+                        mRelContentBuilder.setVersion(tstParser.getVersion());
+                    }
+                } catch (Exception ex) {
+                    System.err.println(String.format("Cannot parse %s", file.getAbsolutePath()));
+                    ex.printStackTrace();
+                }
+                Entry fEntry = fileEntry.build();
+                entryList.add(fEntry);
+                mEntries.put(fEntry.getRelativePath(), fEntry);
+                folderSize += file.length();
+            } else if (file.isDirectory()) {
+                // Checks subfolders
+                Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
+                subFolderEntry.setParentFolder(folderRelativePath);
+                Entry sfEntry = subFolderEntry.build();
+                entryList.add(sfEntry);
+                mEntries.put(sfEntry.getRelativePath(), sfEntry);
+                folderSize += sfEntry.getSize();
+            }
+        }
+        folderEntry.setName(folderRelativePath);
+        folderEntry.setSize(folderSize);
+        folderEntry.setType(Entry.EntryType.FOLDER);
+        folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
+        folderEntry.setRelativePath(folderRelativePath);
+        return folderEntry;
+    }
+
+    // Parse a file
+    private static TestModuleConfig parseTestModuleConfig(Entry.Builder fEntry, File file)
+            throws Exception {
+        if (file.getName().endsWith(CONFIG_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.CONFIG);
+            return parseConfigFile(file);
+        } else if (file.getName().endsWith(APK_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.APK);
+        } else if (file.getName().endsWith(JAR_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.JAR);
+        } else if (file.getName().endsWith(SO_EXT_TAG)) {
+            fEntry.setType(Entry.EntryType.SO);
+        } else {
+            // Just file in general
+            fEntry.setType(Entry.EntryType.FILE);
+        }
+        return null;
+    }
+
+    private static TestModuleConfig parseConfigFile(File file) throws Exception {
+        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+        TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
+        xmlReader.setContentHandler(testModuleXmlHandler);
+        FileReader fileReader = null;
+        try {
+            fileReader = new FileReader(file);
+            xmlReader.parse(new InputSource(fileReader));
+            return testModuleXmlHandler.getTestModuleConfig();
+        } finally {
+            if (null != fileReader) {
+                fileReader.close();
+            }
+        }
+    }
+
+    private static String getFileContentId(File file) {
+        String id = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            FileInputStream fis = new FileInputStream(file);
+            byte[] dataBytes = new byte[10240];
+            int nread = 0;
+            while ((nread = fis.read(dataBytes)) != -1) {
+                md.update(dataBytes, 0, nread);
+            }
+            // Converts to Base64 String
+            id = Base64.getEncoder().encodeToString(md.digest());
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+        }
+        return id;
+    }
+
+    private static String getFolderContentId(Entry.Builder folderEntry, List<Entry> entryList) {
+        String id = null;
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            for (Entry entry : entryList) {
+                md.update(entry.getContentId().getBytes(StandardCharsets.UTF_8));
+            }
+            // Converts to Base64 String
+            id = Base64.getEncoder().encodeToString(md.digest());
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+        }
+        return id;
+    }
+
+    // writes releaes content to a CSV file
+    public void writeRelesaeContentCsvFile(String relNameVer, String csvFile) {
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            //Header
+            pWriter.printf("release,type,name,size,relative_path,content_id,parent_folder\n");
+            for (Entry entry : getFileEntriesList()) {
+                pWriter.printf(
+                        "%s,%s,%s,%d,%s,%s,%s\n",
+                        relNameVer,
+                        entry.getType(),
+                        entry.getName(),
+                        entry.getSize(),
+                        entry.getRelativePath(),
+                        entry.getContentId(),
+                        entry.getParentFolder());
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    // writes known failures to a CSV file
+    public void writeKnownFailureCsvFile(String relNameVer, String csvFile) {
+        ReleaseContent relContent = getReleaseContent();
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            //Header
+            pWriter.printf("release,compatibility:exclude-filter\n");
+            for (String kf : relContent.getKnownFailuresList()) {
+                pWriter.printf("%s,%s\n", relNameVer, kf);
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public Collection<Entry> getFileEntriesList() {
+        return getReleaseContent().getEntries().values();
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java
new file mode 100644
index 0000000..5251f07
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestModuleConfigHandler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * {@link DefaultHandler} that builds an empty {@link ApiCoverage} object from scanning
+ * TestModule.xml.
+ */
+class TestModuleConfigHandler extends DefaultHandler {
+    private static final String CONFIGURATION_TAG = "configuration";
+    private static final String DESCRIPTION_TAG = "description";
+    private static final String OPTION_TAG = "option";
+    private static final String TARGET_PREPARER_TAG = "target_preparer";
+    private static final String TEST_TAG = "test";
+    private static final String CLASS_TAG = "class";
+    private static final String NAME_TAG = "name";
+    private static final String KEY_TAG = "key";
+    private static final String VALUE_TAG = "value";
+    private static final String MODULE_NAME_TAG = "module-name";
+    private static final String GTEST_CLASS_TAG = "com.android.tradefed.testtype.GTest";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_TAG = "jar";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+
+    private TestModuleConfig.Builder mTestModuleConfig;
+    private TestModuleConfig.TargetPreparer.Builder mTargetPreparer;
+    private TestModuleConfig.TestClass.Builder mTestCase;
+    private String mModuleName = null;
+
+    TestModuleConfigHandler(String configFileName) {
+        mTestModuleConfig = TestModuleConfig.newBuilder();
+        mTestCase = null;
+        mTargetPreparer = null;
+        // Default Module Name is the Config File Name
+        mModuleName = configFileName.replaceAll(".config$", "");
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String name, Attributes attributes)
+            throws SAXException {
+        super.startElement(uri, localName, name, attributes);
+
+        switch (localName) {
+            case CONFIGURATION_TAG:
+                if (null != attributes.getValue(DESCRIPTION_TAG)) {
+                    mTestModuleConfig.setDescription(attributes.getValue(DESCRIPTION_TAG));
+                } else {
+                    mTestModuleConfig.setDescription("WARNING: no description.");
+                }
+                break;
+            case TEST_TAG:
+                mTestCase = TestModuleConfig.TestClass.newBuilder();
+                mTestCase.setTestClass(attributes.getValue(CLASS_TAG));
+                break;
+            case TARGET_PREPARER_TAG:
+                mTargetPreparer = TestModuleConfig.TargetPreparer.newBuilder();
+                mTargetPreparer.setTestClass(attributes.getValue(CLASS_TAG));
+                break;
+            case OPTION_TAG:
+                Option.Builder option = Option.newBuilder();
+                option.setName(attributes.getValue(NAME_TAG));
+                option.setValue(attributes.getValue(VALUE_TAG));
+                String keyStr = attributes.getValue(KEY_TAG);
+                if (null != keyStr) {
+                    option.setKey(keyStr);
+                }
+                if (null != mTestCase) {
+                    mTestCase.addOptions(option);
+                    switch (option.getName()) {
+                        case JAR_TAG:
+                            mTestModuleConfig.addTestJars(option.getValue());
+                            break;
+                        case GTEST_CLASS_TAG:
+                            mModuleName = option.getValue();
+                            break;
+                    }
+                } else if (null != mTargetPreparer) {
+                    mTargetPreparer.addOptions(option);
+                    if (TEST_FILE_NAME_TAG.equalsIgnoreCase(option.getName())) {
+                        mTestModuleConfig.addTestFileNames(option.getValue());
+                    }
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String name) throws SAXException {
+        super.endElement(uri, localName, name);
+        switch (localName) {
+            case CONFIGURATION_TAG:
+                mTestModuleConfig.setModuleName(mModuleName);
+                break;
+            case TARGET_PREPARER_TAG:
+                mTestModuleConfig.addTargetPreparers(mTargetPreparer);
+                mTargetPreparer = null;
+                break;
+            case TEST_TAG:
+                mTestModuleConfig.addTestClasses(mTestCase);
+                mTestCase = null;
+                break;
+        }
+    }
+
+    public String getModuleName() {
+        return mModuleName;
+    }
+
+    public String getTestClassName() {
+        //return the 1st Test Class
+        return mTestModuleConfig.getTestClassesList().get(0).getTestClass();
+    }
+
+    public TestModuleConfig getTestModuleConfig() {
+        return mTestModuleConfig.build();
+    }
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java
new file mode 100644
index 0000000..77346f8
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteParser.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.cts.releaseparser.ReleaseProto.*;
+import com.android.tradefed.testtype.IRemoteTest;
+
+import junit.framework.Test;
+
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.AnnotationElement;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.DexFile;
+import org.jf.dexlib2.iface.Method;
+import org.jf.dexlib2.iface.value.TypeEncodedValue;
+import org.junit.runners.Suite.SuiteClasses;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Modifier;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Paths;
+import java.nio.charset.StandardCharsets;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.NoSuchAlgorithmException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+class TestSuiteParser {
+    // JUNIT3 Test suffix
+    private static final String TEST_TAG = "Test;";
+    // Some may ends with Tests e.g. cts/tests/tests/accounts/src/android/accounts/cts/AbstractAuthenticatorTests.java
+    private static final String TESTS_TAG = "Tests;";
+    private static final String TEST_PREFIX_TAG = "test";
+    private static final String DEPRECATED_ANNOTATION_TAG = "Ljava/lang/Deprecated;";
+    private static final String RUN_WITH_ANNOTATION_TAG = "Lorg/junit/runner/RunWith;";
+    private static final String TEST_ANNOTATION_TAG = "Lorg/junit/Test;";
+    private static final String SUPPRESS_ANNOTATION_TAG = "/Suppress;";
+    private static final String ANDROID_JUNIT4_TEST_TAG =
+            "Landroid/support/test/runner/AndroidJUnit4;";
+    private static final String PARAMETERIZED_TEST_TAG = "Lorg/junit/runners/Parameterized;";
+
+    // configuration option
+    private static final String NOT_SHARDABLE_TAG = "not-shardable";
+    // test class option
+    private static final String RUNTIME_HIT_TAG = "runtime-hint";
+    // com.android.tradefed.testtype.AndroidJUnitTest option
+    private static final String PACKAGE_TAG = "package";
+    // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+    private static final String JAR_TAG = "jar";
+    // com.android.tradefed.testtype.GTest option
+    private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+    private static final String MODULE_TAG = "module-name";
+    private static final String TESTCASES_FOLDER_FORMAT = "testcases/%s";
+
+    private static final String SUITE_API_INSTALLER_TAG =
+            "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+    private static final String HOST_TEST_CLASS_TAG =
+            "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+    // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+    private static final String TEST_FILE_NAME_TAG = "test-file-name";
+    // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+    private static final String PUSH_TAG = "push";
+
+    // test class
+    private static final String ANDROID_JUNIT_TEST_TAG =
+            "com.android.tradefed.testtype.AndroidJUnitTest";
+    private static final String DEQP_TEST_TAG = "com.drawelements.deqp.runner.DeqpTestRunner";
+    private static final String GTEST_TAG = "com.android.tradefed.testtype.GTest";
+    private static final String LIBCORE_TEST_TAG = "com.android.compatibility.testtype.LibcoreTest";
+    private static final String DALVIK_TEST_TAG = "com.android.compatibility.testtype.DalvikTest";
+
+    // Target File Extensions
+    private static final String CONFIG_EXT_TAG = ".config";
+    private static final String CONFIG_REGEX = ".config$";
+    private static final String JAR_EXT_TAG = ".jar";
+    private static final String APK_EXT_TAG = ".apk";
+    private static final String SO_EXT_TAG = ".so";
+
+    // [module].[class]#[method]
+    public static final String TESTCASE_NAME_FORMAT = "%s.%s#%s";
+
+    private final String mFolderPath;
+    private ReleaseContent mRelContent;
+    private final int mApiLevel;
+    private TestSuite.Builder mTSBuilder;
+
+    TestSuiteParser(ReleaseContent relContent, String folder, int apiL) {
+        mFolderPath = folder;
+        mRelContent = relContent;
+        mApiLevel = apiL;
+    }
+
+    public TestSuite getTestSuite() {
+        if (mTSBuilder == null) {
+            mTSBuilder = praseTestSuite();
+        }
+        return mTSBuilder.build();
+    }
+
+    private TestSuite.Builder praseTestSuite() {
+        TestSuite.Builder tsBuilder = TestSuite.newBuilder();
+
+        tsBuilder.setName(mRelContent.getName());
+        tsBuilder.setVersion(mRelContent.getVersion());
+        tsBuilder.setBuildNumber(mRelContent.getBuildNumber());
+
+        // Iterates all file
+        for (Entry entry : getFileEntriesList(mRelContent)) {
+            // Only parses test module config files
+            if (Entry.EntryType.CONFIG == entry.getType()) {
+                TestModuleConfig config = entry.getTestModuleConfig();
+                TestSuite.Module.Builder moduleBuilder = praseModule(config);
+                moduleBuilder.setConfigFile(entry.getRelativePath());
+                tsBuilder.addModules(moduleBuilder);
+            }
+        }
+        return tsBuilder;
+    }
+
+    private TestSuite.Module.Builder praseModule(TestModuleConfig config) {
+        TestSuite.Module.Builder moduleBuilder = TestSuite.Module.newBuilder();
+        // parse test package and class
+        List<TestModuleConfig.TestClass> testClassesList = config.getTestClassesList();
+        moduleBuilder.setName(config.getModuleName());
+        for (TestModuleConfig.TestClass tClass : testClassesList) {
+            String testClass = tClass.getTestClass();
+            moduleBuilder.setTestClass(testClass);
+            switch (testClass) {
+                case ANDROID_JUNIT_TEST_TAG:
+                    moduleBuilder.setTestType(TestSuite.TestType.ANDROIDJUNIT);
+                    parseAndroidJUnitTest(moduleBuilder, config, tClass.getTestClass());
+                    break;
+                case HOST_TEST_CLASS_TAG:
+                    moduleBuilder.setTestType(TestSuite.TestType.JAVAHOST);
+                    parseJavaHostTest(moduleBuilder, config, tClass.getTestClass());
+                    break;
+                default:
+                    //ToDo
+                    moduleBuilder.setTestType(TestSuite.TestType.UNKNOWN);
+                    TestSuite.Module.Package.Builder pkgBuilder =
+                            TestSuite.Module.Package.newBuilder();
+                    moduleBuilder.addPackages(pkgBuilder);
+                    System.err.printf(
+                            "ToDo Test Type: %s %s\n", tClass.getTestClass(), tClass.getPackage());
+            }
+        }
+        return moduleBuilder;
+    }
+
+    private void parseAndroidJUnitTest(
+            TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
+        // getting apk list from Test Module Configuration
+        List<TestModuleConfig.TargetPreparer> tPrepList = config.getTargetPreparersList();
+        for (TestModuleConfig.TargetPreparer tPrep : tPrepList) {
+            for (Option opt : tPrep.getOptionsList()) {
+                if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+                    TestSuite.Module.Package.Builder pkgBuilder =
+                            TestSuite.Module.Package.newBuilder();
+                    String testFileName = opt.getValue();
+                    Entry tEntry = getFileEntry(testFileName);
+                    pkgBuilder.setName(testFileName);
+                    pkgBuilder.setPackageFile(tEntry.getRelativePath());
+                    pkgBuilder.setContentId(tEntry.getContentId());
+                    parseApkTestCase(pkgBuilder, config);
+                    moduleBuilder.addPackages(pkgBuilder);
+                }
+            }
+        }
+    }
+
+    private Entry getFileEntry(String name) {
+        Entry fEntry = null;
+        for (Entry et : getFileEntriesList(mRelContent)) {
+            if (name.equals(et.getName())) {
+                fEntry = et;
+                break;
+            }
+        }
+        return fEntry;
+    }
+    // Parses test case list from an APK
+    private void parseApkTestCase(
+            TestSuite.Module.Package.Builder pkgBuilder, TestModuleConfig config) {
+        DexFile dexFile = null;
+        String apkPath = Paths.get(mFolderPath, pkgBuilder.getPackageFile()).toString();
+        String moduleName = config.getModuleName();
+
+        // Loads a Dex file
+        try {
+            dexFile = DexFileFactory.loadDexFile(apkPath, Opcodes.forApi(mApiLevel));
+
+            // Iterates through all clesses in the Dex file
+            for (ClassDef classDef : dexFile.getClasses()) {
+                // adjust the format Lclass/y;
+                String className = classDef.getType().replace('/', '.');
+                // remove L...;
+                if (className.length() > 2) {
+                    className = className.substring(1, className.length() - 1);
+                }
+
+                // Parses test classes
+                TestClassType cType = chkTestClassType(classDef);
+                TestSuite.Module.Package.Class.Builder tClassBuilder =
+                        TestSuite.Module.Package.Class.newBuilder();
+                switch (cType) {
+                    case JUNIT3:
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        // Checks all test method
+                        for (Method method : classDef.getMethods()) {
+                            // Only care about Public
+                            if ((method.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0) {
+                                String mName = method.getName();
+                                // Warn current test result accounting does not work well with Supress
+                                if (hasAnnotationSuffix(
+                                        method.getAnnotations(), SUPPRESS_ANNOTATION_TAG)) {
+                                    System.err.printf("%s#%s with Suppress:\n", className, mName);
+                                } else if (mName.startsWith(TEST_PREFIX_TAG)) {
+                                    // Junit3 style test case name starts with test
+                                    tClassBuilder.addMethods(
+                                            newTestBuilder(
+                                                    moduleName, className, method.getName()));
+                                } else if (hasAnnotationSuffix(
+                                        method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                    tClassBuilder.addMethods(
+                                            newTestBuilder(
+                                                    moduleName, className, method.getName()));
+                                    System.err.printf(
+                                            "%s#%s JUNIT3 mixes with %s annotation:\n",
+                                            className, mName, TEST_ANNOTATION_TAG);
+                                }
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    case PARAMETERIZED:
+                        // ToDo need to find a way to count Parameterized tests
+                        System.err.printf("To count Parameterized tests: %s\n", className);
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        for (Method method : classDef.getMethods()) {
+                            // Junit4 style test case annotated with @Test
+                            if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                tClassBuilder.addMethods(
+                                        newTestBuilder(moduleName, className, method.getName()));
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    case JUNIT4:
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        for (Method method : classDef.getMethods()) {
+                            // Junit4 style test case annotated with @Test
+                            if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                tClassBuilder.addMethods(
+                                        newTestBuilder(moduleName, className, method.getName()));
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    default:
+                        // Not a known test class
+                }
+            }
+        } catch (IOException | DexFileFactory.DexFileNotFoundException ex) {
+            System.err.println("Unable to load dex file: " + apkPath);
+            // ex.printStackTrace();
+        }
+    }
+
+    private TestSuite.Module.Package.Class.Method.Builder newTestBuilder(
+            String moduleName, String className, String testName) {
+        TestSuite.Module.Package.Class.Method.Builder testBuilder =
+                TestSuite.Module.Package.Class.Method.newBuilder();
+        testBuilder.setName(testName);
+        // Check if it's an known failure
+        String nfFilter = getKnownFailureFilter(moduleName, className, testName);
+        if (null != nfFilter) {
+            testBuilder.setKnownFailureFilter(nfFilter);
+        }
+        return testBuilder;
+    }
+
+    private void parseJavaHostTest(
+            TestSuite.Module.Builder moduleBuilder, TestModuleConfig config, String tClass) {
+        TestSuite.Module.Package.Builder pkgBuilder = TestSuite.Module.Package.newBuilder();
+        //Assuming there is only one test Jar
+        String testFileName = config.getTestJars(0);
+        Entry tEntry = getFileEntry(testFileName);
+        String jarPath = tEntry.getRelativePath();
+
+        pkgBuilder.setName(testFileName);
+        pkgBuilder.setPackageFile(jarPath);
+        pkgBuilder.setContentId(tEntry.getContentId());
+        Collection<Class<?>> classes =
+                getJarTestClasses(
+                        Paths.get(mFolderPath, jarPath).toFile(),
+                        // Includes [x]-tradefed.jar for classes such as CompatibilityHostTestBase
+                        Paths.get(mFolderPath, mRelContent.getTestSuiteTradefed()).toFile());
+
+        for (Class<?> c : classes) {
+            TestSuite.Module.Package.Class.Builder tClassBuilder =
+                    TestSuite.Module.Package.Class.newBuilder();
+            tClassBuilder.setTestClassType(TestClassType.JAVAHOST);
+            tClassBuilder.setName(c.getName());
+
+            for (java.lang.reflect.Method m : c.getMethods()) {
+                int mdf = m.getModifiers();
+                if (Modifier.isPublic(mdf) || Modifier.isProtected(mdf)) {
+                    if (m.getName().startsWith(TEST_PREFIX_TAG)) {
+                        TestSuite.Module.Package.Class.Method.Builder methodBuilder =
+                                TestSuite.Module.Package.Class.Method.newBuilder();
+                        methodBuilder.setName(m.getName());
+                        // Check if it's an known failure
+                        String nfFilter =
+                                getKnownFailureFilter(
+                                        config.getModuleName(), c.getName(), m.getName());
+                        if (null != nfFilter) {
+                            methodBuilder.setKnownFailureFilter(nfFilter);
+                        }
+                        tClassBuilder.addMethods(methodBuilder);
+                    }
+                }
+            }
+            pkgBuilder.addClasses(tClassBuilder);
+        }
+        moduleBuilder.addPackages(pkgBuilder);
+    }
+
+    private static boolean hasAnnotation(Set<? extends Annotation> annotations, String tag) {
+        for (Annotation annotation : annotations) {
+            if (annotation.getType().equals(tag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasAnnotationSuffix(Set<? extends Annotation> annotations, String tag) {
+        for (Annotation annotation : annotations) {
+            if (annotation.getType().endsWith(tag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static TestClassType chkTestClassType(ClassDef classDef) {
+        // Only care about Public Class
+        if ((classDef.getAccessFlags() & AccessFlags.PUBLIC.getValue()) == 0) {
+            return TestClassType.UNKNOWN;
+        }
+
+        for (Annotation annotation : classDef.getAnnotations()) {
+            if (annotation.getType().equals(DEPRECATED_ANNOTATION_TAG)) {
+                return TestClassType.UNKNOWN;
+            }
+            if (annotation.getType().equals(RUN_WITH_ANNOTATION_TAG)) {
+                for (AnnotationElement annotationEle : annotation.getElements()) {
+                    if ("value".equals(annotationEle.getName())) {
+                        String aValue = ((TypeEncodedValue) annotationEle.getValue()).getValue();
+                        if (ANDROID_JUNIT4_TEST_TAG.equals(aValue)) {
+                            return TestClassType.JUNIT4;
+                        } else if (PARAMETERIZED_TEST_TAG.equals(aValue)) {
+                            return TestClassType.PARAMETERIZED;
+                        }
+                    }
+                }
+                System.err.printf("Unknown test class type: %s\n", classDef.getType());
+                return TestClassType.JUNIT4;
+            }
+        }
+
+        if (classDef.getType().endsWith(TEST_TAG) || classDef.getType().endsWith(TESTS_TAG)) {
+            return TestClassType.JUNIT3;
+        } else {
+            return TestClassType.UNKNOWN;
+        }
+    }
+
+    private static boolean isTargetClass(List<String> pkgList, String className) {
+        boolean found = false;
+        for (String pkg : pkgList) {
+            if (className.startsWith(pkg)) {
+                found = true;
+                break;
+            }
+        }
+        return found;
+    }
+
+    private static Collection<Class<?>> getJarTestClasses(File jarTestFile, File tfFile)
+            throws IllegalArgumentException {
+        List<Class<?>> classes = new ArrayList<>();
+
+        try (JarFile jarFile = new JarFile(jarTestFile)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+
+            URL[] urls = {
+                new URL(String.format("jar:file:%s!/", jarTestFile.getAbsolutePath())),
+                new URL(String.format("jar:file:%s!/", tfFile.getAbsolutePath()))
+            };
+            URLClassLoader cl =
+                    URLClassLoader.newInstance(urls, JarTestFinder.class.getClassLoader());
+
+            while (e.hasMoreElements()) {
+                JarEntry je = e.nextElement();
+                if (je.isDirectory()
+                        || !je.getName().endsWith(".class")
+                        || je.getName().contains("$")
+                        || je.getName().contains("junit/")) {
+                    continue;
+                }
+                String className = getClassName(je.getName());
+
+                /*if (!className.endsWith("Test")) {
+                    continue;
+                }*/
+                try {
+                    Class<?> cls = cl.loadClass(className);
+
+                    if (IRemoteTest.class.isAssignableFrom(cls)
+                            || Test.class.isAssignableFrom(cls)) {
+                        classes.add(cls);
+                    } else if (!Modifier.isAbstract(cls.getModifiers())
+                            && hasJUnit4Annotation(cls)) {
+                        classes.add(cls);
+                    }
+                } catch (ClassNotFoundException | Error x) {
+                    System.err.println(
+                            String.format(
+                                    "Cannot find test class %s from %s",
+                                    className, jarTestFile.getName()));
+                    x.printStackTrace();
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return classes;
+    }
+
+    /** Helper to determine if we are dealing with a Test class with Junit4 annotations. */
+    protected static boolean hasJUnit4Annotation(Class<?> classObj) {
+        if (classObj.isAnnotationPresent(SuiteClasses.class)) {
+            return true;
+        }
+        if (classObj.isAnnotationPresent(RunWith.class)) {
+            return true;
+        }
+        /*for (Method m : classObj.getMethods()) {
+            if (m.isAnnotationPresent(org.junit.Test.class)) {
+                return true;
+            }
+        }*/
+        return false;
+    }
+
+    private static String getClassName(String name) {
+        // -6 because of .class
+        return name.substring(0, name.length() - 6).replace('/', '.');
+    }
+
+    private String getKnownFailureFilter(String tModule, String tClass, String tMethod) {
+        List<String> knownFailures = mRelContent.getKnownFailuresList();
+        String tsName = String.format(TESTCASE_NAME_FORMAT, tModule, tClass, tMethod);
+        for (String kf : knownFailures) {
+            if (tsName.startsWith(kf)) {
+                return kf;
+            }
+        }
+        return null;
+    }
+
+    // Iterates though all test suite content and prints them.
+    public void writeCsvFile(String relNameVer, String csvFile) {
+        TestSuite ts = getTestSuite();
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+            //Header
+            pWriter.println(
+                    "release,module,test_class,test,test_package,test_type,known_failure_filter,package_content_id");
+            for (TestSuite.Module module : ts.getModulesList()) {
+                for (TestSuite.Module.Package pkg : module.getPackagesList()) {
+                    for (TestSuite.Module.Package.Class cls : pkg.getClassesList()) {
+                        for (TestSuite.Module.Package.Class.Method mtd : cls.getMethodsList()) {
+                            pWriter.printf(
+                                    "%s,%s,%s,%s,%s,%s,%s,%s\n",
+                                    relNameVer,
+                                    module.getName(),
+                                    cls.getName(),
+                                    mtd.getName(),
+                                    pkg.getPackageFile(),
+                                    cls.getTestClassType(),
+                                    mtd.getKnownFailureFilter(),
+                                    getTestTargetContentId(pkg.getPackageFile()));
+                        }
+                    }
+                }
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    // Iterates though all test module and prints them.
+    public void writeModuleCsvFile(String relNameVer, String csvFile) {
+        TestSuite ts = getTestSuite();
+        try {
+            FileWriter fWriter = new FileWriter(csvFile);
+            PrintWriter pWriter = new PrintWriter(fWriter);
+
+            //Header
+            pWriter.print(
+                    "release,module,test_no,known_failure_no,test_type,test_class,component,description,test_config_file,test_file_names,test_jars,module_content_id\n");
+
+            for (TestSuite.Module module : ts.getModulesList()) {
+                int classCnt = 0;
+                int methodCnt = 0;
+                int kfCnt = 0;
+                for (TestSuite.Module.Package pkg : module.getPackagesList()) {
+                    for (TestSuite.Module.Package.Class cls : pkg.getClassesList()) {
+                        for (TestSuite.Module.Package.Class.Method mtd : cls.getMethodsList()) {
+                            // Filter out known failures
+                            if (mtd.getKnownFailureFilter().isEmpty()) {
+                                methodCnt++;
+                            } else {
+                                kfCnt++;
+                            }
+                        }
+                        classCnt++;
+                    }
+                }
+                String config = module.getConfigFile();
+                Entry entry = mRelContent.getEntries().get(config);
+                TestModuleConfig tmConfig = entry.getTestModuleConfig();
+                pWriter.printf(
+                        "%s,%s,%d,%d,%s,%s,%s,%s,%s,%s,%s,%s\n",
+                        relNameVer,
+                        module.getName(),
+                        methodCnt,
+                        kfCnt,
+                        module.getTestType(),
+                        module.getTestClass(),
+                        tmConfig.getComponent(),
+                        tmConfig.getDescription(),
+                        config,
+                        String.join(" ", tmConfig.getTestFileNamesList()),
+                        String.join(" ", tmConfig.getTestJarsList()),
+                        getTestModuleContentId(
+                                entry,
+                                tmConfig.getTestFileNamesList(),
+                                tmConfig.getTestJarsList()));
+            }
+            pWriter.flush();
+            pWriter.close();
+        } catch (IOException e) {
+            System.err.println("IOException:" + e.getMessage());
+        }
+    }
+
+    public static Collection<Entry> getFileEntriesList(ReleaseContent relContent) {
+        return relContent.getEntries().values();
+    }
+
+    // get Test Module Content Id = config cid + apk cids + jar cids
+    private String getTestModuleContentId(Entry config, List<String> apks, List<String> jars) {
+        String id = null;
+        //Starts with config file content_id
+        String idStr = config.getContentId();
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            //Add all apk content_id
+            for (String apk : apks) {
+                idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, apk));
+            }
+            //Add all jar content_id
+            for (String jar : jars) {
+                idStr += getTestTargetContentId(String.format(TESTCASES_FOLDER_FORMAT, jar));
+            }
+            md.update(idStr.getBytes(StandardCharsets.UTF_8));
+            // Converts to Base64 String
+            id = Base64.getEncoder().encodeToString(md.digest());
+            // System.out.println("getTestModuleContentId: " + idStr);
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("NoSuchAlgorithmException:" + e.getMessage());
+        }
+        return id;
+    }
+
+    private String getTestTargetContentId(String targetFile) {
+        Entry entry = mRelContent.getEntries().get(targetFile);
+        if (entry != null) {
+            return entry.getContentId();
+        } else {
+            System.err.println("No getTestTargetContentId: " + targetFile);
+            return "";
+        }
+    }
+
+}
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradfedParser.java b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradfedParser.java
new file mode 100644
index 0000000..b3ad861
--- /dev/null
+++ b/tools/release-parser/src/com/android/cts/releaseparser/TestSuiteTradfedParser.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.releaseparser;
+
+import com.android.tradefed.testtype.IRemoteTest;
+
+import java.lang.reflect.Modifier;
+import java.io.*;
+import java.nio.file.Paths;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import junit.framework.Test;
+
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.AnnotationElement;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.DexFile;
+import org.jf.dexlib2.iface.Method;
+import org.junit.runners.Suite.SuiteClasses;
+import org.junit.runner.RunWith;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+class TestSuiteTradefedParser {
+    private static final String TEST_SUITE_INFO_PROPERTIES_FILE = "test-suite-info.properties";
+    private static final String KNOWN_FAILURES_XML_FILE = "-known-failures.xml";
+    private static final String EXCLUDE_FILTER_TAG = "compatibility:exclude-filter";
+    private static final String NAME_TAG = "name";
+    private static final String VALUE_TAG = "value";
+
+    private File mTfFile;
+    private List<String> mKnownFailureList;
+    private String mName;
+    private String mFullname;
+    private String mBuildNumber;
+    private String mTargetArch;
+    private String mVersion;
+
+    TestSuiteTradefedParser(File tfFile) {
+        mTfFile = tfFile;
+    }
+
+    public List<String> getKnownFailureList() {
+        if (mKnownFailureList == null) {
+            mKnownFailureList = new ArrayList<String>();
+            praseKnownFailure();
+        }
+        return mKnownFailureList;
+    }
+
+    public String getName() {
+        if (mName == null) {
+            praseTestSuiteInfo();
+        }
+        return mName;
+    }
+
+    public String getFullname() {
+        if (mFullname == null) {
+            praseTestSuiteInfo();
+        }
+        return mFullname;
+    }
+
+    public String getBuildNumber() {
+        if (mBuildNumber == null) {
+            praseTestSuiteInfo();
+        }
+        return mBuildNumber;
+    }
+
+    public String getTargetArch() {
+        if (mTargetArch == null) {
+            praseTestSuiteInfo();
+        }
+        return mTargetArch;
+    }
+
+    public String getVersion() {
+        if (mVersion == null) {
+            praseTestSuiteInfo();
+        }
+        return mVersion;
+    }
+
+    private void praseKnownFailure() {
+        try {
+            ZipFile zip = new ZipFile(mTfFile);
+            try {
+                Enumeration<? extends ZipEntry> entries = zip.entries();
+                while (entries.hasMoreElements()) {
+                    ZipEntry entry = entries.nextElement();
+
+                    if (entry.getName().endsWith(KNOWN_FAILURES_XML_FILE)) {
+                        SAXParserFactory spf = SAXParserFactory.newInstance();
+                        spf.setNamespaceAware(false);
+                        SAXParser saxParser = spf.newSAXParser();
+                        InputStream xmlStream = zip.getInputStream(entry);
+                        KnownFailuresXmlHandler kfXmlHandler =
+                                new KnownFailuresXmlHandler();
+                        saxParser.parse(xmlStream, kfXmlHandler);
+                        xmlStream.close();
+                    }
+                }
+            } finally {
+                zip.close();
+            }
+        } catch (Exception e) {
+            System.err.println(String.format("Cannot praseKnownFailure %s", e.getMessage()));
+        }
+    }
+
+    private class KnownFailuresXmlHandler extends DefaultHandler {
+        @Override
+        public void startElement(String uri, String localName, String name, Attributes attributes)
+                throws SAXException {
+            super.startElement(uri, localName, name, attributes);
+            if (EXCLUDE_FILTER_TAG.equals(attributes.getValue(NAME_TAG))) {
+                String kfFilter = attributes.getValue(VALUE_TAG).replace(' ', '.');
+                mKnownFailureList.add(kfFilter);
+            }
+        }
+    }
+
+    private void praseTestSuiteInfo() {
+        try {
+            ZipFile zip = new ZipFile(mTfFile);
+            try {
+                Enumeration<? extends ZipEntry> entries = zip.entries();
+                while (entries.hasMoreElements()) {
+                    ZipEntry entry = entries.nextElement();
+
+                    if (entry.getName().equals(TEST_SUITE_INFO_PROPERTIES_FILE)) {
+                        InputStream inStream = zip.getInputStream(entry);
+                        InputStreamReader isReader = new InputStreamReader(inStream, "UTF-8");
+                        BufferedReader bfReader = new BufferedReader(isReader);
+                        String ln;
+                        while((ln = bfReader.readLine()) != null) {
+                            String[] tokens = ln.split(" = ");
+                            switch (tokens[0]) {
+                                case "build_number":
+                                    mBuildNumber = tokens[1];
+                                    break;
+                                case "target_arch":
+                                    mTargetArch = tokens[1];
+                                    break;
+                                case "name":
+                                    mName = tokens[1];
+                                    break;
+                                case "fullname":
+                                    mFullname = tokens[1];
+                                    break;
+                                case "version":
+                                    mVersion = tokens[1];
+                                    break;
+                            }
+                        }
+                        inStream.close();
+                        isReader.close();
+                        bfReader.close();
+                    }
+                }
+            } finally {
+                zip.close();
+            }
+        } catch (Exception e) {
+            System.err.println(String.format("Cannot %s %s", TEST_SUITE_INFO_PROPERTIES_FILE, e.getMessage()));
+        }
+    }
+
+}