Merge "Exempt libcore CTS tests from hidden API checks." am: 2c4dbf020f am: 6571677fe1
am: 3e6ae15899

Change-Id: Iae105eb5e14271bcc14127efe24060636656cc39
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/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/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 4730203..c2eabd2 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2754,9 +2754,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>
@@ -2768,7 +2768,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>
@@ -2798,7 +2798,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">
@@ -2814,7 +2814,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>
@@ -2831,7 +2831,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>
@@ -2849,7 +2849,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>
@@ -2866,7 +2866,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>
@@ -2884,7 +2884,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>
@@ -2901,7 +2901,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>
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.mk b/common/device-side/util/Android.mk
index 931a493..8839465 100644
--- a/common/device-side/util/Android.mk
+++ b/common/device-side/util/Android.mk
@@ -36,6 +36,8 @@
 
 LOCAL_SDK_VERSION := test_current
 
+LOCAL_JARJAR_RULES := $(LOCAL_PATH)/protobuf-jarjar-rules.txt
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
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/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/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/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/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 b67ce81..a75422b 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;
@@ -290,10 +290,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/theme/assets/Q/260dpi.zip b/hostsidetests/theme/assets/Q/260dpi.zip
index 68423a4..eb94f3c 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..9c57432 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 35390b9..813e89c 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..63833a5 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..990d536 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..775799a 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..30caca6 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/560dpi.zip b/hostsidetests/theme/assets/Q/560dpi.zip
index e1527ed..5e5f00e 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..4c5632b 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..cc056fc 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..334a0aa 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..ec20390 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..3de69ee 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..cb43250 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..8a4f210 100644
--- a/hostsidetests/theme/assets/Q/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/Q/xxxhdpi.zip
Binary files differ
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/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 577975f..09f7476 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -17,9 +17,6 @@
 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;
 
@@ -40,6 +37,7 @@
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Rule;
+import org.junit.rules.RuleChain;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
@@ -62,36 +60,43 @@
     public static final SettingsStateKeeperRule mServiceSettingsKeeper =
             new SettingsStateKeeperRule(sContext, Settings.Secure.AUTOFILL_SERVICE);
 
