Merge "Update TypefaceTest to reflect API changes." into oc-dev
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index b1c8177..9e98b79 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -19,11 +19,26 @@
 import subprocess
 import time
 import sys
-import textwrap
+
+import its.caps
 import its.device
 from its.device import ItsSession
 
 CHART_DELAY = 1  # seconds
+FACING_EXTERNAL = 2
+SKIP_RET_CODE = 101  # note this must be same as tests/scene*/test_*
+
+
+def skip_sensor_fusion():
+    """Determine if sensor fusion test is skipped for this camera."""
+
+    skip_code = SKIP_RET_CODE
+    with ItsSession() as cam:
+        props = cam.get_camera_properties()
+        if (its.caps.sensor_fusion(props) and its.caps.manual_sensor(props) and
+                props['android.lens.facing'] is not FACING_EXTERNAL):
+            skip_code = None
+    return skip_code
 
 
 def main():
@@ -45,61 +60,58 @@
                all android devices.
     """
 
-    SKIP_RET_CODE = 101
-
     # Not yet mandated tests
     NOT_YET_MANDATED = {
-        "scene0":[
+        "scene0": [
             "test_jitter"
-        ],
-        "scene1":[
+            ],
+        "scene1": [
             "test_ae_af",
             "test_ae_precapture_trigger",
             "test_crop_region_raw",
             "test_ev_compensation_advanced",
             "test_ev_compensation_basic",
             "test_yuv_plus_jpeg"
-        ],
-        "scene2":[],
-        "scene3":[
+            ],
+        "scene2": [],
+        "scene3": [
             "test_lens_movement_reporting",
             "test_lens_position"
-        ],
-        "scene4":[],
-        "scene5":[],
-        "sensor_fusion":[]
+            ],
+        "scene4": [],
+        "scene5": [],
+        "sensor_fusion": []
     }
 
-    all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5"]
+    all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5",
+                  "sensor_fusion"]
 
     auto_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4"]
 
     scene_req = {
-        "scene0" : None,
-        "scene1" : "A grey card covering at least the middle 30% of the scene",
-        "scene2" : "A picture containing human faces",
-        "scene3" : "The ISO 12233 chart",
-        "scene4" : "A specific test page of a circle covering at least the "
-                   "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
-                   "for more details",
-        "scene5" : "Capture images with a diffuser attached to the camera. See "
-                   "CameraITS.pdf section 2.3.4 for more details",
-        "sensor_fusion" : "Rotating checkboard pattern. See "
-                          "sensor_fusion/SensorFusion.pdf for detailed "
-                          "instructions. Note that this test will be skipped "
-                          "on devices not supporting REALTIME camera timestamp."
-                          "If that is the case, no scene setup is required and "
-                          "you can just answer Y when being asked if the scene "
-                          "is okay"
+        "scene0": None,
+        "scene1": "A grey card covering at least the middle 30% of the scene",
+        "scene2": "A picture containing human faces",
+        "scene3": "The ISO 12233 chart",
+        "scene4": "A specific test page of a circle covering at least the "
+                  "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
+                  "for more details",
+        "scene5": "Capture images with a diffuser attached to the camera. See "
+                  "CameraITS.pdf section 2.3.4 for more details",
+        "sensor_fusion": "Rotating checkboard pattern. See "
+                         "sensor_fusion/SensorFusion.pdf for detailed "
+                         "instructions.\nNote that this test will be skipped "
+                         "on devices not supporting REALTIME camera timestamp."
     }
     scene_extra_args = {
-        "scene5" : ["doAF=False"]
+        "scene5": ["doAF=False"]
     }
 
     camera_ids = []
     scenes = []
     chart_host_id = None
     result_device_id = None
+    rot_rig_id = None
 
     for s in sys.argv[1:]:
         if s[:7] == "camera=" and len(s) > 7:
@@ -110,6 +122,9 @@
             chart_host_id = s[6:]
         elif s[:7] == 'result=' and len(s) > 7:
             result_device_id = s[7:]
+        elif s[:8] == 'rot_rig=' and len(s) > 8:
+            rot_rig_id = s[8:]  # valid values: 'default' or '$VID:$PID:$CH'
+            # The default '$VID:$PID:$CH' is '04d8:fc73:1'
 
     auto_scene_switch = chart_host_id is not None
     merge_result_switch = result_device_id is not None
@@ -128,7 +143,6 @@
             else:
                 try:
                     # Try replace "X" to "sceneX"
-                    scene_num = int(s)
                     scene_str = "scene" + s
                     if scene_str not in possible_scenes:
                         valid_scenes = False
@@ -140,7 +154,7 @@
 
         if not valid_scenes:
             print "Unknown scene specifiied:", s
-            assert(False)
+            assert False
         scenes = temp_scenes
 
     # Initialize test results
@@ -157,7 +171,7 @@
     device_id_arg = "device=" + device_id
     print "Testing device " + device_id
 
-    #Sanity Check for devices
+    # Sanity Check for devices
     device_bfp = its.device.get_device_fingerprint(device_id)
     assert device_bfp is not None
 
@@ -167,19 +181,19 @@
 
     if merge_result_switch:
         result_device_bfp = its.device.get_device_fingerprint(result_device_id)
-        assert device_bfp == result_device_bfp, \
-            "Can not merge result to a different build, from %s to %s" \
-             % (device_bfp, result_device_bfp)
+        assert_err_msg = ('Cannot merge result to a different build, from '
+                          '%s to %s' % (device_bfp, result_device_bfp))
+        assert device_bfp == result_device_bfp, assert_err_msg
 
     # user doesn't specify camera id, run through all cameras
     if not camera_ids:
         camera_ids_path = os.path.join(topdir, "camera_ids.txt")
         out_arg = "out=" + camera_ids_path
         cmd = ['python',
-               os.path.join(os.getcwd(),"tools/get_camera_ids.py"), out_arg,
+               os.path.join(os.getcwd(), "tools/get_camera_ids.py"), out_arg,
                device_id_arg]
-        retcode = subprocess.call(cmd,cwd=topdir)
-        assert(retcode == 0)
+        cam_code = subprocess.call(cmd, cwd=topdir)
+        assert cam_code == 0
         with open(camera_ids_path, "r") as f:
             for line in f:
                 camera_ids.append(line.replace('\n', ''))
@@ -196,8 +210,8 @@
             screen_id_arg = ('screen=%s' % chart_host_id)
             cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
                                           'wake_up_screen.py'), screen_id_arg]
-            retcode = subprocess.call(cmd)
-            assert retcode == 0
+            wake_code = subprocess.call(cmd)
+            assert wake_code == 0
 
     for camera_id in camera_ids:
         # Loop capturing images until user confirm test scene is correct
@@ -209,8 +223,9 @@
             os.mkdir(os.path.join(topdir, camera_id, d))
 
         for scene in scenes:
-            tests = [(s[:-3],os.path.join("tests", scene, s))
-                     for s in os.listdir(os.path.join("tests",scene))
+            skip_code = None
+            tests = [(s[:-3], os.path.join("tests", scene, s))
+                     for s in os.listdir(os.path.join("tests", scene))
                      if s[-3:] == ".py" and s[:4] == "test"]
             tests.sort()
 
@@ -219,36 +234,42 @@
             numskip = 0
             num_not_mandated_fail = 0
             numfail = 0
-            if scene_req[scene] != None:
+            validate_switch = True
+            if scene_req[scene] is not None:
                 out_path = os.path.join(topdir, camera_id, scene+".jpg")
                 out_arg = "out=" + out_path
+                if scene == 'sensor_fusion':
+                    skip_code = skip_sensor_fusion()
+                    if rot_rig_id or skip_code == SKIP_RET_CODE:
+                        validate_switch = False
+                if scene == 'scene5':
+                    validate_switch = False
                 cmd = None
                 if auto_scene_switch:
-                    if not merge_result_switch or \
-                            (merge_result_switch and camera_ids[0] == '0'):
-                        scene_arg = "scene=" + scene
+                    if (not merge_result_switch or
+                            (merge_result_switch and camera_ids[0] == '0')):
+                        scene_arg = 'scene=' + scene
                         cmd = ['python',
                                os.path.join(os.getcwd(), 'tools/load_scene.py'),
                                scene_arg, screen_id_arg]
                     else:
                         time.sleep(CHART_DELAY)
                 else:
-                    # Skip scene validation for scene 5 running in parallel
-                    if not merge_result_switch or scene != 'scene5':
-                        scene_arg = "scene=" + scene_req[scene]
+                    # Skip scene validation under certain conditions
+                    if validate_switch and not merge_result_switch:
+                        scene_arg = 'scene=' + scene_req[scene]
                         extra_args = scene_extra_args.get(scene, [])
                         cmd = ['python',
-                                os.path.join(os.getcwd(),"tools/validate_scene.py"),
-                                camera_id_arg, out_arg,
-                                scene_arg, device_id_arg] + extra_args
-
+                               os.path.join(os.getcwd(),
+                                            'tools/validate_scene.py'),
+                               camera_id_arg, out_arg,
+                               scene_arg, device_id_arg] + extra_args
                 if cmd is not None:
-                    retcode = subprocess.call(cmd,cwd=topdir)
-                    assert(retcode == 0)
+                    valid_scene_code = subprocess.call(cmd, cwd=topdir)
+                    assert valid_scene_code == 0
             print "Start running ITS on camera %s, %s" % (camera_id, scene)
-
             # Run each test, capturing stdout and stderr.
-            for (testname,testpath) in tests:
+            for (testname, testpath) in tests:
                 if auto_scene_switch:
                     if merge_result_switch and camera_ids[0] == '0':
                         # Send an input event to keep the screen not dimmed.
@@ -260,25 +281,37 @@
                         cmd = ('adb -s %s shell input keyevent FOCUS'
                                % chart_host_id)
                         subprocess.call(cmd.split())
-                cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
-                      sys.argv[1:] + [camera_id_arg]
-                outdir = os.path.join(topdir,camera_id,scene)
-                outpath = os.path.join(outdir,testname+"_stdout.txt")
-                errpath = os.path.join(outdir,testname+"_stderr.txt")
                 t0 = time.time()
-                with open(outpath,"w") as fout, open(errpath,"w") as ferr:
-                    retcode = subprocess.call(
-                            cmd,stderr=ferr,stdout=fout,cwd=outdir)
+                outdir = os.path.join(topdir, camera_id, scene)
+                outpath = os.path.join(outdir, testname+'_stdout.txt')
+                errpath = os.path.join(outdir, testname+'_stderr.txt')
+                if scene == 'sensor_fusion':
+                    if skip_code is not SKIP_RET_CODE:
+                        if rot_rig_id:
+                            print 'Rotating phone w/ rig %s' % rot_rig_id
+                            rig = ('python tools/rotation_rig.py rotator=%s' %
+                                   rot_rig_id)
+                            subprocess.Popen(rig.split())
+                        else:
+                            print 'Rotate phone 15s as shown in SensorFusion.pdf'
+                    else:
+                        test_code = skip_code
+                if skip_code is not SKIP_RET_CODE:
+                    cmd = ['python', os.path.join(os.getcwd(), testpath)]
+                    cmd += sys.argv[1:] + [camera_id_arg]
+                    with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
+                        test_code = subprocess.call(
+                            cmd, stderr=ferr, stdout=fout, cwd=outdir)
                 t1 = time.time()
 
                 test_failed = False
-                if retcode == 0:
+                if test_code == 0:
                     retstr = "PASS "
                     numpass += 1
-                elif retcode == SKIP_RET_CODE:
+                elif test_code == SKIP_RET_CODE:
                     retstr = "SKIP "
                     numskip += 1
-                elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
+                elif test_code != 0 and testname in NOT_YET_MANDATED[scene]:
                     retstr = "FAIL*"
                     num_not_mandated_fail += 1
                 else:
@@ -294,16 +327,15 @@
 
             if numskip > 0:
                 skipstr = ", %d test%s skipped" % (
-                        numskip, "s" if numskip > 1 else "")
+                    numskip, "s" if numskip > 1 else "")
             else:
                 skipstr = ""
 
             test_result = "\n%d / %d tests passed (%.1f%%)%s" % (
-                    numpass + num_not_mandated_fail, len(tests) - numskip,
-                    100.0 * float(numpass + num_not_mandated_fail) /
-                            (len(tests) - numskip)
-                            if len(tests) != numskip else 100.0,
-                    skipstr)
+                numpass + num_not_mandated_fail, len(tests) - numskip,
+                100.0 * float(numpass + num_not_mandated_fail) /
+                (len(tests) - numskip)
+                if len(tests) != numskip else 100.0, skipstr)
             print test_result
 
             if num_not_mandated_fail > 0:
@@ -316,7 +348,7 @@
 
             passed = numfail == 0
             results[scene][result_key] = (ItsSession.RESULT_PASS if passed
-                    else ItsSession.RESULT_FAIL)
+                                          else ItsSession.RESULT_FAIL)
             results[scene][ItsSession.SUMMARY_KEY] = summary_path
 
         print "Reporting ITS result to CtsVerifier"
@@ -335,15 +367,15 @@
             screen_id_arg = ('screen=%s' % chart_host_id)
             cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
                                           'turn_off_screen.py'), screen_id_arg]
-            retcode = subprocess.call(cmd)
-            assert retcode == 0
+            screen_off_code = subprocess.call(cmd)
+            assert screen_off_code == 0
 
             print 'Shutting down DUT screen: ', device_id
             screen_id_arg = ('screen=%s' % device_id)
             cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
-                                      'turn_off_screen.py'), screen_id_arg]
-            retcode = subprocess.call(cmd)
-            assert retcode == 0
+                                          'turn_off_screen.py'), screen_id_arg]
+            screen_off_code = subprocess.call(cmd)
+            assert screen_off_code == 0
 
     print "ITS tests finished. Please go back to CtsVerifier and proceed"
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 99a4ee3..341575b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -992,6 +992,7 @@
                 android:configChanges="keyboardHidden|orientation|screenSize"
                 android:screenOrientation="nosensor" />
 
+        <!-- FeatureSummaryActivity is replaced by CTS SystemFeaturesTest
         <activity android:name=".features.FeatureSummaryActivity" android:label="@string/feature_summary">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -999,6 +1000,7 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_features" />
         </activity>
+        -->
 
         <activity android:name=".location.GpsTestActivity"
                 android:label="@string/location_gps_test"
@@ -2394,6 +2396,10 @@
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_STATUS" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.BYOD_DISK_ENCRYPTION_STATUS" />
+                <category android:name="android.intent.category.DEFAULT"></category>
+            </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
             <meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
         </activity>
@@ -2420,6 +2426,7 @@
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.action.BYOD_CHECK_DISK_ENCRYPTION" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO_WITH_EXTRA_OUTPUT" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 02cb70f..1d3ea85 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2045,7 +2045,33 @@
         The only way to disable the encryption is to factory reset the device.
     </string>
     <string name="provisioning_byod_profileowner">Profile owner installed</string>
-    <string name="provisioning_byod_diskencryption">Full disk encryption enabled</string>
+    <string name="provisioning_byod_disk_encryption">Full disk encryption enabled</string>
+    <string name="provisioning_byod_disk_encryption_default_key_toast">
+        Cannot secure device with screen lock. Please re-run the test if you forgot to select
+        the \"require PIN to boot\" option.
+    </string>
+    <string name="provisioning_byod_disk_encryption_no_pin_toast">
+        No PIN is detected. Please re-run the test if you forgot to set a PIN.
+    </string>
+    <string name="provisioning_byod_set_screen_lock_dialog_message">
+        Next, you will be asked to set a screen lock for the device.\n
+        \n
+        Please set \"1111\" as the new PIN (or any other PIN that you can memorize).
+        You have to enter this PIN again later in order to finish the test.\n
+        \n
+        You may be asked whether the PIN should be required to boot the device. Please answer yes.\n
+        \n
+        Tap Go button to set a new screen lock.
+    </string>
+    <string name="provisioning_byod_remove_screen_lock_dialog_message">
+        The test is almost finished. \n
+        \n
+        Next, you will be asked to remove the screen lock that you just set. Please enter \"1111\"
+        (or your own PIN) when prompted for the old PIN, and do not set any new screen lock. This
+        is to make sure that the device returns to the initial state after this test.\n
+        \n
+        Tap Go button to remove existing screen lock.
+    </string>
     <string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
     <string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
     <string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index ffe29d2..8c779c5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -118,11 +118,16 @@
 
         ICaseResult caseResult = moduleResult.getOrCreateResult(TEST_CASE_NAME);
         int count = mAdapter.getCount();
+        int notExecutedCount = 0;
         for (int i = 0; i < count; i++) {
             TestListItem item = mAdapter.getItem(i);
             if (item.isTest()) {
                 ITestResult currentTestResult = caseResult.getOrCreateResult(item.testName);
-                currentTestResult.setResultStatus(getTestResultStatus(mAdapter.getTestResult(i)));
+                TestStatus resultStatus = getTestResultStatus(mAdapter.getTestResult(i));
+                if (resultStatus == null) {
+                    ++notExecutedCount;
+                }
+                currentTestResult.setResultStatus(resultStatus);
                 // TODO: report test details with Extended Device Info (EDI) or CTS metrics
                 // String details = mAdapter.getTestDetails(i);
 
@@ -133,6 +138,7 @@
             }
         }
         moduleResult.setDone(true);
+        moduleResult.setNotExecuted(notExecutedCount);
 
         return result;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index d1977d8..395eac0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -246,6 +246,22 @@
             new Feature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF, false),
             new Feature(PackageManager.FEATURE_PICTURE_IN_PICTURE, false),
             new Feature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, false),
+            // FEATURE_FILE_BASED_ENCRYPTION is hide
+            new Feature("android.software.file_based_encryption", false),
+    };
+
+    public static final Feature[] ALL_O_FEATURES = {
+            new Feature(PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE, false),
+            // FEATURE_TELEPHONY_CARRIERLOCK is SystemApi
+            new Feature("android.hardware.telephony.carrierlock", false),
+            new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
+            new Feature(PackageManager.FEATURE_EMBEDDED, false),
+            new Feature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP, false),
+            new Feature(PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS, false),
+            new Feature(PackageManager.FEATURE_VR_HEADTRACKING, false),
+            // FEATURE_CTS is hide
+            new Feature("android.software.cts", false),
+            new Feature(PackageManager.FEATURE_WIFI_AWARE, false),
     };
 
     @Override
@@ -279,6 +295,9 @@
 
         // add features from latest to last so that the latest requirements are put in the set first
         int apiVersion = Build.VERSION.SDK_INT;
+        if (apiVersion >= Build.VERSION_CODES.O) {
+            Collections.addAll(features, ALL_O_FEATURES);
+        }
         if (apiVersion >= Build.VERSION_CODES.N) {
             Collections.addAll(features, ALL_NYC_FEATURES);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index f52d7eb..3b5632b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.verifier.managedprovisioning;
 
+import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -23,6 +24,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.Log;
@@ -30,6 +32,7 @@
 import android.view.View.OnClickListener;
 import android.widget.Toast;
 
+import com.android.compatibility.common.util.PropertyUtil;
 import com.android.cts.verifier.ArrayTestListAdapter;
 import com.android.cts.verifier.DialogTestListActivity;
 import com.android.cts.verifier.R;
@@ -39,9 +42,10 @@
 import com.android.cts.verifier.location.LocationListenerActivity;
 
 /**
- * CTS verifier test for BYOD managed provisioning flow.
- * This activity is responsible for starting the managed provisioning flow and verify the outcome of provisioning.
- * It performs the following verifications:
+ * CTS verifier test for BYOD managed provisioning flow
+ *
+ * This activity is responsible for starting the managed provisioning flow and verify the outcome of
+ * provisioning. It performs the following verifications:
  *   Full disk encryption is enabled.
  *   Profile owner is correctly installed.
  *   Profile owner shows up in the Settings app.
@@ -51,15 +55,20 @@
  */
 public class ByodFlowTestActivity extends DialogTestListActivity {
 
-    private final String TAG = "ByodFlowTestActivity";
+    private static final String TAG = "ByodFlowTestActivity";
     private static ConnectivityManager mCm;
     private static final int REQUEST_MANAGED_PROVISIONING = 0;
     private static final int REQUEST_PROFILE_OWNER_STATUS = 1;
     private static final int REQUEST_INTENT_FILTERS_STATUS = 2;
+    private static final int REQUEST_CHECK_DISK_ENCRYPTION = 3;
+    private static final int REQUEST_SET_LOCK_FOR_ENCRYPTION = 4;
 
     private ComponentName mAdminReceiverComponent;
+    private KeyguardManager mKeyguardManager;
+    private ByodFlowTestHelper mByodFlowTestHelper;
 
     private DialogTestListItem mProfileOwnerInstalled;
+    private DialogTestListItem mDiskEncryptionTest;
     private DialogTestListItem mProfileAccountVisibleTest;
     private DialogTestListItem mDeviceAdminVisibleTest;
     private DialogTestListItem mWorkAppVisibleTest;
@@ -109,9 +118,12 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mByodFlowTestHelper = new ByodFlowTestHelper(this);
         mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+        mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
 
-        enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+        mByodFlowTestHelper.setup();
+
         mPrepareTestButton.setText(R.string.provisioning_byod_start);
         mPrepareTestButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -149,17 +161,24 @@
         switch (requestCode) {
             case REQUEST_MANAGED_PROVISIONING:
                 return;
-            case REQUEST_PROFILE_OWNER_STATUS: {
+            case REQUEST_PROFILE_OWNER_STATUS:
                 // Called after queryProfileOwner()
                 handleStatusUpdate(resultCode, data);
-            } break;
-            case REQUEST_INTENT_FILTERS_STATUS: {
+                break;
+            case REQUEST_CHECK_DISK_ENCRYPTION:
+                // Called after checkDiskEncryption()
+                handleDiskEncryptionStatus(resultCode, data);
+                break;
+            case REQUEST_SET_LOCK_FOR_ENCRYPTION:
+                // Called after handleDiskEncryptionStatus() to set screen lock if necessary
+                handleSetLockForEncryption();
+                break;
+            case REQUEST_INTENT_FILTERS_STATUS:
                 // Called after checkIntentFilters()
                 handleIntentFiltersStatus(resultCode);
-            } break;
-            default: {
+                break;
+            default:
                 super.handleActivityResult(requestCode, resultCode, data);
-            }
         }
     }
 
@@ -174,8 +193,7 @@
     public void finish() {
         // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
         // clean up the provisioned profile.
-        Utils.requestDeleteManagedProfile(this);
-        enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+        mByodFlowTestHelper.tearDown();
         super.finish();
     }
 
@@ -190,6 +208,15 @@
             }
         };
 
+        mDiskEncryptionTest = new DialogTestListItem(this,
+                R.string.provisioning_byod_disk_encryption,
+                "BYOD_DiskEncryptionTest") {
+            @Override
+            public void performTest(DialogTestListActivity activity) {
+                checkDiskEncryption();
+            }
+        };
+
         /*
          * To keep the image in this test up to date, use the instructions in
          * {@link ByodIconSamplerActivity}.
@@ -399,6 +426,11 @@
                 policyTransparencyTestIntent, null);
 
         adapter.add(mProfileOwnerInstalled);
+        if (PropertyUtil.getFirstApiLevel() >= VERSION_CODES.N_MR1) {
+            // Previous devices were not required to entangle the disk encryption key with lock
+            // screen credentials.
+            adapter.add(mDiskEncryptionTest);
+        }
 
         // Badge related tests
         adapter.add(mWorkAppVisibleTest);
@@ -605,6 +637,59 @@
         }
     }
 
+    private void checkDiskEncryption() {
+        try {
+            Intent intent = new Intent(ByodHelperActivity.ACTION_CHECK_DISK_ENCRYPTION);
+            startActivityForResult(intent, REQUEST_CHECK_DISK_ENCRYPTION);
+        } catch (ActivityNotFoundException e) {
+            Log.d(TAG, "checkDiskEncryption: ActivityNotFoundException", e);
+            setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+            Utils.showToast(this, R.string.provisioning_byod_no_activity);
+        }
+    }
+
+    private void handleDiskEncryptionStatus(int resultCode, Intent data) {
+        if (resultCode != RESULT_OK || data == null) {
+            Log.e(TAG, "Failed to get result for disk encryption, result code: " + resultCode);
+            setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+            return;
+        }
+
+        final int status = data.getIntExtra(ByodHelperActivity.EXTRA_ENCRYPTION_STATUS,
+                DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED);
+        switch (status) {
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER:
+                setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_PASSED);
+                break;
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY:
+                if (!mKeyguardManager.isDeviceSecure()) {
+                    Utils.setScreenLock(this, REQUEST_SET_LOCK_FOR_ENCRYPTION);
+                    return;
+                }
+                Log.e(TAG, "Disk encryption key is not entangled with lock screen credentials");
+                Toast.makeText(this, R.string.provisioning_byod_disk_encryption_default_key_toast,
+                        Toast.LENGTH_LONG).show();
+                // fall through
+            default:
+                setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+        }
+
+        if (mKeyguardManager.isDeviceSecure()) {
+            Utils.removeScreenLock(this);
+        }
+    }
+
+    private void handleSetLockForEncryption() {
+        if (mKeyguardManager.isDeviceSecure()) {
+            checkDiskEncryption();
+        } else {
+            setTestResult(mDiskEncryptionTest, TestResult.TEST_RESULT_FAILED);
+            Toast.makeText(this, R.string.provisioning_byod_disk_encryption_no_pin_toast,
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
     private void checkIntentFilters() {
         try {
             // Enable component HandleIntentActivity before intent filters are checked.
@@ -640,29 +725,6 @@
                 intentFiltersSet ? TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
     }
 
-    /**
-     *  Disable or enable app components in the current profile. When they are disabled only the
-     * counterpart in the other profile can respond (via cross-profile intent filter).
-     * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
-     *                      {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
-     */
-    private void enableComponent(final int enabledState) {
-        final String[] components = {
-            ByodHelperActivity.class.getName(),
-            WorkStatusTestActivity.class.getName(),
-            PermissionLockdownTestActivity.ACTIVITY_ALIAS,
-            AuthenticationBoundKeyTestActivity.class.getName(),
-            VpnTestActivity.class.getName(),
-            RecentsRedactionActivity.class.getName(),
-            CommandReceiverActivity.class.getName(),
-            SetSupportMessageActivity.class.getName()
-        };
-        for (String component : components) {
-            getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
-                    enabledState, PackageManager.DONT_KILL_APP);
-        }
-    }
-
     private void setHandleIntentActivityEnabledSetting(final int enableState) {
         getPackageManager().setComponentEnabledSetting(
             new ComponentName(ByodFlowTestActivity.this, HandleIntentActivity.class.getName()),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
new file mode 100644
index 0000000..d1308ad
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -0,0 +1,51 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class ByodFlowTestHelper {
+    private Context mContext;
+    private PackageManager mPackageManager;
+
+    public ByodFlowTestHelper(Context context) {
+        this.mContext = context;
+        this.mPackageManager = mContext.getPackageManager();
+    }
+
+    public void setup() {
+        setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+    }
+
+    /**
+     * Clean up things. This has to be working even it is called multiple times.
+     */
+    public void tearDown() {
+        Utils.requestDeleteManagedProfile(mContext);
+        setComponentsEnabledState(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
+    }
+
+    /**
+     * Disable or enable app components in the current profile. When they are disabled only the
+     * counterpart in the other profile can respond (via cross-profile intent filter).
+     *
+     * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
+     *                     {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
+     */
+    private void setComponentsEnabledState(final int enabledState) {
+        final String[] components = {
+                ByodHelperActivity.class.getName(),
+                WorkStatusTestActivity.class.getName(),
+                PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+                AuthenticationBoundKeyTestActivity.class.getName(),
+                VpnTestActivity.class.getName(),
+                RecentsRedactionActivity.class.getName(),
+                CommandReceiverActivity.class.getName(),
+                SetSupportMessageActivity.class.getName()
+        };
+        for (String component : components) {
+            mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
+                    enabledState, PackageManager.DONT_KILL_APP);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 6ad1634..e9b6523 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -81,6 +81,15 @@
     public static final String EXTRA_PROVISIONED = "extra_provisioned";
     public static final String EXTRA_PARAMETER_1 = "extra_parameter_1";
 
+    // Primary -> managed intent: check if the disk of the device is encrypted
+    public static final String ACTION_CHECK_DISK_ENCRYPTION =
+            "com.android.cts.verifier.managedprovisioning.action.BYOD_CHECK_DISK_ENCRYPTION";
+    // Managed -> primary intent: update disk encryption status in primary's CtsVerifier
+    public static final String ACTION_DISK_ENCRYPTION_STATUS =
+            "com.android.cts.verifier.managedprovisioning.action.BYOD_DISK_ENCRYPTION_STATUS";
+    // Int extra field indicating the encryption status of the device storage
+    public static final String EXTRA_ENCRYPTION_STATUS = "extra_encryption_status";
+
     // Primary -> managed intent: set unknown sources restriction and install package
     public static final String ACTION_INSTALL_APK = "com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK";
     public static final String EXTRA_ALLOW_NON_MARKET_APPS = "allow_non_market_apps";
@@ -212,6 +221,11 @@
                 mDevicePolicyManager.wipeData(0);
                 showToast(R.string.provisioning_byod_profile_deleted);
             }
+        } else if (action.equals(ACTION_CHECK_DISK_ENCRYPTION)) {
+            final int status = mDevicePolicyManager.getStorageEncryptionStatus();
+            final Intent response = new Intent(ACTION_DISK_ENCRYPTION_STATUS)
+                    .putExtra(EXTRA_ENCRYPTION_STATUS, status);
+            setResult(RESULT_OK, response);
         } else if (action.equals(ACTION_INSTALL_APK)) {
             boolean allowNonMarket = intent.getBooleanExtra(EXTRA_ALLOW_NON_MARKET_APPS, false);
             boolean wasAllowed = !isUnknownSourcesRestrictionSet();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index a59abbc..1c40ac1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -85,6 +85,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
         filter.addAction(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+        filter.addAction(ByodHelperActivity.ACTION_CHECK_DISK_ENCRYPTION);
         filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
         filter.addAction(ByodHelperActivity.ACTION_CHECK_INTENT_FILTERS);
         filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE);
@@ -122,6 +123,7 @@
         // Work -> primary direction
         filter = new IntentFilter();
         filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+        filter.addAction(ByodHelperActivity.ACTION_DISK_ENCRYPTION_STATUS);
         filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
         filter.addAction(LocationListenerActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES);
         dpm.addCrossProfileIntentFilter(getWho(context), filter,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 67e7fd4..0cae6be 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -78,6 +78,10 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Tidy up in case previous run crashed.
+        new ByodFlowTestHelper(this).tearDown();
+
         if (ACTION_CHECK_DEVICE_OWNER.equals(getIntent().getAction())) {
             DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
                     Context.DEVICE_POLICY_SERVICE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
index 1d23175..7ab8e7e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java
@@ -80,6 +80,9 @@
             return;
         }
 
+        // Tidy up in case previous run crashed.
+        new ByodFlowTestHelper(this).tearDown();
+
         setContentView(R.layout.requesting_bugreport_device_owner);
         setInfoResources(R.string.device_owner_requesting_bugreport_tests,
                 R.string.device_owner_requesting_bugreport_tests_info, 0);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index eecf9a7..f231b01 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -110,7 +110,7 @@
             Settings.ACTION_DEVICE_INFO_SETTINGS,
             Settings.ACTION_PRIVACY_SETTINGS,
             Settings.ACTION_DEVICE_INFO_SETTINGS,
-            Settings.ACTION_MANAGE_EXTERNAL_SOURCES,
+            Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
             Settings.ACTION_SYNC_SETTINGS,
             Settings.ACTION_WIRELESS_SETTINGS,
             Settings.ACTION_WIRELESS_SETTINGS,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index 92bb555..f12d698 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -17,19 +17,20 @@
 package com.android.cts.verifier.managedprovisioning;
 
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
-import android.widget.Toast;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.cts.verifier.IntentDrivenTestActivity;
 import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
-import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 
@@ -58,9 +59,8 @@
     static void requestDeleteManagedProfile(Context context) {
         try {
             Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             context.startActivity(intent);
-            String message = context.getString(R.string.provisioning_byod_delete_profile);
-            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
         }
         catch (ActivityNotFoundException e) {
             Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
@@ -93,4 +93,41 @@
     static void showToast(Context context, int messageId) {
         Toast.makeText(context, messageId, Toast.LENGTH_SHORT).show();
     }
+
+    /**
+     * Prompts the tester to set a screen lock credential, or change it if one exists.
+     *
+     * An instruction dialog is shown before the tester is sent to the ChooseLockGeneric activity
+     * in Settings.
+     *
+     * @param activity The calling activity where the result is handled
+     * @param requestCode The callback request code when the lock is set
+     */
+    static void setScreenLock(Activity activity, int requestCode) {
+        final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
+        new AlertDialog.Builder(activity)
+                .setTitle(R.string.provisioning_byod)
+                .setMessage(R.string.provisioning_byod_set_screen_lock_dialog_message)
+                .setPositiveButton(R.string.go_button_text, (DialogInterface dialog, int which) ->
+                        activity.startActivityForResult(intent, requestCode))
+                .show();
+    }
+
+    /**
+     * Prompts the tester to remove the current screen lock credential.
+     *
+     * An instruction dialog is shown before the tester is sent to the ChooseLockGeneric activity
+     * in Settings.
+     *
+     * @param activity The calling activity
+     */
+    static void removeScreenLock(Activity activity) {
+        final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
+        new AlertDialog.Builder(activity)
+                .setTitle(R.string.provisioning_byod)
+                .setMessage(R.string.provisioning_byod_remove_screen_lock_dialog_message)
+                .setPositiveButton(R.string.go_button_text, (DialogInterface dialog, int which) ->
+                        activity.startActivity(intent))
+                .show();
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
index 996a056..6794059 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
@@ -67,6 +67,7 @@
 import java.util.NoSuchElementException;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class UsbDeviceTestActivity extends PassFailButtons.Activity {
@@ -842,14 +843,12 @@
         runAndAssertException(() -> connection.requestWait(-1), IllegalArgumentException.class);
 
         long startTime = now();
-        UsbRequest req = connection.requestWait(100);
-        assertNull(req);
+        runAndAssertException(() -> connection.requestWait(100), TimeoutException.class);
         assertTrue(now() - startTime >= 100);
         assertTrue(now() - startTime < 400);
 
         startTime = now();
-        req = connection.requestWait(0);
-        assertNull(req);
+        runAndAssertException(() -> connection.requestWait(0), TimeoutException.class);
         assertTrue(now() - startTime < 400);
     }
 
@@ -860,7 +859,7 @@
      * @param in         The endpoint to receive requests from
      */
     private void receiveAfterTimeout(@NonNull UsbDeviceConnection connection,
-            @NonNull UsbEndpoint in, int timeout) throws InterruptedException {
+            @NonNull UsbEndpoint in, long timeout) throws InterruptedException, TimeoutException {
         UsbRequest reqQueued = new UsbRequest();
         ByteBuffer buffer = ByteBuffer.allocate(1);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
index 7ecdc30..a73f964 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
@@ -285,12 +285,11 @@
         // 6. request network
         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
-                peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
-                PASSPHRASE);
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
-                networkSpecifier).build();
+                mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(peerHandle)
+                        : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+                                PASSPHRASE)).build();
         CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
         cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
@@ -374,12 +373,11 @@
         // 4. Request network
         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
-                peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
-                PASSPHRASE);
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
-                networkSpecifier).build();
+                mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(peerHandle)
+                        : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+                                PASSPHRASE)).build();
         CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
         cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
index a2e0de5..3dc31e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
@@ -265,13 +265,13 @@
         // 6. Request network (as Responder) and wait for network
         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
-                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac)
-                : mWifiAwareSession.createNetworkSpecifierPassphrase(
-                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac, PASSPHRASE);
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
-                networkSpecifier).build();
+                mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac)
+                        : mWifiAwareSession.createNetworkSpecifierPassphrase(
+                                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac,
+                                PASSPHRASE)).build();
         CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
         cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
@@ -397,13 +397,13 @@
         // 8. Request network (as Initiator) and wait for network
         ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
-                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac)
-                : mWifiAwareSession.createNetworkSpecifierPassphrase(
-                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac, PASSPHRASE);
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
-                networkSpecifier).build();
+                mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac)
+                        : mWifiAwareSession.createNetworkSpecifierPassphrase(
+                                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac,
+                                PASSPHRASE)).build();
         CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
         cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
         mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
diff --git a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
index 001b18a..7cbeccf 100644
--- a/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
+++ b/apps/CtsVerifierUSBCompanion/src/com/android/cts/verifierusbcompanion/DeviceTestCompanion.java
@@ -76,7 +76,8 @@
         assertEquals(42, sizeBuffer[0]);
         assertEquals(1, numRead);
 
-        return Charset.forName("UTF-8").decode(ByteBuffer.wrap(nextTestNameBytes)).toString();
+        return Charset.forName("UTF-8").decode(
+                ByteBuffer.wrap(nextTestNameBytes)).toString().trim();
     }
 
     /**
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
new file mode 100644
index 0000000..4ac8403
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
@@ -0,0 +1,89 @@
+/*
+ * 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.compatibility.common.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A receiver that allows caller to wait for the broadcast synchronously. Notice that you should not
+ * reuse the instance. Usage is typically like this:
+ * <pre>
+ *     BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(context, "action");
+ *     try {
+ *         receiver.register();
+ *         Intent intent = receiver.awaitForBroadcast();
+ *         // assert the intent
+ *     } finally {
+ *         receiver.unregisterQuietly();
+ *     }
+ * </pre>
+ */
+public class BlockingBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = "BlockingBroadcast";
+
+    private static final int DEFAULT_TIMEOUT_SECONDS = 10;
+
+    private final BlockingQueue<Intent> mBlockingQueue;
+    private final String mExpectedAction;
+    private final Context mContext;
+
+    public BlockingBroadcastReceiver(Context context, String expectedAction) {
+        mContext = context;
+        mExpectedAction = expectedAction;
+        mBlockingQueue = new ArrayBlockingQueue<>(1);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (mExpectedAction.equals(intent.getAction())) {
+            mBlockingQueue.add(intent);
+        }
+    }
+
+    public void register() {
+        mContext.registerReceiver(this, new IntentFilter(mExpectedAction));
+    }
+
+    /**
+     * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
+     * if no broadcast with expected action is received within 10 seconds.
+     */
+    public @Nullable Intent awaitForBroadcast() {
+        try {
+            return mBlockingQueue.poll(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "waitForBroadcast get interrupted: ", e);
+        }
+        return null;
+    }
+
+    public void unregisterQuietly() {
+        try {
+            mContext.unregisterReceiver(this);
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to unregister BlockingBroadcastReceiver: ", ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
index c63b54a..19278d0 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
@@ -29,13 +29,15 @@
 import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class SilentProvisioningTestManager {
     private static final long TIMEOUT_SECONDS = 120L;
-    private static final String TAG = "SilentProvisioningTestManager";
+    private static final String TAG = "SilentProvisioningTest";
 
     private final LinkedBlockingQueue<Boolean> mProvisioningResults = new LinkedBlockingQueue(1);
 
@@ -51,18 +53,14 @@
     };
 
     private final Context mContext;
-    private final BlockingReceiver mManagedProfileProvisionedReceiver;
-    private final BlockingReceiver mManagedProfileAddedReceiver;
+    private Intent mReceivedProfileProvisionedIntent;
 
     public SilentProvisioningTestManager(Context context) {
         mContext = context.getApplicationContext();
-        mManagedProfileProvisionedReceiver =
-                new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
-        mManagedProfileAddedReceiver = new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
     }
 
     public Intent getReceviedProfileProvisionedIntent() {
-        return mManagedProfileProvisionedReceiver.getReceivedIntent();
+        return mReceivedProfileProvisionedIntent;
     }
 
     public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
@@ -82,23 +80,33 @@
     }
 
     private boolean waitManagedProfileProvisioning() throws InterruptedException {
-        mManagedProfileProvisionedReceiver.register();
-        mManagedProfileAddedReceiver.register();
+        BlockingBroadcastReceiver managedProfileProvisionedReceiver =
+                new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
+        BlockingBroadcastReceiver managedProfileAddedReceiver =
+                new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
+        try {
+            managedProfileProvisionedReceiver.register();
+            managedProfileAddedReceiver.register();
 
-        if (!pollProvisioningResult()) {
-            return false;
+            if (!pollProvisioningResult()) {
+                return false;
+            }
+
+            mReceivedProfileProvisionedIntent =
+                    managedProfileProvisionedReceiver.awaitForBroadcast();
+            if (mReceivedProfileProvisionedIntent == null) {
+                Log.i(TAG, "managedProfileProvisionedReceiver.awaitForBroadcast(): failed");
+                return false;
+            }
+
+            if (managedProfileAddedReceiver.awaitForBroadcast() == null) {
+                Log.i(TAG, "managedProfileAddedReceiver.awaitForBroadcast(): failed");
+                return false;
+            }
+        } finally {
+            managedProfileProvisionedReceiver.unregisterQuietly();
+            managedProfileAddedReceiver.unregisterQuietly();
         }
-
-        if (!mManagedProfileProvisionedReceiver.await()) {
-            Log.i(TAG, "mManagedProfileProvisionedReceiver.await(): false");
-            return false;
-        }
-
-        if (!mManagedProfileAddedReceiver.await()) {
-            Log.i(TAG, "mManagedProfileAddedReceiver.await(): false");
-            return false;
-        }
-
         return true;
     }
 
diff --git a/common/host-side/tradefed/res/report/compatibility_failures.xsl b/common/host-side/tradefed/res/report/compatibility_failures.xsl
index be65b91..ef067f2 100644
--- a/common/host-side/tradefed/res/report/compatibility_failures.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_failures.xsl
@@ -82,12 +82,6 @@
                             </td>
                         </tr>
                         <tr>
-                            <td class="rowtitle">Tests Not Executed</td>
-                            <td>
-                                <xsl:value-of select="Result/Summary/@not_executed"/>
-                            </td>
-                        </tr>
-                        <tr>
                             <td class="rowtitle">Modules Done</td>
                             <td>
                                 <xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,8 +128,8 @@
                             <th>Module</th>
                             <th>Passed</th>
                             <th>Failed</th>
-                            <th>Not Executed</th>
                             <th>Total Tests</th>
+                            <th>Done</th>
                         </tr>
                         <xsl:for-each select="Result/Module">
                             <tr>
@@ -155,10 +149,10 @@
                                     <xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
                                 </td>
                                 <td>
-                                    <xsl:value-of select="count(TestCase/Test[@result = 'not_executed'])"/>
+                                    <xsl:value-of select="count(TestCase/Test[@result = 'fail']) + @pass"/>
                                 </td>
                                 <td>
-                                    <xsl:value-of select="count(TestCase/Test[@result = 'fail']) + @pass + count(TestCase/Test[@result = 'not_executed']) "/>
+                                    <xsl:value-of select="@done"/>
                                 </td>
                             </tr>
                         </xsl:for-each> <!-- end Module -->
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsd b/common/host-side/tradefed/res/report/compatibility_result.xsd
index 9b2758c..95bae85 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsd
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsd
@@ -36,7 +36,6 @@
 
   <xs:complexType name="summaryType">
     <xs:attribute name="failed" type="xs:integer"/>
-    <xs:attribute name="not_executed" type="xs:integer"/>
     <xs:attribute name="pass" type="xs:integer"/>
   </xs:complexType>
 
@@ -121,7 +120,6 @@
     <xs:restriction base="xs:string">
       <xs:enumeration value="pass"/>
       <xs:enumeration value="fail"/>
-      <xs:enumeration value="not_executed"/>
     </xs:restriction>
   </xs:simpleType>
-</xs:schema>
\ No newline at end of file
+</xs:schema>
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsl b/common/host-side/tradefed/res/report/compatibility_result.xsl
index b8c1245..a0c337a 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsl
@@ -82,12 +82,6 @@
                             </td>
                         </tr>
                         <tr>
-                            <td class="rowtitle">Tests Not Executed</td>
-                            <td>
-                                <xsl:value-of select="Result/Summary/@not_executed"/>
-                            </td>
-                        </tr>
-                        <tr>
                             <td class="rowtitle">Modules Done</td>
                             <td>
                                 <xsl:value-of select="Result/Summary/@modules_done"/>
@@ -134,8 +128,8 @@
                             <th>Module</th>
                             <th>Passed</th>
                             <th>Failed</th>
-                            <th>Not Executed</th>
                             <th>Total Tests</th>
+                            <th>Done</th>
                         </tr>
                         <xsl:for-each select="Result/Module">
                             <tr>
@@ -150,10 +144,10 @@
                                     <xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
                                 </td>
                                 <td>
-                                    <xsl:value-of select="@not_executed"/>
+                                    <xsl:value-of select="count(TestCase/Test)"/>
                                 </td>
                                 <td>
-                                    <xsl:value-of select="count(TestCase/Test) + @not_executed"/>
+                                    <xsl:value-of select="@done"/>
                                 </td>
                             </tr>
                         </xsl:for-each> <!-- end Module -->
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index 09fed81..6fe1155 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -18,7 +18,7 @@
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.ResultHandler;
@@ -361,7 +361,6 @@
                         Integer.toString(i),
                         Integer.toString(result.countResults(TestStatus.PASS)),
                         Integer.toString(result.countResults(TestStatus.FAIL)),
-                        Integer.toString(result.getNotExecuted()),
                         moduleProgress,
                         CompatibilityBuildHelper.getDirSuffix(result.getStartTime()),
                         result.getTestPlan(),
@@ -372,9 +371,8 @@
             }
 
             // add the table header to the beginning of the list
-            table.add(0, Arrays.asList("Session", "Pass", "Fail", "Not Executed",
-                    "Modules Complete", "Result Directory", "Test Plan", "Device serial(s)",
-                    "Build ID", "Product"));
+            table.add(0, Arrays.asList("Session", "Pass", "Fail", "Modules Complete",
+                "Result Directory", "Test Plan", "Device serial(s)", "Build ID", "Product"));
             tableFormatter.displayTable(table, new PrintWriter(System.out, true));
         } else {
             printLine(String.format("No results found"));
@@ -404,7 +402,7 @@
     }
 
     private void addSubPlan(String[] flatArgs) {
-        SubPlanCreator creator = new SubPlanCreator();
+        SubPlanHelper creator = new SubPlanHelper();
         try {
             ArgsOptionParser optionParser = new ArgsOptionParser(creator);
             optionParser.parse(Arrays.asList(flatArgs));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index fc57e22..534ab85 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -17,7 +17,7 @@
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
+import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.IModuleResult;
@@ -106,6 +106,9 @@
     @Option(name = "use-log-saver", description = "Also saves generated result with log saver")
     private boolean mUseLogSaver = false;
 
+    @Option(name = "compress-logs", description = "Whether logs will be saved with compression")
+    private boolean mCompressLogs = true;
+
     private CompatibilityBuildHelper mBuildHelper;
     private File mResultDir = null;
     private File mLogDir = null;
@@ -368,23 +371,10 @@
     public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
         mCurrentModuleResult.inProgress(false);
         mCurrentModuleResult.addRuntime(elapsedTime);
-        if (!mModuleWasDone) {
-            // Not executed count now represents an upper-bound for a fix to b/33211104.
-            // Only setNotExecuted this number if the module has already been completely executed.
-            int testCountDiff = Math.max(mTotalTestsInModule - mCurrentTestNum, 0);
-            if (isShardResultReporter()) {
-                // reset value, which is added to total count for master shard upon merge
-                mCurrentModuleResult.setNotExecuted(testCountDiff);
-            } else {
-                // increment value for master shard
-                mCurrentModuleResult.setNotExecuted(mCurrentModuleResult.getNotExecuted()
-                        + testCountDiff);
-            }
-            if (mCanMarkDone) {
-                // Only mark module done if status of the invocation allows it (mCanMarkDone) and
-                // if module has not already been marked done.
-                mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
-            }
+        if (!mModuleWasDone && mCanMarkDone) {
+            // Only mark module done if status of the invocation allows it (mCanMarkDone) and
+            // if module has not already been marked done.
+            mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
         }
         if (isShardResultReporter()) {
             // Forward module results to the master.
@@ -514,6 +504,7 @@
             } else {
                 info("Test Result: %s", resultFile.getCanonicalPath());
             }
+            info("Test Logs: %s", mLogDir.getCanonicalPath());
             debug("Full Result: %s", zippedResults.getCanonicalPath());
 
             saveLog(resultFile, zippedResults);
@@ -525,11 +516,10 @@
             CLog.e(e);
         }
         // print the run results last.
-        info("Invocation finished in %s. PASSED: %d, FAILED: %d, NOT EXECUTED: %d, MODULES: %s",
+        info("Invocation finished in %s. PASSED: %d, FAILED: %d, MODULES: %s",
                 TimeUtil.formatElapsedTime(elapsedTime),
                 mResult.countResults(TestStatus.PASS),
                 mResult.countResults(TestStatus.FAIL),
-                mResult.getNotExecuted(),
                 moduleProgress);
     }
 
@@ -554,7 +544,12 @@
             return;
         }
         try {
-            File logFile = mTestLogSaver.saveAndZipLogData(name, type, stream.createInputStream());
+            File logFile = null;
+            if (mCompressLogs) {
+                logFile = mTestLogSaver.saveAndGZipLogData(name, type, stream.createInputStream());
+            } else {
+                logFile = mTestLogSaver.saveLogData(name, type, stream.createInputStream());
+            }
             debug("Saved logs for %s in %s", name, logFile.getAbsolutePath());
         } catch (IOException e) {
             warn("Failed to write log for %s", name);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
similarity index 89%
rename from common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
rename to common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9dbbcbb..950a129 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -33,11 +33,15 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+import com.android.tradefed.util.StreamUtil;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
@@ -50,7 +54,9 @@
 /**
  * Class for creating subplans from compatibility result XML.
  */
-public class SubPlanCreator {
+public class SubPlanHelper {
+
+    private static final String XML_EXT = ".xml";
 
     // result types
     public static final String PASSED = "passed";
@@ -108,22 +114,45 @@
     IInvocationResult mResult = null;
 
     /**
-     * Create an empty {@link SubPlanCreator}.
+     * Create an empty {@link SubPlanHelper}.
      * <p/>
      * All {@link Option} fields must be populated via
      * {@link com.android.tradefed.config.ArgsOptionParser}
      */
-    public SubPlanCreator() {}
+    public SubPlanHelper() {}
 
     /**
-     * Create a {@link SubPlanCreator} using the specified option values.
+     * Create a {@link SubPlanHelper} using the specified option values.
      */
-    public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
+    public SubPlanHelper(String name, int session, Collection<String> resultTypes) {
         mSubPlanName = name;
         mSessionId = session;
         mResultTypes.addAll(resultTypes);
     }
 
+    public static ISubPlan getSubPlanByName(CompatibilityBuildHelper buildHelper, String name) {
+        if (!name.endsWith(XML_EXT)) {
+            name = name + XML_EXT; // only append XML extension to name if not already there
+        }
+        InputStream subPlanInputStream = null;
+        try {
+            File subPlanFile = new File(buildHelper.getSubPlansDir(), name);
+            if (!subPlanFile.exists()) {
+                throw new IllegalArgumentException(
+                        String.format("Could not retrieve subplan \"%s\"", name));
+            }
+            subPlanInputStream = new FileInputStream(subPlanFile);
+            ISubPlan subPlan = new SubPlan();
+            subPlan.parse(subPlanInputStream);
+            return subPlan;
+        } catch (FileNotFoundException | ParseException e) {
+            throw new RuntimeException(
+                    String.format("Unable to find or parse subplan %s", name), e);
+        } finally {
+            StreamUtil.closeStream(subPlanInputStream);
+        }
+    }
+
     /**
      * Set the result from which to derive the subplan.
      * @param result
@@ -333,7 +362,7 @@
             mSubPlanName = createPlanName();
         }
         try {
-            mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + ".xml");
+            mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + XML_EXT);
             if (mSubPlanFile.exists()) {
                 throw new ConfigurationException(String.format("Subplan %s already exists",
                         mSubPlanName));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index a2dad7e..9f07307 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -19,15 +19,16 @@
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
 import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
+import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.ResultHandler;
 import com.android.compatibility.common.util.TestFilter;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ArgsOptionParser;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
@@ -153,10 +154,6 @@
             importance = Importance.ALWAYS)
     private List<String> mTestArgs = new ArrayList<>();
 
-    public enum RetryType {
-        FAILED, NOT_EXECUTED;
-    }
-
     @Option(name = RETRY_OPTION,
             shortName = 'r',
             description = "retry a previous session's failed and not executed tests.",
@@ -366,6 +363,8 @@
                 // Get the tests to run in this shard
                 modules = mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
             }
+            mExcludeFilters.clear();
+            mIncludeFilters.clear();
             // Update BuildInfo in each shard to store the original command-line arguments from
             // the session to be retried. These arguments will be serialized in the report later.
             if (mRetrySessionId != null) {
@@ -669,113 +668,45 @@
      */
     void setupFilters() throws DeviceNotAvailableException {
         if (mRetrySessionId != null) {
-            // Track --module/-m and --test/-t options to ensure we don't overwrite non-null
-            // values on retry
-            String newModuleName = mModuleName;
-            String newTestName = mTestName;
-
-            // Load the invocation result
-            IInvocationResult result = null;
-            try {
-                result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-            if (result == null) {
-                throw new IllegalArgumentException(String.format(
-                        "Could not find session with id %d", mRetrySessionId));
-            }
-
-            String oldBuildFingerprint = result.getBuildFingerprint();
-            String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint");
-            if (oldBuildFingerprint.equals(currentBuildFingerprint)) {
-                CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s",
-                        CompatibilityBuildHelper.getDirSuffix(result.getStartTime()));
-            } else {
-                throw new IllegalArgumentException(String.format(
-                        "Device build fingerprint must match %s to retry session %d",
-                        oldBuildFingerprint, mRetrySessionId));
-            }
-
-            String retryCommandLineArgs = result.getCommandLineArgs();
-            if (retryCommandLineArgs != null) {
-                try {
-                    // parse the command-line string from the result file and set options
-                    ArgsOptionParser parser = new ArgsOptionParser(this);
-                    parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this));
-                } catch (ConfigurationException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-            if ((mModuleName != null && mModuleName != newModuleName)
-                    || (mTestName != null && mTestName != newTestName)) {
-                // These options cannot be changed on retry if non-null for the previous session
-                CLog.w("Cannot override non-null value(s) from session %d for option(s) \"%s\""
-                        + " or \"%s\" on retry", mRetrySessionId, MODULE_OPTION, TEST_OPTION);
-            }
-
-            SubPlanCreator retryPlanCreator = new SubPlanCreator();
-            retryPlanCreator.setResult(result);
-            if (RetryType.FAILED.equals(mRetryType)) {
-                // retry only failed tests
-                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
-            } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
-                // retry only not executed tests
-                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
-            } else {
-                // retry both failed and not executed tests
-                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
-                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
-            }
-            try {
-                ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
-                mIncludeFilters.addAll(retryPlan.getIncludeFilters());
-                mExcludeFilters.addAll(retryPlan.getExcludeFilters());
-            } catch (ConfigurationException e) {
-                throw new RuntimeException ("Failed to create subplan for retry", e);
-            }
-        }
-        if (mSubPlan != null) {
-            try {
-                File subPlanFile = new File(mBuildHelper.getSubPlansDir(), mSubPlan + ".xml");
-                if (!subPlanFile.exists()) {
-                    throw new IllegalArgumentException(
-                            String.format("Could not retrieve subplan \"%s\"", mSubPlan));
-                }
-                InputStream subPlanInputStream = new FileInputStream(subPlanFile);
-                ISubPlan subPlan = new SubPlan();
-                subPlan.parse(subPlanInputStream);
+            RetryFilterHelper helper = new RetryFilterHelper(mBuildHelper, mRetrySessionId);
+            helper.validateBuildFingerprint(mDevice);
+            helper.setAllOptionsFrom(this);
+            helper.setCommandLineOptionsFor(this);
+            helper.populateRetryFilters();
+            mIncludeFilters = helper.getIncludeFilters();
+            mExcludeFilters = helper.getExcludeFilters();
+            helper.tearDown();
+        } else {
+            if (mSubPlan != null) {
+                ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan);
                 mIncludeFilters.addAll(subPlan.getIncludeFilters());
                 mExcludeFilters.addAll(subPlan.getExcludeFilters());
-            } catch (FileNotFoundException | ParseException e) {
-                throw new RuntimeException(
-                        String.format("Unable to find or parse subplan %s", mSubPlan), e);
             }
-        }
-        if (mModuleName != null) {
-            try {
-                List<String> modules = ModuleRepo.getModuleNamesMatching(
-                        mBuildHelper.getTestsDir(), mModuleName);
-                if (modules.size() == 0) {
-                    throw new IllegalArgumentException(
-                            String.format("No modules found matching %s", mModuleName));
-                } else if (modules.size() > 1) {
-                    throw new IllegalArgumentException(String.format(
-                            "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
-                            mModuleName, ArrayUtil.join("\n", modules)));
-                } else {
-                    String module = modules.get(0);
-                    cleanFilters(mIncludeFilters, module);
-                    cleanFilters(mExcludeFilters, module);
-                    mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
+            if (mModuleName != null) {
+                try {
+                    List<String> modules = ModuleRepo.getModuleNamesMatching(
+                            mBuildHelper.getTestsDir(), mModuleName);
+                    if (modules.size() == 0) {
+                        throw new IllegalArgumentException(
+                                String.format("No modules found matching %s", mModuleName));
+                    } else if (modules.size() > 1) {
+                        throw new IllegalArgumentException(String.format("Multiple modules found"
+                                + " matching %s:\n%s\nWhich one did you mean?\n",
+                                mModuleName, ArrayUtil.join("\n", modules)));
+                    } else {
+                        String module = modules.get(0);
+                        cleanFilters(mIncludeFilters, module);
+                        cleanFilters(mExcludeFilters, module);
+                        mIncludeFilters.add(
+                                new TestFilter(mAbiName, module, mTestName).toString());
+                    }
+                } catch (FileNotFoundException e) {
+                    throw new RuntimeException(e);
                 }
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
+            } else if (mTestName != null) {
+                throw new IllegalArgumentException(
+                        "Test name given without module name. Add --module <module-name>");
             }
-        } else if (mTestName != null) {
-            throw new IllegalArgumentException(
-                    "Test name given without module name. Add --module <module-name>");
         }
     }
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index ad12490..794e45e 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -286,6 +286,7 @@
                         configFile.getName()), e);
             }
         }
+        mExcludeFilters.clear();
         TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts);
     }
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
new file mode 100644
index 0000000..9a68089
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
@@ -0,0 +1,322 @@
+/*
+ * 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.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.LightInvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.ArrayUtil;
+
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper for generating --include-filter and --exclude-filter values on compatibility retry.
+ */
+public class RetryFilterHelper {
+
+    @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+            description = "the subplan to run",
+            importance = Importance.IF_UNSET)
+    protected String mSubPlan;
+
+    @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+            description = "the include module filters to apply.",
+            importance = Importance.ALWAYS)
+    protected Set<String> mIncludeFilters = new HashSet<>();
+
+    @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+            description = "the exclude module filters to apply.",
+            importance = Importance.ALWAYS)
+    protected Set<String> mExcludeFilters = new HashSet<>();
+
+    @Option(name = CompatibilityTest.ABI_OPTION,
+            shortName = 'a',
+            description = "the abi to test.",
+            importance = Importance.IF_UNSET)
+    protected String mAbiName = null;
+
+    @Option(name = CompatibilityTest.MODULE_OPTION,
+            shortName = 'm',
+            description = "the test module to run.",
+            importance = Importance.IF_UNSET)
+    protected String mModuleName = null;
+
+    @Option(name = CompatibilityTest.TEST_OPTION,
+            shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+            description = "the test run.",
+            importance = Importance.IF_UNSET)
+    protected String mTestName = null;
+
+    @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+            description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+            + " of a certain status. Possible values include \"failed\" and \"not_executed\".",
+            importance = Importance.IF_UNSET)
+    protected RetryType mRetryType = null;
+
+    /* Instance variables handy for retreiving the result to be retried */
+    private CompatibilityBuildHelper mBuild = null;
+    private int mSessionId;
+
+    /* Sets to be populated by retry logic and returned by getter methods */
+    private Set<String> mRetryIncludes;
+    private Set<String> mRetryExcludes;
+
+    /**
+     * Constructor for a {@link RetryFilterHelper}. Requires a CompatibilityBuildHelper for
+     * retrieving previous sessions and the ID of the session to retry.
+     */
+    public RetryFilterHelper(CompatibilityBuildHelper build, int sessionId) {
+        mBuild = build;
+        mSessionId = sessionId;
+    }
+
+    /**
+     * Throws an {@link IllegalArgumentException} if the device build fingerprint doesn't match
+     * the fingerprint recorded in the previous session's result.
+     */
+    public void validateBuildFingerprint(ITestDevice device) throws DeviceNotAvailableException {
+        String oldBuildFingerprint = new LightInvocationResult(getResult()).getBuildFingerprint();
+        String currentBuildFingerprint = device.getProperty("ro.build.fingerprint");
+        if (!oldBuildFingerprint.equals(currentBuildFingerprint)) {
+            throw new IllegalArgumentException(String.format(
+                    "Device build fingerprint must match %s to retry session %d",
+                    oldBuildFingerprint, mSessionId));
+        }
+    }
+
+    /**
+     * Copy all applicable options from an input object to this instance of RetryFilterHelper.
+     */
+    public void setAllOptionsFrom(Object obj) {
+        clearOptions(); // Remove existing options first
+        OptionCopier.copyOptionsNoThrow(obj, this);
+    }
+
+    /**
+     * Set a single option on this instance of RetryFilterHelper
+     * @throws {@link ConfigurationException} if the option cannot be set.
+     */
+    public void setOption(String option, String value) throws ConfigurationException {
+        OptionSetter setter = new OptionSetter(this);
+        setter.setOptionValue(option, value);
+    }
+
+    /**
+     * Clear all option values of this RetryFilterHelper.
+     */
+    public void clearOptions() {
+        mSubPlan = null;
+        mIncludeFilters = new HashSet<>();
+        mExcludeFilters = new HashSet<>();
+        mModuleName = null;
+        mTestName = null;
+        mRetryType = null;
+        mAbiName = null;
+    }
+
+    /**
+     * Using command-line arguments from the previous session's result, set the input object's
+     * option values to the values applied in the previous session.
+     */
+    public void setCommandLineOptionsFor(Object obj) {
+        // only need light version to retrieve command-line args
+        IInvocationResult result = new LightInvocationResult(getResult());
+        String retryCommandLineArgs = result.getCommandLineArgs();
+        if (retryCommandLineArgs != null) {
+            try {
+                // parse the command-line string from the result file and set options
+                ArgsOptionParser parser = new ArgsOptionParser(obj);
+                parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, obj));
+            } catch (ConfigurationException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve an instance of the result to retry using the instance variables referencing
+     * the build and the desired session ID. While it is faster to load this result once and
+     * store it as an instance variable, {@link IInvocationResult} objects are large, and
+     * memory is of greater concern.
+     */
+    public IInvocationResult getResult() {
+        IInvocationResult result = null;
+        try {
+            result = ResultHandler.findResult(mBuild.getResultsDir(), mSessionId);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        if (result == null) {
+            throw new IllegalArgumentException(String.format(
+                    "Could not find session with id %d", mSessionId));
+        }
+        return result;
+    }
+
+    /**
+     * Populate mRetryIncludes and mRetryExcludes based on the options and the result set for
+     * this instance of RetryFilterHelper.
+     */
+    public void populateRetryFilters() {
+        mRetryIncludes = new HashSet<>(mIncludeFilters); // reset for each population
+        mRetryExcludes = new HashSet<>(mExcludeFilters); // reset for each population
+        if (RetryType.CUSTOM.equals(mRetryType)) {
+            Set<String> customIncludes = new HashSet<>(mIncludeFilters);
+            Set<String> customExcludes = new HashSet<>(mExcludeFilters);
+            if (mSubPlan != null) {
+                ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+                customIncludes.addAll(retrySubPlan.getIncludeFilters());
+                customExcludes.addAll(retrySubPlan.getExcludeFilters());
+            }
+            // If includes were added, only use those includes. Also use excludes added directly
+            // or by subplan. Otherwise, default to normal retry.
+            if (!customIncludes.isEmpty()) {
+                mRetryIncludes.clear();
+                mRetryIncludes.addAll(customIncludes);
+                mRetryExcludes.addAll(customExcludes);
+                return;
+            }
+        }
+        // remove any extra filtering options
+        // TODO(aaronholden) remove non-plan includes (e.g. those in cts-vendor-interface)
+        // TODO(aaronholden) remove non-known-failure excludes
+        mModuleName = null;
+        mTestName = null;
+        mSubPlan = null;
+        populateFiltersBySubPlan();
+        populatePreviousSessionFilters();
+    }
+
+    /* Generation of filters based on previous sessions is implemented thoroughly in SubPlanHelper,
+     * and retry filter generation is just a subset of the use cases for the subplan retry logic.
+     * Use retry type to determine which result types SubPlanHelper targets. */
+    private void populateFiltersBySubPlan() {
+        SubPlanHelper retryPlanCreator = new SubPlanHelper();
+        retryPlanCreator.setResult(getResult());
+        if (RetryType.FAILED.equals(mRetryType)) {
+            // retry only failed tests
+            retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+        } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
+            // retry only not executed tests
+            retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+        } else {
+            // retry both failed and not executed tests
+            retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+            retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+        }
+        try {
+            ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuild);
+            mRetryIncludes.addAll(retryPlan.getIncludeFilters());
+            mRetryExcludes.addAll(retryPlan.getExcludeFilters());
+        } catch (ConfigurationException e) {
+            throw new RuntimeException ("Failed to create subplan for retry", e);
+        }
+    }
+
+    /* Retrieves the options set via command-line on the previous session, and generates/adds
+     * filters accordingly */
+    private void populatePreviousSessionFilters() {
+        // Temporarily store options from this instance in another instance
+        RetryFilterHelper tmpHelper = new RetryFilterHelper(mBuild, mSessionId);
+        tmpHelper.setAllOptionsFrom(this);
+        // Copy command-line args from previous session to this RetryFilterHelper's options
+        setCommandLineOptionsFor(this);
+
+        mRetryIncludes.addAll(mIncludeFilters);
+        mRetryExcludes.addAll(mExcludeFilters);
+        if (mSubPlan != null) {
+            ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+            mRetryIncludes.addAll(retrySubPlan.getIncludeFilters());
+            mRetryExcludes.addAll(retrySubPlan.getExcludeFilters());
+        }
+        if (mModuleName != null) {
+            try {
+                List<String> modules = ModuleRepo.getModuleNamesMatching(
+                        mBuild.getTestsDir(), mModuleName);
+                if (modules.size() == 0) {
+                    throw new IllegalArgumentException(
+                            String.format("No modules found matching %s", mModuleName));
+                } else if (modules.size() > 1) {
+                    throw new IllegalArgumentException(String.format(
+                            "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
+                            mModuleName, ArrayUtil.join("\n", modules)));
+                } else {
+                    String module = modules.get(0);
+                    cleanFilters(mRetryIncludes, module);
+                    cleanFilters(mRetryExcludes, module);
+                    mRetryIncludes.add(new TestFilter(mAbiName, module, mTestName).toString());
+                }
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        } else if (mTestName != null) {
+            throw new IllegalArgumentException(
+                "Test name given without module name. Add --module <module-name>");
+        }
+
+        // Copy options for current session back to this instance
+        setAllOptionsFrom(tmpHelper);
+    }
+
+    /* Helper method designed to remove filters in a list not applicable to the given module */
+    private static void cleanFilters(Set<String> filters, String module) {
+        Set<String> cleanedFilters = new HashSet<String>();
+        for (String filter : filters) {
+            if (module.equals(TestFilter.createFrom(filter).getName())) {
+                cleanedFilters.add(filter); // Module name matches, filter passes
+            }
+        }
+        filters.clear();
+        filters.addAll(cleanedFilters);
+    }
+
+    /** Retrieve include filters to be applied on retry */
+    public Set<String> getIncludeFilters() {
+        return new HashSet<>(mRetryIncludes);
+    }
+
+    /** Retrieve exclude filters to be applied on retry */
+    public Set<String> getExcludeFilters() {
+        return new HashSet<>(mRetryExcludes);
+    }
+
+    /** Clears retry filters and internal storage of options, except buildInfo and session ID */
+    public void tearDown() {
+        clearOptions();
+        mRetryIncludes = null;
+        mRetryExcludes = null;
+        // keep references to buildInfo and session ID
+    }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
new file mode 100644
index 0000000..b5d3cd5
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
@@ -0,0 +1,26 @@
+/*
+ * 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.compatibility.common.tradefed.util;
+
+/**
+ * Enum for --retry-type option value in compatibility testing.
+ */
+public enum RetryType {
+    FAILED,
+    NOT_EXECUTED,
+    CUSTOM;
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index bb96ed2..b6861db 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -25,7 +25,7 @@
 import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
 import com.android.compatibility.common.tradefed.result.MetadataReporterTest;
 import com.android.compatibility.common.tradefed.result.ResultReporterTest;
-import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
+import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
 import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
 import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBaseTest;
@@ -36,6 +36,7 @@
 import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
 import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
 import com.android.compatibility.common.tradefed.util.OptionHelperTest;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelperTest;
 
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -67,7 +68,7 @@
     ConsoleReporterTest.class,
     MetadataReporterTest.class,
     ResultReporterTest.class,
-    SubPlanCreatorTest.class,
+    SubPlanHelperTest.class,
 
     // targetprep
     PropertyCheckTest.class,
@@ -84,6 +85,7 @@
     // util
     CollectorUtilTest.class,
     OptionHelperTest.class,
+    RetryFilterHelperTest.class,
 })
 public class UnitTests {
     // empty on purpose
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
index c8896a9..8ef909e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
@@ -185,7 +185,6 @@
         mReporter.invocationEnded(500);
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         IInvocationResult result = mReporter.getResult();
-        assertEquals(0, result.getNotExecuted());
         assertEquals(2, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         assertEquals(1, result.getModules().size());
@@ -212,7 +211,6 @@
         mReporter.invocationEnded(500);
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         IInvocationResult result = mReporter.getResult();
-        assertEquals(1, result.getNotExecuted());
         assertEquals(1, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         // Module should not be seen as complete.
@@ -243,7 +241,6 @@
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         IInvocationResult result = mReporter.getResult();
         // FIXME: All tests should be marked as executed and not aggregating the count.
-        assertEquals(3, result.getNotExecuted());
         assertEquals(2, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         // FIXME: Module should be complete since all its test have run.
@@ -283,7 +280,6 @@
         mReporter.invocationEnded(500);
 
         IInvocationResult result = mReporter.getResult();
-        assertEquals(1, result.getNotExecuted());
         assertEquals(1, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         // Module should not be seen as complete.
@@ -313,7 +309,6 @@
         result = mReporter.getResult();
         // FIXME: We should only have 1 not_executed in the retry too. They should not aggregate
         // from one run to another.
-        assertEquals(2, result.getNotExecuted());
         assertEquals(1, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         // Module should not be seen as complete.
@@ -352,7 +347,6 @@
         mReporter.invocationEnded(500);
 
         IInvocationResult result = mReporter.getResult();
-        assertEquals(1, result.getNotExecuted());
         assertEquals(1, result.countResults(TestStatus.PASS));
         assertEquals(1, result.countResults(TestStatus.FAIL));
         // Module should not be seen as complete.
@@ -386,7 +380,6 @@
 
         // Check retry results
         result = mReporter.getResult();
-        assertEquals(0, result.getNotExecuted());
         assertEquals(3, result.countResults(TestStatus.PASS));
         assertEquals(0, result.countResults(TestStatus.FAIL));
         // Module should be marked as complete after retry.
@@ -489,7 +482,6 @@
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         // Check aggregated results to make sure it's consistent.
         IInvocationResult result = mReporter.getResult();
-        assertEquals(0, result.getNotExecuted());
         assertEquals(4, result.countResults(TestStatus.PASS));
         assertEquals(4, result.countResults(TestStatus.FAIL));
         assertEquals(2, result.getModules().size());
@@ -540,7 +532,6 @@
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         // Check aggregated results to make sure it's consistent.
         IInvocationResult result = mReporter.getResult();
-        assertEquals(4, result.getNotExecuted());
         assertEquals(4, result.countResults(TestStatus.PASS));
         assertEquals(4, result.countResults(TestStatus.FAIL));
         assertEquals(2, result.getModules().size());
@@ -592,7 +583,6 @@
         EasyMock.verify(mMockDevice, mMockBuildInfo);
 
         IInvocationResult result = mReporter.getResult();
-        assertEquals(0, result.getNotExecuted());
         assertEquals(2, result.countResults(TestStatus.PASS));
         assertEquals(2, result.countResults(TestStatus.FAIL));
         // FIXME: Only one module should be expected since within the one shard requested to run
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
index 763acb7..23fcb9a 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
@@ -93,7 +93,6 @@
         mInvocationResult = resultReporter.getResult();
         mModuleResult = mInvocationResult.getOrCreateModule("Module-1");
         mModuleResult.setDone(true);
-        mModuleResult.setNotExecuted(0);
         ICaseResult caseResult = mModuleResult.getOrCreateResult("Case-1");
         ITestResult test1 = caseResult.getOrCreateResult("Test1");
         test1.passed(mReportLog);
@@ -103,7 +102,6 @@
         IModuleResult moduleResult2 = mInvocationResult.getOrCreateModule("Module-2");
         ICaseResult caseResult2 = moduleResult2.getOrCreateResult("Case-2");
         mModuleResult.setDone(false);
-        mModuleResult.setNotExecuted(1);
         ITestResult test3 = caseResult2.getOrCreateResult("Test3");
         test3.passed(mReportLog);
 
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
similarity index 96%
rename from common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
rename to common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
index e3240c1..db6ca6e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
@@ -39,7 +39,7 @@
 import java.util.Arrays;
 import java.util.Set;
 
-public class SubPlanCreatorTest extends TestCase {
+public class SubPlanHelperTest extends TestCase {
 
     // Values used to populate mock results
     private static final String SUITE_NAME = "CTS";
@@ -75,7 +75,7 @@
     private static final String SP_RESULT_TYPE_NOT_EXECUTED = "not_executed";
 
     private CompatibilityBuildHelper mBuildHelper;
-    private SubPlanCreator mSubPlanCreator;
+    private SubPlanHelper mSubPlanHelper;
 
     private File mResultsDir = null;
     private File mResultDir = null;
@@ -89,8 +89,8 @@
         mBuildHelper = new SpctMockCompatibilityBuildHelper(new BuildInfo("0", "", ""));
         populateResults();
 
-        mSubPlanCreator = new SubPlanCreator();
-        ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanCreator);
+        mSubPlanHelper = new SubPlanHelper();
+        ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanHelper);
         optionParser.parse(Arrays.asList(
             "-n", SP_NAME,
             "--session", SP_SESSION,
@@ -109,7 +109,7 @@
     }
 
     public void testCreateSubPlan() throws Exception {
-        ISubPlan plan = mSubPlanCreator.createSubPlan(mBuildHelper);
+        ISubPlan plan = mSubPlanHelper.createSubPlan(mBuildHelper);
         Set<String> planIncludes = plan.getIncludeFilters();
         Set<String> planExcludes = plan.getExcludeFilters();
         TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
@@ -137,7 +137,6 @@
         moduleATest1.setResultStatus(TestStatus.PASS);
         ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
         moduleATest2.setResultStatus(null); // not executed test
-        moduleA.setNotExecuted(1);
 
         IModuleResult moduleB = result.getOrCreateModule(ID_B);
         moduleB.setDone(true);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
new file mode 100644
index 0000000..05d35ec
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.tradefed.config.OptionSetter;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link RetryFilterHelper}
+ */
+public class RetryFilterHelperTest extends TestCase {
+
+    private static final String TEST_STRING = "abcd";
+    private static final RetryType TEST_RETRY_TYPE = RetryType.FAILED;
+
+    public void testSetAllOptionsFrom() throws Exception {
+        RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+        RetryFilterHelper otherObj = new RetryFilterHelper(null, 0);
+        OptionSetter otherObjSetter = new OptionSetter(otherObj);
+        otherObjSetter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+        helper.setAllOptionsFrom(otherObj);
+        assertEquals(TEST_STRING, helper.mSubPlan);
+    }
+
+    public void testClearOptions() throws Exception {
+        RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+        OptionSetter setter = new OptionSetter(helper);
+        setter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.INCLUDE_FILTER_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.EXCLUDE_FILTER_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.ABI_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.MODULE_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_RETRY_TYPE.name());
+        helper.clearOptions();
+        assertTrue(helper.mSubPlan == null);
+        assertTrue(helper.mIncludeFilters.isEmpty());
+        assertTrue(helper.mExcludeFilters.isEmpty());
+        assertTrue(helper.mAbiName == null);
+        assertTrue(helper.mModuleName == null);
+        assertTrue(helper.mTestName == null);
+        assertTrue(helper.mRetryType == null);
+    }
+
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
index 32fa532..ce39f38 100644
--- a/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
+++ b/common/util/src/com/android/compatibility/common/util/ChecksumReporter.java
@@ -331,7 +331,6 @@
         sb.append(buildFingerprint).append(SEPARATOR)
                 .append(module.getId()).append(SEPARATOR)
                 .append(module.isDone()).append(SEPARATOR)
-                .append(module.getNotExecuted()).append(SEPARATOR)
                 .append(module.countResults(TestStatus.FAIL));
         return sb.toString();
     }
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 6306287..5f88bcd 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -93,7 +93,6 @@
     private static final String MODULES_DONE_ATTR = "modules_done";
     private static final String MODULES_TOTAL_ATTR = "modules_total";
     private static final String NAME_ATTR = "name";
-    private static final String NOT_EXECUTED_ATTR = "not_executed";
     private static final String OS_ARCH_ATTR = "os_arch";
     private static final String OS_NAME_ATTR = "os_name";
     private static final String OS_VERSION_ATTR = "os_version";
@@ -210,9 +209,6 @@
                 boolean done = Boolean.parseBoolean(parser.getAttributeValue(NS, DONE_ATTR));
                 IModuleResult module = invocation.getOrCreateModule(moduleId);
                 module.initializeDone(done);
-                int notExecuted = Integer.parseInt(
-                        parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
-                module.setNotExecuted(notExecuted);
                 long runtime = Long.parseLong(parser.getAttributeValue(NS, RUNTIME_ATTR));
                 module.addRuntime(runtime);
                 while (parser.nextTag() == XmlPullParser.START_TAG) {
@@ -300,7 +296,6 @@
                     throws IOException, XmlPullParserException {
         int passed = result.countResults(TestStatus.PASS);
         int failed = result.countResults(TestStatus.FAIL);
-        int notExecuted = result.getNotExecuted();
         File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
         OutputStream stream = new FileOutputStream(resultFile);
         XmlSerializer serializer = XmlPullParserFactory.newInstance(TYPE, null).newSerializer();
@@ -371,7 +366,6 @@
         serializer.startTag(NS, SUMMARY_TAG);
         serializer.attribute(NS, PASS_ATTR, Integer.toString(passed));
         serializer.attribute(NS, FAILED_ATTR, Integer.toString(failed));
-        serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(notExecuted));
         serializer.attribute(NS, MODULES_DONE_ATTR,
                 Integer.toString(result.getModuleCompleteCount()));
         serializer.attribute(NS, MODULES_TOTAL_ATTR,
@@ -385,7 +379,6 @@
             serializer.attribute(NS, ABI_ATTR, module.getAbi());
             serializer.attribute(NS, RUNTIME_ATTR, String.valueOf(module.getRuntime()));
             serializer.attribute(NS, DONE_ATTR, Boolean.toString(module.isDone()));
-            serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(module.getNotExecuted()));
             serializer.attribute(NS, PASS_ATTR,
                     Integer.toString(module.countResults(TestStatus.PASS)));
             for (ICaseResult cr : module.getResults()) {
diff --git a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
index 4035dd3..49c4045 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
@@ -49,8 +49,6 @@
     private static final String NAME_B = "ModuleB";
     private static final String DONE_A = "false";
     private static final String DONE_B = "true";
-    private static final String NOT_EXECUTED_A = "1";
-    private static final String NOT_EXECUTED_B = "0";
     private static final String RUNTIME_A = "100";
     private static final String RUNTIME_B = "200";
     private static final String ABI = "mips64";
@@ -105,10 +103,10 @@
             "  <Build build_fingerprint=\"%s\" " + BUILD_ID + "=\"%s\" " +
                BUILD_PRODUCT + "=\"%s\" />\n";
     private static final String XML_SUMMARY =
-            "  <Summary pass=\"%d\" failed=\"%d\" not_executed=\"%d\" " +
+            "  <Summary pass=\"%d\" failed=\"%d\" " +
             "modules_done=\"1\" modules_total=\"1\" />\n";
     private static final String XML_MODULE =
-            "  <Module name=\"%s\" abi=\"%s\" device=\"%s\" runtime=\"%s\" done=\"%s\" not_executed=\"%s\">\n" +
+            "  <Module name=\"%s\" abi=\"%s\" device=\"%s\" runtime=\"%s\" done=\"%s\">\n" +
             "%s" +
             "  </Module>\n";
     private static final String XML_CASE =
@@ -167,7 +165,6 @@
         moduleATest1.setResultStatus(TestStatus.PASS);
         ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
         moduleATest2.setResultStatus(null); // not executed test
-        moduleA.setNotExecuted(1);
         // Module B: test3 fails, test4 passes with report log, test5 passes with skip
         IModuleResult moduleB = result.getOrCreateModule(ID_B);
         moduleB.setDone(true);
@@ -231,7 +228,7 @@
             String moduleATest = String.format(XML_TEST_PASS, METHOD_1);
             String moduleACases = String.format(XML_CASE, CLASS_A, moduleATest);
             String moduleA = String.format(XML_MODULE, NAME_A, ABI, DEVICE_A, RUNTIME_A, DONE_A,
-                    NOT_EXECUTED_A, moduleACases);
+                    moduleACases);
             String moduleBTest3 = String.format(XML_TEST_FAIL, METHOD_3, MESSAGE, STACK_TRACE,
                     BUG_REPORT, LOGCAT, SCREENSHOT);
             String moduleBTest4 = String.format(XML_TEST_RESULT, METHOD_4, SUMMARY_SOURCE,
@@ -241,7 +238,7 @@
             String moduleBTests = String.join("", moduleBTest3, moduleBTest4, moduleBTest5);
             String moduleBCases = String.format(XML_CASE, CLASS_B, moduleBTests);
             String moduleB = String.format(XML_MODULE, NAME_B, ABI, DEVICE_B, RUNTIME_B, DONE_B,
-                    NOT_EXECUTED_B, moduleBCases);
+                    moduleBCases);
             String modules = String.join("", moduleA, moduleB);
             String hostName = "";
             try {
@@ -265,7 +262,6 @@
     static void checkLightResult(IInvocationResult lightResult) throws Exception {
         assertEquals("Expected 3 passes", 3, lightResult.countResults(TestStatus.PASS));
         assertEquals("Expected 1 failure", 1, lightResult.countResults(TestStatus.FAIL));
-        assertEquals("Expected 1 not executed", 1, lightResult.getNotExecuted());
 
         Map<String, String> buildInfo = lightResult.getInvocationInfo();
         assertEquals("Incorrect Build ID", EXAMPLE_BUILD_ID, buildInfo.get(BUILD_ID));
@@ -287,7 +283,6 @@
     static void checkResult(IInvocationResult result) throws Exception {
         assertEquals("Expected 3 passes", 3, result.countResults(TestStatus.PASS));
         assertEquals("Expected 1 failure", 1, result.countResults(TestStatus.FAIL));
-        assertEquals("Expected 1 not executed", 1, result.getNotExecuted());
 
         Map<String, String> buildInfo = result.getInvocationInfo();
         assertEquals("Incorrect Build ID", EXAMPLE_BUILD_ID, buildInfo.get(BUILD_ID));
diff --git a/development/ide/eclipse/cts-tradefed-harness.classpath b/development/ide/eclipse/cts-tradefed-harness.classpath
new file mode 100644
index 0000000..3355a7d
--- /dev/null
+++ b/development/ide/eclipse/cts-tradefed-harness.classpath
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="cts/common/host-side/tradefed/src"/>
+	<classpathentry kind="src" path="cts/common/host-side/util/src"/>
+	<classpathentry kind="src" path="cts/common/util/src"/>
+	<classpathentry kind="src" path="cts/libs/json/src"/>
+	<classpathentry kind="src" path="tools/loganalysis/src"/>
+	<classpathentry kind="src" path="tools/tradefederation/src"/>
+	<classpathentry kind="src" path="tools/tradefederation/remote/src"/>
+	<classpathentry kind="src" path="external/easymock/src"/>
+	<classpathentry kind="src" path="external/guava/guava/src"/>
+	<classpathentry kind="src" path="external/hamcrest/hamcrest-core/src/main/java"/>
+	<classpathentry kind="src" path="external/jline/src/src/main/java"/>
+	<classpathentry kind="src" path="external/jsr305/ri/src/main/java"/>
+	<classpathentry kind="src" path="external/junit/src/main/java"/>
+	<classpathentry kind="src" path="external/mockito/src/main/java"/>
+	<classpathentry kind="src" path="external/objenesis/main/src/main/java"/>
+	<classpathentry kind="src" path="external/protobuf/java/core/src/main/java"/>
+	<classpathentry including="com/" kind="src" path="out/host/common/obj/JAVA_LIBRARIES/cts-tradefed_intermediates"/>
+	<classpathentry kind="src" path="out/host/common/obj/JAVA_LIBRARIES/host-libprotobuf-java-full_intermediates/proto/src"/>
+	<classpathentry kind="src" path="out/host/common/obj/JAVA_LIBRARIES/tradefed-protos_intermediates/proto/src"/>
+	<classpathentry kind="src" path="tools/tradefederation/tests/src"/>
+	<classpathentry kind="src" path="cts/common/host-side/tradefed/tests/src"/>
+	<classpathentry kind="src" path="cts/common/host-side/util/tests/src"/>
+	<classpathentry kind="lib" path="external/mockito/lib/byte-buddy-1.6.9.jar"/>
+	<classpathentry kind="lib" path="external/mockito/lib/byte-buddy-agent-1.6.9.jar"/>
+	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/bouncycastle_intermediates/classes-jarjar.jar"/>
+	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/conscrypt_intermediates/classes-jarjar.jar"/>
+	<classpathentry kind="lib" path="out/target/common/obj/JAVA_LIBRARIES/okhttp_intermediates/classes-jarjar.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/commons-compress/commons-compress-prebuilt.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/ddmlib/ddmlib-prebuilt.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/devtools-annotations/devtools-annotations-prebuilt.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/json/json-prebuilt.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/kxml2/kxml2-2.3.0.jar"/>
+	<classpathentry kind="lib" path="prebuilts/misc/common/sdklib/sdklib-prebuilt.jar"/>
+	<classpathentry kind="lib" path="prebuilts/sdk/tools/jack-jacoco-reporter.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 6ee51ae..e8dca57 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -291,29 +291,34 @@
             final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
 
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
-            enableWriteSettings(WRITE_PKG);
-            runDeviceTests(
-                    WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", users[0]);
-
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
 
             for (int user : users) {
+                enableWriteSettings(WRITE_PKG, user);
+                runDeviceTests(
+                        WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", user);
+
                 runDeviceTests(
                         NONE_PKG, NONE_PKG + ".ReadDefaultUris", "testPlayDefaultUris", user);
             }
         } finally {
             // Make sure the provider and uris are reset on failure.
-            runDeviceTests(
-                    WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", users[0]);
+            for (int user : users) {
+                runDeviceTests(
+                        WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", user);
+            }
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
             removeUsersForTest(users);
         }
     }
 
-    private void enableWriteSettings(String packageName) throws DeviceNotAvailableException {
+    private void enableWriteSettings(String packageName, int userId)
+            throws DeviceNotAvailableException {
         StringBuilder cmd = new StringBuilder();
-        cmd.append("appops set ");
+        cmd.append("appops set --user ");
+        cmd.append(userId);
+        cmd.append(" ");
         cmd.append(packageName);
         cmd.append(" android:write_settings allow");
         getDevice().executeShellCommand(cmd.toString());
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index 3b6cbb1..cf8a354 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -64,6 +64,11 @@
         getDevice().uninstallPackage(PKG);
     }
 
+    public void testUsesLibrary() throws Exception {
+        assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
+        runDeviceTests(PKG, ".UsesLibraryTest", "testUsesLibrary");
+    }
+
     public void testMissingLibrary() throws Exception {
         assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK), false, false));
         runDeviceTests(PKG, ".UsesLibraryTest", "testMissingLibrary");
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
index f5666d1..4b0bec3 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -103,6 +103,7 @@
      * Verify we can write to our own package dirs.
      */
     public void testAllPackageDirsWritable() throws Exception {
+        final long testValue = 12345000;
         final List<File> paths = getAllPackageSpecificPaths(getContext());
         for (File path : paths) {
             assertNotNull("Valid media must be inserted during CTS", path);
@@ -122,6 +123,12 @@
 
             assertEquals(32, readInt(directChild));
             assertEquals(64, readInt(subdirChild));
+
+            assertTrue("Must be able to set last modified", directChild.setLastModified(testValue));
+            assertTrue("Must be able to set last modified", subdirChild.setLastModified(testValue));
+
+            assertEquals(testValue, directChild.lastModified());
+            assertEquals(testValue, subdirChild.lastModified());
         }
 
         for (File path : paths) {
diff --git a/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
index 7a937d5..31f653d 100644
--- a/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/InstantCookieApp/AndroidManifest.xml
@@ -17,7 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="test.instant.cookie"
         android:versionCode="1"
-        android:versionName="1.0">
+        android:versionName="1.0"
+        android:targetSandboxVersion="2">
 
     <application/>
 
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
index 64495a4..e47f6ed 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/Utils.java
@@ -25,10 +25,12 @@
 
 import junit.framework.AssertionFailedError;
 
+import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -152,7 +154,21 @@
         return success;
     }
 
-    public static boolean shouldHaveQuota(StructUtsname uname) {
+    public static boolean shouldHaveQuota(StructUtsname uname) throws Exception {
+        try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
+            String line;
+            while ((line = br.readLine()) != null) {
+                final String[] fields = line.split(" ");
+                final String target = fields[1];
+                final String format = fields[2];
+
+                if (target.equals("/data") && !format.equals("ext4")) {
+                    Log.d(TAG, "Assuming no quota support because /data is " + format);
+                    return false;
+                }
+            }
+        }
+
         final Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)").matcher(uname.release);
         if (!matcher.find()) {
             throw new IllegalStateException("Failed to parse version: " + uname.release);
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index 8066f70..72a420d 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -201,7 +201,7 @@
         final long manualSize = getSizeManual(Environment.getExternalStorageDirectory());
         final long statsSize = stats.queryExternalStatsForUser(null, user).getTotalBytes();
 
-        assertEquals(manualSize, statsSize);
+        assertMostlyEquals(manualSize, statsSize);
     }
 
     public void testVerifyCategory() throws Exception {
@@ -234,16 +234,24 @@
         // Ask apps to allocate some cached data
         final long targetA = doAllocateProvider(PKG_A, 0.5, 1262304000);
         final long targetB = doAllocateProvider(PKG_B, 2.0, 1420070400);
+        final long totalAllocated = targetA + targetB;
 
         // Apps using up some cache space shouldn't change how much we can
         // allocate, or how much we think is free; but it should decrease real
         // disk space.
-        assertMostlyEquals(beforeAllocatable, sm.getAllocatableBytes(filesDir, 0),
-                10 * MB_IN_BYTES);
-        assertMostlyEquals(beforeFree, stats.getFreeBytes(null),
-                10 * MB_IN_BYTES);
-        assertMostlyEquals(targetA + targetB, beforeRaw - filesDir.getUsableSpace(),
-                10 * MB_IN_BYTES);
+        if (stats.isQuotaSupported(null)) {
+            assertMostlyEquals(beforeAllocatable,
+                    sm.getAllocatableBytes(filesDir, 0), 10 * MB_IN_BYTES);
+            assertMostlyEquals(beforeFree,
+                    stats.getFreeBytes(null), 10 * MB_IN_BYTES);
+        } else {
+            assertMostlyEquals(beforeAllocatable - totalAllocated,
+                    sm.getAllocatableBytes(filesDir, 0), 10 * MB_IN_BYTES);
+            assertMostlyEquals(beforeFree - totalAllocated,
+                    stats.getFreeBytes(null), 10 * MB_IN_BYTES);
+        }
+        assertMostlyEquals(beforeRaw - totalAllocated,
+                filesDir.getUsableSpace(), 10 * MB_IN_BYTES);
 
         assertMostlyEquals(targetA, getCacheBytes(PKG_A, user));
         assertMostlyEquals(targetB, getCacheBytes(PKG_B, user));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index f8f6d92..4340c87 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -267,6 +267,11 @@
         getUiDevice().pressBack();
         waitForIdle();
 
+        if (isTv()) {
+            getUiDevice().pressHome();
+            waitForIdle();
+        }
+
         // Open the app details settings
         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -506,4 +511,9 @@
         getInstrumentation().getUiAutomation().waitForIdle(IDLE_TIMEOUT_MILLIS,
                 GLOBAL_TIMEOUT_MILLIS);
     }
+
+    private static boolean isTv() {
+        return getInstrumentation().getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
  }
diff --git a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
index c7b8f8b..0a1f012 100644
--- a/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsesLibraryApp/src/com/android/cts/useslibrary/UsesLibraryTest.java
@@ -34,6 +34,17 @@
 public class UsesLibraryTest extends InstrumentationTestCase {
     private static final String TAG = "UsesLibraryTest";
 
+    public void testUsesLibrary() throws Exception {
+        ClassLoader loader = getClass().getClassLoader();
+        if (loader instanceof BaseDexClassLoader) {
+            Object[] dexElements = getDexElementsFromClassLoader((BaseDexClassLoader) loader);
+            for (Object dexElement : dexElements) {
+                DexFile dexFile = getDexFileFromDexElement(dexElement);
+                assertTrue(isDexFileBackedByOatFile(dexFile));
+            }
+        }
+    }
+
     public void testMissingLibrary() throws Exception {
         ClassLoader loader = getClass().getClassLoader();
         if (loader instanceof BaseDexClassLoader) {
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
index cee1a50..398a75b 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.content">
 
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index fe4414b..2b08e1f 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -25,7 +25,10 @@
 import android.content.Intent;
 import android.content.SyncRequest;
 import android.content.SyncResult;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.uiautomator.By;
@@ -45,13 +48,17 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class CtsSyncAccountAccessOtherCertTestCases {
-    private static final long SYNC_TIMEOUT_MILLIS = 10000; // 20 sec
+    private static final long SYNC_TIMEOUT_MILLIS = 10000; // 10 sec
     private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
 
     public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
 
     @Test
     public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
+        if (!hasDataConnection()) {
+            return;
+        }
+
         Intent intent = new Intent(getContext(), StubActivity.class);
         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
 
@@ -63,6 +70,8 @@
                 result.getString(AccountManager.KEY_ACCOUNT_NAME),
                 result.getString(AccountManager.KEY_ACCOUNT_TYPE));
 
+        waitForSyncManagerAccountChangeUpdate();
+
         try {
             CountDownLatch latch = new CountDownLatch(1);
 
@@ -114,4 +123,17 @@
     private UiDevice getUiDevice() {
         return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     }
+
+    private void waitForSyncManagerAccountChangeUpdate() {
+        // Wait for the sync manager to be notified for the new account.
+        // Unfortunately, there is no way to detect this event, sigh...
+        SystemClock.sleep(5000);
+    }
+
+    private boolean hasDataConnection() {
+        ConnectivityManager connectivityManager = getContext().getSystemService(
+                ConnectivityManager.class);
+        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+    }
 }
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
index ea46d61..2ecd27d 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.content">
 
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
index 17be8b6..223281b 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
@@ -27,7 +27,10 @@
 import android.content.Intent;
 import android.content.SyncRequest;
 import android.content.SyncResult;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import org.junit.Test;
@@ -41,10 +44,14 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class CtsSyncAccountAccessSameCertTestCases {
-    private static final long SYNC_TIMEOUT_MILLIS = 10000; // 20 sec
+    private static final long SYNC_TIMEOUT_MILLIS = 10000; // 10 sec
 
     @Test
     public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
+        if (!hasDataConnection()) {
+            return;
+        }
+
         Intent intent = new Intent(getContext(), StubActivity.class);
         Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
 
@@ -56,6 +63,8 @@
                 result.getString(AccountManager.KEY_ACCOUNT_NAME),
                         result.getString(AccountManager.KEY_ACCOUNT_TYPE));
 
+        waitForSyncManagerAccountChangeUpdate();
+
         try {
             CountDownLatch latch = new CountDownLatch(1);
 
@@ -85,4 +94,17 @@
     private Context getContext() {
         return InstrumentationRegistry.getInstrumentation().getContext();
     }
+
+    private void waitForSyncManagerAccountChangeUpdate() {
+        // Wait for the sync manager to be notified for the new account.
+        // Unfortunately, there is no way to detect this event, sigh...
+        SystemClock.sleep(5000);
+    }
+
+    private boolean hasDataConnection() {
+        ConnectivityManager connectivityManager = getContext().getSystemService(
+                ConnectivityManager.class);
+        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index d7c3bcf..6a01ea6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -46,7 +46,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordSucceeds("1234", caseDescription);
@@ -56,7 +56,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient()); // length not checked for this quality
+        assertPasswordSufficiency(true); // length not checked for this quality
 
         // TODO(ascull): fix resetPassword() logic so these succeed
         assertPasswordFails("1234", caseDescription);
@@ -67,7 +67,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordSucceeds("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -79,7 +79,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());            // failure
+        assertPasswordSufficiency(false);            // failure
 
         String caseDescription = "initial";
         assertPasswordSucceeds("1234", caseDescription);
@@ -89,7 +89,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -99,7 +99,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordSucceeds("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -111,7 +111,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordFails("1234", caseDescription);      // can't change
@@ -121,7 +121,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -131,7 +131,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -143,7 +143,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordFails("1234", caseDescription);
@@ -153,7 +153,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -163,7 +163,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -372,6 +372,21 @@
     private void assertPasswordSucceeds(String password, String restriction) {
         boolean passwordResetResult = dpm.resetPassword(password, /* flags= */0);
         assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
+    }
+
+    private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
+        int retries = 15;
+        // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
+        while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
+            retries--;
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+                break;
+            }
+        }
+        assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
+
     }
 }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
index e655d7b..a21d9e6 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
@@ -20,8 +20,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.test.ActivityInstrumentationTestCase2;
 import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
 
 import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java
new file mode 100644
index 0000000..28c7e25
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/SanityTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.managedprofile;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+/**
+ * Basic sanity test to ensure some basic functionalities of work profile are working.
+ */
+public class SanityTest {
+    private static final ComponentName SIMPLE_APP_ACTIVITY =
+            new ComponentName(
+                    "com.android.cts.launcherapps.simpleapp",
+                    "com.android.cts.launcherapps.simpleapp.SimpleActivity");
+    /**
+     * Broadcast sent from com.android.cts.launcherapps.simpleapp.SimpleActivity.
+     */
+    private static final String ACTIVITY_LAUNCHED_ACTION =
+            "com.android.cts.launchertests.LauncherAppsTests.LAUNCHED_ACTION";
+
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    /**
+     * Verify that we can launch an app that installed in work profile only.
+     */
+    @Test
+    public void testLaunchAppInstalledInProfileOnly() throws Exception {
+        BlockingBroadcastReceiver receiver =
+                new BlockingBroadcastReceiver(mContext, ACTIVITY_LAUNCHED_ACTION);
+        try {
+            receiver.register();
+            Intent intent = new Intent();
+            intent.setComponent(SIMPLE_APP_ACTIVITY);
+            // Finish the activity after that.
+            intent.putExtra("finish", true);
+            mContext.startActivity(intent);
+            Intent receivedBroadcast = receiver.awaitForBroadcast();
+            assertNotNull(receivedBroadcast);
+            assertEquals(ACTIVITY_LAUNCHED_ACTION, receivedBroadcast.getAction());
+        } finally {
+            receiver.unregisterQuietly();
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
index d033785..a773737 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
@@ -27,7 +27,9 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mUserId = createUser();
+        if (isTestEnabled()) {
+            mUserId = createUser();
+        }
     }
 
     @Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index ba36fbf..84a08ee 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -71,6 +71,8 @@
     private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
     private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
 
+    private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+    private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
 
     private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.SECONDS.toMillis(15);
 
@@ -870,6 +872,15 @@
         }
     }
 
+    public void testSanityCheck() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Install SimpleApp in work profile only and check activity in it can be launched.
+        installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+    }
+
     private void disableActivityForUser(String activityName, int userId)
             throws DeviceNotAvailableException {
         String command = "am start -W --user " + userId
diff --git a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
index f76bf37..d4937aa 100644
--- a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
@@ -28,6 +28,10 @@
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
 
+        <service android:name=".BatteryStatsBackgroundService" android:exported="true" />
+
+        <activity android:name=".BatteryStatsForegroundActivity" android:exported="true" />
+
         <service android:name=".BatteryStatsWifiTransferTests$TransferService" android:exported="true" />
 
         <service android:name=".SimpleForegroundService" android:exported="true" />
@@ -35,9 +39,7 @@
         <service android:name=".SimpleJobService"
                 android:permission="android.permission.BIND_JOB_SERVICE" />
 
-        <activity android:name=".SimpleActivity" android:label="BatteryStats Test Activity" />
-
-        <activity android:name=".BatteryStatsWifiScanTests$WiFiScanActivity" android:label="BatteryStats Wifi scan Test Activity" />
+        <activity android:name=".SimpleActivity" android:label="BatteryStats Test Activity" android:exported="true" />
 
         <service android:name=".BatteryStatsAuthenticator"
             android:exported="false">
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
new file mode 100644
index 0000000..a02fe83
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import org.junit.Assert;
+
+/** An service (to be run as a background process) which performs one of a number of actions. */
+public class BatteryStatsBackgroundService extends IntentService {
+    private static final String TAG = BatteryStatsBackgroundService.class.getSimpleName();
+
+    public BatteryStatsBackgroundService() {
+        super(BatteryStatsBackgroundService.class.getName());
+    }
+
+    @Override
+    public void onHandleIntent(Intent intent) {
+        try {
+            if (!isAppInBackground(this)) {
+                Log.e(TAG, "BackgroundService is not a background process!");
+                Assert.fail("Test requires BackgroundService to be in background.");
+            }
+        } catch(ReflectiveOperationException ex) {
+            Log.w(TAG, "Couldn't determine if app is in background. Proceeding with test anyway.");
+        }
+
+        doAction(this, intent.getStringExtra(KEY_ACTION));
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
new file mode 100644
index 0000000..7528cac
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
@@ -0,0 +1,183 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class BatteryStatsBgVsFgActions {
+    private static final String TAG = BatteryStatsBgVsFgActions.class.getSimpleName();
+
+    public static final String KEY_ACTION = "action";
+    public static final String ACTION_JOB_SCHEDULE = "action.jobs";
+    public static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
+    /** Perform the action specified by the given action code (see constants above). */
+    public static void doAction(Context ctx, String actionCode) {
+        if (actionCode == null) {
+            Log.e(TAG, "Intent was missing action.");
+            return;
+        }
+        sleep(100);
+        switch(actionCode) {
+            case ACTION_JOB_SCHEDULE:
+                doScheduleJob(ctx);
+                break;
+            case ACTION_WIFI_SCAN:
+                doWifi(ctx);
+                break;
+            default:
+                Log.e(TAG, "Intent had invalid action");
+        }
+        sleep(100);
+    }
+
+    private static void doScheduleJob(Context ctx) {
+        final ComponentName JOB_COMPONENT_NAME =
+                new ComponentName("com.android.server.cts.device.batterystats",
+                        SimpleJobService.class.getName());
+        JobScheduler js = ctx.getSystemService(JobScheduler.class);
+        if (js == null) {
+            Log.e(TAG, "JobScheduler service not available");
+            return;
+        }
+        final JobInfo job = (new JobInfo.Builder(1, JOB_COMPONENT_NAME))
+                .setOverrideDeadline(0)
+                .build();
+        CountDownLatch latch = SimpleJobService.resetCountDownLatch();
+        js.schedule(job);
+        // Job starts in main thread so wait in another thread to see if job finishes.
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                waitForReceiver(null, 3_000, latch, null);
+                return null;
+            }
+        }.execute();
+    }
+
+    private static void doWifi(Context ctx) {
+        IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+        CountDownLatch onReceiveLatch = new CountDownLatch(1);
+        BroadcastReceiver receiver = registerReceiver(ctx, onReceiveLatch, intentFilter);
+        ctx.getSystemService(WifiManager.class).startScan();
+        waitForReceiver(ctx, 3_000, onReceiveLatch, receiver);
+    }
+
+    /** Register receiver to determine when given action is complete. */
+    private static BroadcastReceiver registerReceiver(
+            Context ctx, CountDownLatch onReceiveLatch, IntentFilter intentFilter) {
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                onReceiveLatch.countDown();
+            }
+        };
+        // run Broadcast receiver in a different thread since the foreground activity will wait.
+        HandlerThread handlerThread = new HandlerThread("br_handler_thread");
+        handlerThread.start();
+        Looper looper = handlerThread.getLooper();
+        Handler handler = new Handler(looper);
+        ctx.registerReceiver(receiver, intentFilter, null, handler);
+        return receiver;
+    }
+
+    /**
+     * Uses the receiver to wait until the action is complete. ctx and receiver may be null if no
+     * receiver is needed to be unregistered.
+     */
+    private static void waitForReceiver(Context ctx,
+            int maxWaitTimeMs, CountDownLatch latch, BroadcastReceiver receiver) {
+        try {
+            boolean didFinish = latch.await(maxWaitTimeMs, TimeUnit.MILLISECONDS);
+            if (didFinish) {
+                Log.v(TAG, "Finished performing action");
+            } else {
+                // This is not necessarily a problem. If we just want to make sure a count was
+                // recorded for the request, it doesn't matter if the action actually finished.
+                Log.w(TAG, "Did not finish in specified time.");
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted exception while awaiting action to finish", e);
+        }
+        if (ctx != null && receiver != null) {
+            ctx.unregisterReceiver(receiver);
+        }
+    }
+
+    /** Determines whether the package is running as a background process. */
+    public static boolean isAppInBackground(Context context) throws ReflectiveOperationException {
+        String pkgName = context.getPackageName();
+        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
+        if (processes == null) {
+            return false;
+        }
+        for (ActivityManager.RunningAppProcessInfo r : processes){
+            // BatteryStatsImpl treats as background if procState is >=
+            // Activitymanager.PROCESS_STATE_IMPORTANT_BACKGROUND (corresponding
+            // to BatteryStats.PROCESS_STATE_BACKGROUND).
+            // Due to lack of permissions, only the current app should show up in the list of
+            // processes, which is desired in this case; but in case this changes later, we check
+            // that the package name matches anyway.
+            int processState = -1;
+            int backgroundCode = -1;
+            try {
+                processState = ActivityManager.RunningAppProcessInfo.class
+                        .getField("processState").getInt(r);
+                backgroundCode = (Integer) ActivityManager.class
+                        .getDeclaredField("PROCESS_STATE_IMPORTANT_BACKGROUND").get(null);
+            } catch (ReflectiveOperationException ex) {
+                Log.e(TAG, "Failed to get proc state info via reflection", ex);
+                throw ex;
+            }
+            if (processState < backgroundCode) { // if foreground process
+                for (String rpkg : r.pkgList) {
+                    if (pkgName.equals(rpkg)) {
+                        return false;
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /** Puts the current thread to sleep. */
+    private static void sleep(int millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted exception while sleeping", e);
+        }
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
new file mode 100644
index 0000000..d6869a1
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions
+        .ACTION_JOB_SCHEDULE;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.junit.Assert;
+
+/** An activity (to be run as a foreground process) which performs one of a number of actions. */
+public class BatteryStatsForegroundActivity extends Activity {
+    private static final String TAG = BatteryStatsForegroundActivity.class.getSimpleName();
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+
+        Intent intent = this.getIntent();
+        if (intent == null) {
+            Log.e(TAG, "Intent was null.");
+            finish();
+        }
+        try {
+            if (isAppInBackground(this)) {
+                Log.e(TAG, "ForegroundActivity is a background process!");
+                Assert.fail("Test requires ForegroundActivity to be in foreground.");
+            }
+        } catch(ReflectiveOperationException ex) {
+            Log.w(TAG, "Couldn't determine if app is in foreground. Proceeding with test anyway");
+        }
+
+        doAction(this, intent.getStringExtra(KEY_ACTION));
+        finish();
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java
deleted file mode 100644
index 4bf8ec5..0000000
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiScanTests.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.server.cts.device.batterystats;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.InstrumentationRegistry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Used by BatteryStatsValidationTest.
- */
-@RunWith(AndroidJUnit4.class)
-public class BatteryStatsWifiScanTests {
-    private CountDownLatch mResultsReceivedSignal;
-    private WifiManager mWifiManager;
-    private Context mContext;
-    private boolean mHasFeature;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mHasFeature = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
-        if (!mHasFeature) {
-            return;
-        }
-        mWifiManager = mContext.getSystemService(WifiManager.class);
-        mResultsReceivedSignal = new CountDownLatch(1);
-        registerReceiver(mContext, mResultsReceivedSignal);
-    }
-
-    @Test
-    public void testBackgroundScan() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        mWifiManager.startScan();
-        mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void testForegroundScan() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        WiFiScanActivity.mResultsReceivedSignal = mResultsReceivedSignal;
-        Intent intent = new Intent(mContext, WiFiScanActivity.class);
-        mContext.startActivity(intent);
-        mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
-        WiFiScanActivity.instance.finish();
-    }
-
-    static void registerReceiver(Context ctx, CountDownLatch onReceiveLatch) {
-        ctx.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                onReceiveLatch.countDown();
-            }
-        }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
-    }
-
-    public static class WiFiScanActivity extends Activity {
-        private static CountDownLatch mResultsReceivedSignal;
-        private static Activity instance;
-
-        @Override
-        public void onCreate(Bundle bundle) {
-            instance = this;
-            super.onCreate(bundle);
-            BatteryStatsWifiScanTests.registerReceiver(this, mResultsReceivedSignal);
-            getSystemService(WifiManager.class).startScan();
-        }
-    }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index a499482..f6e9e31 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -28,12 +28,22 @@
     private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk";
     private static final String DEVICE_SIDE_TEST_PACKAGE
             = "com.android.server.cts.device.batterystats";
+    private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
+            = "com.android.server.cts.device.batterystats/.BatteryStatsBackgroundService";
+    private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+            = "com.android.server.cts.device.batterystats/.BatteryStatsForegroundActivity";
     private static final String DEVICE_SIDE_JOB_COMPONENT
             = "com.android.server.cts.device.batterystats/.SimpleJobService";
     private static final String DEVICE_SIDE_SYNC_COMPONENT
             = "com.android.server.cts.device.batterystats.provider/"
             + "com.android.server.cts.device.batterystats";
 
+
+    // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here).
+    public static final String KEY_ACTION = "action";
+    public static final String ACTION_JOB_SCHEDULE = "action.jobs";
+    public static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -107,16 +117,41 @@
         batteryOffScreenOn();
     }
 
+    public void testJobBgVsFg() throws Exception {
+        batteryOnScreenOff();
+        installPackage(DEVICE_SIDE_TEST_APK, true);
+
+        // Foreground test.
+        executeForeground(ACTION_JOB_SCHEDULE);
+        Thread.sleep(4_000);
+        assertValueRange("jb", "", 6, 1, 1); // count
+        assertValueRange("jb", "", 8, 0, 0); // background_count
+
+        // Background test.
+        executeBackground(ACTION_JOB_SCHEDULE);
+        Thread.sleep(4_000);
+        assertValueRange("jb", "", 6, 2, 2); // count
+        assertValueRange("jb", "", 8, 1, 1); // background_count
+
+        batteryOffScreenOn();
+    }
+
     public void testWifiScans() throws Exception {
         batteryOnScreenOff();
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWifiScanTests",
-                "testBackgroundScan");
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsWifiScanTests",
-                "testForegroundScan");
+        // Foreground count test.
+        executeForeground(ACTION_WIFI_SCAN);
+        Thread.sleep(4_000);
+        assertValueRange("wfl", "", 7, 1, 1); // scan_count
+        assertValueRange("wfl", "", 11, 0, 0); // scan_count_bg
 
-        assertValueRange("wfl", "", 7, 2, 2);
+        // Background count test.
+        executeBackground(ACTION_WIFI_SCAN);
+        Thread.sleep(4_000);
+        assertValueRange("wfl", "", 7, 2, 2); // scan_count
+        assertValueRange("wfl", "", 11, 1, 1); // scan_count_bg
+
         batteryOffScreenOn();
     }
 
@@ -194,7 +229,7 @@
         long foregroundBytes = getDownloadedBytes();
         assertTrue(foregroundBytes > 0);
         long min = foregroundBytes + MIN_HTTP_HEADER_BYTES;
-        long max = foregroundBytes + (10 * 1024); // Add some fuzzing.
+        long max = foregroundBytes + (30 * 1024); // Add some fuzzing.
         assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx
         assertValueRange("nt", "", 11, 1, 40); // wifi_bytes_tx
 
@@ -203,7 +238,7 @@
         long backgroundBytes = getDownloadedBytes();
         assertTrue(backgroundBytes > 0);
         min += backgroundBytes + MIN_HTTP_HEADER_BYTES;
-        max += backgroundBytes + (10 * 1024);
+        max += backgroundBytes + (30 * 1024);
         assertValueRange("nt", "", 6, min, max); // wifi_bytes_rx
         assertValueRange("nt", "", 11, 2, 80); // wifi_bytes_tx
 
@@ -274,6 +309,35 @@
     }
 
     /**
+     * Runs a (background) service to perform the given action.
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     */
+    private void executeBackground(String actionValue) throws Exception {
+        allowBackgroundServices();
+        getDevice().executeShellCommand(String.format(
+                "am startservice -n '%s' -e %s %s",
+                DEVICE_SIDE_BG_SERVICE_COMPONENT, KEY_ACTION, actionValue));
+    }
+
+    /** Required to successfully start a background service from adb in O. */
+    private void allowBackgroundServices() throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
+    }
+
+    /**
+     * Runs an activity (in the foreground) to perform the given action.
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     */
+    private void executeForeground(String actionValue) throws Exception {
+        getDevice().executeShellCommand(String.format(
+                "am start -n '%s' -e %s %s",
+                DEVICE_SIDE_FG_ACTIVITY_COMPONENT, KEY_ACTION, actionValue));
+    }
+
+    /**
      * Returns the bytes downloaded for the wifi transfer download tests.
      */
     private long getDownloadedBytes() throws Exception {
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
index 0e27a66..10a1b77 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
@@ -43,7 +43,6 @@
                 found = true;
                 assertEquals(State.POSTED, record.getState());
                 assertTrue(record.getImportance() > 0 /* NotificationManager.IMPORTANCE_NONE */);
-                assertEquals(record.getKey(), record.getGroupKey());
 
                 // Ensure these fields exist, at least
                 record.getFlags();
diff --git a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
index 3602076..d8b2816 100644
--- a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
+++ b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
@@ -20,96 +20,12 @@
      */
     private static final String INSECURE_DEVICE_ADB_COMMAND = "find %s -type %s -perm /o=rwx 2>/dev/null";
 
-    /**
-     * Whitelist exceptions of allowed world accessbale char files under /dev
-     */
-    private static final Set<String> CHAR_DEV_EXCEPTIONS = new HashSet<String>(
-        Arrays.asList(
-            // All exceptions should be alphabetical and associated with a bug number.
-            "/dev/adsprpc-smd", // b/11710243
-            "/dev/alarm",      // b/9035217
-            "/dev/ashmem",
-            "/dev/binder",
-            "/dev/card0",       // b/13159510
-            "/dev/renderD128",
-            "/dev/renderD129",  // b/23798677
-            "/dev/dri/card0",   // b/13159510
-            "/dev/dri/renderD128",
-            "/dev/dri/renderD129", // b/23798677
-            "/dev/felica",     // b/11142586
-            "/dev/felica_ant", // b/11142586
-            "/dev/felica_cen", // b/11142586
-            "/dev/felica_pon", // b/11142586
-            "/dev/felica_rfs", // b/11142586
-            "/dev/felica_rws", // b/11142586
-            "/dev/felica_uicc", // b/11142586
-            "/dev/full",
-            "/dev/galcore",
-            "/dev/genlock",    // b/9035217
-            "/dev/graphics/galcore",
-            "/dev/hwbinder",   // b/30886151
-            "/dev/ion",
-            "/dev/kgsl-2d0",   // b/11271533
-            "/dev/kgsl-2d1",   // b/11271533
-            "/dev/kgsl-3d0",   // b/9035217
-            "/dev/log/events", // b/9035217
-            "/dev/log/main",   // b/9035217
-            "/dev/log/radio",  // b/9035217
-            "/dev/log/system", // b/9035217
-            "/dev/mali0",       // b/9106968
-            "/dev/mali",        // b/11142586
-            "/dev/mm_interlock", // b/12955573
-            "/dev/mm_isp",      // b/12955573
-            "/dev/mm_v3d",      // b/12955573
-            "/dev/msm_rotator", // b/9035217
-            "/dev/null",
-            "/dev/nvhost-as-gpu",
-            "/dev/nvhost-ctrl", // b/9088251
-            "/dev/nvhost-ctrl-gpu",
-            "/dev/nvhost-dbg-gpu",
-            "/dev/nvhost-gpu",
-            "/dev/nvhost-gr2d", // b/9088251
-            "/dev/nvhost-gr3d", // b/9088251
-            "/dev/nvhost-tsec",
-            "/dev/nvhost-prof-gpu",
-            "/dev/nvhost-vic",
-            "/dev/nvmap",       // b/9088251
-            "/dev/pmsg0",       // b/31857082
-            "/dev/ptmx",        // b/9088251
-            "/dev/pvrsrvkm",    // b/9108170
-            "/dev/pvr_sync",
-            "/dev/quadd",
-            "/dev/random",
-            "/dev/snfc_cen",    // b/11142586
-            "/dev/snfc_hsel",   // b/11142586
-            "/dev/snfc_intu_poll", // b/11142586
-            "/dev/snfc_rfs",    // b/11142586
-            "/dev/tegra-throughput",
-            "/dev/tiler",       // b/9108170
-            "/dev/tty",
-            "/dev/urandom",
-            "/dev/ump",         // b/11142586
-            "/dev/xt_qtaguid",  // b/9088251
-            "/dev/zero",
-            "/dev/fimg2d",      // b/10428016
-            "/dev/mobicore-user" // b/10428016
-    ));
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mDevice = getDevice();
     }
 
-    public void testAllCharacterDevicesAreSecure() throws DeviceNotAvailableException {
-        Set <String> insecure = getAllInsecureDevicesInDirAndSubdir("/dev", "c");
-        Set <String> insecurePts = getAllInsecureDevicesInDirAndSubdir("/dev/pts", "c");
-        insecure.removeAll(CHAR_DEV_EXCEPTIONS);
-        insecure.removeAll(insecurePts);
-        assertTrue("Found insecure character devices: " + insecure.toString(),
-                insecure.isEmpty());
-    }
-
     public void testAllBlockDevicesAreSecure() throws Exception {
         Set<String> insecure = getAllInsecureDevicesInDirAndSubdir("/dev", "b");
         assertTrue("Found insecure block devices: " + insecure.toString(),
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 767939a..8ed60e6 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -235,8 +235,17 @@
         }
     }
 
-    private boolean isFullTrebleDevice() throws Exception {
-        return PropertyUtil.getFirstApiLevel(mDevice) > 25;
+    // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
+    /**
+     * Returns {@code true} if this device is required to be a full Treble device.
+     */
+    public static boolean isFullTrebleDevice(ITestDevice device)
+            throws DeviceNotAvailableException {
+        return PropertyUtil.getFirstApiLevel(device) > 25;
+    }
+
+    private boolean isFullTrebleDevice() throws DeviceNotAvailableException {
+        return isFullTrebleDevice(mDevice);
     }
 
     /**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
index ff9e62b..3a50232 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
@@ -24,7 +24,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util
+LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util  \
+     platform-test-annotations-host
 
 LOCAL_CTS_TEST_PACKAGE := android.server
 
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index 9dda45a..3cdc754 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -59,6 +59,10 @@
             android:taskAffinity="nobody.but.TranslucentActivity"
             android:exported="true"
         />
+        <activity android:name=".DialogWhenLargeActivity"
+                android:exported="true"
+                android:theme="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge"
+        />
         <activity android:name=".NoRelaunchActivity"
                 android:resizeableActivity="true"
                 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|fontScale"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
new file mode 100644
index 0000000..139c648
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.cts;
+
+/**
+ * Activity with DialogWhenLarge Theme.
+ */
+public class DialogWhenLargeActivity extends AbstractLifecycleLogActivity {
+    private static final String TAG = DialogWhenLargeActivity.class.getSimpleName();
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
index a7d4042..add7e42 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
@@ -40,12 +40,12 @@
         private ConfigurationChangeObserver() {
         }
 
-        private boolean findConfigurationChange(String activityName)
+        private boolean findConfigurationChange(String activityName, String logSeparator)
                 throws DeviceNotAvailableException, InterruptedException {
             int tries = 0;
             boolean observedChange = false;
             while (tries < 5 && !observedChange) {
-                final String[] lines = getDeviceLogsForComponent(activityName);
+                final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
                 log("Looking at logcat");
                 for (int i = lines.length - 1; i >= 0; i--) {
                     final String line = lines[i].trim();
@@ -72,16 +72,18 @@
         launchActivityInStack(TEST_ACTIVITY_NAME, FREEFORM_WORKSPACE_STACK_ID);
 
         setDeviceRotation(0);
-        clearLogcat();
+        String logSeparator = clearLogcat();
         resizeActivityTask(TEST_ACTIVITY_NAME, 0, 0, 100, 100);
         ConfigurationChangeObserver c = new ConfigurationChangeObserver();
-        final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME);
+        final boolean reportedSizeAfterResize = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+                logSeparator);
         assertTrue("Expected to observe configuration change when resizing",
                 reportedSizeAfterResize);
 
-        clearLogcat();
+        logSeparator = clearLogcat();
         setDeviceRotation(2);
-        final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME);
+        final boolean reportedSizeAfterRotation = c.findConfigurationChange(TEST_ACTIVITY_NAME,
+                logSeparator);
         assertFalse("Not expected to observe configuration change after flip rotation",
                 reportedSizeAfterRotation);
     }
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
index b31fb16..f6df4a8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
@@ -77,7 +77,8 @@
 
     private void startActivityAndVerifyResult(final String entryActivity,
             final String actualActivity, boolean shouldStart) throws Exception {
-        clearLogcat();
+        // See TODO below
+        // final String logSeparator = clearLogcat();
 
         // Pass in different data only when cold starting. This is to make the intent
         // different in subsequent warm/hot launches, so that the entrypoint alias
@@ -96,7 +97,7 @@
         // still catch most failures.
 
         // Verify adb logcat log
-        //verifyLogcat(actualActivity, shouldStart);
+        //verifyLogcat(actualActivity, shouldStart, logSeparator);
     }
 
     private static final Pattern sNotStartedWarningPattern = Pattern.compile(
@@ -149,12 +150,12 @@
     private static final Pattern sDisplayTimePattern =
             Pattern.compile("(.+): Displayed (.*): (\\+{0,1})([0-9]+)ms(.*)");
 
-    void verifyLogcat(String actualActivityName, boolean shouldStart)
+    void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator)
             throws DeviceNotAvailableException {
         int displayCount = 0;
         String activityName = null;
 
-        for (String line : getDeviceLogsForComponent("ActivityManager")) {
+        for (String line : getDeviceLogsForComponent("ActivityManager", logSeparator)) {
             line = line.trim();
 
             Matcher matcher = sDisplayTimePattern.matcher(line);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
index 7c3f39a..284f1a7 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
@@ -17,6 +17,8 @@
 
 import static android.server.cts.ActivityManagerState.STATE_RESUMED;
 
+import android.platform.test.annotations.Presubmit;
+
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.log.LogUtil.CLog;
 
@@ -24,6 +26,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
+
 /**
  * Build: mmma -j32 cts/hostsidetests/services
  * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests
@@ -34,9 +38,13 @@
     private static final String PORTRAIT_ACTIVITY_NAME = "PortraitOrientationActivity";
     private static final String LANDSCAPE_ACTIVITY_NAME = "LandscapeOrientationActivity";
     private static final String NIGHT_MODE_ACTIVITY = "NightModeActivity";
+    private static final String DIALOG_WHEN_LARGE_ACTIVITY = "DialogWhenLargeActivity";
 
     private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
 
+    private static final int SMALL_WIDTH_DP = 426;
+    private static final int SMALL_HEIGHT_DP = 320;
+
     /**
      * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
      * has an updated size when the Activity is resized from fullscreen to docked state.
@@ -47,11 +55,15 @@
      * docked state.
      */
     public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
+        String logSeparator = clearLogcat();
         launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
+        logSeparator = clearLogcat();
         moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
         assertSizesAreSane(fullscreenSizes, dockedSizes);
     }
@@ -61,11 +73,15 @@
      * from docked state to fullscreen (reverse).
      */
     public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
+        String logSeparator = clearLogcat();
         launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
+        logSeparator = clearLogcat();
         moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
         assertSizesAreSane(fullscreenSizes, dockedSizes);
     }
@@ -75,8 +91,10 @@
      */
     public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
         setDeviceRotation(0);
+        final String logSeparator = clearLogcat();
         launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
         rotateAndCheckSizes(initialSizes);
     }
@@ -87,6 +105,7 @@
      */
     public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
         setDeviceRotation(0);
+        final String logSeparator = clearLogcat();
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         // Launch our own activity to side in case Recents (or other activity to side) doesn't
         // support rotation.
@@ -94,7 +113,8 @@
                 .execute();
         // Launch target activity in docked stack.
         getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
         rotateAndCheckSizes(initialSizes);
     }
@@ -106,17 +126,19 @@
     public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
         setDeviceRotation(0);
 
+        final String logSeparator = clearLogcat();
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         getLaunchActivityBuilder().setToSide(true).setTargetActivityName(RESIZEABLE_ACTIVITY_NAME)
                 .execute();
-        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
 
         rotateAndCheckSizes(initialSizes);
     }
 
     private void rotateAndCheckSizes(ReportedSizes prevSizes) throws Exception {
         for (int rotation = 3; rotation >= 0; --rotation) {
-            clearLogcat();
+            final String logSeparator = clearLogcat();
             final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
                     RESIZEABLE_ACTIVITY_NAME).mStackId;
             final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
@@ -131,7 +153,8 @@
                 return;
             }
 
-            final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME);
+            final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
+                    logSeparator);
             assertSizesRotate(prevSizes, rotatedSizes);
             prevSizes = rotatedSizes;
         }
@@ -161,13 +184,16 @@
      */
     private void moveActivityFullSplitFull(String activityName) throws Exception {
         // Launch to fullscreen stack and record size.
+        String logSeparator = clearLogcat();
         launchActivityInStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
+                logSeparator);
         final Rectangle displayRect = getDisplayRect(activityName);
 
         // Move to docked stack.
+        logSeparator = clearLogcat();
         moveActivityToStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes dockedSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
         assertSizesAreSane(initialFullscreenSizes, dockedSizes);
         // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
         // will come up.
@@ -177,11 +203,13 @@
 
         // Resize docked stack to fullscreen size. This will trigger activity relaunch with
         // non-empty override configuration corresponding to fullscreen size.
+        logSeparator = clearLogcat();
         runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
                 + displayRect.width + " " + displayRect.height);
         // Move activity back to fullscreen stack.
         moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
+                logSeparator);
 
         // After activity configuration was changed twice it must report same size as original one.
         assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
@@ -203,6 +231,26 @@
     }
 
     /**
+     * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
+     * screen.
+     */
+    @Presubmit
+    public void testDialogWhenLargeSplitSmall() throws Exception {
+        launchActivityInStack(DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+        final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
+                .getStackById(DOCKED_STACK_ID);
+        final WindowManagerState.Display display =
+                mAmWmState.getWmState().getDisplay(stack.mDisplayId);
+        final int density = display.getDpi();
+        final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
+        final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
+
+        runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
+                + smallWidthPx + " " + smallHeightPx);
+        mAmWmState.waitForValidState(mDevice, DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+    }
+
+    /**
      * Test that device handles consequent requested orientations and displays the activities.
      */
     public void testFullscreenAppOrientationRequests() throws Exception {
@@ -320,8 +368,9 @@
      */
     private void moveActivitySplitFullSplit(String activityName) throws Exception {
         // Launch to docked stack and record size.
+        String logSeparator = clearLogcat();
         launchActivityInStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
         // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
         // will come up.
         launchActivityInStack(activityName, DOCKED_STACK_ID);
@@ -329,13 +378,15 @@
                 false /* compareTaskAndStackBounds */);
 
         // Move to fullscreen stack.
+        logSeparator = clearLogcat();
         moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
-        final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
         assertSizesAreSane(fullscreenSizes, initialDockedSizes);
 
         // Move activity back to docked stack.
+        logSeparator = clearLogcat();
         moveActivityToStack(activityName, DOCKED_STACK_ID);
-        final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName);
+        final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
 
         // After activity configuration was changed twice it must report same size as original one.
         assertSizesAreSame(initialDockedSizes, finalDockedSizes);
@@ -396,10 +447,11 @@
         assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
     }
 
-    private ReportedSizes getActivityDisplaySize(String activityName) throws Exception {
+    private ReportedSizes getActivityDisplaySize(String activityName, String logSeparator)
+            throws Exception {
         mAmWmState.computeState(mDevice, new String[] { activityName },
                 false /* compareTaskAndStackBounds */);
-        final ReportedSizes details = getLastReportedSizesForActivity(activityName);
+        final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
         assertNotNull(details);
         return details;
     }
@@ -431,8 +483,6 @@
      * Test launching an activity which requests specific UI mode during creation.
      */
     public void testLaunchWithUiModeChange() throws Exception {
-        clearLogcat();
-
         // Launch activity that changes UI mode and handles this configuration change.
         launchActivity(NIGHT_MODE_ACTIVITY);
         mAmWmState.waitForActivityState(mDevice, NIGHT_MODE_ACTIVITY, STATE_RESUMED);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
index ede2b87..ba56639 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
@@ -17,6 +17,7 @@
 package android.server.cts;
 
 import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import static android.server.cts.StateLogger.logE;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -52,12 +53,12 @@
 
     public void testChangeFontScaleRelaunch() throws Exception {
         // Should relaunch and receive no onConfigurationChanged()
-        testChangeFontScale(TEST_ACTIVITY_NAME, true);
+        testChangeFontScale(TEST_ACTIVITY_NAME, true /* relaunch */);
     }
 
     public void testChangeFontScaleNoRelaunch() throws Exception {
         // Should receive onConfigurationChanged() and no relaunch
-        testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false);
+        testChangeFontScale(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
     }
 
     private void testRotation(
@@ -72,10 +73,10 @@
         mAmWmState.computeState(mDevice, waitForActivitiesVisible);
 
         for (int rotation = 0; rotation < 4; rotation += rotationStep) {
-            clearLogcat();
+            final String logSeparator = clearLogcat();
             setDeviceRotation(rotation);
             mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-            assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange);
+            assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, logSeparator);
         }
     }
 
@@ -89,10 +90,11 @@
         mAmWmState.computeState(mDevice, waitForActivitiesVisible);
 
         for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
-            clearLogcat();
+            final String logSeparator = clearLogcat();
             setFontScale(fontScale);
             mAmWmState.computeState(mDevice, waitForActivitiesVisible);
-            assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1);
+            assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
+                    logSeparator);
         }
     }
 
@@ -101,29 +103,30 @@
      * must be recreated and its asset sequence number must be incremented.
      */
     public void testUpdateApplicationInfo() throws Exception {
-        clearLogcat();
+        final String firstLogSeparator = clearLogcat();
 
         // Launch an activity that prints applied config.
         launchActivity(TEST_ACTIVITY_NAME);
-        final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME);
-        clearLogcat();
+        final int assetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, firstLogSeparator);
 
+        final String logSeparator = clearLogcat();
         // Update package info.
         executeShellCommand("am update-appinfo all " + componentName);
         mAmWmState.waitForWithAmState(mDevice, (amState) -> {
             // Wait for activity to be resumed and asset seq number to be updated.
             try {
-                return readAssetSeqNumber(TEST_ACTIVITY_NAME) == assetSeq + 1
+                return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
                         && amState.hasActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
             } catch (Exception e) {
+                logE("Error waiting for valid state: " + e.getMessage());
                 return false;
             }
         }, "Waiting asset sequence number to be updated and for activity to be resumed.");
 
         // Check if activity is relaunched and asset seq is updated.
         assertRelaunchOrConfigChanged(TEST_ACTIVITY_NAME, 1 /* numRelaunch */,
-                0 /* numConfigChange */);
-        final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME);
+                0 /* numConfigChange */, logSeparator);
+        final int newAssetSeq = readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator);
         assertEquals("Asset sequence number must be incremented.", assetSeq + 1, newAssetSeq);
     }
 
@@ -131,8 +134,8 @@
             "(.+): Configuration: \\{(.*) as.(\\d+)(.*)\\}");
 
     /** Read asset sequence number in last applied configuration from logs. */
-    private int readAssetSeqNumber(String activityName) throws Exception {
-        final String[] lines = getDeviceLogsForComponent(activityName);
+    private int readAssetSeqNumber(String activityName, String logSeparator) throws Exception {
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
         for (int i = lines.length - 1; i >= 0; i--) {
             final String line = lines[i].trim();
             final Matcher matcher = sConfigurationPattern.matcher(line);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index e950983..ea25e04 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -135,11 +135,42 @@
     }
 
     /**
+     * Tests that launch on secondary display is not permitted if device has the feature disabled.
+     * Activities requested to be launched on a secondary display in this case should land on the
+     * default display.
+     */
+    public void testMultiDisplayDisabled() throws Exception {
+        if (supportsMultiDisplay()) {
+            // Only check devices with the feature disabled.
+            return;
+        }
+
+        // Create new virtual display.
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+        // Launch activity on new secondary display.
+        launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
+        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+
+        mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
+
+        // Check that activity is on the right display.
+        final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+        final ActivityManagerState.ActivityStack frontStack
+                = mAmWmState.getAmState().getStackById(frontStackId);
+        assertEquals("Launched activity must be resumed",
+                getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+        assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
+                frontStack.mDisplayId);
+        mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
+    }
+
+    /**
      * Tests that any new activity launch in Vr mode is in Vr display.
      */
     public void testVrActivityLaunch() throws Exception {
-        // VR Mode is not supported on this device, bail from this test.
-        if (!supportsVrMode()) {
+        if (!supportsVrMode() || !supportsMultiDisplay()) {
+            // VR Mode is not supported on this device, bail from this test.
             return;
         }
 
@@ -185,8 +216,8 @@
      * Tests that any activity already present is re-launched in Vr display in vr mode.
      */
     public void testVrActivityReLaunch() throws Exception {
-        // VR Mode is not supported on this device, bail from this test.
-        if (!supportsVrMode()) {
+        if (!supportsVrMode() || !supportsMultiDisplay()) {
+            // VR Mode is not supported on this device, bail from this test.
             return;
         }
 
@@ -235,8 +266,8 @@
      * Tests that any new activity launch post Vr mode is in the main display.
      */
     public void testActivityLaunchPostVr() throws Exception {
-        // VR Mode is not supported on this device, bail from this test.
-        if (!supportsVrMode()) {
+        if (!supportsVrMode() || !supportsMultiDisplay()) {
+            // VR Mode is not supported on this device, bail from this test.
             return;
         }
 
@@ -304,7 +335,6 @@
                 DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
     }
 
-
     public void testCreateMultipleVirtualDisplays() throws Exception {
         // Create new virtual display.
         final List<DisplayState> newDisplays = new VirtualDisplayBuilder(this).build(3);
@@ -316,10 +346,13 @@
      * Tests launching an activity on virtual display.
      */
     public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
         // Launch activity on new secondary display.
+        final String logSeparator = clearLogcat();
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
 
@@ -337,7 +370,8 @@
         mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
 
         // Check that activity config corresponds to display config.
-        final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME);
+        final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
+                logSeparator);
         assertEquals("Activity launched on secondary display must have proper configuration",
                 CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
     }
@@ -348,6 +382,8 @@
      * primary display.
      */
     public void testConsequentLaunchActivity() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
@@ -378,6 +414,8 @@
      * first one - it must appear on the secondary display, because it was launched from there.
      */
     public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
@@ -407,6 +445,8 @@
      * Tests launching an activity to secondary display from activity on primary display.
      */
     public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Start launching activity.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         // Create new virtual display.
@@ -435,6 +475,8 @@
      * visibility is not affected.
      */
     public void testLaunchActivitiesAffectsVisibility() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Start launching activity.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -465,6 +507,8 @@
      * Test that move-task works when moving between displays.
      */
     public void testMoveTaskBetweenDisplays() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -493,6 +537,8 @@
      * This version launches virtual display creator to fullscreen stack.
      */
     public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Start launching activity into docked stack.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -506,6 +552,8 @@
      * This version launches virtual display creator to docked stack.
      */
     public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Setup split-screen.
         launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
 
@@ -555,6 +603,8 @@
      * is moved correctly.
      */
     public void testStackFocusSwitchOnStackEmptied() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Start launching activity.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -590,6 +640,8 @@
      * Tests that input events on the primary display take focus from the virtual display.
      */
     public void testStackFocusSwitchOnTouchEvent() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
@@ -615,6 +667,8 @@
 
     /** Test that system is allowed to launch on secondary displays. */
     public void testPermissionLaunchFromSystem() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -647,6 +701,8 @@
 
     /** Test that launching from app that is on external display is allowed. */
     public void testPermissionLaunchFromAppOnSecondary() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -688,6 +744,8 @@
 
     /** Tests that an activity can launch an activity from a different UID into its own task. */
     public void testPermissionLaunchMultiUidTask() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
@@ -719,6 +777,8 @@
      * doesn't have anything on the display.
      */
     public void testPermissionLaunchFromOwner() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -757,6 +817,8 @@
      * that external display is not allowed.
      */
     public void testPermissionLaunchFromDifferentApp() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -773,7 +835,7 @@
         assertTrue("Focused stack must be on secondary display",
                 FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
 
-        clearLogcat();
+        final String logSeparator = clearLogcat();
 
         // Launch other activity with different uid and check it is launched on primary display.
         final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
@@ -785,7 +847,7 @@
         boolean match = false;
         final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
         while (tries < 5 && !match) {
-            String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver");
+            String[] logs = getDeviceLogsForComponent("LaunchBroadcastReceiver", logSeparator);
             for (String line : logs) {
                 Matcher m = pattern.matcher(line);
                 if (m.matches()) {
@@ -814,6 +876,8 @@
      * Test that virtual display content is hidden when device is locked.
      */
     public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
+        if (!supportsMultiDisplay() || !isHandheld()) { return; }
+
         // Create new usual virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -838,6 +902,8 @@
      * locked.
      */
     public void testShowWhenLockedVirtualDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new show-with-insecure-keyguard virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this)
                 .setCanShowWithInsecureKeyguard(true)
@@ -863,6 +929,8 @@
      * Test that only private virtual display can show content with insecure keyguard.
      */
     public void testShowWhenLockedPublicVirtualDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Try to create new show-with-insecure-keyguard public virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this)
                 .setPublicDisplay(true)
@@ -879,6 +947,8 @@
      * Test that all activities that were on the private display are destroyed on display removal.
      */
     public void testContentDestroyOnDisplayRemoved() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new private virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -893,7 +963,7 @@
                 RESIZEABLE_ACTIVITY_NAME);
 
         // Destroy the display and check if activities are removed from system.
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         destroyVirtualDisplays();
         final String activityName1
                 = ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
@@ -923,14 +993,16 @@
         assertFalse("Activity windows from removed display must be destroyed",
                 mAmWmState.getWmState().containsWindow(windowName2));
         // Check activity logs.
-        assertActivityDestroyed(TEST_ACTIVITY_NAME);
-        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME);
+        assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
+        assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
     }
 
     /**
      * Test that the update of display metrics updates all its content.
      */
     public void testDisplayResize() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Start launching activity.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
 
@@ -942,22 +1014,34 @@
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
         // Launch a resizeable activity on new secondary display.
-        clearLogcat();
+        final String initialLogSeparator = clearLogcat();
         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
         mAmWmState.assertFocusedActivity("Launched activity must be focused",
                 RESIZEABLE_ACTIVITY_NAME);
 
         // Grab reported sizes and compute new with slight size change.
-        final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
+                initialLogSeparator);
         final Rectangle initialBounds
                 = mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds();
         final Rectangle newBounds = new Rectangle(initialBounds.x, initialBounds.y,
                 initialBounds.width + SIZE_VALUE_SHIFT, initialBounds.height + SIZE_VALUE_SHIFT);
 
         // Resize the docked stack, so that activity with virtual display will also be resized.
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+
+        mAmWmState.waitForWithAmState(mDevice, amState -> {
+            try {
+                return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
+                        && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
+            } catch (Exception e) {
+                logE("Error waiting for valid state: " + e.getMessage());
+                return false;
+            }
+        }, "Wait for the configuration change to happen and for activity to be resumed.");
+
         mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY,
                 VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
         mAmWmState.assertDockedTaskBounds(newBounds.width, newBounds.height,
@@ -973,9 +1057,10 @@
 
         // Check if activity in virtual display was resized properly.
         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
-                1 /* numConfigChange */);
+                1 /* numConfigChange */, logSeparator);
 
-        final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME);
+        final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
+                logSeparator);
         assertTrue(updatedSize.widthDp <= initialSize.widthDp);
         assertTrue(updatedSize.heightDp <= initialSize.heightDp);
         assertTrue(updatedSize.displayWidth <= initialSize.displayWidth);
@@ -986,11 +1071,18 @@
                 widthUpdated ^ heightUpdated);
     }
 
+    /** Read the number of configuration changes sent to activity from logs. */
+    private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
+        return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
+    }
+
     /**
      * Tests that when activities that handle configuration changes are moved between displays,
      * they receive onMovedToDisplay and onConfigurationChanged callbacks.
      */
     public void testOnMovedToDisplayCallback() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
@@ -1000,7 +1092,7 @@
         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
                 RESIZEABLE_ACTIVITY_NAME);
 
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
         mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
         mAmWmState.assertFocusedActivity("Focus must be on moved activity",
@@ -1009,8 +1101,8 @@
                 FULLSCREEN_WORKSPACE_STACK_ID);
 
         // Check if client received the callbacks.
-        assertMovedToDisplay(RESIZEABLE_ACTIVITY_NAME);
-        assertMovedToDisplay("LifecycleLogView");
+        assertMovedToDisplay(RESIZEABLE_ACTIVITY_NAME, logSeparator);
+        assertMovedToDisplay("LifecycleLogView", logSeparator);
     }
 
     /**
@@ -1018,6 +1110,8 @@
      * matching task on some other display - that task will moved to the target display.
      */
     public void testMoveToDisplayOnLaunch() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Launch activity with unique affinity, so it will the only one in its task.
         launchActivity(LAUNCHING_ACTIVITY);
 
@@ -1072,6 +1166,8 @@
      * Tests that when primary display is rotated secondary displays are not affected.
      */
     public void testRotationNotAffectingSecondaryScreen() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         // Create new virtual display.
         final DisplayState newDisplay = new VirtualDisplayBuilder(this)
                 .setResizeDisplay(false)
@@ -1079,11 +1175,12 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
 
         // Launch activity on new secondary display.
+        String logSeparator = clearLogcat();
         launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
                 RESIZEABLE_ACTIVITY_NAME);
         final ReportedSizes initialSizes = getLastReportedSizesForActivity(
-                RESIZEABLE_ACTIVITY_NAME);
+                RESIZEABLE_ACTIVITY_NAME, logSeparator);
         assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
 
         // Rotate primary display and check that activity on secondary display is not affected.
@@ -1093,12 +1190,13 @@
         final int initialRotation = mAmWmState.getWmState().getRotation();
         setDeviceRotation((initialRotation + 1) % 4);
 
+        logSeparator = clearLogcat();
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
                 TEST_ACTIVITY_NAME);
         final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
-                TEST_ACTIVITY_NAME);
+                TEST_ACTIVITY_NAME, logSeparator);
         assertEquals("Sizes of secondary display must not change after rotation of primary display",
                 initialSizes, testActivitySizes);
     }
@@ -1108,6 +1206,8 @@
      * matching the task component root does.
      */
     public void testTaskMatchAcrossDisplays() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
         launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
@@ -1153,6 +1253,8 @@
      * even if the focused stack is not on that activity's display.
      */
     public void testNewTaskSameDisplay() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
         final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
@@ -1171,7 +1273,7 @@
         mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
 
-        // Check that the second activity is launched onto the fullscren stack
+        // Check that the second activity is launched onto the fullscreen stack
         final ActivityManagerState.ActivityStack fullscreenStack =
                 mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
         assertEquals("Activity launched on default display must be resumed",
@@ -1198,9 +1300,10 @@
 
     private void rotateAndCheckSameSizes(String activityName) throws Exception {
         for (int rotation = 3; rotation >= 0; --rotation) {
-            clearLogcat();
+            final String logSeparator = clearLogcat();
             setDeviceRotation(rotation);
-            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName);
+            final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
+                    logSeparator);
             assertNull("Sizes must not change after rotation", rotatedSizes);
         }
     }
@@ -1343,9 +1446,9 @@
     }
 
     /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
-    private void assertMovedToDisplay(String componentName) throws Exception {
+    private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
         final ActivityLifecycleCounts lifecycleCounts
-                = new ActivityLifecycleCounts(componentName);
+                = new ActivityLifecycleCounts(componentName, logSeparator);
         if (lifecycleCounts.mDestroyCount != 0) {
             fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
                     + " time(s), wasn't expecting any");
@@ -1681,4 +1784,9 @@
         return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
                 " --es command destroy_display";
     }
+
+    /** Checks if the device supports multi-display. */
+    private boolean supportsMultiDisplay() throws Exception {
+        return hasDeviceFeature("android.software.activities_on_secondary_displays");
+    }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index fb87ec2..1cf60f4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -379,7 +379,7 @@
         final Rectangle initialDockBounds =
                 mAmWmState.getWmState().getStack(DOCKED_STACK_ID).getBounds();
 
-        clearLogcat();
+        final String logSeparator = clearLogcat();
 
         Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
         resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
@@ -392,8 +392,8 @@
         resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
         mAmWmState.computeState(mDevice,
                 new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-        assertActivityLifecycle(TEST_ACTIVITY_NAME, true);
-        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false);
+        assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
+        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
     }
 
     private Rectangle computeNewDockBounds(
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
index cc36af1..df583d9 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
@@ -101,14 +101,14 @@
 
         mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
 
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         resizeActivityTask(TEST_ACTIVITY,
                 TEST_TASK_OFFSET, TEST_TASK_OFFSET, TEST_TASK_SIZE_2, TEST_TASK_SIZE_1);
         resizeActivityTask(NO_RELAUNCH_ACTIVITY,
                 TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, TEST_TASK_SIZE_2, TEST_TASK_SIZE_1);
         mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
 
-        assertActivityLifecycle(TEST_ACTIVITY, true);
-        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false);
+        assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
+        assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false /* relaunched */, logSeparator);
     }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 9da97a2..666ca73 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -715,17 +715,17 @@
         // Launch a PiP activity and ensure configuration change only happened once, and that the
         // configuration change happened after the picture-in-picture and multi-window callbacks
         launchActivity(PIP_ACTIVITY);
-        clearLogcat();
+        String logSeparator = clearLogcat();
         executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
         mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
         assertPinnedStackExists();
-        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY);
+        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
 
         // Trigger it to go back to fullscreen and ensure that only triggered one configuration
         // change as well
-        clearLogcat();
+        logSeparator = clearLogcat();
         launchActivity(PIP_ACTIVITY);
-        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY);
+        assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
     }
 
     public void testPreventSetAspectRatioWhileExpanding() throws Exception {
@@ -864,8 +864,10 @@
      * Asserts that the activity received exactly one of each of the callbacks when entering and
      * exiting picture-in-picture.
      */
-    private void assertValidPictureInPictureCallbackOrder(String activityName) throws Exception {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+    private void assertValidPictureInPictureCallbackOrder(String activityName, String logSeparator)
+            throws Exception {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
 
         if (lifecycleCounts.mConfigurationChangedCount != 1) {
             fail(activityName + " has received " + lifecycleCounts.mConfigurationChangedCount
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
index 4afd607..28161bc 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
@@ -98,7 +98,7 @@
         if (!isHandheld()) {
             return;
         }
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         gotoKeyguard();
         mAmWmState.computeState(mDevice, null);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -108,20 +108,20 @@
         mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
         mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
         assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat();
+        assertOnDismissSucceededInLogcat(logSeparator);
     }
 
     public void testDismissKeyguardActivity_method_cancelled() throws Exception {
         if (!isHandheld()) {
             return;
         }
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         gotoKeyguard();
         mAmWmState.computeState(mDevice, null);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
         launchActivity("DismissKeyguardMethodActivity");
         pressBackButton();
-        assertOnDismissCancelledInLogcat();
+        assertOnDismissCancelledInLogcat(logSeparator);
         mAmWmState.computeState(mDevice, new String[] {});
         mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
index ca86637..d578644 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
@@ -37,23 +37,24 @@
         assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
     }
 
-    protected void assertOnDismissSucceededInLogcat() throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded");
+    protected void assertOnDismissSucceededInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded", logSeparator);
     }
 
-    protected void assertOnDismissCancelledInLogcat() throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled");
+    protected void assertOnDismissCancelledInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissCancelled", logSeparator);
     }
 
-    protected void assertOnDismissErrorInLogcat() throws Exception {
-        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError");
+    protected void assertOnDismissErrorInLogcat(String logSeparator) throws Exception {
+        assertInLogcat("KeyguardDismissLoggerCallback", "onDismissError", logSeparator);
     }
 
-    private void assertInLogcat(String activityName, String entry) throws Exception {
+    private void assertInLogcat(String activityName, String entry, String logSeparator)
+            throws Exception {
         final Pattern pattern = Pattern.compile("(.+)" + entry);
         int tries = 0;
         while (tries < 5) {
-            final String[] lines = getDeviceLogsForComponent(activityName);
+            final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
             log("Looking at logcat");
             for (int i = lines.length - 1; i >= 0; i--) {
                 final String line = lines[i].trim();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
index fa611ad..f17221ef 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
@@ -174,7 +174,7 @@
         if (!isHandheld()) {
             return;
         }
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         gotoKeyguard();
         mAmWmState.computeState(mDevice, null);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -183,28 +183,28 @@
         mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
         mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
         assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat();
+        assertOnDismissSucceededInLogcat(logSeparator);
     }
 
     public void testDismissKeyguardActivity_method_notTop() throws Exception {
         if (!isHandheld()) {
             return;
         }
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         gotoKeyguard();
         mAmWmState.computeState(mDevice, null);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
         launchActivity("BroadcastReceiverActivity");
         launchActivity("TestActivity");
         executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguardMethod true");
-        assertOnDismissErrorInLogcat();
+        assertOnDismissErrorInLogcat(logSeparator);
     }
 
     public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
         if (!isHandheld()) {
             return;
         }
-        clearLogcat();
+        final String logSeparator = clearLogcat();
         sleepDevice();
         mAmWmState.computeState(mDevice, null);
         assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
@@ -213,7 +213,7 @@
         mAmWmState.computeState(mDevice, new String[] { "TurnScreenOnDismissKeyguardActivity"});
         mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
         assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
-        assertOnDismissSucceededInLogcat();
+        assertOnDismissSucceededInLogcat(logSeparator);
     }
 
     public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
index 4f9c3ac..4483947 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
@@ -28,6 +28,7 @@
 import java.lang.String;
 import java.util.HashSet;
 import java.util.List;
+import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -39,6 +40,7 @@
 public abstract class ActivityManagerTestBase extends DeviceTestCase {
     private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
     private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
+    private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
 
     // Constants copied from ActivityManager.StackId. If they are changed there, these must be
     // updated.
@@ -687,13 +689,22 @@
         return output;
     }
 
-    protected void clearLogcat() throws DeviceNotAvailableException {
+    /**
+     * Tries to clear logcat and inserts log separator in case clearing didn't succeed, so we can
+     * always find the starting point from where to evaluate following logs.
+     * @return Unique log separator.
+     */
+    protected String clearLogcat() throws DeviceNotAvailableException {
         mDevice.executeAdbCommand("logcat", "-c");
+        final String uniqueString = UUID.randomUUID().toString();
+        executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString);
+        return uniqueString;
     }
 
-    protected void assertActivityLifecycle(String activityName, boolean relaunched)
-            throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+    protected void assertActivityLifecycle(String activityName, boolean relaunched,
+            String logSeparator) throws DeviceNotAvailableException {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
 
         if (relaunched) {
             if (lifecycleCounts.mDestroyCount < 1) {
@@ -722,9 +733,10 @@
     }
 
     protected void assertRelaunchOrConfigChanged(
-            String activityName, int numRelaunch, int numConfigChange)
+            String activityName, int numRelaunch, int numConfigChange, String logSeparator)
             throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
 
         if (lifecycleCounts.mDestroyCount != numRelaunch) {
             fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
@@ -738,8 +750,10 @@
         }
     }
 
-    protected void assertActivityDestroyed(String activityName) throws DeviceNotAvailableException {
-        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName);
+    protected void assertActivityDestroyed(String activityName, String logSeparator)
+            throws DeviceNotAvailableException {
+        final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+                logSeparator);
 
         if (lifecycleCounts.mDestroyCount != 1) {
             fail(activityName + " has been destroyed " + lifecycleCounts.mDestroyCount
@@ -753,20 +767,37 @@
         }
     }
 
-    protected String[] getDeviceLogsForComponent(String componentName)
+    protected String[] getDeviceLogsForComponent(String componentName, String logSeparator)
             throws DeviceNotAvailableException {
-        return mDevice.executeAdbCommand(
-                "logcat", "-v", "brief", "-d", componentName + ":I", "*:S").split("\\n");
+        return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
     }
 
-    protected String[] getDeviceLogsForComponents(final String[] componentNames)
-            throws DeviceNotAvailableException {
-        String filters = "";
-        for (int i = 0; i < componentNames.length; i++) {
-            filters += componentNames[i] + ":I ";
+    protected String[] getDeviceLogsForComponents(final String[] componentNames,
+            String logSeparator) throws DeviceNotAvailableException {
+        String filters = LOG_SEPARATOR + ":I ";
+        for (String component : componentNames) {
+            filters += component + ":I ";
         }
-        return mDevice.executeAdbCommand(
+        final String[] result = mDevice.executeAdbCommand(
                 "logcat", "-v", "brief", "-d", filters, "*:S").split("\\n");
+        if (logSeparator == null) {
+            return result;
+        }
+
+        // Make sure that we only check logs after the separator.
+        int i = 0;
+        boolean lookingForSeparator = true;
+        while (i < result.length && lookingForSeparator) {
+            if (result[i].contains(logSeparator)) {
+                lookingForSeparator = false;
+            }
+            i++;
+        }
+        final String[] filteredResult = new String[result.length - i];
+        for (int curPos = 0; i < result.length; curPos++, i++) {
+            filteredResult[curPos] = result[i];
+        }
+        return filteredResult;
     }
 
     private static final Pattern sCreatePattern = Pattern.compile("(.+): onCreate");
@@ -819,9 +850,9 @@
         }
     }
 
-    ReportedSizes getLastReportedSizesForActivity(String activityName)
+    ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator)
             throws DeviceNotAvailableException {
-        final String[] lines = getDeviceLogsForComponent(activityName);
+        final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
         for (int i = lines.length - 1; i >= 0; i--) {
             final String line = lines[i].trim();
             final Matcher matcher = sNewConfigPattern.matcher(line);
@@ -852,9 +883,10 @@
         int mLastPictureInPictureModeChangedLineIndex;
         int mDestroyCount;
 
-        public ActivityLifecycleCounts(String activityName) throws DeviceNotAvailableException {
+        public ActivityLifecycleCounts(String activityName, String logSeparator)
+                throws DeviceNotAvailableException {
             int lineIndex = 0;
-            for (String line : getDeviceLogsForComponent(activityName)) {
+            for (String line : getDeviceLogsForComponent(activityName, logSeparator)) {
                 line = line.trim();
                 lineIndex++;
 
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
index 8cb377d..46c9b03 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <activity android:name=".DialogTestActivity"
                 android:exported="true"
         />
-        <activity android:name=".MovingPopupTestActivity"
+        <activity android:name=".MovingChildTestActivity"
                   android:exported="true"
         />
     </application>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
similarity index 67%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
index 91b9788..de6f597 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
@@ -18,23 +18,23 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.WindowManager;
 import android.view.Window;
 import android.view.Gravity;
 import android.view.View;
 import android.widget.Space;
-import android.widget.PopupWindow;
 import android.widget.Button;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-// This activity will parent a Popup to the main window, and then move
-// the main window around. We can use this to verify the Popup
+// This activity will parent a Child to the main window, and then move
+// the main window around. We can use this to verify the Child
 // is properly updated.
-public class MovingPopupTestActivity extends Activity {
+public class MovingChildTestActivity extends Activity {
     Space mView;
-    PopupWindow mPopupWindow;
     int mX = 0;
     int mY = 0;
 
@@ -43,6 +43,7 @@
             public void run() {
                 final Window w = getWindow();
                 final WindowManager.LayoutParams attribs = w.getAttributes();
+                attribs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
                 attribs.x = mX % 1000;
                 attribs.y = mY % 1000;
                 w.setAttributes(attribs);
@@ -52,16 +53,19 @@
             }
     };
 
-    final Runnable makePopup = new Runnable() {
+    final Runnable makeChild = new Runnable() {
             @Override
             public void run() {
-                Button b = new Button(MovingPopupTestActivity.this);
+                Button b = new Button(MovingChildTestActivity.this);
+                WindowManager.LayoutParams p = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
+                p.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+                p.x = 0;
+                p.y = 0;
+                p.token = mView.getWindowToken();
+                p.setTitle("ChildWindow");
 
-                mPopupWindow = new PopupWindow(MovingPopupTestActivity.this);
-                mPopupWindow.setContentView(b);
-                mPopupWindow.setWidth(50);
-                mPopupWindow.setHeight(50);
-                mPopupWindow.showAtLocation(mView, 0, 0, 0);
+                ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).addView(b, p);
 
                 mView.postDelayed(moveWindow, 50);
             }
@@ -76,6 +80,6 @@
         mView = new Space(this);
 
         setContentView(mView, p);
-        mView.post(makePopup);
+        mView.post(makeChild);
     }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
index 6c7cad0..218fcfe 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
@@ -32,17 +32,17 @@
 import android.server.cts.ActivityManagerTestBase;
 import android.server.cts.WindowManagerState.WindowState;
 
-public class PopupMovementTests extends ParentChildTestBase {
+public class ChildMovementTests extends ParentChildTestBase {
     private List<WindowState> mWindowList = new ArrayList();
 
     @Override
     String intentKey() {
-        return "android.server.FrameTestApp.PopupTestCase";
+        return "android.server.FrameTestApp.ChildTestCase";
     }
 
     @Override
     String activityName() {
-        return "MovingPopupTestActivity";
+        return "MovingChildTestActivity";
     }
 
     WindowState getSingleWindow(String fullWindowName) {
@@ -66,7 +66,7 @@
     }
 
     void doSingleTest(ParentChildTest t) throws Exception {
-        String popupName = "PopupWindow";
+        String popupName = "ChildWindow";
         final String[] waitForVisible = new String[] { popupName };
 
         mAmWmState.setUseActivityNamesForWindowNames(false);
@@ -85,7 +85,7 @@
 
     SurfaceTraceReceiver.SurfaceObserver observer = new SurfaceTraceReceiver.SurfaceObserver() {
         int transactionCount = 0;
-        boolean sawPopupMove = false;
+        boolean sawChildMove = false;
         boolean sawMainMove = false;
         int timesSeen = 0;
 
@@ -93,7 +93,7 @@
         public void openTransaction() {
             transactionCount++;
             if (transactionCount == 1) {
-                sawPopupMove = false;
+                sawChildMove = false;
                 sawMainMove = false;
             }
         }
@@ -105,7 +105,7 @@
                 return;
             }
             synchronized (monitor) {
-                if (sawPopupMove ^ sawMainMove ) {
+                if (sawChildMove ^ sawMainMove ) {
                     monitor.notifyAll();
                     return;
                 }
@@ -119,7 +119,7 @@
         @Override
         public void setPosition(String windowName, float x, float y) {
             if (windowName.equals(popupName)) {
-                sawPopupMove = true;
+                sawChildMove = true;
                 timesSeen++;
             } else if (windowName.equals(mainName)) {
                 sawMainMove = true;
@@ -128,10 +128,10 @@
     };
 
     /**
-     * Here we test that a Popup moves in the same transaction
-     * as its parent. We launch an activity with a Popup which will
+     * Here we test that a Child moves in the same transaction
+     * as its parent. We launch an activity with a Child which will
      * move around its own main window. Then we listen to WindowManager transactions.
-     * Since the Popup is static within the window, if we ever see one of
+     * Since the Child is static within the window, if we ever see one of
      * them move xor the other one we have a problem!
      */
     public void testSurfaceMovesWithParent() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
index 56b1b40..6fb5fbd 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
@@ -135,7 +135,9 @@
     // With FLAG_LAYOUT_NO_LIMITS  we should get the size we request, even if its much
     // larger than the screen.
     public void testOversizedDimensionsNoLimits() throws Exception {
-        doParentChildTest("OversizedDimensionsNoLimits",
+        // TODO(b/36890978): We only run this in fullscreen because of the
+        // unclear status of NO_LIMITS for non-child surfaces in MW modes
+        doFullscreenTest("OversizedDimensionsNoLimits",
             (WindowState parent, WindowState dialog) -> {
                 Rectangle contentFrame = parent.getContentFrame();
                 Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
diff --git a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
index d599546..a8871a3 100644
--- a/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
+++ b/hostsidetests/trustedvoice/src/android/trustedvoice/cts/TrustedVoiceHostTest.java
@@ -99,6 +99,8 @@
             getDevice().executeShellCommand(SLEEP_COMMAND);
             // Start the APK and wait for it to complete.
             getDevice().executeShellCommand(START_COMMAND);
+            // Give the activity some time to start
+            Thread.sleep(500);
             // Dump logcat.
             String logs = getDevice().executeAdbCommand(
                     "logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
diff --git a/hostsidetests/webkit/AndroidTest.xml b/hostsidetests/webkit/AndroidTest.xml
index 8641395..5a0eeca 100644
--- a/hostsidetests/webkit/AndroidTest.xml
+++ b/hostsidetests/webkit/AndroidTest.xml
@@ -17,7 +17,6 @@
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWebViewStartupApp.apk" />
-        <option name="test-file-name" value="CtsWebViewRendererCrash.apk" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/webkit/renderprocesscrash/Android.mk b/hostsidetests/webkit/renderprocesscrash/Android.mk
deleted file mode 100644
index 846cdc8..0000000
--- a/hostsidetests/webkit/renderprocesscrash/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES :=
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsWebViewRendererCrash
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml b/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml
deleted file mode 100644
index d1744e5..0000000
--- a/hostsidetests/webkit/renderprocesscrash/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.webkit.renderprocesscrash">
-
-    <uses-permission android:name="android.permission.INTERNET" />
-
-    <application android:maxRecents="1">
-        <activity android:name=".RenderProcessCrashActivity"
-            android:label="RenderProcessCrashActivity"
-            android:screenOrientation="nosensor">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java
deleted file mode 100644
index 2f65afc..0000000
--- a/hostsidetests/webkit/src/com/android/cts/webkit/RenderProcessCrashTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.webkit;
-
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.TestDeviceOptions;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import java.util.Scanner;
-
-/**
- * This test lanuches RenderProcessCrashActivity which crashes render process, and
- * checks specific crash log in Logcat to verify Render process crashed.
- */
-public class RenderProcessCrashTest extends DeviceTestCase {
-    /**
-     * The package name of the APK.
-     */
-    private static final String PACKAGE = "com.android.cts.webkit.renderprocesscrash";
-
-    /**
-     * The class name of the main activity in the APK.
-     */
-    private static final String CLASS = "RenderProcessCrashActivity";
-
-    /**
-     * The command to launch the main activity.
-     */
-    private static final String START_COMMAND = String.format(
-            "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
-
-    /**
-     * The test string to look for.
-     */
-    private static final String TEST_STRING = "kill (OOM or update) wasn't handed by all associated"
-        + " webviews, killing application.";
-
-    /**
-     * Tests the string was successfully logged to Logcat from the activity.
-     *
-     * @throws Exception
-     */
-    public void testCrash() throws Exception {
-        ITestDevice device = getDevice();
-        // Clear logcat.
-        device.executeAdbCommand("logcat", "-c");
-        TestDeviceOptions options = new TestDeviceOptions();
-        options.setLogcatOptions("-v brief -d chromium:E *:S");
-        device.setOptions(options);
-
-        // Start the APK.
-        device.executeShellCommand(START_COMMAND);
-        // Dump logcat.
-        device.startLogcat();
-        // Search for string.
-        Scanner in = new Scanner(device.getLogcat().createInputStream());
-        boolean found = false;
-        int tryTimes = 10;
-        while (tryTimes-- > 0) {
-            while (in.hasNextLine()) {
-                String line = in.nextLine().trim();
-                if(line.endsWith(TEST_STRING)) {
-                    found = true;
-                    break;
-                }
-            }
-            if (found) break;
-            Thread.sleep(1000);
-        }
-        in.close();
-        device.stopLogcat();
-        assertTrue("Can't not find crash log " + TEST_STRING, found);
-    }
-}
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index e914ee2..7d06682 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -17,14 +17,21 @@
 package android.jobscheduler;
 
 import android.annotation.TargetApi;
+import android.app.job.JobInfo;
 import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
 import android.app.job.JobService;
+import android.app.job.JobWorkItem;
 import android.content.ClipData;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Process;
 import android.util.Log;
 
+import junit.framework.Assert;
+
+import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -40,15 +47,29 @@
     /** Wait this long before timing out the test. */
     private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
 
+    private JobParameters mParams;
+
+    ArrayList<Intent> mReceivedWork = new ArrayList<Intent>();
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (TestEnvironment.getTestEnvironment().getExpectedWork() != null) {
+            TestEnvironment.getTestEnvironment().notifyExecution(mParams, 0, 0, mReceivedWork,
+                    null);
+        }
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
-        Log.e(TAG, "Created test service.");
+        Log.i(TAG, "Created test service.");
     }
 
     @Override
     public boolean onStartJob(JobParameters params) {
         Log.i(TAG, "Test job executing: " + params.getJobId());
+        mParams = params;
 
         int permCheckRead = PackageManager.PERMISSION_DENIED;
         int permCheckWrite = PackageManager.PERMISSION_DENIED;
@@ -60,8 +81,44 @@
                     Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
         }
 
-        TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead, permCheckWrite);
-        return false;  // No work to do.
+        TestWorkItem[] expectedWork = TestEnvironment.getTestEnvironment().getExpectedWork();
+        if (expectedWork != null) {
+            try {
+                if (TestEnvironment.getTestEnvironment().awaitDoWork()) {
+                    TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+                            permCheckWrite, null, "Spent too long waiting to start executing work");
+                    return false;
+                }
+            } catch (InterruptedException e) {
+                TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+                        permCheckWrite, null, "Failed waiting for work: " + e);
+                return false;
+            }
+            JobWorkItem work;
+            int index = 0;
+            while ((work = params.dequeueWork()) != null) {
+                Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
+                mReceivedWork.add(work.getIntent());
+                params.completeWork(work);
+                if (index < expectedWork.length && expectedWork[index].subitems != null) {
+                    final TestWorkItem[] sub = expectedWork[index].subitems;
+                    final JobInfo ji = expectedWork[index].jobInfo;
+                    final JobScheduler js = (JobScheduler) getSystemService(
+                            Context.JOB_SCHEDULER_SERVICE);
+                    for (int subi = 0; subi < sub.length; subi++) {
+                        js.enqueue(ji, new JobWorkItem(sub[subi].intent));
+                    }
+                }
+            }
+            Log.i(TAG, "Done with all work at #" + index);
+            // We don't notifyExecution here because we want to make sure the job properly
+            // stops itself.
+            return true;
+        } else {
+            TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
+                    permCheckWrite, null, null);
+            return false;  // No work to do.
+        }
     }
 
     @Override
@@ -69,6 +126,24 @@
         return false;
     }
 
+    public static final class TestWorkItem {
+        public final Intent intent;
+        public final JobInfo jobInfo;
+        public final TestWorkItem[] subitems;
+
+        public TestWorkItem(Intent _intent) {
+            intent = _intent;
+            jobInfo = null;
+            subitems = null;
+        }
+
+        public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
+            intent = _intent;
+            jobInfo = _jobInfo;
+            subitems = _subitems;
+        }
+    }
+
     /**
      * Configures the expected behaviour for each test. This object is shared across consecutive
      * tests, so to clear state each test is responsible for calling
@@ -80,9 +155,13 @@
         //public static final int INVALID_JOB_ID = -1;
 
         private CountDownLatch mLatch;
+        private CountDownLatch mDoWorkLatch;
+        private TestWorkItem[] mExpectedWork;
         private JobParameters mExecutedJobParameters;
         private int mExecutedPermCheckRead;
         private int mExecutedPermCheckWrite;
+        private ArrayList<Intent> mExecutedReceivedWork;
+        private String mExecutedErrorMessage;
 
         public static TestEnvironment getTestEnvironment() {
             if (kTestEnvironment == null) {
@@ -91,6 +170,10 @@
             return kTestEnvironment;
         }
 
+        public TestWorkItem[] getExpectedWork() {
+            return mExpectedWork;
+        }
+
         public JobParameters getLastJobParameters() {
             return mExecutedJobParameters;
         }
@@ -103,12 +186,23 @@
             return mExecutedPermCheckWrite;
         }
 
+        public ArrayList<Intent> getLastReceivedWork() {
+            return mExecutedReceivedWork;
+        }
+
+        public String getLastErrorMessage() {
+            return mExecutedErrorMessage;
+        }
+
         /**
          * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
          * job on this service.
          */
         public boolean awaitExecution() throws InterruptedException {
             final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            if (getLastErrorMessage() != null) {
+                Assert.fail(getLastErrorMessage());
+            }
             return executed;
         }
 
@@ -121,11 +215,18 @@
             return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
         }
 
-        private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite) {
+        public boolean awaitDoWork() throws InterruptedException {
+            return !mDoWorkLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        }
+
+        private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite,
+                ArrayList<Intent> receivedWork, String errorMsg) {
             //Log.d(TAG, "Job executed:" + params.getJobId());
             mExecutedJobParameters = params;
             mExecutedPermCheckRead = permCheckRead;
             mExecutedPermCheckWrite = permCheckWrite;
+            mExecutedReceivedWork = receivedWork;
+            mExecutedErrorMessage = errorMsg;
             mLatch.countDown();
         }
 
@@ -138,6 +239,15 @@
             }
         }
 
+        public void setExpectedWork(TestWorkItem[] work) {
+            mExpectedWork = work;
+            mDoWorkLatch = new CountDownLatch(1);
+        }
+
+        public void readyToWork() {
+            mDoWorkLatch.countDown();
+        }
+
         /** Called in each testCase#setup */
         public void setUp() {
             mLatch = null;
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
index be3f9c9..5ffa347 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
@@ -54,8 +54,6 @@
 
         mBuilder = new JobInfo.Builder(BATTERY_JOB_ID, kJobServiceComponent);
         SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler monitor-battery on");
-        String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
-                + mContext.getPackageName() + " false");
     }
 
     @Override
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
index 9a8b642..47089d4 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ClipDataJobTest.java
@@ -19,16 +19,10 @@
 
 import android.annotation.TargetApi;
 import android.app.job.JobInfo;
-import android.content.ClipData;
 import android.content.ContentProviderClient;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.Bundle;
 import android.os.Process;
-import android.os.SystemClock;
-
-import com.android.compatibility.common.util.SystemUtil;
 
 /**
  * Schedules jobs with the {@link android.app.job.JobScheduler} that grant permissions through
@@ -41,16 +35,7 @@
     /** Unique identifier for the job scheduled by this suite of tests. */
     public static final int CLIP_DATA_JOB_ID = ClipDataJobTest.class.hashCode();
 
-    static final String MY_PACKAGE = "android.jobscheduler.cts";
-
-    static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
-    static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
-    static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
-
-    private JobInfo.Builder mBuilder;
-    private Uri mFirstUri;
-    private Bundle mFirstUriBundle;
-    private ClipData mClipData;
+    JobInfo.Builder mBuilder;
     private ContentProviderClient mProvider;
 
     @Override
@@ -58,62 +43,15 @@
         super.setUp();
 
         mBuilder = new JobInfo.Builder(CLIP_DATA_JOB_ID, kJobServiceComponent);
-        mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
-        mFirstUriBundle = new Bundle();
-        mFirstUriBundle.putParcelable("uri", mFirstUri);
-        mClipData = new ClipData("JobPerm", new String[] { "application/*" },
-                new ClipData.Item(mFirstUri));
         mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
-        String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
-                + mContext.getPackageName() + " false");
+        assertNotNull(mProvider);
     }
 
     @Override
     public void tearDown() throws Exception {
+        super.tearDown();
         mProvider.close();
         mJobScheduler.cancel(CLIP_DATA_JOB_ID);
-        // Put storage service back in to normal operation.
-        SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
-    }
-
-    // Note we are just using storage state as a way to control when the job gets executed.
-    void setStorageState(boolean low) throws Exception {
-        String res;
-        if (low) {
-            res = SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd devicestoragemonitor force-low -f");
-        } else {
-            res = SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd devicestoragemonitor force-not-low -f");
-        }
-        int seq = Integer.parseInt(res.trim());
-        long startTime = SystemClock.elapsedRealtime();
-
-        // Wait for the storage update to be processed by job scheduler before proceeding.
-        int curSeq;
-        do {
-            curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd jobscheduler get-storage-seq").trim());
-            if (curSeq == seq) {
-                return;
-            }
-        } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
-
-        fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
-    }
-
-    void waitPermissionRevoke(Uri uri, int access, long timeout) {
-        long startTime = SystemClock.elapsedRealtime();
-        while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
-                 != PackageManager.PERMISSION_GRANTED) {
-            try {
-                Thread.sleep(50);
-            } catch (InterruptedException e) {
-            }
-            if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
-                fail("Timed out waiting for permission revoke");
-            }
-        }
     }
 
     /**
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
index b2cf29c..c92e521 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
@@ -17,14 +17,26 @@
 
 import android.annotation.TargetApi;
 import android.app.Instrumentation;
+import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
+import android.content.ClipData;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.jobscheduler.MockJobService;
 import android.jobscheduler.TriggerContentJobService;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
 
 /**
  * Common functionality from which the other test case classes derive.
@@ -47,10 +59,37 @@
 
     Context mContext;
 
+    static final String MY_PACKAGE = "android.jobscheduler.cts";
+
+    static final String JOBPERM_PACKAGE = "android.jobscheduler.cts.jobperm";
+    static final String JOBPERM_AUTHORITY = "android.jobscheduler.cts.jobperm.provider";
+    static final String JOBPERM_PERM = "android.jobscheduler.cts.jobperm.perm";
+
+    Uri mFirstUri;
+    Bundle mFirstUriBundle;
+    ClipData mClipData;
+
+    boolean mStorageStateChanged;
+
     @Override
     public void injectInstrumentation(Instrumentation instrumentation) {
         super.injectInstrumentation(instrumentation);
         mContext = instrumentation.getContext();
+        kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
+        kTriggerContentServiceComponent = new ComponentName(getContext(),
+                TriggerContentJobService.class);
+        mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        mFirstUri = Uri.parse("content://" + JOBPERM_AUTHORITY + "/protected/foo");
+        mFirstUriBundle = new Bundle();
+        mFirstUriBundle.putParcelable("uri", mFirstUri);
+        mClipData = new ClipData("JobPerm", new String[] { "application/*" },
+                new ClipData.Item(mFirstUri));
+        try {
+            SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
+                    + mContext.getPackageName() + " false");
+        } catch (IOException e) {
+            Log.w("ConstraintTest", "Failed setting inactive false", e);
+        }
     }
 
     public Context getContext() {
@@ -62,13 +101,18 @@
         super.setUp();
         kTestEnvironment.setUp();
         kTriggerTestEnvironment.setUp();
-        kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
-        kTriggerContentServiceComponent = new ComponentName(getContext(),
-                TriggerContentJobService.class);
-        mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
         mJobScheduler.cancelAll();
     }
 
+    @Override
+    public void tearDown() throws Exception {
+        if (mStorageStateChanged) {
+            // Put storage service back in to normal operation.
+            SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
+            mStorageStateChanged = false;
+        }
+    }
+
     /**
      * The scheduler will usually only flush its queue of unexpired jobs when the device is
      * considered to be on stable power - that is, plugged in for a period of 2 minutes.
@@ -77,4 +121,45 @@
     protected void sendExpediteStableChargingBroadcast() {
         getContext().sendBroadcast(EXPEDITE_STABLE_CHARGING);
     }
+
+    void waitPermissionRevoke(Uri uri, int access, long timeout) {
+        long startTime = SystemClock.elapsedRealtime();
+        while (getContext().checkUriPermission(uri, Process.myPid(), Process.myUid(), access)
+                != PackageManager.PERMISSION_GRANTED) {
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+            }
+            if ((SystemClock.elapsedRealtime()-startTime) >= timeout) {
+                fail("Timed out waiting for permission revoke");
+            }
+        }
+    }
+
+    // Note we are just using storage state as a way to control when the job gets executed.
+    void setStorageState(boolean low) throws Exception {
+        mStorageStateChanged = true;
+        String res;
+        if (low) {
+            res = SystemUtil.runShellCommand(getInstrumentation(),
+                    "cmd devicestoragemonitor force-low -f");
+        } else {
+            res = SystemUtil.runShellCommand(getInstrumentation(),
+                    "cmd devicestoragemonitor force-not-low -f");
+        }
+        int seq = Integer.parseInt(res.trim());
+        long startTime = SystemClock.elapsedRealtime();
+
+        // Wait for the storage update to be processed by job scheduler before proceeding.
+        int curSeq;
+        do {
+            curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
+                    "cmd jobscheduler get-storage-seq").trim());
+            if (curSeq == seq) {
+                return;
+            }
+        } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
+
+        fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
+    }
 }
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
new file mode 100644
index 0000000..ad5a20f
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.jobscheduler.cts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobWorkItem;
+import android.content.ContentProviderClient;
+import android.content.Intent;
+import android.jobscheduler.MockJobService.TestWorkItem;
+
+import java.util.ArrayList;
+
+/**
+ * Schedules jobs with the {@link android.app.job.JobScheduler} by enqueue work in to
+ * them and processing it.
+ */
+@TargetApi(26)
+public class EnqueueJobWorkTest extends ConstraintTest {
+    private static final String TAG = "ClipDataJobTest";
+
+    /** Unique identifier for the job scheduled by this suite of tests. */
+    public static final int ENQUEUE_WORK_JOB_ID = EnqueueJobWorkTest.class.hashCode();
+
+    private JobInfo.Builder mBuilder;
+    private ContentProviderClient mProvider;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mBuilder = new JobInfo.Builder(ENQUEUE_WORK_JOB_ID, kJobServiceComponent);
+        //mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        //mProvider.close();
+        mJobScheduler.cancel(ENQUEUE_WORK_JOB_ID);
+    }
+
+    private boolean intentEquals(Intent i1, Intent i2) {
+        if (i1 == i2) {
+            return true;
+        }
+        if (i1 == null || i2 == null) {
+            return false;
+        }
+        return i1.filterEquals(i2);
+    }
+
+    private void compareWork(TestWorkItem[] expected, ArrayList<Intent> received) {
+        if (received == null) {
+            fail("Didn't receive any expected work.");
+        }
+        ArrayList<TestWorkItem> expectedArray = new ArrayList<>();
+        for (int i = 0; i < expected.length; i++) {
+            expectedArray.add(expected[i]);
+        }
+        for (int i = 0; i < received.size(); i++) {
+            Intent work = received.get(i);
+            if (i >= expected.length) {
+                fail("Received more than " + expected.length + " work items, first extra is "
+                        + work);
+            }
+            if (!intentEquals(work, expectedArray.get(i).intent)) {
+                fail("Received work #" + i + " " + work + " but expected " + expected[i]);
+            }
+            if (i < expected.length && expected[i].subitems != null) {
+                TestWorkItem[] sub = expected[i].subitems;
+                for (int j = 0; j < sub.length; j++) {
+                    expectedArray.add(sub[j]);
+                }
+            }
+        }
+        if (received.size() < expected.length) {
+            fail("Received only " + received.size() + " work items, but expected "
+                            + expected.length);
+        }
+    }
+
+    /**
+     * Test basic enqueueing of work.
+     */
+    public void testEnqueueOneWork() throws Exception {
+        Intent work1 = new Intent("work1");
+        TestWorkItem[] work = new TestWorkItem[] { new TestWorkItem(work1) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        mJobScheduler.enqueue(mBuilder.setOverrideDeadline(0).build(), new JobWorkItem(work1));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+        if (kTestEnvironment.getLastErrorMessage() != null) {
+            fail(kTestEnvironment.getLastErrorMessage());
+        }
+    }
+
+    /**
+     * Test basic enqueueing batches of work.
+     */
+    public void testEnqueueMultipleWork() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        Intent work3 = new Intent("work3");
+        Intent work4 = new Intent("work4");
+        Intent work5 = new Intent("work5");
+        Intent work6 = new Intent("work6");
+        Intent work7 = new Intent("work7");
+        Intent work8 = new Intent("work8");
+        TestWorkItem[] work = new TestWorkItem[] {
+                new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3),
+                new TestWorkItem(work4), new TestWorkItem(work5), new TestWorkItem(work6),
+                new TestWorkItem(work7), new TestWorkItem(work8) };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work7));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work8));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+    }
+
+    /**
+     * Test basic enqueueing batches of work, with new work coming in while processing existing
+     * work.
+     */
+    public void testEnqueueMultipleSubWork() throws Exception {
+        Intent work1 = new Intent("work1");
+        Intent work2 = new Intent("work2");
+        Intent work3 = new Intent("work3");
+        Intent work4 = new Intent("work4");
+        Intent work5 = new Intent("work5");
+        Intent work6 = new Intent("work6");
+        Intent work7 = new Intent("work7");
+        Intent work8 = new Intent("work8");
+        JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+        TestWorkItem[] work = new TestWorkItem[]{
+                new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3),
+                new TestWorkItem(work4, ji, new TestWorkItem[] {
+                        new TestWorkItem(work5), new TestWorkItem(work6),
+                        new TestWorkItem(work7), new TestWorkItem(work8)})
+        };
+        kTestEnvironment.setExpectedExecutions(1);
+        kTestEnvironment.setExpectedWork(work);
+        mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+        mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+        kTestEnvironment.readyToWork();
+        assertTrue("Job with work enqueued did not fire.",
+                kTestEnvironment.awaitExecution());
+        compareWork(work, kTestEnvironment.getLastReceivedWork());
+    }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
index f40dd66..aea4d84 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/StorageConstraintTest.java
@@ -40,40 +40,12 @@
         super.setUp();
 
         mBuilder = new JobInfo.Builder(STORAGE_JOB_ID, kJobServiceComponent);
-        String res = SystemUtil.runShellCommand(getInstrumentation(), "cmd activity set-inactive "
-                + mContext.getPackageName() + " false");
     }
 
     @Override
     public void tearDown() throws Exception {
+        super.tearDown();
         mJobScheduler.cancel(STORAGE_JOB_ID);
-        // Put storage service back in to normal operation.
-        SystemUtil.runShellCommand(getInstrumentation(), "cmd devicestoragemonitor reset");
-    }
-
-    void setStorageState(boolean low) throws Exception {
-        String res;
-        if (low) {
-            res = SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd devicestoragemonitor force-low -f");
-        } else {
-            res = SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd devicestoragemonitor force-not-low -f");
-        }
-        int seq = Integer.parseInt(res.trim());
-        long startTime = SystemClock.elapsedRealtime();
-
-        // Wait for the storage update to be processed by job scheduler before proceeding.
-        int curSeq;
-        do {
-            curSeq = Integer.parseInt(SystemUtil.runShellCommand(getInstrumentation(),
-                    "cmd jobscheduler get-storage-seq").trim());
-            if (curSeq == seq) {
-                return;
-            }
-        } while ((SystemClock.elapsedRealtime()-startTime) < 1000);
-
-        fail("Timed out waiting for job scheduler: expected seq=" + seq + ", cur=" + curSeq);
     }
 
     // --------------------------------------------------------------------------------------------
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
index 8b71171..fe48950 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TriggerContentTest.java
@@ -42,7 +42,7 @@
  */
 @TargetApi(23)
 public class TriggerContentTest extends ConstraintTest {
-    public static final int TRIGGER_CONTENT_JOB_ID = ConnectivityConstraintTest.class.hashCode();
+    public static final int TRIGGER_CONTENT_JOB_ID = TriggerContentTest.class.hashCode();
 
     // The root URI of the media provider, to monitor for generic changes to its content.
     static final Uri MEDIA_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/");
@@ -120,7 +120,7 @@
     }
 
     @Override
-    protected void tearDown() throws Exception {
+    public void tearDown() throws Exception {
         super.tearDown();
         for (int i=0; i<mActiveFiles.length; i++) {
             cleanupActive(i);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 8291633..b67fc28 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -14,6 +14,7 @@
 package android.accessibilityservice.cts;
 
 import android.app.Instrumentation;
+import android.content.pm.PackageManager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.media.AudioManager;
@@ -31,16 +32,25 @@
 public class AccessibilityVolumeTest {
     Instrumentation mInstrumentation;
     AudioManager mAudioManager;
+    // If a platform collects all volumes into one, these tests aren't relevant
+    boolean mSingleVolume;
 
     @Before
     public void setUp() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mAudioManager =
                 (AudioManager) mInstrumentation.getContext().getSystemService(AUDIO_SERVICE);
+        // TVs have a single volume
+        PackageManager pm = mInstrumentation.getContext().getPackageManager();
+        mSingleVolume = (pm != null) && (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
     }
 
     @Test
     public void testChangeAccessibilityVolume_outsideValidAccessibilityService_shouldFail() {
+        if (mSingleVolume) {
+            return;
+        }
         int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
         int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
         mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0);
@@ -50,6 +60,9 @@
 
     @Test
     public void testChangeAccessibilityVolume_inAccessibilityService_shouldWork() {
+        if (mSingleVolume) {
+            return;
+        }
         int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
         int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
         InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
diff --git a/tests/app/src/android/app/cts/ActionBarTest.java b/tests/app/src/android/app/cts/ActionBarTest.java
index e6da6cb..351339b 100644
--- a/tests/app/src/android/app/cts/ActionBarTest.java
+++ b/tests/app/src/android/app/cts/ActionBarTest.java
@@ -24,6 +24,7 @@
 import android.test.UiThreadTest;
 import android.view.KeyEvent;
 import android.view.Window;
+import org.junit.Assume;
 
 public class ActionBarTest extends ActivityInstrumentationTestCase2<ActionBarActivity> {
 
@@ -84,6 +85,7 @@
     }
 
     public void testOptionsMenuKey() {
+        Assume.assumeTrue(mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL));
         final boolean menuIsVisible[] = {false};
         mActivity.getActionBar().addOnMenuVisibilityListener(
                 isVisible -> menuIsVisible[0] = isVisible);
diff --git a/tests/app/src/android/app/cts/ToolbarActionBarTest.java b/tests/app/src/android/app/cts/ToolbarActionBarTest.java
index 53ee982..d625802 100644
--- a/tests/app/src/android/app/cts/ToolbarActionBarTest.java
+++ b/tests/app/src/android/app/cts/ToolbarActionBarTest.java
@@ -21,6 +21,7 @@
 import android.view.KeyEvent;
 
 import android.view.Window;
+import org.junit.Assume;
 
 public class ToolbarActionBarTest extends ActivityInstrumentationTestCase2<ToolbarActivity> {
 
@@ -39,6 +40,7 @@
     }
 
     public void testOptionsMenuKey() {
+        Assume.assumeTrue(mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL));
         final boolean menuIsVisible[] = {false};
         mActivity.getActionBar().addOnMenuVisibilityListener(
                 isVisible -> menuIsVisible[0] = isVisible);
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index aca9453..663fe40 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -31,6 +31,7 @@
         <activity android:name=".WelcomeActivity"/>
         <activity android:name=".ViewAttributesTestActivity" />
         <activity android:name=".AuthenticationActivity" />
+        <activity android:name=".ManualAuthenticationActivity" />
         <activity android:name=".CheckoutActivity"/>
         <activity android:name=".InitializedCheckoutActivity" />
         <activity android:name=".DatePickerCalendarActivity" />
@@ -49,6 +50,9 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".EmptyActivity"/>
+        <activity android:name=".OutOfProcessLoginActivity"
+            android:process="android.autofillservice.cts.outside"/>
 
         <service
             android:name=".InstrumentedAutoFillService"
diff --git a/tests/autofillservice/res/layout/empty.xml b/tests/autofillservice/res/layout/empty.xml
new file mode 100644
index 0000000..7687408
--- /dev/null
+++ b/tests/autofillservice/res/layout/empty.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
diff --git a/tests/autofillservice/res/layout/single_button_activity.xml b/tests/autofillservice/res/layout/single_button_activity.xml
new file mode 100644
index 0000000..9c68b98
--- /dev/null
+++ b/tests/autofillservice/res/layout/single_button_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <Button android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="press me"
+        android:id="@+id/button" />
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 7e8fa9c..7289b5a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -241,7 +241,7 @@
         mActivity.onAddress((v) -> v.check(R.id.work_address));
         mActivity.onSaveCc((v) -> v.setChecked(false));
         mActivity.tapBuy();
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_CREDIT_CARD, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_CREDIT_CARD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         // Assert sanitization on save: everything should be available!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index fb050c3..1d4f652 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -90,7 +90,7 @@
         activity.setDate(2010, 11, 12);
         activity.tapOk();
 
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java b/tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
similarity index 69%
rename from hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java
rename to tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
index d599eba..1a64b8c 100644
--- a/hostsidetests/webkit/renderprocesscrash/src/com/android/cts/webkit/renderprocesscrash/RenderProcessCrashActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/EmptyActivity.java
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.cts.webkit.renderprocesscrash;
+package android.autofillservice.cts;
 
+import android.support.annotation.Nullable;
 import android.app.Activity;
 import android.os.Bundle;
-import android.webkit.WebView;
 
-public class RenderProcessCrashActivity extends Activity {
-
+/**
+ * Empty activity
+ */
+public class EmptyActivity extends Activity {
     @Override
-    public void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        WebView webView = new WebView(this);
-        setContentView(webView);
-        webView.loadUrl("chrome://kill");
+
+        setContentView(R.layout.empty);
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 37fb159..e19fa0c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -28,6 +28,7 @@
 import android.os.UserManager;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillResponse;
+import android.support.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -58,6 +59,12 @@
     static final String ID_LOGIN = "login";
     static final String ID_OUTPUT = "output";
 
+    /** Pass to {@link #setOrientation(int)} to change the display to portrait mode */
+    public static int PORTRAIT = 0;
+
+    /** Pass to {@link #setOrientation(int)} to change the display to landscape mode */
+    public static int LANDSCAPE = 1;
+
     /**
      * Timeout (in milliseconds) until framework binds / unbinds from service.
      */
@@ -88,6 +95,13 @@
      */
     static final int RETRY_MS = 100;
 
+    private final static String ACCELLEROMETER_CHANGE =
+            "content insert --uri content://settings/system --bind name:s:accelerometer_rotation "
+                    + "--bind value:i:%d";
+    private final static String ORIENTATION_CHANGE =
+            "content insert --uri content://settings/system --bind name:s:user_rotation --bind "
+                    + "value:i:%d";
+
     /**
      * Runs a {@code r}, ignoring all {@link RuntimeException} and {@link Error} until the
      * {@link #UI_TIMEOUT_MS} is reached.
@@ -338,7 +352,7 @@
         final AutofillValue value = node.getAutofillValue();
         assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
         assertWithMessage("wrong autofill type on %s", node).that(value.isText()).isTrue();
-        assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue())
+        assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue().toString())
                 .isEqualTo(expectedText);
         return node;
     }
@@ -533,6 +547,56 @@
         return requiredIds;
     }
 
+    /**
+     * Prevents the screen to rotate by itself
+     */
+    public static void disableAutoRotation() {
+        runShellCommand(ACCELLEROMETER_CHANGE, 0);
+        setOrientation(PORTRAIT);
+    }
+
+    /**
+     * Allows the screen to rotate by itself
+     */
+    public static void allowAutoRotation() {
+        runShellCommand(ACCELLEROMETER_CHANGE, 1);
+    }
+
+    /**
+     * Changes the screen orientation. This triggers a activity lifecycle (destroy -> create) for
+     * activities that do not handle this config change such as {@link OutOfProcessLoginActivity}.
+     *
+     * @param value {@link #PORTRAIT} or {@link #LANDSCAPE};
+     */
+    public static void setOrientation(int value) {
+        runShellCommand(ORIENTATION_CHANGE, value);
+    }
+
+    /**
+     * Wait until a process starts and returns the process ID of the process.
+     *
+     * @return The pid of the process
+     */
+    public static int getOutOfProcessPid(@NonNull String processName) throws InterruptedException {
+        long startTime = System.currentTimeMillis();
+
+        while (System.currentTimeMillis() - startTime < UI_TIMEOUT_MS) {
+            String[] allProcessDescs = runShellCommand("ps -eo PID,ARGS=CMD").split("\n");
+
+            for (String processDesc : allProcessDescs) {
+                String[] pidAndName = processDesc.trim().split(" ");
+
+                if (pidAndName[1].equals(processName)) {
+                    return Integer.parseInt(pidAndName[0]);
+                }
+            }
+
+            Thread.sleep(RETRY_MS);
+        }
+
+        throw new IllegalStateException("process not found");
+    }
+
     private Helper() {
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 2d5a33c..e294c33 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -57,10 +57,12 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.autofill.AutofillManager;
 
 import org.junit.After;
@@ -82,7 +84,7 @@
  *
  *  Save
  *  - test cases where only non-savable-ids are changed
- *  - test case where 'no thanks' is tapped (similar to testSaveSnackBarGoesAway())
+ *  - test case where 'no thanks' or 'x' is tapped (similar to former testSaveSnackBarGoesAway())
  *
  *  Other assertions
  *  - illegal state thrown on callback calls
@@ -90,11 +92,8 @@
  */
 public class LoginActivityTest extends AutoFillServiceTestCase {
 
-    // TODO(b/33197203 , b/36855717): remove when bug 36855717 is fixed
-    private static final boolean BUG_36855717_FIXED = false;
-
     // TODO(b/33197203 , b/35707731): remove when fixed
-    private static final boolean SUPPORTS_PARTIOTINED_AUTH = false;
+    private static final boolean SUPPORTS_PARTITIONED_AUTH = false;
 
     @Rule
     public final ActivityTestRule<LoginActivity> mActivityRule =
@@ -174,6 +173,27 @@
     }
 
     @Test
+    public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
+        mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+                return new AccessibilityNodeProvider() {
+                    @Override
+                    public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+                        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+                        if (virtualViewId == View.NO_ID) {
+                          info.addChild(v, 108);
+                        }
+                        return info;
+                    }
+                };
+            }
+        }));
+
+        testAutoFillOneDataset();
+    }
+
+    @Test
     public void testAutoFillOneDatasetAndMoveFocusAround() throws Exception {
         // Set service.
         enableService();
@@ -364,7 +384,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
@@ -777,7 +797,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
@@ -791,53 +811,6 @@
         assertNoDanglingSessions();
     }
 
-    private void setSnackBarLifetimeMs(long timeout) {
-        runShellCommand("cmd autofill set save_timeout %s", timeout);
-    }
-
-    @Test
-    public void testSaveSnackBarGoesAway() throws Exception {
-        enableService();
-        final int timeout = 1000;
-        setSnackBarLifetimeMs(timeout);
-
-        try {
-            // Set expectations.
-            sReplier.addResponse(new CannedFillResponse.Builder()
-                    .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                    .build());
-
-            // Trigger auto-fill.
-            mActivity.onUsername(View::requestFocus);
-
-            // Wait for onFill() before proceeding, otherwise the fields might be changed before
-            // the session started
-            sReplier.getNextFillRequest();
-
-            // Sanity check.
-            sUiBot.assertNoDatasets();
-
-            // Set credentials...
-            mActivity.onUsername((v) -> v.setText("malkovich"));
-            mActivity.onPassword((v) -> v.setText("malkovich"));
-
-            // ...and login
-            final String expectedMessage = getWelcomeMessage("malkovich");
-            final String actualMessage = mActivity.tapLogin();
-            assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
-
-            // Assert the snack bar is shown.
-            sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
-            SystemClock.sleep(timeout);
-            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
-
-            // Sanity check: once timed out, session should be finsihed.
-            assertNoDanglingSessions();
-        } finally {
-            setSnackBarLifetimeMs(5000);
-        }
-    }
-
     @Test
     public void testGenericSave() throws Exception {
         customizedSaveTest(SAVE_DATA_TYPE_GENERIC);
@@ -899,7 +872,7 @@
         assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
 
         // Assert the snack bar is shown and tap "Save".
-        final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(type, saveDescription);
+        final UiObject2 saveSnackBar = sUiBot.assertSaveShowing(saveDescription, type);
         sUiBot.saveForAutofill(saveSnackBar, true);
 
         // Assert save was called.
@@ -975,13 +948,9 @@
         sUiBot.assertNotShownByText("Tap to auth response");
 
         // ...and select it this time
-        if (BUG_36855717_FIXED) {
-            callback.assertUiShownEvent(username);
-        }
+        callback.assertUiShownEvent(username);
         sUiBot.selectDataset("Dataset");
-        if (BUG_36855717_FIXED) {
-            callback.assertUiHiddenEvent(username);
-        }
+        callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
         sUiBot.assertNotShownByText("Tap to auth response");
 
@@ -1036,7 +1005,7 @@
         callback.assertUiShownEvent(username);
         sUiBot.assertShownByText("Tap to auth response");
 
-        if (SUPPORTS_PARTIOTINED_AUTH) {
+        if (SUPPORTS_PARTITIONED_AUTH) {
             // Make sure UI is not show on 2nd field
             final View password = mActivity.getPassword();
             mActivity.onPassword(View::requestFocus);
@@ -1065,13 +1034,9 @@
         callback.assertUiHiddenEvent(username);
         sUiBot.assertNotShownByText("Tap to auth response");
 
-        if (BUG_36855717_FIXED) {
-            callback.assertUiShownEvent(username);
-        }
+        callback.assertUiShownEvent(username);
         sUiBot.selectDataset("Dataset");
-        if (BUG_36855717_FIXED) {
-            callback.assertUiHiddenEvent(username);
-        }
+        callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
         sUiBot.assertNotShownByText("Tap to auth response");
 
@@ -1132,53 +1097,18 @@
     }
 
     @Test
-    public void testDisableSelfWhenConnected() throws Exception {
+    public void testDisableSelf() throws Exception {
         enableService();
 
-        // Set no-op behavior.
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                .build());
-
-        // Trigger auto-fill.
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-
         // Can disable while connected.
-        mActivity.runOnUiThread(() ->
-                InstrumentedAutoFillService.peekInstance().disableSelf());
+        mActivity.runOnUiThread(() -> getContext().getSystemService(
+                AutofillManager.class).disableOwnedAutofillServices());
 
         // Ensure disabled.
         assertServiceDisabled();
     }
 
     @Test
-    public void testDisableSelfWhenDisconnected() throws Exception {
-        enableService();
-
-        // Set no-op behavior.
-        sReplier.addResponse(new CannedFillResponse.Builder()
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
-                .build());
-
-        // Trigger auto-fill.
-        mActivity.onUsername(View::requestFocus);
-        waitUntilConnected();
-        sReplier.getNextFillRequest();
-
-        // Wait until we timeout and disconnect.
-        waitUntilDisconnected();
-
-        // Cannot disable while disconnected.
-        mActivity.runOnUiThread(() ->
-                InstrumentedAutoFillService.peekInstance().disableSelf());
-
-        // Ensure enabled.
-        assertServiceEnabled();
-    }
-
-    @Test
     public void testCustomNegativeSaveButton() throws Exception {
         enableService();
 
@@ -1218,7 +1148,7 @@
         }, intentFilter);
 
         // Trigger the negative button.
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, false);
+        sUiBot.saveForAutofill(false, SAVE_DATA_TYPE_PASSWORD);
 
         // Wait for the custom action.
         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
@@ -1414,7 +1344,7 @@
                 mActivity.tapSave();
 
                 // Assert the snack bar is shown and tap "Save".
-                sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+                sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
 
                 final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
new file mode 100644
index 0000000..c2af4cc1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ManualAuthenticationActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.autofill.AutofillManager;
+
+/**
+ * An activity that authenticates on button press
+ */
+public class ManualAuthenticationActivity extends Activity {
+    private static CannedFillResponse sResponse;
+    private static CannedFillResponse.CannedDataset sDataset;
+
+    public static void setResponse(CannedFillResponse response) {
+        sResponse = response;
+        sDataset = null;
+    }
+
+    public static void setDataset(CannedFillResponse.CannedDataset dataset) {
+        sDataset = dataset;
+        sResponse = null;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.single_button_activity);
+
+        findViewById(R.id.button).setOnClickListener((v) -> {
+            // We should get the assist structure
+            AssistStructure structure = getIntent().getParcelableExtra(
+                    AutofillManager.EXTRA_ASSIST_STRUCTURE);
+            assertWithMessage("structure not called").that(structure).isNotNull();
+
+            Parcelable result = null;
+            if (sResponse != null) {
+                result = sResponse.asFillResponse(structure);
+            } else if (sDataset != null) {
+                result = sDataset.asDataset(structure);
+            } else {
+                throw new IllegalStateException("no dataset or response");
+            }
+
+            // Pass on the auth result
+            Intent intent = new Intent();
+            intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
+            setResult(RESULT_OK, intent);
+
+            // Done
+            finish();
+        });
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
index 3b75868..16a633d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/OptionalSaveActivityTest.java
@@ -130,7 +130,7 @@
         mActivity.save();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_ADDRESS, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -330,7 +330,7 @@
         mActivity.save();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_ADDRESS, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_ADDRESS);
 
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
new file mode 100644
index 0000000..6dd2d11
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/OutOfProcessLoginActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Simple activity showing R.layout.login_activity. Started outside of the test process.
+ */
+public class OutOfProcessLoginActivity extends Activity {
+    private static final String LOG_TAG = OutOfProcessLoginActivity.class.getSimpleName();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        Log.i(LOG_TAG, "onCreate(" + savedInstanceState + ")");
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.login_activity);
+
+        findViewById(R.id.login).setOnClickListener((v) -> {
+            finish();
+        });
+    }
+
+    @Override
+    protected void onStop() {
+        Log.i(LOG_TAG, "onStop()");
+        super.onStop();
+
+        try {
+            getStoppedMarker(this).createNewFile();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "cannot write stopped filed");
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(LOG_TAG, "onDestroy()");
+        super.onDestroy();
+    }
+
+    /**
+     * Get the file that signals that the activity has entered {@link Activity#onStop()}.
+     *
+     * @param context Context of the app
+     * @return The marker file that is written onStop()
+     */
+    @NonNull public static File getStoppedMarker(@NonNull Context context) {
+        return new File(context.getFilesDir(), "stopped");
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index 8c350aa..49ddf8f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -25,7 +25,11 @@
 import static android.autofillservice.cts.GridActivity.ID_L4C2;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertValue;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -210,7 +214,6 @@
                         .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
                         .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
                         .build())
-                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .setExtras(extras)
                 .build();
         sReplier.addResponse(response1);
@@ -272,6 +275,7 @@
                         .setField(ID_L4C1, "l4c1", createPresentation("l4c1"))
                         .setField(ID_L4C2, "l4c2", createPresentation("l4c2"))
                         .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
                 .setExtras(extras)
                 .build();
         sReplier.addResponse(response4);
@@ -285,7 +289,7 @@
         mActivity.setText(1, 1, "L1C1");
         mActivity.save();
 
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         assertWithMessage("wrong number of extras on save request bundle")
@@ -293,6 +297,308 @@
         assertThat(saveRequest.data.getString("numbers4")).isEqualTo("2342");
     }
 
+    @Test
+    public void testSaveOneSaveInfoOnFirstPartitionWithIdsOnSecond() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L2C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger save
+        mActivity.setText(2, 1, "L2C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+    }
+
+    @Test
+    public void testSaveOneSaveInfoOnSecondPartitionWithIdsOnFirst() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+    }
+
+    @Test
+    public void testSaveTwoSaveInfosDifferentTypes() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+                        ID_L2C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.setText(2, 1, "L2C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+        assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+    }
+
+    @Test
+    public void testSaveThreeSaveInfosDifferentTypes() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+                        ID_L2C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 3rd partition.
+        final CannedFillResponse response3 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+                        .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+                        | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
+                .build();
+        sReplier.addResponse(response3);
+        mActivity.focusCell(3, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.setText(2, 1, "L2C1");
+        mActivity.setText(3, 1, "L3C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_CREDIT_CARD,
+                SAVE_DATA_TYPE_USERNAME);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+        assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+        assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+    }
+
+    @Test
+    public void testSaveThreeSaveInfosDifferentTypesIncludingGeneric() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_GENERIC, ID_L2C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 3rd partition.
+        final CannedFillResponse response3 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+                        .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+                        .build())
+                .setRequiredSavableIds(
+                        SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_GENERIC | SAVE_DATA_TYPE_USERNAME,
+                        ID_L3C1)
+                .build();
+        sReplier.addResponse(response3);
+        mActivity.focusCell(3, 1);
+        sReplier.getNextFillRequest();
+
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.setText(2, 1, "L2C1");
+        mActivity.setText(3, 1, "L3C1");
+        mActivity.save();
+
+        // Make sure GENERIC type is not shown on snackbar
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD, SAVE_DATA_TYPE_USERNAME);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+        assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+        assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+    }
+
+    @Test
+    public void testSaveMoreThanThreeSaveInfosDifferentTypes() throws Exception {
+        // Set service.
+        enableService();
+
+        // Trigger 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .build();
+        sReplier.addResponse(response1);
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 2nd partition.
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD,
+                        ID_L2C1)
+                .build();
+        sReplier.addResponse(response2);
+        mActivity.focusCell(2, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 3rd partition.
+        final CannedFillResponse response3 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+                        .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+                        | SAVE_DATA_TYPE_USERNAME, ID_L3C1)
+                .build();
+        sReplier.addResponse(response3);
+        mActivity.focusCell(3, 1);
+        sReplier.getNextFillRequest();
+
+        // Trigger 4th partition.
+        final CannedFillResponse response4 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L4C1, "l4c1", createPresentation("l4c1"))
+                        .setField(ID_L4C2, "l4c2", createPresentation("l4c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD | SAVE_DATA_TYPE_CREDIT_CARD
+                        | SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_ADDRESS, ID_L4C1)
+                .build();
+        sReplier.addResponse(response4);
+        mActivity.focusCell(4, 1);
+        sReplier.getNextFillRequest();
+
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.setText(2, 1, "L2C1");
+        mActivity.setText(3, 1, "L3C1");
+        mActivity.setText(4, 1, "L4C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+        assertValue(saveRequest.structure, ID_L1C1, "L1C1");
+        assertValue(saveRequest.structure, ID_L2C1, "L2C1");
+        assertValue(saveRequest.structure, ID_L3C1, "L3C1");
+        assertValue(saveRequest.structure, ID_L4C1, "L4C1");
+    }
+
     // TODO(b/33197203, b/35707731): test force autofill after autofilled
-    // TODO(b/33197203, b/35707731): add test for saving (1, 2, 3, or more unique save types)
+    // TODO(b/33197203, b/35707731): test save with different subtitles and custom no button
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index f0e8f9f..349149f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -93,7 +93,7 @@
         mActivity.tapLogin();
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
 
         // Assert sanitization on save: everything should be available!
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
new file mode 100644
index 0000000..0454277
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SessionLifecycleTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.ID_LOGIN;
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.LANDSCAPE;
+import static android.autofillservice.cts.Helper.PORTRAIT;
+import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.eventually;
+import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.Helper.getOutOfProcessPid;
+import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.Helper.setOrientation;
+import static android.autofillservice.cts.OutOfProcessLoginActivity.getStoppedMarker;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.PendingIntent;
+import android.app.assist.AssistStructure;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.support.test.rule.ActivityTestRule;
+import android.view.autofill.AutofillValue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test the lifecycle of a autofill session
+ */
+public class SessionLifecycleTest extends AutoFillServiceTestCase {
+    private static final String USERNAME_FULL_ID = "android.autofillservice.cts:id/" + ID_USERNAME;
+    private static final String PASSWORD_FULL_ID = "android.autofillservice.cts:id/" + ID_PASSWORD;
+    private static final String LOGIN_FULL_ID = "android.autofillservice.cts:id/" + ID_LOGIN;
+    private static final String BUTTON_FULL_ID = "android.autofillservice.cts:id/button";
+
+    /**
+     * Use an activity as background so that orientation change always works (Home screen does not
+     * allow rotation
+     */
+    @Rule
+    public final ActivityTestRule<EmptyActivity> mActivityRule =
+            new ActivityTestRule<>(EmptyActivity.class);
+
+    @Before
+    public void removeAllSessions() {
+        destroyAllSessions();
+    }
+
+    /**
+     * Prevents the screen to rotate by itself
+     */
+    @Before
+    public void disableAutoRotation() {
+        Helper.disableAutoRotation();
+    }
+
+    /**
+     * Allows the screen to rotate by itself
+     */
+    @After
+    public void allowAutoRotation() {
+        Helper.allowAutoRotation();
+    }
+
+    @Test
+    public void testSessionRetainedWhileAutofilledAppIsLifecycled() throws Exception {
+        // Set service.
+        enableService();
+
+        try {
+            // Start activity that is autofilled in a separate process so it can be killed
+            Intent outOfProcessAcvitityStartIntent = new Intent(getContext(),
+                    OutOfProcessLoginActivity.class);
+            getContext().startActivity(outOfProcessAcvitityStartIntent);
+
+            // Set expectations.
+            final Bundle extras = new Bundle();
+            extras.putString("numbers", "4815162342");
+
+            // Create the authentication intent (launching a full screen activity)
+            IntentSender authentication = PendingIntent.getActivity(getContext(), 0,
+                    new Intent(getContext(), ManualAuthenticationActivity.class),
+                    0).getIntentSender();
+
+            // Prepare the authenticated response
+            ManualAuthenticationActivity.setResponse(new CannedFillResponse.Builder()
+                    .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                            .setField(ID_USERNAME, AutofillValue.forText("autofilled username"))
+                            .setPresentation(createPresentation("dataset")).build())
+                    .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                    .setExtras(extras).build());
+
+            CannedFillResponse response = new CannedFillResponse.Builder()
+                    .setAuthentication(authentication)
+                    .setPresentation(createPresentation("authenticate"))
+                    .build();
+            sReplier.addResponse(response);
+
+            // Trigger autofill on username
+            sUiBot.selectById(USERNAME_FULL_ID);
+
+            // Wait for fill request to be processed
+            sReplier.getNextFillRequest();
+
+            // Wait until authentication is shown
+            sUiBot.assertShownByText("authenticate");
+
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            setOrientation(LANDSCAPE);
+
+            // Delete stopped marker
+            getStoppedMarker(getContext()).delete();
+
+            // Authenticate
+            sUiBot.selectByText("authenticate");
+
+            // Waiting for activity to stop (stop marker appears)
+            eventually(() -> assertThat(getStoppedMarker(getContext()).exists()).isTrue());
+
+            // onStop might not be finished, hence wait more
+            Thread.sleep(1000);
+
+            // Kill activity that is in the background
+            runShellCommand("kill -9 %d",
+                    getOutOfProcessPid("android.autofillservice.cts.outside"));
+
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            setOrientation(PORTRAIT);
+
+            // Approve authentication
+            sUiBot.selectById(BUTTON_FULL_ID);
+
+            // Wait for dataset to be shown
+            sUiBot.assertShownByText("dataset");
+
+            // Change orientation which triggers a destroy -> create in the app as the activity
+            // cannot deal with such situations
+            setOrientation(LANDSCAPE);
+
+            // Select dataset
+            sUiBot.selectDataset("dataset");
+
+            // Check the results.
+            eventually(() -> assertThat(sUiBot.getTextById(USERNAME_FULL_ID)).isEqualTo(
+                    "autofilled username"));
+
+            // Set password
+            sUiBot.setTextById(PASSWORD_FULL_ID, "new password");
+
+            // Login
+            sUiBot.selectById(LOGIN_FULL_ID);
+
+            // Wait for save UI to be shown
+            sUiBot.assertShownById("android:id/autofill_save_yes");
+
+            // Change orientation to make sure save UI can handle this
+            setOrientation(PORTRAIT);
+
+            // Tap "Save".
+            sUiBot.selectById("android:id/autofill_save_yes");
+
+            // Get save request
+            InstrumentedAutoFillService.SaveRequest saveRequest = sReplier.getNextSaveRequest();
+            assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
+
+            // Make sure data is correctly saved
+            final AssistStructure.ViewNode username = findNodeByResourceId(saveRequest.structure,
+                    ID_USERNAME);
+            assertTextAndValue(username, "autofilled username");
+            final AssistStructure.ViewNode password = findNodeByResourceId(saveRequest.structure,
+                    ID_PASSWORD);
+            assertTextAndValue(password, "new password");
+
+            // Make sure extras were passed back on onSave()
+            assertThat(saveRequest.data).isNotNull();
+            final String extraValue = saveRequest.data.getString("numbers");
+            assertWithMessage("extras not passed on save").that(extraValue).isEqualTo("4815162342");
+
+            eventually(() -> assertNoDanglingSessions());
+        } finally {
+            disableService();
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index 872ccc1..b96e5e3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -89,7 +89,7 @@
         activity.setTime(10, 40);
         activity.tapOk();
 
-        sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
+        sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
         final SaveRequest saveRequest = sReplier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index be9b44b..1f98171 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -39,6 +39,7 @@
 import android.util.Log;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -59,7 +60,7 @@
     private static final String RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD =
             "autofill_save_type_credit_card";
     private static final String RESOURCE_STRING_SAVE_TYPE_USERNAME = "autofill_save_type_username";
-    private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL =
+    private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS =
             "autofill_save_type_email_address";
     private static final String RESOURCE_STRING_AUTOFILL = "autofill";
     private static final String RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE =
@@ -161,10 +162,43 @@
     }
 
     /**
+     * Selects a view by id.
+     */
+    void selectById(String id) {
+        Log.v(TAG, "selectById(): " + id);
+
+        final UiObject2 view = waitForObject(By.res(id));
+        view.click();
+    }
+
+    /**
+     * Asserts the id is shown on the screen.
+     */
+    void assertShownById(String id) {
+        assertThat(waitForObject(By.res(id))).isNotNull();
+    }
+
+    /**
+     * Gets the text set on a view.
+     */
+    String getTextById(String id) {
+        UiObject2 view = waitForObject(By.res(id));
+        return view.getText();
+    }
+
+    /**
+     * Sets a new text on a view.
+     */
+    void setTextById(String id, String newText) {
+        UiObject2 view = waitForObject(By.res(id));
+        view.setText(newText);
+    }
+
+    /**
      * Asserts the save snackbar is showing and returns it.
      */
     UiObject2 assertSaveShowing(int type) {
-        return assertSaveShowing(type, null);
+        return assertSaveShowing(null, type);
     }
 
     /**
@@ -181,46 +215,63 @@
         throw new AssertionError("snack bar is showing");
     }
 
-    UiObject2 assertSaveShowing(int type, String description) {
+    private String getSaveTypeString(int type) {
+        final String typeResourceName;
+        switch (type) {
+            case SAVE_DATA_TYPE_PASSWORD:
+                typeResourceName = RESOURCE_STRING_SAVE_TYPE_PASSWORD;
+                break;
+            case SAVE_DATA_TYPE_ADDRESS:
+                typeResourceName = RESOURCE_STRING_SAVE_TYPE_ADDRESS;
+                break;
+            case SAVE_DATA_TYPE_CREDIT_CARD:
+                typeResourceName = RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD;
+                break;
+            case SAVE_DATA_TYPE_USERNAME:
+                typeResourceName = RESOURCE_STRING_SAVE_TYPE_USERNAME;
+                break;
+            case SAVE_DATA_TYPE_EMAIL_ADDRESS:
+                typeResourceName = RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS;
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported type: " + type);
+        }
+        return getString(typeResourceName);
+    }
+
+    UiObject2 assertSaveShowing(String description, int... types) {
         final UiObject2 snackbar = waitForObject(By.res("android", RESOURCE_ID_SAVE_SNACKBAR),
                 SAVE_TIMEOUT_MS);
 
         final UiObject2 titleView = snackbar.findObject(By.res("android", RESOURCE_ID_SAVE_TITLE));
         assertWithMessage("save title (%s)", RESOURCE_ID_SAVE_TITLE).that(titleView).isNotNull();
 
-        final String serviceLabel = InstrumentedAutoFillService.class.getSimpleName();
-        final String expectedTitle;
-        if (type == SAVE_DATA_TYPE_GENERIC) {
-            expectedTitle = getString(RESOURCE_STRING_SAVE_TITLE, serviceLabel);
-        } else {
-            final String typeResourceName;
-            switch (type) {
-                case SAVE_DATA_TYPE_PASSWORD:
-                    typeResourceName = RESOURCE_STRING_SAVE_TYPE_PASSWORD;
-                    break;
-                case SAVE_DATA_TYPE_ADDRESS:
-                    typeResourceName = RESOURCE_STRING_SAVE_TYPE_ADDRESS;
-                    break;
-                case SAVE_DATA_TYPE_CREDIT_CARD:
-                    typeResourceName = RESOURCE_STRING_SAVE_TYPE_CREDIT_CARD;
-                    break;
-                case SAVE_DATA_TYPE_USERNAME:
-                    typeResourceName = RESOURCE_STRING_SAVE_TYPE_USERNAME;
-                    break;
-                case SAVE_DATA_TYPE_EMAIL_ADDRESS:
-                    typeResourceName = RESOURCE_STRING_SAVE_TYPE_EMAIL;
-                    break;
-                default:
-                    throw new IllegalArgumentException("Unsupported type: " + type);
-            }
-            final String typeString = getString(typeResourceName);
-            expectedTitle = getString(RESOURCE_STRING_SAVE_TITLE_WITH_TYPE, typeString,
-                    serviceLabel);
-        }
-
         final String actualTitle = titleView.getText();
         Log.d(TAG, "save title: " + actualTitle);
-        assertThat(actualTitle).isEqualTo(expectedTitle);
+
+        final String serviceLabel = InstrumentedAutoFillService.class.getSimpleName();
+        switch (types.length) {
+            case 1:
+                final String expectedTitle = (types[0] == SAVE_DATA_TYPE_GENERIC)
+                        ? getString(RESOURCE_STRING_SAVE_TITLE, serviceLabel)
+                        : getString(RESOURCE_STRING_SAVE_TITLE_WITH_TYPE,
+                                getSaveTypeString(types[0]), serviceLabel);
+                assertThat(actualTitle).isEqualTo(expectedTitle);
+                break;
+            case 2:
+                // We cannot predict the order...
+                assertThat(actualTitle).contains(getSaveTypeString(types[0]));
+                assertThat(actualTitle).contains(getSaveTypeString(types[1]));
+                break;
+            case 3:
+                // We cannot predict the order...
+                assertThat(actualTitle).contains(getSaveTypeString(types[0]));
+                assertThat(actualTitle).contains(getSaveTypeString(types[1]));
+                assertThat(actualTitle).contains(getSaveTypeString(types[2]));
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid types: " + Arrays.toString(types));
+        }
 
         if (description != null) {
             final UiObject2 saveSubTitle = snackbar.findObject(By.text(description));
@@ -233,11 +284,11 @@
     /**
      * Taps an option in the save snackbar.
      *
-     * @param type expected type of save info.
      * @param yesDoIt {@code true} for 'YES', {@code false} for 'NO THANKS'.
+     * @param types expected types of save info.
      */
-    void saveForAutofill(int type, boolean yesDoIt) {
-        final UiObject2 saveSnackBar = assertSaveShowing(type, null);
+    void saveForAutofill(boolean yesDoIt, int... types) {
+        final UiObject2 saveSnackBar = assertSaveShowing(null, types);
         saveForAutofill(saveSnackBar, yesDoIt);
     }
 
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 6bb3421..e4ca5af 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -240,6 +240,16 @@
    bug: 23827982
 },
 {
+   description: "VP9 encoder is not a standard requirement of android as of O.",
+   names: [
+     "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0320x0180",
+     "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0640x0360",
+     "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1280x0720",
+     "android.media.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1920x1080"
+   ],
+   bug: 33090965
+},
+{
   description: "protected broadcast not working",
   names: [
    "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
diff --git a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
index 0ce4aa3..1b28290 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
@@ -35,23 +35,16 @@
     public ActivityTestRule<NonConfigOnStopActivity> mActivityRule =
             new ActivityTestRule<>(NonConfigOnStopActivity.class);
 
-    @After
-    public void resetActivity() {
-        FragmentTestUtil.resetOrientation();
-    }
-
     /**
      * When a fragment is added during onStop(), it shouldn't show up in non-config
      * state when restored.
      */
     @Test
     public void nonConfigStop() throws Throwable {
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // nothing to do -- we can't change the orientation
-        }
+        NonConfigOnStopActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
 
         // A fragment was added in onStop(), but we shouldn't see it here...
-        Activity activity = OrientationChangeActivity.sActivity;
         assertTrue(activity.getFragmentManager().getFragments().isEmpty());
     }
 
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index 4b2ae16..c175164 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -24,8 +24,6 @@
 import android.app.FragmentManager;
 import android.app.FragmentManagerNonConfig;
 import android.app.Instrumentation;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
@@ -35,8 +33,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -218,57 +214,28 @@
     }
 
     /**
-     * Switches the orientation of the OrientationChangeActivity.
+     * Restarts the RecreatedActivity and waits for the new activity to be resumed.
      *
-     * @return {@code true} if the orientation changed or {@code false} if the screen is square
-     * or some other error happens
+     * @return The newly-restarted Activity
      */
-    public static boolean switchOrientation() throws InterruptedException {
-        OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
-
-        int currentOrientation = activity.getResources().getConfiguration().orientation;
-
-        int nextOrientation;
-        int expectedOrientation;
-        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-            expectedOrientation = Configuration.ORIENTATION_PORTRAIT;
-        } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-            expectedOrientation = Configuration.ORIENTATION_LANDSCAPE;
-        } else {
-            return false; // Don't know what to do with square or unknown orientations
-        }
-
+    public static <T extends RecreatedActivity> T recreateActivity(
+            ActivityTestRule<? extends Activity> rule, T activity) throws InterruptedException {
         // Now switch the orientation
-        LoaderActivity.sResumed = new CountDownLatch(1);
-        LoaderActivity.sDestroyed = new CountDownLatch(1);
+        RecreatedActivity.sResumed = new CountDownLatch(1);
+        RecreatedActivity.sDestroyed = new CountDownLatch(1);
 
-        activity.setRequestedOrientation(nextOrientation);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-        assertTrue(LoaderActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        runOnUiThreadRethrow(rule, () -> {
+            activity.recreate();
+        });
+        assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS));
+        assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        T newActivity = (T) RecreatedActivity.sActivity;
 
-        int switchedOrientation =
-                LoaderActivity.sActivity.getResources().getConfiguration().orientation;
-        assertEquals(expectedOrientation, switchedOrientation);
-        return true;
+        waitForExecution(rule);
+
+        RecreatedActivity.clearState();
+        return newActivity;
     }
+}
 
-    /**
-     * After calling {@link #switchOrientation()}, this must be called in an After.
-     */
-    public static void resetOrientation() {
-        final OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
-        final int unspecifiedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-        if (activity != null && activity.getRequestedOrientation() != unspecifiedOrientation) {
-            OrientationChangeActivity.sResumed = new CountDownLatch(1);
-            activity.setRequestedOrientation(unspecifiedOrientation);
-            // Wait for the orientation change to settle, if there was a change
-            try {
-                OrientationChangeActivity.sResumed.await(1, TimeUnit.SECONDS);
-            } catch (InterruptedException e) {
-                // I guess there wasn't a change in orientation after all
-            }
-        }
-        OrientationChangeActivity.clearState();
-    }}
+
diff --git a/tests/fragment/src/android/fragment/cts/LoaderActivity.java b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
index 5654b42..9c69f75 100644
--- a/tests/fragment/src/android/fragment/cts/LoaderActivity.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
@@ -15,7 +15,6 @@
  */
 package android.fragment.cts;
 
-import android.app.Activity;
 import android.app.LoaderManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
@@ -24,13 +23,11 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import java.util.concurrent.CountDownLatch;
-
 /**
  * This Activity sets the text when loading completes. It also tracks the Activity in
  * a static variable, so it must be cleared in test tear down.
  */
-public class LoaderActivity extends OrientationChangeActivity {
+public class LoaderActivity extends RecreatedActivity {
     public TextView textView;
     public TextView textViewB;
 
diff --git a/tests/fragment/src/android/fragment/cts/LoaderTest.java b/tests/fragment/src/android/fragment/cts/LoaderTest.java
index 880d13e..2eb897e 100755
--- a/tests/fragment/src/android/fragment/cts/LoaderTest.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderTest.java
@@ -22,14 +22,10 @@
 
 import android.app.Fragment;
 import android.app.FragmentManager;
-import android.app.Instrumentation;
 import android.app.LoaderManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
-import android.content.Intent;
 import android.content.Loader;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
@@ -37,7 +33,6 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,25 +50,17 @@
     public ActivityTestRule<LoaderActivity> mActivityRule =
             new ActivityTestRule<>(LoaderActivity.class);
 
-    @After
-    public void resetActivity() {
-        FragmentTestUtil.resetOrientation();
-    }
-
     /**
      * Test to ensure that there is no Activity leak due to Loader
      */
     @Test
     public void testLeak() throws Throwable {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        LoaderActivity.sResumed = new CountDownLatch(1);
-        instrumentation.startActivitySync(intent);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-
+        // Restart the activity because mActivityRule keeps a strong reference to the
+        // old activity.
+        LoaderActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
         LoaderFragment fragment = new LoaderFragment();
-        FragmentManager fm = LoaderActivity.sActivity.getFragmentManager();
+        FragmentManager fm = activity.getFragmentManager();
 
         fm.beginTransaction()
                 .add(fragment, "1")
@@ -87,12 +74,11 @@
                 .commit();
 
         FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+        fm = null; // clear it so that it can be released
 
         WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
 
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
 
         // Wait for everything to settle. We have to make sure that the old Activity
         // is ready to be collected.
@@ -113,12 +99,9 @@
 
         assertEquals("Loaded!", activity.textView.getText().toString());
 
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
 
         // After orientation change, the text should still be loaded properly
-        activity = (LoaderActivity) LoaderActivity.sActivity;
         assertEquals("Loaded!", activity.textView.getText().toString());
     }
 
diff --git a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
index e61bd50..a93b616 100644
--- a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
+++ b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
@@ -17,7 +17,7 @@
 
 import android.app.Fragment;
 
-public class NonConfigOnStopActivity extends OrientationChangeActivity {
+public class NonConfigOnStopActivity extends RecreatedActivity {
     @Override
     protected void onStop() {
         super.onStop();
diff --git a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
similarity index 92%
rename from tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
rename to tests/fragment/src/android/fragment/cts/RecreatedActivity.java
index d8aeb31..82b32a9 100644
--- a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
+++ b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
@@ -20,9 +20,9 @@
 
 import java.util.concurrent.CountDownLatch;
 
-public class OrientationChangeActivity extends Activity {
+public class RecreatedActivity extends Activity {
     // These must be cleared after each test using clearState()
-    public static OrientationChangeActivity sActivity;
+    public static RecreatedActivity sActivity;
     public static CountDownLatch sResumed;
     public static CountDownLatch sDestroyed;
 
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index 2779ea7..2d13ed2 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -58,6 +58,8 @@
         // list are probably not meant to be implemented in an application.
         HIDDEN_INTERFACE_WHITELIST.add("public abstract boolean android.companion.DeviceFilter.matches(D)");
         HIDDEN_INTERFACE_WHITELIST.add("public static <D> boolean android.companion.DeviceFilter.matches(android.companion.DeviceFilter<D>,D)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract java.lang.String android.companion.DeviceFilter.getDeviceDisplayName(D)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract int android.companion.DeviceFilter.getMediumType()");
         HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.nfc.tech.TagTechnology.reconnect() throws java.io.IOException");
         HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.os.IBinder.shellCommand(java.io.FileDescriptor,java.io.FileDescriptor,java.io.FileDescriptor,java.lang.String[],android.os.ShellCallback,android.os.ResultReceiver) throws android.os.RemoteException");
         HIDDEN_INTERFACE_WHITELIST.add("public abstract int android.text.ParcelableSpan.getSpanTypeIdInternal()");
diff --git a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
index c52aba6..a0d7f16 100644
--- a/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
+++ b/tests/tests/contactsproviderwipe/src/android/provider/cts/contactsproviderwipe/ContactsContract_Wipe.java
@@ -179,8 +179,11 @@
 
         wipeContactsProvider();
 
+        // Accessing CP2 to make sure the process starts.
+        getDatabaseCreationTimestamp();
+
         assertTrue("Didn't receive content change notification",
-                latch.await(60, TimeUnit.SECONDS));
+                latch.await(120, TimeUnit.SECONDS));
 
         assertEquals(ProviderStatus.CONTENT_URI, notifiedUri.get());
     }
@@ -201,7 +204,10 @@
 
         wipeContactsProvider();
 
+        // Accessing CP2 to make sure the process starts.
+        getDatabaseCreationTimestamp();
+
         assertTrue("Didn't receive contacts wipe broadcast",
-                latch.await(60, TimeUnit.SECONDS));
+                latch.await(120, TimeUnit.SECONDS));
     }
 }
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index b69a8c5..096eef9 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -276,6 +276,9 @@
     }
 
     public void testAlarmClockShowTimers() {
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
+            return;
+        }
         Intent intent = new Intent(AlarmClock.ACTION_SHOW_TIMERS);
         assertCanBeHandled(intent);
     }
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
index 45418cf..3015b07 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
@@ -49,6 +49,11 @@
         // Check that all four orientations report the same configuration value.
         for (int i = 0; i < ORIENTATIONS.length; i++) {
             Activity activity = startOrientationActivity(ORIENTATIONS[i]);
+            if (activity.isInMultiWindowMode()) {
+                // activity.setRequestedOrientation has no effect in multiwindow mode.
+                tearDown();
+                return;
+            }
             Configuration mConfig = activity.getResources().getConfiguration();
             int actualSize = mConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
             int actualLong = mConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
diff --git a/tests/tests/graphics/assets/almost-red-adobe.png b/tests/tests/graphics/assets/almost-red-adobe.png
new file mode 100644
index 0000000..531b5a4
--- /dev/null
+++ b/tests/tests/graphics/assets/almost-red-adobe.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png b/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png
new file mode 100644
index 0000000..1d6177f
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/alpha_mask.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index a137784..5454e2f 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index d5288d1..ea6441a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index 237c86e..06eccb8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index 0a4b40f..64a6476 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 194f36c..5012e1c 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -26,6 +26,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,11 +41,14 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BitmapColorSpaceTest {
+    private static final String LOG_TAG = "BitmapColorSpaceTest";
+
     private Resources mResources;
 
     @Before
@@ -52,12 +56,92 @@
         mResources = InstrumentationRegistry.getTargetContext().getResources();
     }
 
+    @SuppressWarnings("deprecation")
+    @Test
+    public void createWithColorSpace() {
+        Bitmap b;
+        ColorSpace cs;
+
+        // We don't test HARDWARE configs because they are not compatible with mutable bitmaps
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true, null);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
+                ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true, null);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGBA_F16, true,
+                ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true, null);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565, true,
+                ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true, null);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ALPHA_8, true,
+                ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true, null);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_4444, true,
+                ColorSpace.get(ColorSpace.Named.ADOBE_RGB));
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createWithNonRgbColorSpace() {
+        Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888, true,
+                ColorSpace.get(ColorSpace.Named.CIE_LAB));
+    }
+
     @Test
     public void sRGB() {
         Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
         ColorSpace cs = b.getColorSpace();
         assertNotNull(cs);
         assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+        cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
     }
 
     @Test
@@ -67,6 +151,16 @@
             ColorSpace cs = b.getColorSpace();
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
         } catch (IOException e) {
             fail();
         }
@@ -79,6 +173,16 @@
             ColorSpace cs = b.getColorSpace();
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
         } catch (IOException e) {
             fail();
         }
@@ -139,7 +243,7 @@
 
     @Test
     public void getPixel() {
-        verifyGetPixel("green-p3.png", 0x75fb4cff, 0xff03ff00);
+        verifyGetPixel("green-p3.png", 0x75fb4cff, 0xff00ff00);
         verifyGetPixel("translucent-green-p3.png", 0x3a7d267f, 0x7f00ff00); // 50% translucent
     }
 
@@ -151,22 +255,34 @@
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
 
-            ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
-            b.copyPixelsToBuffer(dst);
-            dst.rewind();
-            // Stored as RGBA
-            assertEquals(rawColor, dst.asIntBuffer().get());
+            verifyGetPixel(b, rawColor, srgbColor);
 
-            int srgb = b.getPixel(31, 31);
-            assertEquals(srgbColor, srgb);
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            verifyGetPixel(b, rawColor, srgbColor);
+
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            verifyGetPixel(b, rawColor, srgbColor);
         } catch (IOException e) {
             fail();
         }
     }
 
+    private static void verifyGetPixel(@NonNull Bitmap b,
+            @ColorInt int rawColor, @ColorInt int srgbColor) {
+        ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+        b.copyPixelsToBuffer(dst);
+        dst.rewind();
+
+        // Stored as RGBA
+        assertEquals(rawColor, dst.asIntBuffer().get());
+
+        int srgb = b.getPixel(15, 15);
+        almostEqual(srgbColor, srgb, 3, 15 * b.getWidth() + 15);
+    }
+
     @Test
     public void getPixels() {
-        verifyGetPixels("green-p3.png", 0xff03ff00);
+        verifyGetPixels("green-p3.png", 0xff00ff00);
         verifyGetPixels("translucent-green-p3.png", 0x7f00ff00); // 50% translucent
     }
 
@@ -177,16 +293,28 @@
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
 
-            int[] pixels = new int[b.getWidth() * b.getHeight()];
-            b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
-            for (int pixel : pixels) {
-                assertEquals(expected, pixel);
-            }
+            verifyGetPixels(b, expected);
+
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            verifyGetPixels(b, expected);
+
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            verifyGetPixels(b, expected);
         } catch (IOException e) {
             fail();
         }
     }
 
+    private static void verifyGetPixels(@NonNull Bitmap b, @ColorInt int expected) {
+        int[] pixels = new int[b.getWidth() * b.getHeight()];
+        b.getPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+
+        for (int i = 0; i < pixels.length; i++) {
+            int pixel = pixels[i];
+            almostEqual(expected, pixel, 3, i);
+        }
+    }
+
     @Test
     public void setPixel() {
         verifySetPixel("green-p3.png", 0xffff0000, 0xea3323ff);
@@ -204,18 +332,29 @@
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
 
-            b.setPixel(0, 0, newColor);
+            verifySetPixel(b, newColor, expectedColor);
 
-            ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
-            b.copyPixelsToBuffer(dst);
-            dst.rewind();
-            // Stored as RGBA
-            assertEquals(expectedColor, dst.asIntBuffer().get());
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            verifySetPixel(b, newColor, expectedColor);
+
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            verifySetPixel(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
         }
     }
 
+    private static void verifySetPixel(@NonNull Bitmap b,
+            @ColorInt int newColor, @ColorInt int expectedColor) {
+        b.setPixel(0, 0, newColor);
+
+        ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+        b.copyPixelsToBuffer(dst);
+        dst.rewind();
+        // Stored as RGBA
+        assertEquals(expectedColor, dst.asIntBuffer().get());
+    }
+
     @Test
     public void setPixels() {
         verifySetPixels("green-p3.png", 0xffff0000, 0xea3323ff);
@@ -233,25 +372,36 @@
             assertNotNull(cs);
             assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
 
-            int[] pixels = new int[b.getWidth() * b.getHeight()];
-            Arrays.fill(pixels, newColor);
-            b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+            verifySetPixels(b, newColor, expectedColor);
 
-            ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
-            b.copyPixelsToBuffer(dst);
-            dst.rewind();
+            b = Bitmap.createBitmap(b, 0, 0, b.getWidth() / 2, b.getHeight() / 2);
+            verifySetPixels(b, newColor, expectedColor);
 
-            IntBuffer buffer = dst.asIntBuffer();
-            //noinspection ForLoopReplaceableByForEach
-            for (int i = 0; i < pixels.length; i++) {
-                // Stored as RGBA
-                assertEquals(expectedColor, buffer.get());
-            }
+            b = Bitmap.createScaledBitmap(b, b.getWidth() / 2, b.getHeight() / 2, true);
+            verifySetPixels(b, newColor, expectedColor);
         } catch (IOException e) {
             fail();
         }
     }
 
+    private static void verifySetPixels(@NonNull Bitmap b,
+            @ColorInt int newColor, @ColorInt int expectedColor) {
+        int[] pixels = new int[b.getWidth() * b.getHeight()];
+        Arrays.fill(pixels, newColor);
+        b.setPixels(pixels, 0, b.getWidth(), 0, 0, b.getWidth(), b.getHeight());
+
+        ByteBuffer dst = ByteBuffer.allocate(b.getByteCount());
+        b.copyPixelsToBuffer(dst);
+        dst.rewind();
+
+        IntBuffer buffer = dst.asIntBuffer();
+        //noinspection ForLoopReplaceableByForEach
+        for (int i = 0; i < pixels.length; i++) {
+            // Stored as RGBA
+            assertEquals(expectedColor, buffer.get());
+        }
+    }
+
     @Test
     public void writeColorSpace() {
         verifyColorSpaceMarshalling("green-srgb.png", ColorSpace.get(ColorSpace.Named.SRGB));
@@ -400,4 +550,65 @@
             fail();
         }
     }
+
+    @Test
+    public void copy() {
+        Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
+        Bitmap c = b.copy(Bitmap.Config.ARGB_8888, false);
+        ColorSpace cs = c.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        c = b.copy(Bitmap.Config.ARGB_8888, true);
+        cs = c.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+
+        try (InputStream in = mResources.getAssets().open("green-p3.png")) {
+            b = BitmapFactory.decodeStream(in);
+            c = b.copy(Bitmap.Config.ARGB_8888, false);
+            cs = c.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+            c = b.copy(Bitmap.Config.ARGB_8888, true);
+            cs = c.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+        } catch (IOException e) {
+            fail();
+        }
+
+        try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) {
+            b = BitmapFactory.decodeStream(in);
+            c = b.copy(Bitmap.Config.RGBA_F16, false);
+            cs = c.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+            c = b.copy(Bitmap.Config.RGBA_F16, true);
+            cs = c.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private static void almostEqual(@ColorInt int expected,
+            @ColorInt int pixel, int threshold, int index) {
+        int diffA = Math.abs(expected >>> 24 - pixel >>> 24);
+        int diffR = Math.abs((expected >> 16) & 0xff - (pixel >> 16) & 0xff);
+        int diffG = Math.abs((expected >>  8) & 0xff - (pixel >>  8) & 0xff);
+        int diffB = Math.abs((expected      ) & 0xff - (pixel      ) & 0xff);
+
+        boolean pass = diffA + diffR + diffG + diffB < threshold;
+        if (!pass) {
+            Log.d(LOG_TAG, "Expected 0x" + Integer.toHexString(expected) +
+                    " but was 0x" + Integer.toHexString(pixel) + " with index " + index);
+        }
+
+        assertTrue(pass);
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
index 114a385..02c9425 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRGBAF16Test.java
@@ -15,38 +15,41 @@
  */
 package android.graphics.cts;
 
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BitmapRGBAF16Test {
     private Bitmap mOpaqueBitmap;
     private Bitmap mTransparentBitmap;
+    private Resources mResources;
 
     @Before
     public void setup() {
-        Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+        mResources = InstrumentationRegistry.getTargetContext().getResources();
 
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inMutable = true;
 
         // The bitmaps are in raw-nodpi/ to guarantee aapt and framework leave them untouched
-        mOpaqueBitmap = BitmapFactory.decodeResource(resources, R.raw.p3_opaque, options);
-        mTransparentBitmap = BitmapFactory.decodeResource(resources, R.raw.p3_transparent, options);
+        mOpaqueBitmap = BitmapFactory.decodeResource(mResources, R.raw.p3_opaque, options);
+        mTransparentBitmap = BitmapFactory.decodeResource(mResources, R.raw.p3_transparent, options);
     }
 
     @Test
@@ -133,4 +136,16 @@
         assertTrue(before != after);
         assertEquals(0x7f102030, after);
     }
+
+    @Test
+    public void testCopyFromA8() {
+        Bitmap res = BitmapFactory.decodeResource(mResources, R.drawable.alpha_mask);
+        Bitmap mask = Bitmap.createBitmap(res.getWidth(), res.getHeight(),
+                Bitmap.Config.ALPHA_8);
+        Canvas c = new Canvas(mask);
+        c.drawBitmap(res, 0, 0, null);
+
+        Bitmap b = mask.copy(Config.RGBA_F16, false);
+        assertNotNull(b);
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 64dba77..69dbcd8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -51,12 +51,15 @@
 import android.text.SpannedString;
 import android.util.DisplayMetrics;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Vector;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class CanvasTest {
@@ -163,6 +166,35 @@
     }
 
     @Test
+    public void testSetBitmapCleanClip() {
+        mCanvas.setBitmap(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+        Rect r = new Rect(2, 2, 8, 8);
+        mCanvas.save();
+        mCanvas.clipRect(r);
+        assertEquals(r, mCanvas.getClipBounds());
+
+        // "reset" the canvas, and then check that the clip is wide open
+        // and not the previous value
+
+        mCanvas.setBitmap(Bitmap.createBitmap(20, 20, Config.ARGB_8888));
+        r = new Rect(0, 0, 20, 20);
+        assertEquals(r, mCanvas.getClipBounds());
+    }
+
+    @Test
+    public void testSetBitmapSaveCount() {
+        Canvas c = new Canvas(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+        int initialSaveCount = c.getSaveCount();
+
+        c.save();
+        assertEquals(c.getSaveCount(), initialSaveCount + 1);
+
+        // setBitmap should restore the saveCount to its original/base value
+        c.setBitmap(Bitmap.createBitmap(10, 10, Config.ARGB_8888));
+        assertEquals(c.getSaveCount(), initialSaveCount);
+    }
+
+    @Test
     public void testIsOpaque() {
         assertFalse(mCanvas.isOpaque());
     }
@@ -2114,4 +2146,24 @@
             assertEquals(matrices.elementAt(i), mCanvas.getMatrix());
         }
     }
+
+    @Test
+    public void testDrawBitmapColorBehavior() {
+        try {
+            // Create a wide gamut bitmap where the pixel value is slightly less than max red.
+            Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+            InputStream in = resources.getAssets().open("almost-red-adobe.png");
+            Bitmap bitmap = BitmapFactory.decodeStream(in);
+
+            // Draw the bitmap to an sRGB canvas.
+            Bitmap canvasBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(canvasBitmap);
+            canvas.drawBitmap(bitmap, 0, 0, null);
+
+            // Verify that the pixel is now max red.
+            Assert.assertEquals(0xFFFF0000, canvasBitmap.getPixel(0, 0));
+        } catch (IOException e) {
+            Assert.fail();
+        }
+    }
 }
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 3b890f2..16a202e 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -71,11 +71,14 @@
 }
 
 static bool already_loaded(const std::string& library, const std::string& err) {
-  if (err.find("dlopen failed: library \"" + library + "\"") != 0 ||
-      err.find("is not accessible for the namespace \"classloader-namespace\"") == std::string::npos) {
-    return false;
+  // SELinux denials for /vendor libraries may return with library not found
+  if (err.find("dlopen failed: library \"" + library + "\"") == 0 &&
+      (err.find("not found") != std::string::npos ||
+      err.find("is not accessible for the namespace \"classloader-namespace\"") != std::string::npos)) {
+    return true;
   }
-  return true;
+
+  return false;
 }
 
 static bool check_lib(const std::string& path,
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 8bfd79b..f01b833 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -56,6 +56,7 @@
 import android.os.SystemProperties;
 import android.security.KeyStoreException;
 import android.security.keystore.AttestationUtils;
+import android.security.keystore.DeviceIdAttestationException;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.test.AndroidTestCase;
@@ -357,9 +358,9 @@
     }
 
     public void testDeviceIdAttestation() throws Exception {
-        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL);
-        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI);
-        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID);
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL, null);
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI, "Unable to retrieve IMEI");
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID, "Unable to retrieve MEID");
     }
 
     @SuppressWarnings("deprecation")
@@ -855,13 +856,23 @@
         }
     }
 
-    private void testDeviceIdAttestationFailure(int idType) throws Exception {
+    private void testDeviceIdAttestationFailure(int idType,
+            String acceptableDeviceIdAttestationFailureMessage) throws Exception {
         try {
             AttestationUtils.attestDeviceIds(getContext(), new int[] {idType}, "123".getBytes());
             fail("Attestation should have failed.");
         } catch (SecurityException e) {
-            // Attestation is expected to fail with a SecurityException as we do not hold
+            // Attestation is expected to fail. If the device has the device ID type we are trying
+            // to attest, it should fail with a SecurityException as we do not hold
             // READ_PRIVILEGED_PHONE_STATE permission.
+        } catch (DeviceIdAttestationException e) {
+            // Attestation is expected to fail. If the device does not have the device ID type we
+            // are trying to attest (e.g. no IMEI on devices without a radio), it should fail with
+            // a corresponding DeviceIdAttestationException.
+            if (acceptableDeviceIdAttestationFailureMessage == null ||
+                    !acceptableDeviceIdAttestationFailureMessage.equals(e.getMessage())) {
+                throw e;
+            }
         }
     }
 }
diff --git a/tests/tests/location/src/android/location/cts/LocationTest.java b/tests/tests/location/src/android/location/cts/LocationTest.java
index 18faaee..bec6350 100644
--- a/tests/tests/location/src/android/location/cts/LocationTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationTest.java
@@ -36,6 +36,9 @@
 public class LocationTest extends AndroidTestCase {
     private static final float DELTA = 0.1f;
     private final float TEST_ACCURACY = 1.0f;
+    private final float TEST_VERTICAL_ACCURACY = 2.0f;
+    private final float TEST_SPEED_ACCURACY = 3.0f;
+    private final float TEST_BEARING_ACCURACY = 4.0f;
     private final double TEST_ALTITUDE = 1.0;
     private final double TEST_LATITUDE = 50;
     private final float TEST_BEARING = 1.0f;
@@ -51,6 +54,7 @@
     private static final String ENABLED_KEY = "enabled";
     private static final String MESSENGER_KEY = "messenger";
 
+
     public void testConstructor() {
         new Location("LocationProvider");
 
@@ -271,6 +275,46 @@
         assertFalse(location.hasAccuracy());
     }
 
+    public void testAccessVerticalAccuracy() {
+        Location location = new Location("");
+        assertFalse(location.hasVerticalAccuracy());
+
+        location.setVerticalAccuracyMeters(1.0f);
+        assertEquals(1.0, location.getVerticalAccuracyMeters(), DELTA);
+        assertTrue(location.hasVerticalAccuracy());
+
+        location.removeVerticalAccuracy();
+        assertEquals(0.0, location.getVerticalAccuracyMeters(), DELTA);
+        assertFalse(location.hasVerticalAccuracy());
+    }
+
+    public void testAccessSpeedAccuracy() {
+        Location location = new Location("");
+        assertFalse(location.hasSpeedAccuracy());
+
+        location.setSpeedAccuracyMetersPerSecond(1.0f);
+        assertEquals(1.0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+        assertTrue(location.hasSpeedAccuracy());
+
+        location.removeSpeedAccuracy();
+        assertEquals(0.0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+        assertFalse(location.hasSpeedAccuracy());
+    }
+
+    public void testAccessBearingAccuracy() {
+        Location location = new Location("");
+        assertFalse(location.hasBearingAccuracy());
+
+        location.setBearingAccuracyDegrees(1.0f);
+        assertEquals(1.0, location.getBearingAccuracyDegrees(), DELTA);
+        assertTrue(location.hasBearingAccuracy());
+
+        location.removeBearingAccuracy();
+        assertEquals(0.0, location.getBearingAccuracyDegrees(), DELTA);
+        assertFalse(location.hasBearingAccuracy());
+    }
+
+
     public void testAccessAltitude() {
         Location location = new Location("");
         assertFalse(location.hasAltitude());
@@ -411,6 +455,15 @@
         assertFalse(location.hasBearing());
         assertEquals(0, location.getAccuracy(), DELTA);
         assertFalse(location.hasAccuracy());
+
+        assertEquals(0, location.getVerticalAccuracyMeters(), DELTA);
+        assertEquals(0, location.getSpeedAccuracyMetersPerSecond(), DELTA);
+        assertEquals(0, location.getBearingAccuracyDegrees(), DELTA);
+
+        assertFalse(location.hasVerticalAccuracy());
+        assertFalse(location.hasSpeedAccuracy());
+        assertFalse(location.hasBearingAccuracy());
+
         assertNull(location.getExtras());
     }
 
@@ -486,6 +539,9 @@
         assertNotNull(l);
         assertEquals(TEST_PROVIDER, l.getProvider());
         assertEquals(TEST_ACCURACY, l.getAccuracy(), DELTA);
+        assertEquals(TEST_VERTICAL_ACCURACY, l.getVerticalAccuracyMeters(), DELTA);
+        assertEquals(TEST_SPEED_ACCURACY, l.getSpeedAccuracyMetersPerSecond(), DELTA);
+        assertEquals(TEST_BEARING_ACCURACY, l.getBearingAccuracyDegrees(), DELTA);
         assertEquals(TEST_ALTITUDE, l.getAltitude(), DELTA);
         assertEquals(TEST_LATITUDE, l.getLatitude(), DELTA);
         assertEquals(TEST_BEARING, l.getBearing(), DELTA);
@@ -498,6 +554,10 @@
     private Location createTestLocation() {
         Location l = new Location(TEST_PROVIDER);
         l.setAccuracy(TEST_ACCURACY);
+        l.setVerticalAccuracyMeters(TEST_VERTICAL_ACCURACY);
+        l.setSpeedAccuracyMetersPerSecond(TEST_SPEED_ACCURACY);
+        l.setBearingAccuracyDegrees(TEST_BEARING_ACCURACY);
+
         l.setAltitude(TEST_ALTITUDE);
         l.setLatitude(TEST_LATITUDE);
         l.setBearing(TEST_BEARING);
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index b2ae259..1fb5460 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -45,23 +45,26 @@
 LOCAL_MULTILIB := both
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    ctsmediautil \
     compatibility-device-util \
-    ctstestserver \
+    ctsdeviceutillegacy \
+    ctsmediautil \
     ctstestrunner \
-    ndkaudio \
+    ctstestserver \
     junit \
-    legacy-android-test
+    legacy-android-test \
+    ndkaudio
 
 LOCAL_JNI_SHARED_LIBRARIES := \
-    libctsimagereader_jni \
-    libctsmediacodec_jni \
     libaudio_jni \
+    libctsimagereader_jni \
+    libctsmediadrm_jni \
+    libctsmediacodec_jni \
     libnativehelper_compat_libc++ \
     libndkaudioLib
 
 # do not compress VP9 video files
 LOCAL_AAPT_FLAGS := -0 .vp9
+LOCAL_AAPT_FLAGS += -0 .ts
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+    AMediaObjects();
+    virtual ~AMediaObjects();
+
+    void setCrypto(AMediaCrypto* const theCrypto) {
+        mCrypto = theCrypto;
+    }
+    void setDrm(AMediaDrm* const theDrm) {
+        mDrm = theDrm;
+    }
+    void setVideoExtractor(AMediaExtractor* const theExtractor) {
+        mVideoExtractor = theExtractor;
+    }
+    void setAudioExtractor(AMediaExtractor* const theExtractor) {
+        mAudioExtractor = theExtractor;
+    }
+
+    AMediaCrypto* getCrypto() const { return mCrypto; }
+    AMediaDrm* getDrm() const { return mDrm; }
+    AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+    AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+    AMediaCrypto *mCrypto;
+    AMediaDrm* mDrm;
+    AMediaExtractor* mAudioExtractor;
+    AMediaExtractor* mVideoExtractor;
+
+    // Disallow copy and assignment
+    AMediaObjects(const AMediaObjects&);
+    void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+        mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+    if (mCrypto) {
+        AMediaCrypto_delete(mCrypto);
+    }
+    if (mAudioExtractor) {
+        AMediaExtractor_delete(mAudioExtractor);
+    }
+    if (mVideoExtractor) {
+        AMediaExtractor_delete(mVideoExtractor);
+    }
+    if (mDrm) {
+        AMediaDrm_release(mDrm);
+    }
+}
+
+}  // anonymous namespace
+#endif  // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 39dbea3..e42d388 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,27 +14,67 @@
 #
 LOCAL_PATH := $(call my-dir)
 
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
 include $(CLEAR_VARS)
 
-LOCAL_MODULE    := libctsmediacodec_jni
+LOCAL_MODULE := libctsmediacodec_jni
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-	native-media-jni.cpp \
-	codec-utils-jni.cpp  \
-	md5_utils.cpp
+  native-media-jni.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp
 
 LOCAL_C_INCLUDES := \
-	$(JNI_H_INCLUDE) \
-	system/core/include
+  $(JNI_H_INCLUDE) \
+  system/core/include
 
 LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
 
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk libEGL
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libEGL
 
 LOCAL_SDK_VERSION := current
 
 LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
 
 include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  CtsMediaDrmJniOnLoad.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp \
+  native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+  $(JNI_H_INCLUDE) \
+  system/core/include
+
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libdl libEGL
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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 <jni.h>
+#include <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..8e520e1 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
  */
 void
 MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
-  register UWORD32 a, b, c, d;
+  UWORD32 a, b, c, d;
 
   a = buf[0];
   b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..b98a6af
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+    jfieldID surface;
+    jfieldID mimeType;
+    jfieldID audioUrl;
+    jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+    jobject surface;
+    jstring mimeType;
+    jstring audioUrl;
+    jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kWidevineUuid[kUuidSize] = {
+    0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
+    0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+    // BMFF box header (4 bytes size + 'pssh')
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+    // full box header (version = 1 flags = 0)
+    0x01, 0x00, 0x00, 0x00,
+    // system id
+    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+    // number of key ids
+    0x00, 0x00, 0x00, 0x01,
+    // key id
+    0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+    0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+    // size of data, must be zero
+    0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+    0x7b, 0x22, 0x6b, 0x69, 0x64,
+    0x73, 0x22, 0x3a, 0x5b, 0x22,
+    0x59, 0x41, 0x59, 0x65, 0x41,
+    0x58, 0x35, 0x48, 0x66, 0x6f,
+    0x64, 0x2b, 0x56, 0x39, 0x41,
+    0x4e, 0x48, 0x74, 0x41, 0x4e,
+    0x48, 0x67, 0x22, 0x5d, 0x2c,
+    0x22, 0x74, 0x79, 0x70, 0x65,
+    0x22, 0x3a, 0x22, 0x74, 0x65,
+    0x6d, 0x70, 0x6f, 0x72, 0x61,
+    0x72, 0x79, 0x22, 0x7d,
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+        "\"kid\":\"YAYeAX5Hfod+V9ANHtANHg\",\"k\":" \
+        "\"GoogleTestKeyBase64ggg\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+    return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+    JNIEnv* env, jbyteArray const &byteArray) {
+    uint8_t* buffer = reinterpret_cast<uint8_t*>(
+        env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+    std::vector<uint8_t> vector;
+    for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+        vector.push_back(buffer[i]);
+    }
+    return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+    Uuid juuid;
+    juuid.resize(0);
+    if (uuid != NULL) {
+        juuid = jbyteArrayToVector(env, uuid);
+    }
+    return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+    if (NULL == uuid) {
+        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (isUuidSizeValid(juuid)) {
+         return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+    } else {
+          jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                  "invalid UUID size, expected %u bytes", kUuidSize);
+    }
+    return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
+    params.surface = env->GetObjectField(
+        playbackParams, gFieldIds.surface);
+
+    params.mimeType = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.mimeType));
+
+    params.audioUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.audioUrl));
+
+    params.videoUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+    JNIEnv* env, jclass clazz, jbyteArray uuid,
+    jstring name, jobject outValue) {
+
+    if (NULL == uuid || NULL == name || NULL == outValue) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "One or more null input parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    const char *utf8_name = env->GetStringUTFChars(name, NULL);
+    const char *utf8_outValue = NULL;
+    media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            utf8_name, &utf8_outValue);
+    env->ReleaseStringUTFChars(name, utf8_name);
+
+    if (NULL != utf8_outValue) {
+        clazz = env->GetObjectClass(outValue);
+        jmethodID mId = env->GetMethodID (clazz, "append",
+                "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+        jstring outString = env->NewStringUTF(
+                static_cast<const char *>(utf8_outValue));
+        env->CallObjectMethod(outValue, mId, outString);
+    } else {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "get property string returns %d", status);
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+    if (NULL == uuid || NULL == videoUrl) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null videoUrl");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(videoUrl, 0);
+    if (url) {
+        media_status_t status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+    if (psshInfo == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+        return JNI_FALSE;
+    }
+
+    jboolean testResult = JNI_FALSE;
+    for (size_t i = 0; i < psshInfo->numentries; i++) {
+        PsshEntry *entry = &psshInfo->entries[i];
+
+        // We do not have clearkey content that contains ClearKey UUID in the
+        // pssh box. So we have to test if it has Widevine UUID instead.
+        // TODO: Replace kWidevineUuid with uuid when test content contains
+        // ClearKey UUID.
+        if (0 == memcmp(entry->uuid, kWidevineUuid, sizeof(entry->uuid))) {
+            aMediaObjects.setCrypto(
+                AMediaCrypto_new(entry->uuid, entry->data, entry->datalen));
+            if (aMediaObjects.getCrypto()) {
+                testResult = JNI_TRUE;
+            } else {
+                ALOGE("Failed to create media crypto=%zd", i);
+                testResult = JNI_FALSE;
+            }
+            break;
+        }
+    }
+    return testResult;
+}
+
+static bool isVideo(const char* mime) {
+    return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+    return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+        const char* mime, const AMediaCrypto* crypto,
+        const ANativeWindow* window, AMediaCodec** codec) {
+
+    *codec = AMediaCodec_createDecoderByType(mime);
+    if (codec == NULL) {
+        ALOGE("cannot create codec for %s", mime);
+        return;
+    }
+
+    AMediaCodec_configure(*codec, format,
+            const_cast<ANativeWindow*>(window),
+            const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+        const AMediaCrypto* crypto, const ANativeWindow* window,
+        AMediaCodec** codec) {
+    size_t numTracks = AMediaExtractor_getTrackCount(
+        const_cast<AMediaExtractor*>(extractor));
+    AMediaFormat* trackFormat = NULL;
+    for (size_t i = 0; i < numTracks; ++i) {
+        trackFormat = AMediaExtractor_getTrackFormat(
+            const_cast<AMediaExtractor*>(extractor), i);
+        if (trackFormat) {
+            ALOGV("track %zd format: %s", i,
+                    AMediaFormat_toString(trackFormat));
+
+            const char* mime = "";
+            if (!AMediaFormat_getString(
+                trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+                ALOGE("no mime type");
+                AMediaFormat_delete(trackFormat);
+                return;
+            } else if (isAudio(mime) || isVideo(mime)) {
+                AMediaExtractor_selectTrack(
+                    const_cast<AMediaExtractor*>(extractor), i);
+                ALOGV("track %zd codec format: %s", i,
+                        AMediaFormat_toString(trackFormat));
+
+                addTrack(trackFormat, mime, crypto, window, codec);
+                AMediaCodec_start(*codec);
+                AMediaCodec_flush(*codec);
+                AMediaExtractor_seekTo(
+                    const_cast<AMediaExtractor*>(extractor), 0,
+                            AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+            }
+            AMediaFormat_delete(trackFormat);
+        }
+    }
+}
+
+static int64_t getSystemNanoTime() {
+    timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+        int64_t* presentationTimeUs, bool* eosReached) {
+    media_status_t status = AMEDIA_OK;
+
+    ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+    if (bufferIndex >= 0) {
+        size_t bufsize;
+        uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+        int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+        if (sampleSize < 0) {
+            sampleSize = 0;
+            *eosReached = true;
+        }
+
+        *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+        AMediaCodecCryptoInfo *cryptoInfo =
+            AMediaExtractor_getSampleCryptoInfo(extractor);
+        if (cryptoInfo) {
+            status = AMediaCodec_queueSecureInputBuffer(
+                codec, bufferIndex, 0, cryptoInfo,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+            AMediaCodecCryptoInfo_delete(cryptoInfo);
+        } else {
+            status = AMediaCodec_queueInputBuffer(
+                codec, bufferIndex, 0, sampleSize,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+        }
+        AMediaExtractor_advance(extractor);
+    }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+    int64_t* startTimeNano) {
+
+    AMediaCodecBufferInfo info;
+    ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+    if (bufferIndex >= 0) {
+        if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+            return true;  // eos reached
+        }
+
+        if (*startTimeNano < 0) {
+            *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+        }
+        int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+                getSystemNanoTime();
+        if (delay > 0) {
+            usleep(delay / 1000);
+        }
+
+        AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+        ALOGV("output buffers changed");
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+        AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+        ALOGV("format changed to: %s", AMediaFormat_toString(format));
+        AMediaFormat_delete(format);
+    } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+         ALOGV("no output buffer right now");
+         usleep(20000);
+    } else {
+         ALOGV("unexpected info code: %zd", bufferIndex);
+    }
+    return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+        PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+    ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+    AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+    AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+    AMediaCodec* audioCodec = NULL;
+    AMediaCodec* videoCodec = NULL;
+    AMediaCrypto* crypto = NULL;
+
+    crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "failed to create crypto object");
+        return JNI_FALSE;
+    }
+
+    addTracks(audioExtractor, NULL, NULL, &audioCodec);
+    addTracks(videoExtractor, crypto, window, &videoCodec);
+
+    bool sawAudioInputEos = false;
+    bool sawAudioOutputEos = false;
+    bool sawVideoInputEos = false;
+    bool sawVideoOutputEos = false;
+    int64_t videoPresentationTimeUs = 0;
+    int64_t videoStartTimeNano = -1;
+    struct timespec timeSpec;
+    clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+    time_t startTimeSec = timeSpec.tv_sec;
+
+    while (!sawAudioOutputEos && !sawVideoOutputEos) {
+        if (!sawVideoInputEos) {
+            fillDecoder(videoCodec, videoExtractor,
+                    &videoPresentationTimeUs, &sawVideoInputEos);
+        }
+
+        if (!sawAudioInputEos) {
+            // skip audio, still need to advance the audio extractor
+            AMediaExtractor_advance(audioExtractor);
+        }
+
+        if (!sawVideoOutputEos) {
+            sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+                    &videoStartTimeNano);
+        }
+
+        clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+        if (timeSpec.tv_sec >= static_cast<time_t>(
+            (startTimeSec + kPlayTimeSeconds))) {
+            // stop reading samples and drain the output buffers
+            sawAudioInputEos = sawVideoInputEos = true;
+            sawAudioOutputEos = true; // ignore audio
+        }
+    }
+
+    if (audioCodec) {
+        AMediaCodec_stop(audioCodec);
+        AMediaCodec_delete(audioCodec);
+    }
+    if (videoCodec) {
+        AMediaCodec_stop(videoCodec);
+        AMediaCodec_delete(videoCodec);
+    }
+
+    AMediaCrypto_delete(crypto);
+    ANativeWindow_release(window);
+    return JNI_TRUE;
+}
+
+static void listener(
+    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+    AMediaDrmEventType eventType,
+    int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+    switch (eventType) {
+        case EVENT_PROVISION_REQUIRED:
+            ALOGD("EVENT_PROVISION_REQUIRED received");
+            break;
+        case EVENT_KEY_REQUIRED:
+            ALOGD("EVENT_KEY_REQUIRED received");
+            break;
+        case EVENT_KEY_EXPIRED:
+            ALOGD("EVENT_KEY_EXPIRED received");
+            break;
+        case EVENT_VENDOR_DEFINED:
+            gGotVendorDefinedEvent = true;
+            ALOGD("EVENT_VENDOR_DEFINED received");
+            break;
+        default:
+            ALOGD("Unknown event received");
+            break;
+    }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+    if (NULL == uuid || NULL == playbackParams) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null playback parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    PlaybackParams params;
+    initPlaybackParams(env, playbackParams, params);
+
+    AMediaObjects aMediaObjects;
+    media_status_t status = AMEDIA_OK;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "setOnEventListener failed");
+        return JNI_FALSE;
+    }
+
+    aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getAudioExtractor(), url);
+        env->ReleaseStringUTFChars(params.audioUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set audio data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    url = env->GetStringUTFChars(params.videoUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(params.videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    AMediaDrmSessionId sessionId;
+    status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "openSession failed");
+        return JNI_FALSE;
+    }
+
+    // Pointer to keyRequest memory, which remains until the next
+    // AMediaDrm_getKeyRequest call or until the drm object is released.
+    const uint8_t* keyRequest;
+    size_t keyRequestSize = 0;
+
+    // The server recognizes "video/mp4" but not "video/avc".
+    status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+            kClearkeyPssh, sizeof(kClearkeyPssh),
+            "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+            NULL, 0, &keyRequest, &keyRequestSize);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "getKeyRequest failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    if (kKeyRequestSize != keyRequestSize) {
+        ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+        return JNI_FALSE;
+    }
+
+    if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+        ALOGE("Invalid keyRequest data is returned");
+        return JNI_FALSE;
+    }
+
+    AMediaDrmKeySetId keySetId;
+    gGotVendorDefinedEvent = false;
+    status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+            reinterpret_cast<const uint8_t*>(kResponse),
+            sizeof(kResponse), &keySetId);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "provideKeyResponse failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    // Check if the event listener has received the expected event sent by
+    // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+    const char *utf8_outValue = NULL;
+    status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            "listenerTestSupport", &utf8_outValue);
+    if (status == AMEDIA_OK && NULL != utf8_outValue) {
+        std::string eventType(utf8_outValue);
+        if (eventType.compare("true") == 0) {
+            int count = 0;
+            while (!gGotVendorDefinedEvent && count++ < 5) {
+               // Prevents race condition when the event arrives late
+               usleep(1000);
+            }
+            if (!gGotVendorDefinedEvent) {
+                ALOGE("Event listener did not receive the expected event.");
+                jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                        "Event listener did not receive the expected event.");
+                AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+                return JNI_FALSE;
+           }
+        }
+    }
+
+    playContent(env, aMediaObjects, params, sessionId, juuid);
+
+    status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "closeSession failed");
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "isCryptoSchemeSupportedNative", "([B)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+    { "testClearKeyPlaybackNative",
+            "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+    { "testGetPropertyStringNative",
+            "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+    { "testPsshNative", "([BLjava/lang/String;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+    jint result = JNI_ERR;
+    jclass testClass =
+        env->FindClass("android/media/cts/NativeClearKeySystemTest");
+    if (testClass) {
+        jclass playbackParamsClass = env->FindClass(
+            "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+        if (playbackParamsClass) {
+            jclass surfaceClass =
+                env->FindClass("android/view/Surface");
+            if (surfaceClass) {
+                gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+                        "surface", "Landroid/view/Surface;");
+            } else {
+                gFieldIds.surface = NULL;
+            }
+            gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+                    "mimeType", "Ljava/lang/String;");
+            gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+                    "audioUrl", "Ljava/lang/String;");
+            gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+                    "videoUrl", "Ljava/lang/String;");
+        } else {
+            ALOGE("PlaybackParams class not found");
+        }
+
+    } else {
+        ALOGE("NativeClearKeySystemTest class not found");
+    }
+
+    result = env->RegisterNatives(testClass, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+    return result;
+}
diff --git a/tests/tests/media/res/raw/segment000001.ts b/tests/tests/media/res/raw/segment000001.ts
new file mode 100644
index 0000000..fb112ec
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001.ts
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_scrambled.ts b/tests/tests/media/res/raw/segment000001_scrambled.ts
new file mode 100644
index 0000000..4c7d368
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001_scrambled.ts
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index d15643f..ba657bf 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -481,12 +481,12 @@
             Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
             int maxMusicVolume = mAudioManager.getStreamMaxVolume(STREAM_MUSIC);
 
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // set ringer mode to back normal to not interfere with volume tests
                 mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
 
-                int maxVolume = mAudioManager.getStreamMaxVolume(streams[i]);
-                int minVolume = mAudioManager.getStreamMinVolume(streams[i]);
+                int maxVolume = mAudioManager.getStreamMaxVolume(stream);
+                int minVolume = mAudioManager.getStreamMinVolume(stream);
 
                 // validate min
                 assertTrue(String.format("minVolume(%d) must be >= 0", minVolume), minVolume >= 0);
@@ -494,14 +494,15 @@
                         maxVolume),
                         minVolume < maxVolume);
 
-                mAudioManager.setStreamVolume(streams[i], 1, 0);
+                mAudioManager.setStreamVolume(stream, 1, 0);
                 if (mUseFixedVolume) {
-                    assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                    assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
                     continue;
                 }
-                assertEquals(1, mAudioManager.getStreamVolume(streams[i]));
+                assertEquals(String.format("stream=%d", stream),
+                             1, mAudioManager.getStreamVolume(stream));
 
-                if (streams[i] == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
+                if (stream == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
                     // due to new regulations, music sent over a wired headset may be volume limited
                     // until the user explicitly increases the limit, so we can't rely on being able
                     // to set the volume to getStreamMaxVolume(). Instead, determine the current limit
@@ -511,52 +512,52 @@
                     int prevvol = 0;
                     do {
                         prevvol = curvol;
-                        mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
-                        curvol = mAudioManager.getStreamVolume(streams[i]);
+                        mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+                        curvol = mAudioManager.getStreamVolume(stream);
                     } while (curvol != prevvol);
                     maxVolume = maxMusicVolume = curvol;
                 }
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
-                mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
-                assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
+                mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+                assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
 
-                volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, streams[i], 0);
+                volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, stream, 0);
                 Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
-                assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(streams[i]));
+                assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(stream));
 
                 // volume lower
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
-                volume = mAudioManager.getStreamVolume(streams[i]);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
+                volume = mAudioManager.getStreamVolume(stream);
                 while (volume > minVolume) {
-                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_LOWER, 0);
+                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_LOWER, 0);
                     assertEquals(Math.max(0, volume - volumeDelta),
-                            mAudioManager.getStreamVolume(streams[i]));
-                    volume = mAudioManager.getStreamVolume(streams[i]);
+                            mAudioManager.getStreamVolume(stream));
+                    volume = mAudioManager.getStreamVolume(stream);
                 }
 
-                mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
+                mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
 
                 // volume raise
-                mAudioManager.setStreamVolume(streams[i], 1, 0);
-                volume = mAudioManager.getStreamVolume(streams[i]);
+                mAudioManager.setStreamVolume(stream, 1, 0);
+                volume = mAudioManager.getStreamVolume(stream);
                 while (volume < maxVolume) {
-                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
+                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
                     assertEquals(Math.min(volume + volumeDelta, maxVolume),
-                            mAudioManager.getStreamVolume(streams[i]));
-                    volume = mAudioManager.getStreamVolume(streams[i]);
+                            mAudioManager.getStreamVolume(stream));
+                    volume = mAudioManager.getStreamVolume(stream);
                 }
 
                 // volume same
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
                 for (int k = 0; k < maxVolume; k++) {
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
-                    assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
+                    assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
                 }
 
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
             }
 
             if (mUseFixedVolume) {
@@ -659,18 +660,18 @@
                 AudioManager.STREAM_NOTIFICATION,
                 AudioManager.STREAM_SYSTEM};
         if (mUseFixedVolume) {
-            for (int i = 0; i < streams.length; i++) {
-                mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+            for (int stream : streams) {
+                mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
                 assertFalse("Muting should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
 
-                mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+                mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
                 assertFalse("Toggling mute should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
 
-                mAudioManager.setStreamMute(streams[i], true);
+                mAudioManager.setStreamMute(stream, true);
                 assertFalse("Muting should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
             }
         }
     }
@@ -688,16 +689,16 @@
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), false);
             // Verify streams cannot be unmuted without policy access.
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_UNMUTE, 0);
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_UNMUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
 
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
                             0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
@@ -705,7 +706,7 @@
                 }
 
                 try {
-                    mAudioManager.setStreamMute(streams[i], false);
+                    mAudioManager.setStreamMute(stream, false);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
@@ -716,37 +717,37 @@
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), true);
             mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // ensure each stream is on and turned up.
-                mAudioManager.setStreamVolume(streams[i],
-                        mAudioManager.getStreamMaxVolume(streams[i]),
+                mAudioManager.setStreamVolume(stream,
+                        mAudioManager.getStreamMaxVolume(stream),
                         0);
 
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), false);
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
                 try {
                     mAudioManager.adjustStreamVolume(
-                            streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+                            stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
 
                 try {
-                    mAudioManager.setStreamMute(streams[i], true);
+                    mAudioManager.setStreamMute(stream, true);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true);
-                testStreamMuting(streams[i]);
+                testStreamMuting(stream);
             }
         } finally {
             Utils.toggleNotificationPolicyAccess(
@@ -779,25 +780,25 @@
             mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), false);
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // ensure each stream is on and turned up.
-                mAudioManager.setStreamVolume(streams[i],
-                        mAudioManager.getStreamMaxVolume(streams[i]),
+                mAudioManager.setStreamVolume(stream,
+                        mAudioManager.getStreamMaxVolume(stream),
                         0);
-                if (((1 << streams[i]) & muteAffectedStreams) == 0) {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
-                    mAudioManager.setStreamMute(streams[i], true);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+                if (((1 << stream) & muteAffectedStreams) == 0) {
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
+                    mAudioManager.setStreamMute(stream, true);
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
                             0);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
                     continue;
                 }
-                testStreamMuting(streams[i]);
+                testStreamMuting(stream);
             }
         } finally {
             Utils.toggleNotificationPolicyAccess(
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index efe8bb4..61edbba 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -36,12 +36,13 @@
 import java.util.List;
 
 public class AudioRecordingConfigurationTest extends CtsAndroidTestCase {
-    private final static String TAG = "AudioRecordingConfigurationTest";
+    private static final String TAG = "AudioRecordingConfigurationTest";
 
-    private final static int TEST_SAMPLE_RATE = 16000;
-    private final static int TEST_AUDIO_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
+    private static final int TEST_SAMPLE_RATE = 16000;
+    private static final int TEST_AUDIO_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
 
-    private final static int TEST_TIMING_TOLERANCE_MS = 70;
+    private static final int TEST_TIMING_TOLERANCE_MS = 70;
+    private static final long SLEEP_AFTER_STOP_FOR_INACTIVITY_MS = 1000;
 
     private AudioRecord mAudioRecord;
     private Looper mLooper;
@@ -93,6 +94,7 @@
             mAudioRecord.stop();
             mAudioRecord.release();
             mLooper.quit();
+            Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
         }
         super.tearDown();
     }
@@ -127,10 +129,10 @@
 
         // stopping recording: verify there are less active record configurations
         mAudioRecord.stop();
-        Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+        Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
         configs = am.getActiveRecordingConfigurations();
-        assertTrue("end of recording not reported in record configs",
-                configs.size() < nbConfigsDuringRecording);
+        assertEquals("Unexpected number of recording configs after stop",
+                configs.size(), 0);
     }
 
     public void testCallback() throws Exception {
@@ -169,15 +171,23 @@
             assertEquals(AudioRecord.RECORDSTATE_RECORDING, mAudioRecord.getRecordingState());
             Thread.sleep(TEST_TIMING_TOLERANCE_MS);
 
-            assertTrue("AudioRecordingCallback not called", callback.mCalled);
-            assertTrue("Expected record configuration was not found", callback.mParamMatch);
+            assertTrue("AudioRecordingCallback not called after start", callback.mCalled);
+            Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+
+            final AudioDeviceInfo testDevice = mAudioRecord.getRoutedDevice();
+            assertTrue("AudioRecord null routed device after start", testDevice != null);
+            final boolean match = verifyAudioConfig(mAudioRecord.getAudioSource(),
+                    mAudioRecord.getAudioSessionId(), mAudioRecord.getFormat(),
+                    testDevice, callback.mConfigs);
+            assertTrue("Expected record configuration was not found", match);
 
             // stopping recording: callback is called with no match
             callback.reset();
             mAudioRecord.stop();
-            Thread.sleep(TEST_TIMING_TOLERANCE_MS);
-            assertTrue("AudioRecordingCallback not called", callback.mCalled);
-            assertFalse("Should not have found test record configuration", callback.mParamMatch);
+            Thread.sleep(SLEEP_AFTER_STOP_FOR_INACTIVITY_MS);
+            assertTrue("AudioRecordingCallback not called after stop", callback.mCalled);
+            assertEquals("Should not have found record configurations", callback.mConfigs.size(),
+                    0);
 
             // unregister callback and start recording again
             am.unregisterAudioRecordingCallback(callback);
@@ -231,14 +241,14 @@
 
     class MyAudioRecordingCallback extends AudioManager.AudioRecordingCallback {
         boolean mCalled = false;
-        boolean mParamMatch = false;
+        List<AudioRecordingConfiguration> mConfigs;
         final AudioManager mAM;
         final int mTestSource;
         final int mTestSession;
 
         void reset() {
             mCalled = false;
-            mParamMatch = false;
+            mConfigs = null;
         }
 
         MyAudioRecordingCallback(int session, int source) {
@@ -250,8 +260,7 @@
         @Override
         public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
             mCalled = true;
-            mParamMatch = verifyAudioConfig(mTestSource, mTestSession, mAudioRecord.getFormat(),
-                    mAudioRecord.getRoutedDevice(), configs);
+            mConfigs = configs;
         }
     }
 
@@ -266,6 +275,8 @@
         final Iterator<AudioRecordingConfiguration> confIt = configs.iterator();
         while (confIt.hasNext()) {
             final AudioRecordingConfiguration config = confIt.next();
+            final AudioDeviceInfo configDevice = config.getAudioDevice();
+            assertTrue("Current recording config has null device", configDevice != null);
             if ((config.getClientAudioSource() == source)
                     && (config.getClientAudioSessionId() == session)
                     // test the client format matches that requested (same as the AudioRecord's)
@@ -281,7 +292,7 @@
                     && ((config.getFormat().getChannelMask() != AudioFormat.CHANNEL_INVALID)
                             || (config.getFormat().getChannelIndexMask() !=
                                     AudioFormat.CHANNEL_INVALID))
-                    && deviceMatch(device, config.getAudioDevice())) {
+                    && deviceMatch(device, configDevice)) {
                 return true;
             }
         }
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 69f3ee8..5ea33ba 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -66,8 +66,10 @@
     private static final int SLEEP_TIME_MS = 1000;
     private static final int VIDEO_WIDTH_CENC = 1280;
     private static final int VIDEO_HEIGHT_CENC = 720;
-    private static final int VIDEO_WIDTH_WEBM = 320;
-    private static final int VIDEO_HEIGHT_WEBM = 180;
+    private static final int VIDEO_WIDTH_WEBM = 352;
+    private static final int VIDEO_HEIGHT_WEBM = 288;
+    private static final int VIDEO_WIDTH_MPEG2TS = 320;
+    private static final int VIDEO_HEIGHT_MPEG2TS = 240;
     private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(25, TimeUnit.SECONDS);
     private static final String MIME_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
     private static final String MIME_VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
@@ -78,6 +80,10 @@
             "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car_cenc-20120827-88.mp4");
     private static final Uri WEBM_URL = Uri.parse(
             "android.resource://android.media.cts/" + R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt);
+    private static final Uri MPEG2TS_SCRAMBLED_URL = Uri.parse(
+            "android.resource://android.media.cts/" + R.raw.segment000001_scrambled);
+    private static final Uri MPEG2TS_CLEAR_URL = Uri.parse(
+            "android.resource://android.media.cts/" + R.raw.segment000001);
 
     private static final UUID CLEARKEY_SCHEME_UUID =
             new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
@@ -327,15 +333,20 @@
             String initDataType, byte[][] clearKeys,
             Uri audioUrl, boolean audioEncrypted,
             Uri videoUrl, boolean videoEncrypted,
-            int videoWidth, int videoHeight) throws Exception {
-        MediaDrm drm = startDrm(clearKeys, initDataType);
-        if (null == drm) {
-            throw new Error("Failed to create drm.");
-        }
+            int videoWidth, int videoHeight, boolean scrambled) throws Exception {
+        MediaDrm drm = null;
+        mSessionId = null;
+        if (!scrambled) {
+            drm = startDrm(clearKeys, initDataType);
+            if (null == drm) {
+                throw new Error("Failed to create drm.");
+            }
 
-        if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
-            stopDrm(drm);
-            throw new Error("Crypto scheme is not supported.");
+            if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
+                stopDrm(drm);
+                throw new Error("Crypto scheme is not supported.");
+            }
+            mSessionId = openSession(drm);
         }
 
         if (!isResolutionSupported(videoMime, videoFeatures, videoWidth, videoHeight)) {
@@ -364,19 +375,19 @@
         }
         connectionStatus.testConnection(videoUrl);
 
-        mSessionId = openSession(drm);
         mMediaCodecPlayer = new MediaCodecClearKeyPlayer(
                 getActivity().getSurfaceHolder(),
-                mSessionId,
+                mSessionId, scrambled,
                 mContext.getResources());
 
         mMediaCodecPlayer.setAudioDataSource(audioUrl, null, audioEncrypted);
         mMediaCodecPlayer.setVideoDataSource(videoUrl, null, videoEncrypted);
         mMediaCodecPlayer.start();
         mMediaCodecPlayer.prepare();
-        mDrmInitData = mMediaCodecPlayer.getDrmInitData();
-
-        getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+        if (!scrambled) {
+            mDrmInitData = mMediaCodecPlayer.getDrmInitData();
+            getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+        }
         // starts video playback
         mMediaCodecPlayer.startThread();
 
@@ -392,8 +403,10 @@
 
         Log.d(TAG, "playVideo player.reset()");
         mMediaCodecPlayer.reset();
-        closeSession(drm, mSessionId);
-        stopDrm(drm);
+        if (!scrambled) {
+            closeSession(drm, mSessionId);
+            stopDrm(drm);
+        }
     }
 
     public void testClearKeyPlaybackCenc() throws Exception {
@@ -403,7 +416,7 @@
             "cenc", new byte[][] { CLEAR_KEY_CENC },
             CENC_AUDIO_URL, false,
             CENC_VIDEO_URL, true,
-            VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+            VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false);
     }
 
     public void testClearKeyPlaybackWebm() throws Exception {
@@ -412,6 +425,24 @@
             "webm", new byte[][] { CLEAR_KEY_WEBM },
             WEBM_URL, true,
             WEBM_URL, true,
-            VIDEO_WIDTH_WEBM, VIDEO_WIDTH_WEBM);
+            VIDEO_WIDTH_WEBM, VIDEO_HEIGHT_WEBM, false);
+    }
+
+    public void testClearKeyPlaybackMpeg2ts() throws Exception {
+        testClearKeyPlayback(
+            MIME_VIDEO_AVC, new String[0],
+            "mpeg2ts", null,
+            MPEG2TS_SCRAMBLED_URL, false,
+            MPEG2TS_SCRAMBLED_URL, false,
+            VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, true);
+    }
+
+    public void testPlaybackMpeg2ts() throws Exception {
+        testClearKeyPlayback(
+            MIME_VIDEO_AVC, new String[0],
+            "mpeg2ts", null,
+            MPEG2TS_CLEAR_URL, false,
+            MPEG2TS_CLEAR_URL, false,
+            VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false);
     }
 }
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 51be918..12ebeae 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -322,12 +322,10 @@
 
             mSawOutputEOS = true;
 
-            // We need to stop the audio track so that all audio frames are played
-            // and the video codec can consume all of its data.
-            // After audio track stop, getAudioTimeUs will return 0.
-            if (mAudioTrack != null) {
-                mAudioTrack.stop();
-            }
+            // Do not stop audio track here. Video presentation may not finish
+            // yet, stopping the auido track now would result in getAudioTimeUs
+            // returning 0 and prevent video samples from being presented.
+            // We stop the audio track before the playback thread exits.
             return false;
         }
 
@@ -390,4 +388,10 @@
             mAudioTrack.process();
         }
     }
+
+    public void stop() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 1d95463..253a5ab 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -19,11 +19,15 @@
 import android.content.res.AssetFileDescriptor;
 import android.media.AudioManager;
 import android.media.DrmInitData;
+import android.media.MediaCas;
+import android.media.MediaCasException;
+import android.media.MediaCasException.UnsupportedCasException;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaCrypto;
 import android.media.MediaCryptoException;
+import android.media.MediaDescrambler;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.net.Uri;
@@ -60,6 +64,7 @@
     private boolean mEncryptedVideo;
     private volatile boolean mThreadStarted = false;
     private byte[] mSessionId;
+    private boolean mScrambled;
     private CodecState mAudioTrackState;
     private int mMediaFormatHeight;
     private int mMediaFormatWidth;
@@ -72,6 +77,8 @@
     private Map<String, String> mVideoHeaders;
     private Map<UUID, byte[]> mPsshInitData;
     private MediaCrypto mCrypto;
+    private MediaCas mMediaCas;
+    private MediaDescrambler mDescrambler;
     private MediaExtractor mAudioExtractor;
     private MediaExtractor mVideoExtractor;
     private SurfaceHolder mSurfaceHolder;
@@ -91,6 +98,19 @@
             "00000000"            // Size of Data, must be zero
             );
 
+    // ClearKey CAS/Descrambler test provision string
+    private static final String sProvisionStr =
+            "{                                                   " +
+            "  \"id\": 21140844,                                 " +
+            "  \"name\": \"Test Title\",                         " +
+            "  \"lowercase_organization_name\": \"Android\",     " +
+            "  \"asset_key\": {                                  " +
+            "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " +
+            "  },                                                " +
+            "  \"cas_type\": 1,                                  " +
+            "  \"track_types\": [ ]                              " +
+            "}                                                   " ;
+
     /**
      * Convert a hex string into byte array.
      */
@@ -108,8 +128,9 @@
      * Media player class to stream CENC content using MediaCodec class.
      */
     public MediaCodecClearKeyPlayer(
-            SurfaceHolder holder, byte[] sessionId, Resources resources) {
+            SurfaceHolder holder, byte[] sessionId, boolean scrambled, Resources resources) {
         mSessionId = sessionId;
+        mScrambled = scrambled;
         mSurfaceHolder = holder;
         mResources = resources;
         mState = STATE_IDLE;
@@ -127,6 +148,9 @@
                         Log.d(TAG, "Thread interrupted");
                     }
                 }
+                if (mAudioTrackState != null) {
+                    mAudioTrackState.stop();
+                }
             }
         });
     }
@@ -206,6 +230,8 @@
     private void prepareVideo() throws IOException {
         boolean hasVideo = false;
 
+        android.media.DrmInitData drmInitData = mVideoExtractor.getDrmInitData();
+
         for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
             MediaFormat format = mVideoExtractor.getTrackFormat(i);
             String mime = format.getString(MediaFormat.KEY_MIME);
@@ -218,6 +244,14 @@
             Log.d(TAG, "video track #" + i + " " + format + " " + mime +
                   " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
 
+            if (mScrambled && drmInitData != null && mime.startsWith("video/")) {
+                android.media.DrmInitData.SchemeInitData schemeInitData =
+                        drmInitData.get(new UUID(0, i));
+                if (schemeInitData != null) {
+                    mDescrambler.setMediaCasSession(schemeInitData.data);
+                }
+            }
+
             if (!hasVideo) {
                 mVideoExtractor.selectTrack(i);
                 addTrack(i, format, mEncryptedVideo);
@@ -239,11 +273,10 @@
                 }
             }
         }
-        return;
     }
 
     private void setDataSource(MediaExtractor extractor, Uri uri, Map<String, String> headers)
-            throws IOException {
+            throws IOException, MediaCasException {
         String scheme = uri.getScheme();
         if (scheme.startsWith("http")) {
             extractor.setDataSource(uri.toString(), headers);
@@ -256,26 +289,30 @@
         }
     }
 
-    public void prepare() throws IOException, MediaCryptoException {
-        if (null == mAudioExtractor) {
-            mAudioExtractor = new MediaExtractor();
-            if (null == mAudioExtractor) {
-                Log.e(TAG, "Cannot create Audio extractor.");
-                return;
+    private void initCasAndDescrambler(MediaExtractor extractor) throws MediaCasException {
+        int trackCount = extractor.getTrackCount();
+        android.media.DrmInitData drmInitData = extractor.getDrmInitData();
+        for (int trackId = 0; trackId < trackCount; trackId++) {
+            android.media.MediaFormat format = extractor.getTrackFormat(trackId);
+            String mime = format.getString(android.media.MediaFormat.KEY_MIME);
+            Log.d(TAG, "track "+ trackId + ": " + mime);
+            if ("video/scrambled".equals(mime) || "audio/scrambled".equals(mime)) {
+                if (drmInitData == null) {
+                    throw new IllegalArgumentException("found scrambled track without drmInitData!");
+                }
+                android.media.DrmInitData.SchemeInitData schemeInitData =
+                        drmInitData.get(new UUID(0, trackId));
+                int CA_system_id = (schemeInitData.data[0] & 0xff)
+                                | ((schemeInitData.data[1] & 0xff) << 8);
+                mMediaCas = new MediaCas(CA_system_id);
+                mDescrambler = new MediaDescrambler(CA_system_id);
+                mMediaCas.provision(sProvisionStr);
+                extractor.setMediaCas(mMediaCas);
             }
         }
+    }
 
-        if (null == mVideoExtractor){
-            mVideoExtractor = new MediaExtractor();
-            if (null == mVideoExtractor) {
-                Log.e(TAG, "Cannot create Video extractor.");
-                return;
-            }
-        }
-
-        setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
-        setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
-
+    public void prepare() throws IOException, MediaCryptoException, MediaCasException {
         if (null == mCrypto && (mEncryptedVideo || mEncryptedAudio)) {
             try {
                 byte[] initData = new byte[0];
@@ -288,8 +325,29 @@
             mCrypto.setMediaDrmSession(mSessionId);
         } else {
             reset();
-            mCrypto.release();
-            mCrypto = null;
+        }
+
+        if (null == mAudioExtractor) {
+            mAudioExtractor = new MediaExtractor();
+            if (null == mAudioExtractor) {
+                Log.e(TAG, "Cannot create Audio extractor.");
+                return;
+            }
+        }
+        setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
+
+        if (mScrambled) {
+            initCasAndDescrambler(mAudioExtractor);
+            mVideoExtractor = mAudioExtractor;
+        } else {
+            if (null == mVideoExtractor){
+                mVideoExtractor = new MediaExtractor();
+                if (null == mVideoExtractor) {
+                    Log.e(TAG, "Cannot create Video extractor.");
+                    return;
+                }
+            }
+            setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
         }
 
         if (null == mVideoCodecStates) {
@@ -325,11 +383,19 @@
             codec = MediaCodec.createDecoderByType(mime);
         }
 
-        codec.configure(
-                format,
-                isVideo ? mSurfaceHolder.getSurface() : null,
-                mCrypto,
-                0);
+        if (!mScrambled) {
+            codec.configure(
+                    format,
+                    isVideo ? mSurfaceHolder.getSurface() : null,
+                    mCrypto,
+                    0);
+        } else {
+            codec.configure(
+                    format,
+                    isVideo ? mSurfaceHolder.getSurface() : null,
+                    0,
+                    isVideo ? mDescrambler : null);
+        }
 
         CodecState state;
         if (isVideo) {
@@ -483,6 +549,16 @@
             mCrypto = null;
         }
 
+        if (mMediaCas != null) {
+            mMediaCas.release();
+            mMediaCas = null;
+        }
+
+        if (mDescrambler != null) {
+            mDescrambler.release();
+            mDescrambler = null;
+        }
+
         mDurationUs = -1;
         mState = STATE_IDLE;
     }
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..59c2e8e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 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.media.cts;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.net.Uri;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.android.compatibility.common.util.MediaUtils;
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+    private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+    private static final int CONNECTION_RETRIES = 10;
+    private static final int VIDEO_WIDTH_CENC = 1280;
+    private static final int VIDEO_HEIGHT_CENC = 720;
+    private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+    private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+    private static final Uri CENC_AUDIO_URL = Uri.parse(
+        "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+        "car_cenc-20120827-8c.mp4");
+    private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+        "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+        "car_cenc-20120827-88.mp4");
+
+    private static final int UUID_BYTE_SIZE = 16;
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+    private static final UUID BAD_SCHEME_UUID =
+            new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+    private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+    static {
+        try {
+            System.loadLibrary("ctsmediadrm_jni");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+        try {
+            System.loadLibrary("mediandk");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+    }
+
+    public static class PlaybackParams {
+        public Surface surface;
+        public String mimeType;
+        public String audioUrl;
+        public String videoUrl;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (false == deviceHasMediaDrm()) {
+            tearDown();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private boolean deviceHasMediaDrm() {
+        // ClearKey is introduced after KitKat.
+        if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+            Log.i(TAG, "This test is designed to work after Android KitKat.");
+            return false;
+        }
+        return true;
+    }
+
+    private static final byte[] uuidByteArray(UUID uuid) {
+        ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+        buffer.putLong(uuid.getMostSignificantBits());
+        buffer.putLong(uuid.getLeastSignificantBits());
+        return buffer.array();
+    }
+
+    public void testIsCryptoSchemeSupported() throws Exception {
+        assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+    }
+
+    public void testIsCryptoSchemeNotSupported() throws Exception {
+        assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+    }
+
+    public void testPssh() throws Exception {
+        assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                CENC_CLEARKEY_VIDEO_URL.toString()));
+    }
+
+    public void testGetPropertyString() throws Exception {
+        StringBuffer value = new StringBuffer();
+        testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+        assertEquals("ClearKey CDM", value.toString());
+    }
+
+    public void testUnknownPropertyString() throws Exception {
+        try {
+            StringBuffer value = new StringBuffer();
+            testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                    "unknown-property", value);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+            assertThat(e.getMessage(), containsString("get property string returns"));
+        }
+    }
+
+    /**
+     * Tests native clear key system playback.
+     */
+    private void testClearKeyPlayback(
+            String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+            int videoWidth, int videoHeight) throws Exception {
+
+        if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+            throw new Error("Crypto scheme is not supported.");
+        }
+
+        IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+        if (!connectionStatus.isAvailable()) {
+            throw new Error("Network is not available, reason: " +
+                    connectionStatus.getNotConnectedReason());
+        }
+
+        // If device is not online, recheck the status a few times.
+        int retries = 0;
+        while (!connectionStatus.isConnected()) {
+            if (retries++ >= CONNECTION_RETRIES) {
+                throw new Error("Device is not online, reason: " +
+                        connectionStatus.getNotConnectedReason());
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+        connectionStatus.testConnection(videoUrl);
+
+        if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.getPath())) {
+            Log.i(TAG, "Device does not support " +
+                  videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+            return;  // skip
+        }
+
+        PlaybackParams params = new PlaybackParams();
+        params.surface = mActivity.getSurfaceHolder().getSurface();
+        params.mimeType = mimeType;
+        params.audioUrl = audioUrl.toString();
+        params.videoUrl = videoUrl.toString();
+
+        if (!testClearKeyPlaybackNative(
+            uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+            Log.e(TAG, "Fails play back using native media drm APIs.");
+        }
+        params.surface.release();
+    }
+
+    private ArrayList<Integer> intVersion(String version) {
+        String versions[] = version.split("\\.");
+
+        ArrayList<Integer> versionNumbers = Lists.newArrayList();
+        for (String subVersion : versions) {
+            versionNumbers.add(Integer.parseInt(subVersion));
+        }
+        return versionNumbers;
+    }
+
+    private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+    private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+            PlaybackParams params);
+
+    private static native boolean testGetPropertyStringNative(final byte[] uuid,
+            final String name, StringBuffer value);
+
+    private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+    public void testClearKeyPlaybackCenc() throws Exception {
+        testClearKeyPlayback(
+                ISO_BMFF_VIDEO_MIME_TYPE,
+                CENC_AUDIO_URL,
+                CENC_CLEARKEY_VIDEO_URL,
+                VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 5b989cc..e22d8a7 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -332,7 +332,8 @@
         }
     }
 
-    public void testBuffering() throws Throwable {
+    // TODO: unhide this test when we sort out how to expose buffering control API.
+    private void doTestBuffering() throws Throwable {
         final String name = "ringer.mp3";
         mServer = new CtsTestServer(mContext);
         try {
diff --git a/tests/tests/media/src/android/media/cts/VolumeShaperTest.java b/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
index 8206ecb..fce8c80 100644
--- a/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
+++ b/tests/tests/media/src/android/media/cts/VolumeShaperTest.java
@@ -45,7 +45,7 @@
                 .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                 .setCurve(new float[] { 0.f, 1.f } /* times */,
                         new float[] { 0.f, 0.f } /* volumes */)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     // Duck configurations go from 1.f down to 0.2f (not full ramp down).
@@ -54,28 +54,28 @@
                 .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
                 .setCurve(new float[] { 0.f, 1.f } /* times */,
                         new float[] { 1.f, 0.2f } /* volumes */)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     // Ramp configurations go from 0.f up to 1.f
     private static final VolumeShaper.Configuration LINEAR_RAMP =
             new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.LINEAR_RAMP)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     private static final VolumeShaper.Configuration CUBIC_RAMP =
             new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.CUBIC_RAMP)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     private static final VolumeShaper.Configuration SINE_RAMP =
             new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.SINE_RAMP)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     private static final VolumeShaper.Configuration SCURVE_RAMP =
             new VolumeShaper.Configuration.Builder(VolumeShaper.Configuration.SCURVE_RAMP)
-            .setDurationMs((double)RAMP_TIME_MS)
+            .setDurationMillis((double)RAMP_TIME_MS)
             .build();
 
     // internal use only
@@ -85,7 +85,7 @@
                 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_VOLUME_IN_DBFS)
                 .setCurve(new float[] { 0.f, 1.f } /* times */,
                         new float[] { -80.f, 0.f } /* volumes */)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     private static final VolumeShaper.Configuration[] ALL_STANDARD_RAMPS = {
@@ -101,7 +101,7 @@
                 .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC_MONOTONIC)
                 .setCurve(new float[] { 0.f, 0.3f, 0.7f, 1.f } /* times */,
                         new float[] { 0.f, 0.5f, 0.5f, 1.f } /* volumes */)
-                .setDurationMs((double)RAMP_TIME_MS)
+                .setDurationMillis((double)RAMP_TIME_MS)
                 .build();
 
     private static final VolumeShaper.Configuration MONOTONIC_TEST_FAIL =
@@ -266,7 +266,7 @@
             final VolumeShaper.Configuration config =
                     new VolumeShaper.Configuration.Builder()
                     .setCurve(ohOne, ohOne)
-                    .setDurationMs(-1.)
+                    .setDurationMillis(-1.)
                     .build();
             fail(TEST_NAME + " configuration builder should fail on invalid duration");
         } catch (IllegalArgumentException e) {
@@ -291,7 +291,7 @@
         assertEquals(TEST_NAME + " default interpolation should be cubic",
                 VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC, config.getInterpolatorType());
         assertEquals(TEST_NAME + " default duration should be 1000 ms",
-                1000., config.getDurationMs());
+                1000., config.getDurationMillis());
         assertTrue(TEST_NAME + " times should be { 0.f, 1.f }",
                 Arrays.equals(ohOne, config.getTimes()));
         assertTrue(TEST_NAME + " volumes should be { 0.f, 1.f }",
@@ -319,7 +319,7 @@
             checkEqual(TEST_NAME, testRamp, ramp);
 
             ramp = new VolumeShaper.Configuration.Builder(testRamp)
-                    .setDurationMs(10)
+                    .setDurationMillis(10)
                     .build();
             checkNotEqual(TEST_NAME, testRamp, ramp);
 
@@ -563,7 +563,7 @@
                 // we join several LINEAR_RAMPS together - this should effectively
                 // be one long LINEAR_RAMP.
                 volumeShaper.replace(new VolumeShaper.Configuration.Builder(LINEAR_RAMP)
-                                        .setDurationMs((double)(duration - i))
+                                        .setDurationMillis((double)(duration - i))
                                         .build(),
                                 VolumeShaper.Operation.PLAY, true /* join */);
                 assertEquals(TEST_NAME + " linear ramp should continue on join",
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index ac7d703..0318610 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -787,86 +787,6 @@
                 insecure.isEmpty());
     }
 
-    private static final Set<File> CHAR_DEV_EXCEPTIONS = new HashSet<File>(
-            Arrays.asList(
-                // All exceptions should be alphabetical and associated with a bug number.
-                new File("/dev/adsprpc-smd"), // b/11710243
-                new File("/dev/alarm"),      // b/9035217
-                new File("/dev/ashmem"),
-                new File("/dev/binder"),
-                new File("/dev/card0"),       // b/13159510
-                new File("/dev/renderD128"),
-                new File("/dev/renderD129"),  // b/23798677
-                new File("/dev/dri/card0"),   // b/13159510
-                new File("/dev/dri/renderD128"),
-                new File("/dev/dri/renderD129"), // b/23798677
-                new File("/dev/felica"),     // b/11142586
-                new File("/dev/felica_ant"), // b/11142586
-                new File("/dev/felica_cen"), // b/11142586
-                new File("/dev/felica_pon"), // b/11142586
-                new File("/dev/felica_rfs"), // b/11142586
-                new File("/dev/felica_rws"), // b/11142586
-                new File("/dev/felica_uicc"), // b/11142586
-                new File("/dev/full"),
-                new File("/dev/galcore"),
-                new File("/dev/genlock"),    // b/9035217
-                new File("/dev/graphics/galcore"),
-                new File("/dev/ion"),
-                new File("/dev/kgsl-2d0"),   // b/11271533
-                new File("/dev/kgsl-2d1"),   // b/11271533
-                new File("/dev/kgsl-3d0"),   // b/9035217
-                new File("/dev/log/events"), // b/9035217
-                new File("/dev/log/main"),   // b/9035217
-                new File("/dev/log/radio"),  // b/9035217
-                new File("/dev/log/system"), // b/9035217
-                new File("/dev/mali0"),       // b/9106968
-                new File("/dev/mali"),        // b/11142586
-                new File("/dev/mm_interlock"), // b/12955573
-                new File("/dev/mm_isp"),      // b/12955573
-                new File("/dev/mm_v3d"),      // b/12955573
-                new File("/dev/msm_rotator"), // b/9035217
-                new File("/dev/null"),
-                new File("/dev/nvhost-as-gpu"),
-                new File("/dev/nvhost-ctrl"), // b/9088251
-                new File("/dev/nvhost-ctrl-gpu"),
-                new File("/dev/nvhost-dbg-gpu"),
-                new File("/dev/nvhost-gpu"),
-                new File("/dev/nvhost-gr2d"), // b/9088251
-                new File("/dev/nvhost-gr3d"), // b/9088251
-                new File("/dev/nvhost-tsec"),
-                new File("/dev/nvhost-prof-gpu"),
-                new File("/dev/nvhost-vic"),
-                new File("/dev/nvmap"),       // b/9088251
-                new File("/dev/pmsg0"),       // b/31857082
-                new File("/dev/ptmx"),        // b/9088251
-                new File("/dev/pvrsrvkm"),    // b/9108170
-                new File("/dev/pvr_sync"),
-                new File("/dev/quadd"),
-                new File("/dev/random"),
-                new File("/dev/snfc_cen"),    // b/11142586
-                new File("/dev/snfc_hsel"),   // b/11142586
-                new File("/dev/snfc_intu_poll"), // b/11142586
-                new File("/dev/snfc_rfs"),    // b/11142586
-                new File("/dev/tegra-throughput"),
-                new File("/dev/tiler"),       // b/9108170
-                new File("/dev/tty"),
-                new File("/dev/urandom"),
-                new File("/dev/ump"),         // b/11142586
-                new File("/dev/xt_qtaguid"),  // b/9088251
-                new File("/dev/zero"),
-                new File("/dev/fimg2d"),      // b/10428016
-                new File("/dev/mobicore-user") // b/10428016
-            ));
-
-    public void testAllCharacterDevicesAreSecure() throws Exception {
-        Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"), FileUtils.S_IFCHR);
-        Set<File> insecurePts = getAllInsecureDevicesInDirAndSubdir(new File("/dev/pts"), FileUtils.S_IFCHR);
-        insecure.removeAll(CHAR_DEV_EXCEPTIONS);
-        insecure.removeAll(insecurePts);
-        assertTrue("Found insecure character devices: " + insecure.toString(),
-                insecure.isEmpty());
-    }
-
     public void testDevRandomWorldReadableAndWritable() throws Exception {
         File f = new File("/dev/random");
 
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index 54c2371..0197f5d 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -242,7 +242,11 @@
             getContext().sendBroadcast(intent);
             fail("SecurityException expected!");
         } catch (SecurityException e) {}
-
+        try {
+            Intent intent = new Intent("android.provider.Telephony.SECRET_CODE");
+            getContext().sendBroadcast(intent);
+            fail("SecurityException expected!");
+        } catch (SecurityException e) {}
     }
 
     /**
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index 198fe9f..eb394b3 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -185,9 +185,6 @@
         Log.d(LOG_TAG, "setUpClass()");
 
         sInstrumentation = InstrumentationRegistry.getInstrumentation();
-        assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_PRINTING));
-
         sUiDevice = UiDevice.getInstance(sInstrumentation);
 
         // Make sure we start with a clean slate.
@@ -240,6 +237,10 @@
     public void setUp() throws Exception {
         Log.d(LOG_TAG, "setUp()");
 
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_PRINTING));
+
         // Initialize the latches.
         Log.d(LOG_TAG, "init counters");
         mCancelOperationCounter = new CallCounter();
@@ -289,16 +290,16 @@
         clearPrintSpoolerData();
 
         Log.d(LOG_TAG, "enable animations");
-        if (sWindowAnimationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sWindowAnimationScaleBefore)) {
             SystemUtil.runShellCommand(sInstrumentation,
                     "settings put global window_animation_scale " + sWindowAnimationScaleBefore);
         }
-        if (sTransitionAnimationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sTransitionAnimationScaleBefore)) {
             SystemUtil.runShellCommand(sInstrumentation,
                     "settings put global transition_animation_scale " +
                             sTransitionAnimationScaleBefore);
         }
-        if (sAnimatiorDurationScaleBefore != Float.NaN) {
+        if (!Float.isNaN(sAnimatiorDurationScaleBefore)) {
             SystemUtil.runShellCommand(sInstrumentation,
                     "settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
         }
diff --git a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
index 3b924a8..e431a6c 100644
--- a/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
+++ b/tests/tests/security/src/android/security/cts/STKFrameworkTest.java
@@ -48,7 +48,7 @@
         }
 
         Intent intent = new Intent();
-        intent.setAction("android.intent.action.stk.command");
+        intent.setAction("com.android.internal.stk.command");
         intent.putExtra("STK CMD", "test");
         ComponentName cn =
                 ComponentName.unflattenFromString("com.android.stk/com.android.stk.StkCmdReceiver");
@@ -56,11 +56,11 @@
         try {
             mContext.sendBroadcast(intent);
             fail("Able to send broadcast which can be received by any app which has registered " +
-                    "broadcast for action 'android.intent.action.stk.command' since it is not " +
+                    "broadcast for action 'com.android.internal.stk.command' since it is not " +
                     "protected with any permission. Device is vulnerable to CVE-2015-3843.");
         } catch (SecurityException e) {
             /* Pass the Test case: App should not be able to send broadcast using action
-             * 'android.intent.action.stk.command' as it is protected by permission in
+             * 'com.android.internal.stk.command' as it is protected by permission in
              * patched devices
              */
         }
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
index 5ace9ff..ee1f901 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 
 import java.util.ArrayList;
@@ -68,6 +69,23 @@
     }
 
     /**
+     * Tests {@link TelecomManager#getSelfManagedPhoneAccounts()} API to ensure it returns a list of
+     * the registered self-managed {@link android.telecom.PhoneAccount}s.
+     */
+    public void testTelecomManagerGetSelfManagedPhoneAccounts() {
+        if (!mShouldTestTelecom) {
+            return;
+        }
+
+        List<PhoneAccountHandle> phoneAccountHandles =
+                mTelecomManager.getSelfManagedPhoneAccounts();
+
+        assertTrue(phoneAccountHandles.contains(TestUtils.TEST_SELF_MANAGED_HANDLE_1));
+        assertTrue(phoneAccountHandles.contains(TestUtils.TEST_SELF_MANAGED_HANDLE_2));
+        assertFalse(phoneAccountHandles.contains(TestUtils.TEST_PHONE_ACCOUNT_HANDLE));
+    }
+
+    /**
      * Tests the ability to successfully register a self-managed
      * {@link android.telecom.PhoneAccount}.
      */
@@ -348,14 +366,19 @@
         waitOnAllHandlers(getInstrumentation());
     }
 
-    public void testEmergencyCallOngoing() throws Exception {
+    /**
+     * Disabled for now; there is not a reliable means of setting a phone number as a test emergency
+     * number.
+     * @throws Exception
+     */
+    public void DONOTtestEmergencyCallOngoing() throws Exception {
         if (!mShouldTestTelecom) {
             return;
         }
 
+        // TODO: Need to find a reliable way to set a test emergency number.
         // Set 555-1212 as a test emergency number.
-        TestUtils.executeShellCommand(getInstrumentation(),
-                "setprop ril.ecclist 5551212");
+        TestUtils.executeShellCommand(getInstrumentation(), "setprop ril.ecclist 5551212");
 
         Bundle extras = new Bundle();
         extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
index 8ca7598..dfabe71 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderSpanTest.java
@@ -23,11 +23,15 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.Html;
+import android.text.Layout;
 import android.text.SpanWatcher;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.text.method.SingleLineTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.text.style.AlignmentSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
 
@@ -583,4 +587,27 @@
                 ParagraphStyle.class);
         assertEquals(0, paragraphSpans.length);
     }
+
+    @Test
+    public void testCopyConstructorDoesNotEnforceParagraphStyleConstraint() {
+        final SpannableStringBuilder original = new SpannableStringBuilder("\ntest data\nb");
+        final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+                Layout.Alignment.ALIGN_NORMAL);
+        original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+        // test that paragraph style is in the copied when it is valid
+        SpannableStringBuilder copied = new SpannableStringBuilder(original);
+        AlignmentSpan.Standard[] copiedSpans = copied.getSpans(0, copied.length(),
+                AlignmentSpan.Standard.class);
+
+        assertEquals(1, copiedSpans.length);
+
+        // test that paragraph style is in not in the copied when it is invalid
+        final TransformationMethod transformation = SingleLineTransformationMethod.getInstance();
+        final CharSequence transformed = transformation.getTransformation(original, null);
+        copied = new SpannableStringBuilder(transformed);
+        copiedSpans = copied.getSpans(0, copied.length(), AlignmentSpan.Standard.class);
+
+        assertEquals(0, copiedSpans.length);
+    }
 }
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringTest.java b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
index 81dbf9b..3771613 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringTest.java
@@ -16,19 +16,34 @@
 
 package android.text.cts;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.fail;
+
 import android.support.test.filters.SmallTest;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
 import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
 import android.text.Spanned;
+import android.text.method.SingleLineTransformationMethod;
+import android.text.method.TransformationMethod;
+import android.text.style.AlignmentSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.QuoteSpan;
 import android.text.style.UnderlineSpan;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.Locale;
 
-public class SpannableStringTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpannableStringTest {
 
-    @SmallTest
+    @Test
     public void testConstructor() {
         new SpannableString("test");
 
@@ -39,7 +54,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testValueOf() {
         String text = "test valueOf";
         SpannableString spannable = SpannableString.valueOf(text);
@@ -56,7 +71,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testSetSpan() {
         String text = "hello, world";
         SpannableString spannable = new SpannableString(text);
@@ -87,7 +102,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testRemoveSpan() {
         SpannableString spannable = new SpannableString("hello, world");
 
@@ -111,7 +126,7 @@
         assertEquals(0, spannable.getSpanFlags(underlineSpan));
     }
 
-    @SmallTest
+    @Test
     public void testSubSequence() {
         String text = "hello, world";
         SpannableString spannable = new SpannableString(text);
@@ -135,7 +150,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testSubsequence_copiesSpans() {
         SpannableString first = new SpannableString("t\nest data");
         QuoteSpan quoteSpan = new QuoteSpan();
@@ -165,8 +180,7 @@
         }
     }
 
-
-    @SmallTest
+    @Test
     public void testCopyConstructor_copiesAllSpans() {
         SpannableString first = new SpannableString("t\nest data");
         first.setSpan(new QuoteSpan(), 0, 2, Spanned.SPAN_PARAGRAPH);
@@ -189,7 +203,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testCopyGrowable() {
         SpannableString first = new SpannableString("t\nest data");
         final int N_SPANS = 127;
@@ -201,4 +215,27 @@
         Object[] secondSpans = second.getSpans(0, second.length(), Object.class);
         assertEquals(secondSpans.length, N_SPANS + 1);
     }
+
+    @Test
+    public void testCopyConstructorDoesNotEnforceParagraphStyleConstraint() {
+        final SpannableStringBuilder original = new SpannableStringBuilder("\ntest data\nb");
+        final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+                Layout.Alignment.ALIGN_NORMAL);
+        original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+        // test that paragraph style is in the copied when it is valid
+        SpannableString copied = new SpannableString(original);
+        AlignmentSpan.Standard[] copiedSpans = copied.getSpans(0, copied.length(),
+                AlignmentSpan.Standard.class);
+
+        assertEquals(1, copiedSpans.length);
+
+        // test that paragraph style is in not in the copied when it is invalid
+        final TransformationMethod transformation = SingleLineTransformationMethod.getInstance();
+        final CharSequence transformed = transformation.getTransformation(original, null);
+        copied = new SpannableString(transformed);
+        copiedSpans = copied.getSpans(0, copied.length(), AlignmentSpan.Standard.class);
+
+        assertEquals(0, copiedSpans.length);
+    }
 }
diff --git a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
index d8342ca..16f632c 100644
--- a/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateKeyListenerTest.java
@@ -111,16 +111,26 @@
 
     @Test
     public void testGetInputType() {
-        final int expected = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE;
+        // The "normal" input type that has been used consistently until Android O.
+        final int dateTimeType = InputType.TYPE_CLASS_DATETIME
+                | InputType.TYPE_DATETIME_VARIATION_DATE;
+        // Fallback for locales that need more characters.
+        final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
 
+        // Deprecated constructor that needs to preserve pre-existing behavior.
         DateKeyListener dateKeyListener = new DateKeyListener();
-        assertEquals(expected, dateKeyListener.getInputType());
+        assertEquals(dateTimeType, dateKeyListener.getInputType());
 
+        // TYPE_CLASS_DATETIME is fine for English locales.
         dateKeyListener = new DateKeyListener(Locale.US);
-        assertEquals(expected, dateKeyListener.getInputType());
+        assertEquals(dateTimeType, dateKeyListener.getInputType());
+        dateKeyListener = new DateKeyListener(Locale.UK);
+        assertEquals(dateTimeType, dateKeyListener.getInputType());
 
+        // Persian needs more characters then typically provided by datetime inputs, so it falls
+        // back on normal text.
         dateKeyListener = new DateKeyListener(Locale.forLanguageTag("fa-IR"));
-        assertEquals(expected, dateKeyListener.getInputType());
+        assertEquals(textType, dateKeyListener.getInputType());
     }
 
     /*
diff --git a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
index 06bb68c..decd451 100644
--- a/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DateTimeKeyListenerTest.java
@@ -114,17 +114,26 @@
 
     @Test
     public void testGetInputType() {
-        final int expected = InputType.TYPE_CLASS_DATETIME
+        // The "normal" input type that has been used consistently until Android O.
+        final int dateTimeType = InputType.TYPE_CLASS_DATETIME
                 | InputType.TYPE_DATETIME_VARIATION_NORMAL;
+        // Fallback for locales that need more characters.
+        final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
 
+        // Deprecated constructor that needs to preserve pre-existing behavior.
         DateTimeKeyListener listener = DateTimeKeyListener.getInstance();
-        assertEquals(expected, listener.getInputType());
+        assertEquals(dateTimeType, listener.getInputType());
 
+        // TYPE_CLASS_DATETIME is fine for English locales.
         listener = DateTimeKeyListener.getInstance(Locale.US);
-        assertEquals(expected, listener.getInputType());
+        assertEquals(dateTimeType, listener.getInputType());
+        listener = DateTimeKeyListener.getInstance(Locale.UK);
+        assertEquals(dateTimeType, listener.getInputType());
 
+        // Persian needs more characters then typically provided by datetime inputs, so it falls
+        // back on normal text.
         listener = DateTimeKeyListener.getInstance(Locale.forLanguageTag("fa-IR"));
-        assertEquals(expected, listener.getInputType());
+        assertEquals(textType, listener.getInputType());
     }
 
     /*
diff --git a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
index 510d329..7854268 100644
--- a/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/DigitsKeyListenerTest.java
@@ -899,8 +899,9 @@
         assertEquals(-1, acceptedChars.indexOf(usDecimalSeparator));
     }
 
+    // Deprecated constructors that need to preserve pre-existing behavior.
     @Test
-    public void testGetInputType1() {
+    public void testGetInputType_deprecatedConstructors() {
         DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(false, false);
         int expected = InputType.TYPE_CLASS_NUMBER;
         assertEquals(expected, digitsKeyListener.getInputType());
@@ -922,27 +923,57 @@
         assertEquals(expected, digitsKeyListener.getInputType());
     }
 
+    // Deprecated constructors that need to preserve pre-existing behavior.
     @Test
-    public void testGetInputType2() {
-        final Locale irLocale = Locale.forLanguageTag("fa-IR");
-        DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, false);
+    public void testGetInputType_English() {
         int expected = InputType.TYPE_CLASS_NUMBER;
+        DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(
+                Locale.US, false, false);
+        assertEquals(expected, digitsKeyListener.getInputType());
+        digitsKeyListener = DigitsKeyListener.getInstance(
+                Locale.UK, false, false);
         assertEquals(expected, digitsKeyListener.getInputType());
 
-        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, false);
         expected = InputType.TYPE_CLASS_NUMBER
                 | InputType.TYPE_NUMBER_FLAG_SIGNED;
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, true, false);
+        assertEquals(expected, digitsKeyListener.getInputType());
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, true, false);
         assertEquals(expected, digitsKeyListener.getInputType());
 
-        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, true);
         expected = InputType.TYPE_CLASS_NUMBER
                 | InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, false, true);
+        assertEquals(expected, digitsKeyListener.getInputType());
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, false, true);
         assertEquals(expected, digitsKeyListener.getInputType());
 
-        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, true);
         expected = InputType.TYPE_CLASS_NUMBER
                 | InputType.TYPE_NUMBER_FLAG_SIGNED
                 | InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.US, true, true);
+        assertEquals(expected, digitsKeyListener.getInputType());
+        digitsKeyListener = DigitsKeyListener.getInstance(Locale.UK, true, true);
+        assertEquals(expected, digitsKeyListener.getInputType());
+    }
+
+    // Persian needs more characters then typically provided by datetime inputs, so it falls
+    // back on normal text.
+    @Test
+    public void testGetInputType_Persian() {
+        final Locale irLocale = Locale.forLanguageTag("fa-IR");
+        final int expected = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
+
+        DigitsKeyListener digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, false);
+        assertEquals(expected, digitsKeyListener.getInputType());
+
+        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, false);
+        assertEquals(expected, digitsKeyListener.getInputType());
+
+        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, false, true);
+        assertEquals(expected, digitsKeyListener.getInputType());
+
+        digitsKeyListener = DigitsKeyListener.getInstance(irLocale, true, true);
         assertEquals(expected, digitsKeyListener.getInputType());
     }
 
diff --git a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
index 3ed0ad4..bb4ffb6 100644
--- a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
@@ -17,13 +17,11 @@
 package android.text.method.cts;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.reset;
@@ -221,23 +219,6 @@
         assertSame(method0, method1);
     }
 
-    @Test
-    public void testOnFocusChanged() {
-        // lose focus
-        reset(mMethod);
-        assertTrue(mEditText.isFocused());
-        CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_DOWN");
-        assertFalse(mEditText.isFocused());
-        verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
-
-        // gain focus
-        reset(mMethod);
-        assertFalse(mEditText.isFocused());
-        CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "DPAD_UP");
-        assertTrue(mEditText.isFocused());
-        verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
-    }
-
     private void savePasswordPref() {
         try {
             mPasswordPrefBackUp = System.getInt(mActivity.getContentResolver(),
diff --git a/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
index 5db4a6f..ad622d0 100644
--- a/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/SingleLineTransformationMethodTest.java
@@ -25,7 +25,11 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.Spanned;
 import android.text.method.SingleLineTransformationMethod;
+import android.text.style.AlignmentSpan;
 import android.util.TypedValue;
 import android.widget.EditText;
 
@@ -72,6 +76,21 @@
         // TODO cannot get transformed text from the view
     }
 
+    @Test
+    public void testSubsequence_doesNotThrowExceptionWithParagraphSpans() {
+        final SingleLineTransformationMethod method = SingleLineTransformationMethod.getInstance();
+        final SpannableString original = new SpannableString("\ntest data\nb");
+        final AlignmentSpan.Standard span = new AlignmentSpan.Standard(
+                Layout.Alignment.ALIGN_NORMAL);
+        original.setSpan(span, 1, original.length() - 1, Spanned.SPAN_PARAGRAPH);
+
+        final CharSequence transformed = method.getTransformation(original, null);
+        // expectation: should not throw an exception
+        transformed.subSequence(0, transformed.length());
+    }
+
+
+
     private static class MySingleLineTranformationMethod extends SingleLineTransformationMethod {
         @Override
         protected char[] getOriginal() {
diff --git a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
index 68d410d..a887a19 100644
--- a/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
+++ b/tests/tests/text/src/android/text/method/cts/TimeKeyListenerTest.java
@@ -110,17 +110,26 @@
 
     @Test
     public void testGetInputType() {
-        final int expected = InputType.TYPE_CLASS_DATETIME
+        // The "normal" input type that has been used consistently until Android O.
+        final int dateTimeType = InputType.TYPE_CLASS_DATETIME
                 | InputType.TYPE_DATETIME_VARIATION_TIME;
+        // Fallback for locales that need more characters.
+        final int textType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
 
+        // Deprecated constructor that needs to preserve pre-existing behavior.
         TimeKeyListener listener = TimeKeyListener.getInstance();
-        assertEquals(expected, listener.getInputType());
+        assertEquals(dateTimeType, listener.getInputType());
 
+        // TYPE_CLASS_DATETIME is fine for English locales.
         listener = TimeKeyListener.getInstance(Locale.US);
-        assertEquals(expected, listener.getInputType());
+        assertEquals(dateTimeType, listener.getInputType());
+        listener = TimeKeyListener.getInstance(Locale.UK);
+        assertEquals(dateTimeType, listener.getInputType());
 
+        // Persian needs more characters then typically provided by datetime inputs, so it falls
+        // back on normal text.
         listener = TimeKeyListener.getInstance(Locale.forLanguageTag("fa-IR"));
-        assertEquals(expected, listener.getInputType());
+        assertEquals(textType, listener.getInputType());
     }
 
     /*
diff --git a/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java
new file mode 100644
index 0000000..d67fc59
--- /dev/null
+++ b/tests/tests/text/src/android/text/method/cts/TransformationMethodTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 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.text.method.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.method.TransformationMethod;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test that {@link TransformationMethod} interface gets called.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class TransformationMethodTest {
+    private static final int EDIT_TXT_ID = 1;
+
+    private Instrumentation mInstrumentation;
+    private CtsActivity mActivity;
+    private TransformationMethod mMethod;
+    private EditText mEditText;
+    private Button mButton;
+
+    @Rule
+    public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class);
+
+    @Before
+    public void setup() throws Throwable {
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(1000, mActivity::hasWindowFocus);
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mMethod = mock(TransformationMethod.class);
+        when(mMethod.getTransformation(any(), any())).then(returnsFirstArg());
+
+        mActivityRule.runOnUiThread(() -> {
+            mEditText = new EditTextNoIme(mActivity);
+            mEditText.setId(EDIT_TXT_ID);
+            mEditText.setTransformationMethod(mMethod);
+            mButton = new Button(mActivity);
+            mButton.setFocusableInTouchMode(true);
+            LinearLayout layout = new LinearLayout(mActivity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+            layout.addView(mEditText, new LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT));
+            layout.addView(mButton, new LayoutParams(LayoutParams.MATCH_PARENT,
+                    LayoutParams.WRAP_CONTENT));
+            mActivity.setContentView(layout);
+            mEditText.requestFocus();
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(mEditText.isFocused());
+    }
+
+    @Test
+    public void testGetTransformation() throws Throwable {
+        reset(mMethod);
+        when(mMethod.getTransformation(any(), any())).then(returnsFirstArg());
+        mActivityRule.runOnUiThread(() -> mEditText.setText("some text"));
+        mInstrumentation.waitForIdleSync();
+        verify(mMethod, atLeastOnce()).getTransformation(any(), any());
+    }
+
+    @Test
+    public void testOnFocusChanged() throws Throwable {
+        // lose focus
+        reset(mMethod);
+        assertTrue(mEditText.isFocused());
+        mActivityRule.runOnUiThread(() -> mButton.requestFocus());
+        mInstrumentation.waitForIdleSync();
+        verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
+
+        // gain focus
+        reset(mMethod);
+        assertFalse(mEditText.isFocused());
+        mActivityRule.runOnUiThread(() -> mEditText.requestFocus());
+        mInstrumentation.waitForIdleSync();
+        assertTrue(mEditText.isFocused());
+        verify(mMethod, atLeastOnce()).onFocusChanged(any(), any(), anyBoolean(), anyInt(), any());
+    }
+}
diff --git a/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java b/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
index 0a14361..4b55057 100644
--- a/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
+++ b/tests/tests/transition/src/android/transition/cts/ChangeBoundsTest.java
@@ -17,19 +17,24 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
+import android.animation.Animator;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.transition.ChangeBounds;
 import android.transition.Transition;
+import android.transition.TransitionValues;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.LinearInterpolator;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,19 +48,22 @@
     private static final int SMALL_OFFSET_DP = 2;
 
     ChangeBounds mChangeBounds;
+    ValidateBoundsListener mBoundsChangeListener;
 
     @Override
     @Before
     public void setup() {
         super.setup();
         resetChangeBoundsTransition();
+        mBoundsChangeListener = null;
     }
 
     private void resetChangeBoundsTransition() {
         mListener = mock(Transition.TransitionListener.class);
-        mChangeBounds = new ChangeBounds();
+        mChangeBounds = new MyChangeBounds();
         mChangeBounds.setDuration(400);
         mChangeBounds.addListener(mListener);
+        mChangeBounds.setInterpolator(new LinearInterpolator());
         mTransition = mChangeBounds;
     }
 
@@ -65,11 +73,10 @@
 
         validateInScene1();
 
-        startTransition(R.layout.scene6);
+        mBoundsChangeListener = new ValidateBoundsListener(true);
 
-        // now delay for at least a few frames before checking intermediate values:
-        Thread.sleep(150);
-        validateNormalIntermediate();
+        startTransition(R.layout.scene6);
+        // The update listener will validate that it is changing throughout the animation
         waitForEnd(400);
 
         validateInScene6();
@@ -84,11 +91,11 @@
 
         validateInScene1();
 
+        mBoundsChangeListener = new ValidateBoundsListener(true);
+
         startTransition(R.layout.scene6);
 
-        // now delay for at least a few frames before checking intermediate values:
-        Thread.sleep(150);
-        validateClippedIntermediate();
+        // The update listener will validate that it is changing throughout the animation
         waitForEnd(400);
 
         validateInScene6();
@@ -101,11 +108,10 @@
 
         validateInScene6();
 
+        mBoundsChangeListener = new ValidateBoundsListener(false);
         startTransition(R.layout.scene1);
 
-        // now delay for at least a few frames before checking intermediate values:
-        Thread.sleep(150);
-        validateClippedIntermediate();
+        // The update listener will validate that it is changing throughout the animation
         waitForEnd(400);
 
         validateInScene1();
@@ -252,63 +258,6 @@
         assertNull(belowSquare.getClipBounds());
     }
 
-    private void validateIntermediatePosition() {
-        Resources resources = mActivity.getResources();
-        float smallDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-        float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-
-        View redSquare = mActivity.findViewById(R.id.redSquare);
-        View greenSquare = mActivity.findViewById(R.id.greenSquare);
-        assertTrue(redSquare.getTop() != 0);
-        assertTrue(greenSquare.getTop() != 0);
-        assertNotWithinAPixel(smallDim, redSquare.getTop());
-        assertNotWithinAPixel(largeDim, redSquare.getTop());
-        assertNotWithinAPixel(smallDim, greenSquare.getTop());
-        assertNotWithinAPixel(largeDim, greenSquare.getTop());
-    }
-
-    private void validateClippedIntermediate() {
-        validateIntermediatePosition();
-        Resources resources = mActivity.getResources();
-        float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-        View redSquare = mActivity.findViewById(R.id.redSquare);
-        View greenSquare = mActivity.findViewById(R.id.greenSquare);
-
-        assertWithinAPixel(largeDim, redSquare.getWidth());
-        assertWithinAPixel(largeDim, redSquare.getHeight());
-        assertWithinAPixel(largeDim, greenSquare.getWidth());
-        assertWithinAPixel(largeDim, greenSquare.getHeight());
-
-        assertNotNull(redSquare.getClipBounds());
-        assertNotNull(greenSquare.getClipBounds());
-    }
-
-    private void validateNormalIntermediate() {
-        validateIntermediatePosition();
-        Resources resources = mActivity.getResources();
-        float smallDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-        float largeDim = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics());
-        View redSquare = mActivity.findViewById(R.id.redSquare);
-        View greenSquare = mActivity.findViewById(R.id.greenSquare);
-        assertNotWithinAPixel(smallDim, redSquare.getWidth());
-        assertNotWithinAPixel(smallDim, redSquare.getHeight());
-        assertNotWithinAPixel(largeDim, redSquare.getWidth());
-        assertNotWithinAPixel(largeDim, redSquare.getHeight());
-
-        assertNotWithinAPixel(smallDim, greenSquare.getWidth());
-        assertNotWithinAPixel(smallDim, greenSquare.getHeight());
-        assertNotWithinAPixel(largeDim, greenSquare.getWidth());
-        assertNotWithinAPixel(largeDim, greenSquare.getHeight());
-
-        assertNull(redSquare.getClipBounds());
-        assertNull(greenSquare.getClipBounds());
-    }
-
     private static boolean isWithinAPixel(float expectedDim, int dim) {
         return (Math.abs(dim - expectedDim) <= 1);
     }
@@ -322,5 +271,114 @@
         assertTrue("Expected dimension to not be within one pixel of "
                 + expectedDim + ", but was " + dim, !isWithinAPixel(expectedDim, dim));
     }
+
+    private class MyChangeBounds extends ChangeBounds {
+        private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds";
+        @Override
+        public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+                TransitionValues endValues) {
+            Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
+            if (animator != null && mBoundsChangeListener != null) {
+                animator.addListener(mBoundsChangeListener);
+                Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+                Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+            }
+            return animator;
+        }
+    }
+
+    private class ValidateBoundsListener implements ViewTreeObserver.OnDrawListener,
+            Animator.AnimatorListener {
+        final boolean mGrow;
+        final int mMin;
+        final int mMax;
+
+        final Point mRedDimensions = new Point(-1, -1);
+        final Point mGreenDimensions = new Point(-1, -1);
+
+        View mRedSquare;
+        View mGreenSquare;
+
+        boolean mDidChangeSize;
+
+        private ValidateBoundsListener(boolean grow) {
+            mGrow = grow;
+
+            Resources resources = mActivity.getResources();
+            mMin = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    SMALL_SQUARE_SIZE_DP, resources.getDisplayMetrics()));
+            mMax = (int) Math.ceil(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    LARGE_SQUARE_SIZE_DP, resources.getDisplayMetrics()));
+        }
+
+        public void validateView(View view, Point dimensions) {
+            final String name = view.getTransitionName();
+            final boolean clipped = mChangeBounds.getResizeClip();
+            assertEquals(clipped, view.getClipBounds() != null);
+
+            final int width;
+            final int height;
+            if (clipped) {
+                width = view.getClipBounds().width();
+                height = view.getClipBounds().height();
+            } else {
+                width = view.getWidth();
+                height = view.getHeight();
+            }
+            validateDim(name, "width", dimensions.x, width);
+            validateDim(name, "height", dimensions.y, height);
+            dimensions.set(width, height);
+        }
+
+        private void validateDim(String name, String dimen, int lastDim, int newDim) {
+            if (lastDim != -1) {
+                if (mGrow) {
+                    assertTrue(name + " new " + dimen + " " + newDim
+                                    + " is less than previous " + lastDim,
+                            newDim >= lastDim);
+                } else {
+                    assertTrue(name + " new " + dimen + " " + newDim
+                                    + " is more than previous " + lastDim,
+                            newDim <= lastDim);
+                }
+                if (newDim != lastDim) {
+                    mDidChangeSize = true;
+                }
+            }
+            assertTrue(name + " " + dimen + " " + newDim + " must be <= " + mMax,
+                    newDim <= mMax);
+            assertTrue(name + " " + dimen + " " + newDim + " must be >= " + mMin,
+                    newDim >= mMin);
+        }
+
+        @Override
+        public void onDraw() {
+            if (mRedSquare == null) {
+                mRedSquare = mActivity.findViewById(R.id.redSquare);
+                mGreenSquare = mActivity.findViewById(R.id.greenSquare);
+            }
+            validateView(mRedSquare, mRedDimensions);
+            validateView(mGreenSquare, mGreenDimensions);
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mActivity.getWindow().getDecorView().getViewTreeObserver().addOnDrawListener(this);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mActivity.getWindow().getDecorView().getViewTreeObserver().removeOnDrawListener(this);
+            assertTrue(mDidChangeSize);
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+    }
 }
 
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index e42b33f..0b28d05 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -159,6 +159,9 @@
         TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
                 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
         values.put(Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
+        values.put(Programs.COLUMN_REVIEW_RATING_STYLE,
+                RecordedPrograms.REVIEW_RATING_STYLE_STARS);
+        values.put(Programs.COLUMN_REVIEW_RATING, "4.5");
 
         return values;
     }
@@ -256,6 +259,9 @@
         values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, 3);
         values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, 2);
         values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, 1);
+        values.put(RecordedPrograms.COLUMN_REVIEW_RATING_STYLE,
+                RecordedPrograms.REVIEW_RATING_STYLE_STARS);
+        values.put(RecordedPrograms.COLUMN_REVIEW_RATING, "4.5");
 
         return values;
     }
@@ -547,6 +553,8 @@
             verifyStringColumn(cursor, expectedValues, Programs.COLUMN_THUMBNAIL_URI);
             verifyBlobColumn(cursor, expectedValues, Programs.COLUMN_INTERNAL_PROVIDER_DATA);
             verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_VERSION_NUMBER);
+            verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING_STYLE);
+            verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING);
         }
     }
 
@@ -991,6 +999,8 @@
             verifyIntegerColumn(cursor, expectedValues,
                     RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
             verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VERSION_NUMBER);
+            verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING_STYLE);
+            verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING);
         }
     }
 
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
index f66f5f4..c5aa1b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
@@ -81,7 +81,7 @@
                         new Point[] {
                                 point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
                         },
-                        new int[] { 0xff75fb4c, 0xff75fb4c, 0xff75fb4c, 0xffffffff, 0xffba7d26 }
+                        new int[] { 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xffffffff, 0xff7f7f00 }
                 ));
     }
 
@@ -114,7 +114,7 @@
                         new Point[] {
                                 point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
                         },
-                        new int[] { 0xffc07f2c, 0xffc07f2c, 0xffc07f2c, 0xffffffff, 0xffe03f16 }
+                        new int[] { 0xffff7f00, 0xffff7f00, 0xffff7f00, 0xffffffff, 0xffff3f00 }
                 ));
     }
 
diff --git a/tests/tests/view/src/android/view/cts/MenuItemTest.java b/tests/tests/view/src/android/view/cts/MenuItemTest.java
index fc0ae0b..c9d362c 100644
--- a/tests/tests/view/src/android/view/cts/MenuItemTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuItemTest.java
@@ -60,7 +60,7 @@
     }
 
     @Test
-    public void testAccessThumbTint() {
+    public void testAccessIconTint() {
         // Note that this test is not marked as @UiThreadTest. Updating MenuItem does not
         // immediately update the displayed content, and even though the getters are expected
         // to immediately return the just-set value, using instrumentation to wait for the
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 03b8994..ca39aab 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -73,7 +73,6 @@
 import android.util.Xml;
 import android.view.ActionMode;
 import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
@@ -1089,7 +1088,7 @@
         assertTrue(view.hasCalledOnCreateContextMenu());
         assertTrue(mMockParent.hasCreateContextMenu());
         verify(listener, times(1)).onCreateContextMenu(
-                eq(contextMenu), eq(view), any(ContextMenuInfo.class));
+                eq(contextMenu), eq(view), any());
     }
 
     @Test(expected=NullPointerException.class)
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index 70fbd73..93f4846 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -282,6 +282,13 @@
                 android:autoSizeStepGranularity="1px" />
 
             <TextView
+                android:id="@+id/textview_autosize_basic"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/text_view_hello"
+                android:autoSizeTextType="uniform" />
+
+            <TextView
                 android:id="@+id/textview_fontresource_fontfamily"
                 android:text="@string/text_view_hello"
                 android:layout_width="wrap_content"
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index f7fc033..4e33156 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -270,7 +270,7 @@
 
         verifyZeroInteractions(mockClickListener);
         assertTrue(mAdapterView.performItemClick(null, 0, 0));
-        verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(View.class),
+        verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(),
                 eq(0), eq(0L));
 
         setArrayAdapter(mAdapterView);
diff --git a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
index 84c60c8..7595c98 100644
--- a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.database.DataSetObserver;
 import android.support.test.annotation.UiThreadTest;
@@ -411,7 +412,7 @@
         ArrayAdapter.createFromResource(null, R.array.string, R.layout.simple_spinner_item);
     }
 
-    @Test(expected=NullPointerException.class)
+    @Test(expected=Resources.NotFoundException.class)
     public void testCreateFromResourceWithInvalidId() {
         ArrayAdapter.createFromResource(mContext, INVALID_ID, R.layout.simple_spinner_item);
     }
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
index 796073f..1482cae 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
@@ -164,7 +164,7 @@
         mExpandableListView.setOnItemClickListener(mockOnItemClickListener);
         assertTrue(mExpandableListView.performItemClick(null, 100, 99));
         verify(mockOnItemClickListener, times(1)).onItemClick(eq(mExpandableListView),
-                any(View.class), eq(100), eq(99L));
+                any(), eq(100), eq(99L));
     }
 
     @Test
@@ -309,7 +309,7 @@
 
         mExpandableListView.performItemClick(null, 0, 0);
         verify(mockOnGroupClickListener, times(1)).onGroupClick(eq(mExpandableListView),
-                any(View.class), eq(0), eq(0L));
+                any(), eq(0), eq(0L));
     }
 
     @UiThreadTest
@@ -328,7 +328,7 @@
         // click on the child list of the first group
         mExpandableListView.performItemClick(null, 1, 0);
         verify(mockOnChildClickListener, times(1)).onChildClick(eq(mExpandableListView),
-                any(View.class), eq(0), eq(0), eq(0L));
+                any(), eq(0), eq(0), eq(0L));
     }
 
     @UiThreadTest
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index 334a1f6..70f9661 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -504,7 +504,7 @@
         mInstrumentation.waitForIdleSync();
 
         verify(mPopupWindowBuilder.mOnItemClickListener, times(1)).onItemClick(
-                any(AdapterView.class), any(View.class), eq(1), eq(1L));
+                any(AdapterView.class), any(), eq(1), eq(1L));
         // Also verify that the popup window has been dismissed
         assertFalse(mPopupWindow.isShowing());
         verify(mPopupWindowBuilder.mOnDismissListener, times(2)).onDismiss();
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 86f9877..3da2cb4 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -63,6 +63,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.fonts.FontVariationAxis;
+import android.icu.lang.UCharacter;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -114,7 +115,6 @@
 import android.util.TypedValue;
 import android.view.ActionMode;
 import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Gravity;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -3038,7 +3038,7 @@
             ((ContextMenu) invocation.getArguments() [0]).add("menu item");
             return null;
         }).when(mockOnCreateContextMenuListener).onCreateContextMenu(
-                any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
+                any(ContextMenu.class), any(View.class), any());
 
         mTextView.setOnLongClickListener(mockOnLongClickListener);
         mTextView.setOnCreateContextMenuListener(mockOnCreateContextMenuListener);
@@ -3051,16 +3051,16 @@
         assertTrue(mTextView.performLongClick());
         verify(mockOnLongClickListener, times(1)).onLongClick(mTextView);
         verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
-                any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
+                any(ContextMenu.class), eq(mTextView), any());
 
         reset(mockOnCreateContextMenuListener);
         mTextView.setOnLongClickListener(null);
         doNothing().when(mockOnCreateContextMenuListener).onCreateContextMenu(
-                any(ContextMenu.class), any(View.class), any(ContextMenuInfo.class));
+                any(ContextMenu.class), any(View.class), any());
         assertFalse(mTextView.performLongClick());
         verifyNoMoreInteractions(mockOnLongClickListener);
         verify(mockOnCreateContextMenuListener, times(1)).onCreateContextMenu(
-                any(ContextMenu.class), eq(mTextView), any(ContextMenuInfo.class));
+                any(ContextMenu.class), eq(mTextView), any());
     }
 
     @UiThreadTest
@@ -3370,9 +3370,22 @@
         // The default variation settings should be null.
         assertNull(mTextView.getFontVariationSettings());
 
-        final String[] nonEffectiveSettings = {
+        final String[] invalidFormatSettings = {
                 "invalid syntax",
                 "'aaa' 1.0",  // tag is not 4 ascii chars
+        };
+        for (String settings : invalidFormatSettings) {
+            try {
+                mTextView.setFontVariationSettings(settings);
+                fail();
+            } catch (FontVariationAxis.InvalidFormatException e) {
+                // pass.
+            }
+            assertNull("Must not change settings for " + settings,
+                    mTextView.getFontVariationSettings());
+        }
+
+        final String[] nonEffectiveSettings = {
                 "'bbbb' 1.0",  // unsupported tag
                 "'    ' 1.0",  // unsupported tag
                 "'AAAA' 0.7",  // unsupported tag (case sensitive)
@@ -4532,7 +4545,8 @@
         assertEquals(InputType.TYPE_CLASS_NUMBER
                 | InputType.TYPE_NUMBER_FLAG_DECIMAL
                 | InputType.TYPE_NUMBER_FLAG_SIGNED, mTextView.getInputType());
-        assertSame(mTextView.getKeyListener(), DigitsKeyListener.getInstance(true, true));
+        assertSame(mTextView.getKeyListener(),
+                DigitsKeyListener.getInstance(mTextView.getTextLocale(), true, true));
 
         mTextView.setInputType(InputType.TYPE_CLASS_PHONE);
         assertEquals(InputType.TYPE_CLASS_PHONE, mTextView.getInputType());
@@ -5392,28 +5406,75 @@
 
     @UiThreadTest
     @Test
-    public void testAllCapsLocalization() {
-        String testString = "abcdefghijklmnopqrstuvwxyz";
+    public void testAllCaps_Localization() {
+        final String testString = "abcdefghijklmnopqrstuvwxyz i\u0307\u0301 άέήίΐόύΰώάυ ή";
 
-        // The capitalized characters of "i" on Turkish and Azerbaijani are different from English.
-        Locale[] testLocales = {
-            new Locale("az", "AZ"),
-            new Locale("tr", "TR"),
-            new Locale("en", "US"),
+        // Capital "i" in Turkish and Azerbaijani is different from English, Lithuanian has special
+        // rules for uppercasing dotted i with accents, and Greek has complex capitalization rules.
+        final Locale[] testLocales = {
+            new Locale("az", "AZ"),  // Azerbaijani
+            new Locale("tr", "TR"),  // Turkish
+            new Locale("lt", "LT"),  // Lithuanian
+            new Locale("el", "GR"),  // Greek
+            Locale.US,
         };
 
-        TextView tv = new TextView(mActivity);
+        final TextView tv = new TextView(mActivity);
         tv.setAllCaps(true);
         for (Locale locale: testLocales) {
             tv.setTextLocale(locale);
             assertEquals("Locale: " + locale.getDisplayName(),
-                         testString.toUpperCase(locale),
+                         UCharacter.toUpperCase(locale, testString),
                          tv.getTransformationMethod().getTransformation(testString, tv).toString());
         }
     }
 
     @UiThreadTest
     @Test
+    public void testAllCaps_SpansArePreserved() {
+        final Locale greek = new Locale("el", "GR");
+        final String lowerString = "ι\u0301ριδα";  // ίριδα with first letter decomposed
+        final String upperString = "ΙΡΙΔΑ";  // uppercased
+        // expected lowercase to uppercase index map
+        final int[] indexMap = {0, 1, 1, 2, 3, 4, 5};
+        final int flags = Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+
+        final TextView tv = new TextView(mActivity);
+        tv.setTextLocale(greek);
+        tv.setAllCaps(true);
+
+        final Spannable source = new SpannableString(lowerString);
+        source.setSpan(new Object(), 0, 1, flags);
+        source.setSpan(new Object(), 1, 2, flags);
+        source.setSpan(new Object(), 2, 3, flags);
+        source.setSpan(new Object(), 3, 4, flags);
+        source.setSpan(new Object(), 4, 5, flags);
+        source.setSpan(new Object(), 5, 6, flags);
+        source.setSpan(new Object(), 0, 2, flags);
+        source.setSpan(new Object(), 1, 3, flags);
+        source.setSpan(new Object(), 2, 4, flags);
+        source.setSpan(new Object(), 0, 6, flags);
+        final Object[] sourceSpans = source.getSpans(0, source.length(), Object.class);
+
+        final CharSequence transformed =
+                tv.getTransformationMethod().getTransformation(source, tv);
+        assertTrue(transformed instanceof Spanned);
+        final Spanned result = (Spanned) transformed;
+
+        assertEquals(upperString, transformed.toString());
+        final Object[] resultSpans = result.getSpans(0, result.length(), Object.class);
+        assertEquals(sourceSpans.length, resultSpans.length);
+        for (int i = 0; i < sourceSpans.length; i++) {
+            assertSame(sourceSpans[i], resultSpans[i]);
+            final Object span = sourceSpans[i];
+            assertEquals(indexMap[source.getSpanStart(span)], result.getSpanStart(span));
+            assertEquals(indexMap[source.getSpanEnd(span)], result.getSpanEnd(span));
+            assertEquals(source.getSpanFlags(span), result.getSpanFlags(span));
+        }
+    }
+
+    @UiThreadTest
+    @Test
     public void testTextAlignmentDefault() {
         TextView tv = new TextView(mActivity);
         assertEquals(View.TEXT_ALIGNMENT_GRAVITY, tv.getRawTextAlignment());
@@ -6387,6 +6448,7 @@
         final TextUtils.TruncateAt newEllipsizeValue = TextUtils.TruncateAt.END;
         mActivityRule.runOnUiThread(() ->
                 textView.setEllipsize(newEllipsizeValue));
+        mInstrumentation.waitForIdleSync();
         assertEquals(newEllipsizeValue, textView.getEllipsize());
         assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType());
         assertEquals(-1, textView.getAutoSizeMinTextSize());
@@ -6396,6 +6458,7 @@
 
         mActivityRule.runOnUiThread(() ->
                 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM));
+        mInstrumentation.waitForIdleSync();
         assertEquals(newEllipsizeValue, textView.getEllipsize());
         assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, textView.getAutoSizeTextType());
         // The auto-size defaults have been used.
@@ -6713,6 +6776,19 @@
     }
 
     @Test
+    public void testAutoSizeCallers_setHeightForOneLineText() throws Throwable {
+        final TextView autoSizeTextView = (TextView) mActivity.findViewById(
+                R.id.textview_autosize_basic);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM, autoSizeTextView.getAutoSizeTextType());
+        final float initialTextSize = autoSizeTextView.getTextSize();
+        mActivityRule.runOnUiThread(() -> autoSizeTextView.setHeight(
+                autoSizeTextView.getHeight() * 3));
+        mInstrumentation.waitForIdleSync();
+
+        assertTrue(autoSizeTextView.getTextSize() > initialTextSize);
+    }
+
+    @Test
     public void testAutoSizeUniform_obtainStyledAttributes() {
         DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
         TextView autoSizeTextViewUniform = (TextView) mActivity.findViewById(
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 86f2fe3..15249c3 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -121,5 +121,5 @@
     JAR_PATH=${JAR_PATH}:$j
 done
 
-java $RDBG_FLAG -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
+java $RDBG_FLAG -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
 
diff --git a/tools/cts-tradefed/res/config/cts-dev.xml b/tools/cts-tradefed/res/config/cts-dev.xml
index 97c3cdd..4ebda1b 100644
--- a/tools/cts-tradefed/res/config/cts-dev.xml
+++ b/tools/cts-tradefed/res/config/cts-dev.xml
@@ -20,6 +20,7 @@
     <option name="log-level" value="verbose" />
     <option name="skip-preconditions" value="true" />
     <option name="skip-device-info" value="true" />
+    <option name="result-reporter:compress-logs" value="false" />
 
     <option name="plan" value="cts-dev" />
     <option name="compatibility:skip-all-system-status-check" value="true" />
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 4c0d1af..769336d 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -186,4 +186,9 @@
     <option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testGyroscopeHardwareBufferVeryFast" />
     <option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testMagneticFieldHardwareBufferVeryFast" />
 
+    <!-- b/37216168 -->
+    <option name="compatibility:exclude-filter" value="CtsNativeHardwareTestCases AHardwareBufferTest#AHardwareBuffer_SendAndRecv_Succeeds" />
+
+    <!-- b/37108688 -->
+    <option name="compatibility:exclude-filter" value="CtsHardwareTestCases android.hardware.cts.HardwareBufferTest#testCreate" />
 </configuration>
diff --git a/tools/cts-tradefed/res/config/cts-vendor-interface.xml b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
index 5f38264..582cebf 100644
--- a/tools/cts-tradefed/res/config/cts-vendor-interface.xml
+++ b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
@@ -48,6 +48,7 @@
     <option name="compatibility:include-filter" value="CtsPrintTestCases" />
     <option name="compatibility:include-filter" value="CtsProviderTestCases" />
     <option name="compatibility:include-filter" value="CtsRsBlasTestCases" />
+    <option name="compatibility:include-filter" value="CtsSecurityHostTestCases" />
     <option name="compatibility:include-filter" value="CtsSecurityTestCases" />
     <option name="compatibility:include-filter" value="CtsSensorTestCases" />
     <option name="compatibility:include-filter" value="CtsShortcutHostTestCases" />
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 31ee446..7e9c304 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -73,6 +73,10 @@
         devicePolicyFile.deleteOnExit();
         mDevice.pullFile("/sys/fs/selinux/policy", devicePolicyFile);
     }
+
+    private boolean isFullTrebleDevice() throws Exception {
+        return android.security.cts.SELinuxHostTest.isFullTrebleDevice(mDevice);
+    }
 """
 src_body = ""
 src_footer = """}
@@ -82,6 +86,12 @@
     @RestrictedBuildTest
     public void testNeverallowRules() throws Exception {
         String neverallowRule = "$NEVERALLOW_RULE_HERE$";
+        boolean fullTrebleOnly = $FULL_TREBLE_ONLY_BOOL_HERE$;
+
+        if ((fullTrebleOnly) && (!isFullTrebleDevice())) {
+            // This test applies only to Treble devices but this device isn't one
+            return;
+        }
 
         /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
diff --git a/tools/selinux/SELinuxNeverallowTestGen.py b/tools/selinux/SELinuxNeverallowTestGen.py
index bc775d6..e74ba78 100755
--- a/tools/selinux/SELinuxNeverallowTestGen.py
+++ b/tools/selinux/SELinuxNeverallowTestGen.py
@@ -4,35 +4,82 @@
 import sys
 import SELinuxNeverallowTestFrame
 
-usage = "Usage: ./gen_SELinux_CTS_neverallows.py <input policy file> <output cts java source>"
+usage = "Usage: ./SELinuxNeverallowTestGen.py <input policy file> <output cts java source>"
+
+
+class NeverallowRule:
+    statement = ''
+    treble_only = False
+
+    def __init__(self, statement):
+        self.statement = statement
+        self.treble_only = False
+
 
 # extract_neverallow_rules - takes an intermediate policy file and pulls out the
 # neverallow rules by taking all of the non-commented text between the 'neverallow'
 # keyword and a terminating ';'
-# returns: a list of strings representing these rules
+# returns: a list of rules
 def extract_neverallow_rules(policy_file):
     with open(policy_file, 'r') as in_file:
         policy_str = in_file.read()
+
+        # full-Treble only tests are inside sections delimited by BEGIN_TREBLE_ONLY
+        # and END_TREBLE_ONLY comments.
+
+        # uncomment TREBLE_ONLY section delimiter lines
+        remaining = re.sub(
+            r'^\s*#\s*(BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
+            r'\1',
+            policy_str,
+            flags = re.M)
         # remove comments
-        no_comments = re.sub(r'#.+?$', r'', policy_str, flags = re.M)
+        remaining = re.sub(r'#.+?$', r'', remaining, flags = re.M)
         # match neverallow rules
-        return re.findall(r'(^neverallow\s.+?;)', no_comments, flags = re.M |re.S);
+        lines = re.findall(
+            r'^\s*(neverallow\s.+?;|BEGIN_TREBLE_ONLY|END_TREBLE_ONLY)',
+            remaining,
+            flags = re.M |re.S)
+
+        # extract neverallow rules from the remaining lines
+        rules = list()
+        treble_only_depth = 0
+        for line in lines:
+            if line.startswith("BEGIN_TREBLE_ONLY"):
+                treble_only_depth += 1
+                continue
+            elif line.startswith("END_TREBLE_ONLY"):
+                if treble_only_depth < 1:
+                    exit("ERROR: END_TREBLE_ONLY outside of TREBLE_ONLY section")
+                treble_only_depth -= 1
+                continue
+            rule = NeverallowRule(line)
+            rule.treble_only = (treble_only_depth > 0)
+            rules.append(rule)
+
+        if treble_only_depth != 0:
+            exit("ERROR: end of input while inside TREBLE_ONLY section")
+        return rules
 
 # neverallow_rule_to_test - takes a neverallow statement and transforms it into
 # the output necessary to form a cts unit test in a java source file.
 # returns: a string representing a generic test method based on this rule.
-def neverallow_rule_to_test(neverallow_rule, test_num):
-    squashed_neverallow = neverallow_rule.replace("\n", " ")
+def neverallow_rule_to_test(rule, test_num):
+    squashed_neverallow = rule.statement.replace("\n", " ")
     method  = SELinuxNeverallowTestFrame.src_method
     method = method.replace("testNeverallowRules()",
         "testNeverallowRules" + str(test_num) + "()")
-    return method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+    method = method.replace("$NEVERALLOW_RULE_HERE$", squashed_neverallow)
+    method = method.replace(
+        "$FULL_TREBLE_ONLY_BOOL_HERE$",
+        "true" if rule.treble_only else "false")
+    return method
 
 if __name__ == "__main__":
     # check usage
     if len(sys.argv) != 3:
         print usage
-        exit()
+        exit(1)
     input_file = sys.argv[1]
     output_file = sys.argv[2]
 
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index 6e29a24..d5811c3 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -89,13 +89,13 @@
 $(LOCAL_BUILT_MODULE): PRIVATE_CLASS_PATH := $(subst $(space),:,$(vmteststf_dep_jars)):$(HOST_JDK_TOOLS_JAR)
 $(LOCAL_BUILT_MODULE): PRIVATE_JACK_VERSION := $(LOCAL_JACK_VERSION)
 ifndef LOCAL_JACK_ENABLED
-$(LOCAL_BUILT_MODULE) : $(vmteststf_dep_jars) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
+$(LOCAL_BUILT_MODULE) : $(vmteststf_dep_jars) $(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar
 	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
 	$(hide) mkdir -p $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES)/dot/junit $(dir $(PRIVATE_INTERMEDIATES_DEXCORE_JAR))
 	# generated and compile the host side junit tests
 	@echo "Write generated Main_*.java files to $(PRIVATE_INTERMEDIATES_MAIN_FILES)"
 	$(hide) java -cp $(PRIVATE_CLASS_PATH) util.build.BuildDalvikSuite $(PRIVATE_SRC_FOLDER) $(PRIVATE_INTERMEDIATES) \
-		$(HOST_OUT_JAVA_LIBRARIES)/cts-tf-dalvik-buildutil.jar:$(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar \
+		$(HOST_OUT_JAVA_LIBRARIES)/cts-tf-dalvik-buildutil.jar:$(HOST_OUT_JAVA_LIBRARIES)/tradefed.jar \
 		$(PRIVATE_INTERMEDIATES_MAIN_FILES) $(PRIVATE_INTERMEDIATES_CLASSES) $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES) $$RUN_VM_TESTS_RTO
 	@echo "Generate $(PRIVATE_INTERMEDIATES_DEXCORE_JAR)"
 	$(hide) jar -cf $(PRIVATE_INTERMEDIATES_DEXCORE_JAR).jar \