-    @Rule
-    public final TestWatcher watcher = new TestWatcher() {
+    private final TestWatcher mTestWatcher = new TestWatcher() {
         @Override
         protected void starting(Description description) {
-            JUnitHelper.setCurrentTestName(description.getDisplayName());
+            final String testName = description.getDisplayName();
+            Log.v(TAG, "Starting " + testName);
+            JUnitHelper.setCurrentTestName(testName);
         }
 
         @Override
         protected void finished(Description description) {
+            final String testName = description.getDisplayName();
+            Log.v(TAG, "Finished " + testName);
             JUnitHelper.setCurrentTestName(null);
         }
     };
 
-    @Rule
-    public final RetryRule mRetryRule = new RetryRule(2);
+    private final RetryRule mRetryRule = new RetryRule(2);
 
-    @Rule
-    public final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
+    private final AutofillLoggingTestRule mLoggingRule = new AutofillLoggingTestRule(TAG);
 
-    @Rule
-    public final RequiredFeatureRule mRequiredFeatureRule =
+    private final RequiredFeatureRule mRequiredFeatureRule =
             new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
 
-    @Rule
-    public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+    protected final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
             .setDumper(mLoggingRule)
             .run(() -> sReplier.assertNoUnhandledFillRequests())
             .run(() -> sReplier.assertNoUnhandledSaveRequests())
             .add(() -> { return sReplier.getExceptions(); });
 
+    @Rule
+    public final RuleChain mLookAllTheseRules = RuleChain
+            .outerRule(mTestWatcher)
+            .around(mRequiredFeatureRule)
+            .around(mLoggingRule)
+            .around(mSafeCleanerRule)
+            .around(mRetryRule);
+
     protected final Context mContext = sContext;
     protected final String mPackageName;
     protected final UiBot mUiBot;
@@ -113,8 +118,6 @@
 
     @BeforeClass
     public static void prepareScreen() throws Exception {
-        if (!hasAutofillFeature()) return;
-
         // Unlock screen.
         runShellCommand("input keyevent KEYCODE_WAKEUP");
 
@@ -127,24 +130,14 @@
     }
 
     @Before
-    public void cleanupStaticState() {
-        Helper.preTestCleanup();
-        sReplier.reset();
-    }
+    public void preTestCleanup() {
+        Log.d(TAG, "preTestCleanup()");
 
-    @Before
-    public void setVerboseLogging() {
-        try {
-            mLoggingLevel = getLoggingLevel();
-        } catch (Exception e) {
-            Log.w(TAG, "Could not get previous logging level: " + e);
-            mLoggingLevel = "debug";
-        }
-        try {
-            setLoggingLevel("verbose");
-        } catch (Exception e) {
-            Log.w(TAG, "Could not change logging level to verbose: " + e);
-        }
+        disableService();
+
+        InstrumentedAutoFillService.resetStaticState();
+        AuthenticationActivity.resetStaticState();
+        sReplier.reset();
     }
 
     /**
@@ -156,36 +149,18 @@
         WelcomeActivity.finishIt(mUiBot);
     }
 
-    @After
-    public void resetVerboseLogging() {
-        try {
-            setLoggingLevel(mLoggingLevel);
-        } catch (Exception e) {
-            Log.w(TAG, "Could not restore logging level to " + mLoggingLevel + ": " + e);
-        }
-    }
-
-    @After
-    public void ignoreFurtherRequests() {
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
-    }
-
     /**
      * Enables the {@link InstrumentedAutoFillService} for autofill for the current user.
      */
     protected void enableService() {
         Helper.enableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(false);
     }
 
     /**
      * Disables the {@link InstrumentedAutoFillService} for autofill for the current user.
      */
     protected void disableService() {
-        if (!hasAutofillFeature()) return;
-
         Helper.disableAutofillService(getContext(), SERVICE_NAME);
-        InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
     }
 
     /**
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/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 642e372..7587722 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -25,7 +25,6 @@
 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 static org.junit.Assume.assumeTrue;
@@ -48,7 +47,6 @@
  */
 abstract class CustomDescriptionWithLinkTestCase extends AutoFillServiceTestCase {
 
-    private static final String TAG = "CustomDescriptionWithLinkTestCase";
     private static final String ID_LINK = "link";
 
     /**
@@ -69,13 +67,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 +78,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/GridActivity.java b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
index 65deff5..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..b7c80cf 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) {
@@ -1199,6 +1167,26 @@
         return file;
     }
 
+    /**
+     * 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/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 42b6b41..a575f12 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;
@@ -76,16 +78,24 @@
     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;
 
     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,31 +168,40 @@
         return sServiceLabel;
     }
 
-    @Override
-    public void onConnected() {
+    private void handleConnected(boolean connected) {
         synchronized (sLock) {
-            Log.v(TAG, "onConnected(): connected=" + sConnected);
-            sConnected = true;
+            Log.v(TAG, "handleConnected(): from " + sConnected + " to " + connected);
+            sConnected = connected;
         }
     }
 
     @Override
+    public void onConnected() {
+        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;
+        }
+        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());
         }
         sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
                 cancellationSignal, callback, request.getFlags());
@@ -191,12 +210,24 @@
     @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;
+        }
+        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());
         }
         sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
                 request.getDatasetIds());
@@ -208,9 +239,7 @@
         }
     }
 
-    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 +249,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();
     }
 
     /**
@@ -406,8 +428,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 +607,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..2d69cdc 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/JUnitHelper.java
@@ -34,6 +34,10 @@
         sCurrentTestNamer = name;
     }
 
+    public static boolean isRunningTest() {
+        return sCurrentTestNamer != null;
+    }
+
     private JUnitHelper() {
         throw new UnsupportedOperationException("contain static methods only");
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index fa8fa1b..b95309d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -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);
+            }
         }
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 2108a4b..e60215d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -21,6 +21,8 @@
 
 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 +41,38 @@
 
     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());
+    }
+
+    private void handleOffer(MyEvent event) {
+        Log.v(TAG, "handleOffer: " + event);
+        Helper.offer(mEvents, event, MY_TIMEOUT.ms());
+    }
+
     @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());
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index 92fa94b..2785a48 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -75,7 +75,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -134,7 +133,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -194,7 +192,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -228,7 +225,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -286,7 +282,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mPreInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -321,7 +316,6 @@
         newActivty.getAutofillManager().requestAutofill(newActivty.mInput);
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         newActivty.syncRunOnUiThread(() -> {
@@ -367,7 +361,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/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/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index eb59da4..13e397f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -198,8 +198,12 @@
         try {
             saveTest(true);
         } finally {
-            mUiBot.setScreenOrientation(UiBot.PORTRAIT);
-            cleanUpAfterScreenOrientationIsBackToPortrait();
+            try {
+                mUiBot.setScreenOrientation(UiBot.PORTRAIT);
+                cleanUpAfterScreenOrientationIsBackToPortrait();
+            } catch (Exception e) {
+                mSafeCleanerRule.add(e);
+            }
         }
     }
 
@@ -217,7 +221,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -250,25 +253,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,7 +295,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save and start a new session right away.
         mActivity.syncRunOnUiThread(() -> {
@@ -371,7 +377,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Cancel session.
         mActivity.getAutofillManager().cancel();
@@ -422,7 +427,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -559,7 +563,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -628,7 +631,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -683,7 +685,6 @@
         }
 
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -715,7 +716,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -829,7 +829,6 @@
         // Trigger autofill.
         mActivity.getAutofillManager().requestAutofill(mActivity.mInput);
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -891,7 +890,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> {
@@ -1162,7 +1160,6 @@
         // Trigger autofill.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
         sReplier.getNextFillRequest();
-        Helper.assertHasSessions(mPackageName);
 
         // Trigger save.
         mActivity.syncRunOnUiThread(() -> mActivity.mInput.setText("108"));
@@ -1223,7 +1220,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/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index c7cca28..216ba82 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;
@@ -132,8 +134,18 @@
         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);
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 84370ca..be08f75 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -178,6 +178,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..1315798 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;
@@ -84,15 +83,11 @@
     @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/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 36be03a..29c516b 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -204,7 +204,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/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
index c1c3cf0..27a0795 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -505,7 +505,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 003f89b..4746117 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -229,7 +229,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 11e1929..2d7beae 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
@@ -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() {
@@ -573,15 +568,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/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index e187eee..9b99471 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
@@ -74,14 +74,13 @@
 import android.app.ActivityManager;
 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;
@@ -685,13 +684,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/windowmanager/src/android/server/wm/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
index ae9c5b5..1171c18 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;
 
@@ -150,13 +156,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;
@@ -166,8 +171,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);
             }
         }
 
@@ -181,17 +186,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.
@@ -199,9 +204,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/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/content/Android.mk b/tests/tests/content/Android.mk
index dc2c253..85097a1 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -56,7 +56,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/ContentProviderCursorWindowTest.java b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
index 3ab43ee..74024d2 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;
@@ -29,13 +31,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/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/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..f247e17 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 a30db52..2d90148d 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -46,6 +46,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/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/src/TestGenerated.cpp b/tests/tests/neuralnetworks/src/TestGenerated.cpp
index b9044fe..e0f1ff2 100644
--- a/tests/tests/neuralnetworks/src/TestGenerated.cpp
+++ b/tests/tests/neuralnetworks/src/TestGenerated.cpp
@@ -15,4 +15,4 @@
  */
 
 // Include corresponding NNAPI unit tests in frameworks/ml/nn/runtime/test
-#include "test/TestGenerated.cpp"
+#include "test/for-cts/TestGeneratedOneFile.cpp"
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/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 909ac35..93f53cb 100644
--- a/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -21,12 +21,17 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 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;
 
@@ -35,18 +40,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}
      */
@@ -76,6 +99,23 @@
     }
 
     /**
+     * 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.
+     */
+    public void testReadSms() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        assertReadingContentUriRequiresPermission(Telephony.Sms.CONTENT_URI,
+                android.Manifest.permission.READ_SMS);
+    }
+
+    /**
      * Verify that write to settings requires permissions.
      * <p>Tests Permission:
      *   {@link android.Manifest.permission#WRITE_SETTINGS}
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/src/android/print/test/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
index dc4ae9d..812f64e 100644
--- 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;
 
@@ -212,7 +215,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.
@@ -226,12 +230,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);
@@ -240,7 +246,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) {
@@ -252,15 +259,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
@@ -312,7 +323,7 @@
         Instrumentation instrumentation = getInstrumentation();
 
         Log.d(LOG_TAG, "enablePrintServices()");
-        enablePrintServices();
+        enablePrintServices(sDisabledPrintServicesBefore);
 
         Log.d(LOG_TAG, "enableImes()");
         enableImes();
@@ -559,54 +570,42 @@
         }
     }
 
-    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 +727,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 +864,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..24ff705 100644
--- a/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
@@ -21,9 +21,12 @@
 import android.os.Looper;
 import android.print.PrintJob;
 import android.print.PrintManager;
-import androidx.annotation.NonNull;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.TimeUnit;
+
 /**
  * Utilities for print tests
  */
@@ -97,16 +100,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 +127,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
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/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/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
index fd49e76..4487483 100644
--- a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
index 8464b2f..21d6b4c 100644
--- a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -136,6 +136,7 @@
   <cmap>
     <tableVersion version="0"/>
     <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+      <map code="0x000a" name="0em" />  <!-- \n -->
       <map code="0x0020" name="10em" />
       <map code="0x002e" name="10em" />  <!-- . -->
       <map code="0x0043" name="100em" />  <!-- C -->
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/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/uirendering/src/android/uirendering/cts/util/WebViewReadyHelper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/WebViewReadyHelper.java
index 22ee9ca..339e2f0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/WebViewReadyHelper.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/WebViewReadyHelper.java
@@ -10,6 +10,7 @@
 public final class WebViewReadyHelper {
     private final CountDownLatch mLatch;
     private final WebView mWebView;
+    private int mDrawCount = 0;
 
     public WebViewReadyHelper(WebView webview, CountDownLatch latch) {
         mWebView = webview;
@@ -38,6 +39,10 @@
     private OnDrawListener mOnDrawListener = new OnDrawListener() {
         @Override
         public void onDraw() {
+            if (++mDrawCount < 2) {
+                mWebView.postInvalidate();
+                return;
+            }
             mWebView.post(() -> {
                 mWebView.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
                 mLatch.countDown();
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index 598552b..3045d1f 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 6d61e1c..a2a33c4 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -725,6 +725,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/TextViewPrecomputedTextTest.java b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
index 282ee5f..ffa5a28 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
@@ -61,15 +61,15 @@
     @Parameterized.Parameter(5)
     public boolean differentTypeface;
     @Parameterized.Parameter(6)
-    public boolean differentFontVariationSettings;
-    @Parameterized.Parameter(7)
     public boolean differentElegantTextHeight;
-    @Parameterized.Parameter(8)
+    @Parameterized.Parameter(7)
     public boolean differentBreakStrategy;
-    @Parameterized.Parameter(9)
+    @Parameterized.Parameter(8)
     public boolean differentHyphenationFrequency;
-    @Parameterized.Parameter(10)
+    @Parameterized.Parameter(9)
     public boolean differentTextDir;
+    @Parameterized.Parameter(10)
+    public boolean differentFontVariationSettings;
 
     // text size from the default value.
     private Pair<Params, String[]> makeDifferentParams(Params params) {
@@ -156,8 +156,12 @@
         ArrayList<Object[]> allParams = new ArrayList<>();
 
         // Compute the powerset except for all false case.
-        int allParameterCount = 11;
-        for (int bits = 1; bits < (1 << allParameterCount); ++bits) {
+        final int allParameterCount = 11;
+        // The 11-th bit is for font variation settings. Don't add test case if the system don't
+        // have variable fonts.
+        final int fullBits = hasVarFont()
+                ? (1 << allParameterCount) - 1 : (1 << (allParameterCount - 1)) - 1;
+        for (int bits = 1; bits <= fullBits; ++bits) {
             Object[] param = new Object[allParameterCount];
             for (int j = 0; j < allParameterCount; ++j) {
                 param[j] = new Boolean((bits & (1 << j)) != 0);
@@ -167,6 +171,12 @@
         return allParams;
     }
 
+    private static boolean hasVarFont() {
+        final TextPaint copied = new TextPaint((new TextView(getContext())).getPaint());
+        return copied.setFontVariationSettings("'wght' 400")
+                && copied.setFontVariationSettings("'wdth' 100");
+    }
+
     @SmallTest
     @Test
     public void setText() {
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..55031cc
--- /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.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 PARAMETERS_TAG = "Lorg/junit/runners/Parameterized$Parameters";
+    private static final String ANDROID_JUNIT4_TEST_TAG = "AndroidJUnit4.class";
+    private static final String PARAMETERIZED_TAG = "Parameterized.class";
+
+    // 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;
+                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);
+                                    System.err.println(method.getAnnotations());
+                                } else if (mName.startsWith(TEST_PREFIX_TAG)) {
+                                    // Junit3 style test case name starts with test
+                                    TestSuite.Module.Package.Class.Method.Builder methodBuilder =
+                                            TestSuite.Module.Package.Class.Method.newBuilder();
+                                    methodBuilder.setName(mName);
+                                    // Check if it's an known failure
+                                    String nfFilter =
+                                            getKnownFailureFilter(moduleName, className, mName);
+                                    if (null != nfFilter) {
+                                        methodBuilder.setKnownFailureFilter(nfFilter);
+                                    }
+                                    tClassBuilder.addMethods(methodBuilder);
+                                }
+                            }
+                        }
+                        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)) {
+                                String mName = method.getName();
+                                TestSuite.Module.Package.Class.Method.Builder methodBuilder =
+                                        TestSuite.Module.Package.Class.Method.newBuilder();
+                                methodBuilder.setName(mName);
+                                // Check if it's an known failure
+                                String nfFilter =
+                                        getKnownFailureFilter(moduleName, className, mName);
+                                if (null != nfFilter) {
+                                    methodBuilder.setKnownFailureFilter(nfFilter);
+                                }
+                                tClassBuilder.addMethods(methodBuilder);
+                            }
+                        }
+                        pkgBuilder.addClasses(tClassBuilder);
+                        break;
+                    case PARAMETERIZED:
+                        // ToDo WIP
+                        tClassBuilder.setTestClassType(cType);
+                        tClassBuilder.setName(className);
+                        for (Method method : classDef.getMethods()) {
+                            if (hasAnnotation(method.getAnnotations(), TEST_ANNOTATION_TAG)) {
+                                String mName = method.getName();
+                                TestSuite.Module.Package.Class.Method.Builder methodBuilder =
+                                        TestSuite.Module.Package.Class.Method.newBuilder();
+                                methodBuilder.setName(mName);
+                                // Check if it's an known failure
+                                String nfFilter =
+                                        getKnownFailureFilter(moduleName, className, mName);
+                                if (null != nfFilter) {
+                                    methodBuilder.setKnownFailureFilter(nfFilter);
+                                }
+                                tClassBuilder.addMethods(methodBuilder);
+                            }
+                        }
+                        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 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()) {
+                    String aName = annotationEle.getName();
+                    if (aName.equals(ANDROID_JUNIT4_TEST_TAG)) {
+                        return TestClassType.JUNIT4;
+                    } else if (aName.equals(PARAMETERIZED_TAG)) {
+                        return TestClassType.PARAMETERIZED;
+                    }
+                }
+                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()));
+        }
+    }
+
+}