Merge "Allow to skip the recents redaction tests if overview button is absent"
diff --git a/apps/CtsVerifier/res/values-vrheadset/strings.xml b/apps/CtsVerifier/res/values-vrheadset/strings.xml
index c92038d..fef8d7f 100644
--- a/apps/CtsVerifier/res/values-vrheadset/strings.xml
+++ b/apps/CtsVerifier/res/values-vrheadset/strings.xml
@@ -20,5 +20,6 @@
         <item>com.android.cts.verifier.car.CarDockTestActivity</item>
         <item>com.android.cts.verifier.deskclock.DeskClockTestsActivity</item>
         <item>com.android.cts.verifier.screenpinning.ScreenPinningTestActivity</item>
+        <item>com.android.cts.verifier.notifications.ShortcutThrottlingResetActivity</item>
     </string-array>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 9c2992b..e9f29b8 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -891,7 +891,7 @@
     <string name="snsr_mag_verify_calibrated_uncalibrated">Verifying the relationship between
         calibrated and uncalibrated measurements...</string>
     <string name="snsr_mag_calibration_description">Please calibrate the Magnetometer by moving
-        it in 8 shapes in different orientations.</string>
+        it in 8 shapes in different orientations. Click \"Next\" to start and click \"Next\" once again to end calibration.</string>
     <string name="snsr_mag_calibration_complete">When done, leave the device in a flat surface, far
         from all metallic objects (if the test does not pass, try re-running it outdoors).</string>
     <string name="snsr_mag_measurement">-&gt; (%1$.2f, %2$.2f, %3$.2f) : %4$.2f uT</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
index cfa097b..5657edd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
@@ -181,6 +181,14 @@
     protected void onDestroy() {
         super.onDestroy();
         unregisterReceiver(mReceiver);
+        try {
+            mService.stopRendering();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to execute stopRendering", e);
+        }
+        if (mConnection != null) {
+            unbindService(mConnection);
+        }
         mReader.close();
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 229a9dc..b4aa606 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -170,23 +170,23 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_NORMAL);
 
+        SensorTestLogger logger = getTestLogger();
+        logger.logInstructions(R.string.snsr_mag_calibration_description);
+        logger.logInstructions(R.string.snsr_mag_calibration_complete);
+        waitForUserToContinue();
+
         TestSensorEventListener listener = new TestSensorEventListener(environment) {
             @Override
             public void onSensorChanged(SensorEvent event) {
                 clearText();
 
                 float values[] = event.values;
-                SensorTestLogger logger = getTestLogger();
-                logger.logInstructions(R.string.snsr_mag_calibration_description);
                 logger.logMessage(
                         R.string.snsr_mag_measurement,
                         values[0],
                         values[1],
                         values[2],
                         SensorCtsHelper.getMagnitude(values));
-
-                // TODO: automate finding out when the magnetometer is calibrated
-                logger.logInstructions(R.string.snsr_mag_calibration_complete);
             }
         };
 
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java
index 37476f7..70f12d9 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ReadDefaultUris.java
@@ -67,6 +67,5 @@
         mp.stop();
         mp.release();
         Thread.sleep(timeToPlayMs);
-        assertFalse(mAudioManager.isMusicActive());
     }
 }
diff --git a/hostsidetests/incident/apps/procstatsapp/Android.mk b/hostsidetests/incident/apps/procstatsapp/Android.mk
new file mode 100644
index 0000000..a02c29d
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsProcStatsProtoApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ctstestrunner \
+    compatibility-device-util \
+    android-support-v4
+
+LOCAL_SDK_VERSION := test_current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/incident/apps/procstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/procstatsapp/AndroidManifest.xml
new file mode 100644
index 0000000..a0cccb4
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.server.cts.procstats" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".SimpleActivity" android:exported="true" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.server.cts.procstats" />
+</manifest>
diff --git a/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java b/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java
new file mode 100644
index 0000000..69ff75e
--- /dev/null
+++ b/hostsidetests/incident/apps/procstatsapp/src/com/android/server/cts/procstats/SimpleActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.procstats;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+public class SimpleActivity extends Activity {
+
+    private static final String TAG = "ProcstatsAppRunningTest";
+
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        Log.i(TAG, "Procstats app is running");
+    }
+}
+
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 57d64bb..386c324 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -15,14 +15,9 @@
  */
 package com.android.server.cts;
 
-import com.android.ddmlib.IShellOutputReceiver;
 import com.android.tradefed.log.LogUtil;
 
-import com.google.common.base.Charsets;
-
 import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Test for "dumpsys batterystats -c
@@ -710,64 +705,6 @@
     }
 
     /**
-    * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
-    * the given tag.
-    * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
-    * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
-    */
-    private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
-        IShellOutputReceiver receiver = new IShellOutputReceiver() {
-            private final StringBuilder mOutputBuffer = new StringBuilder();
-            private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
-
-            @Override
-            public void addOutput(byte[] data, int offset, int length) {
-                if (!isCancelled()) {
-                    synchronized (mOutputBuffer) {
-                        String s = new String(data, offset, length, Charsets.UTF_8);
-                        mOutputBuffer.append(s);
-                        if (checkBufferForText()) {
-                            mIsCanceled.set(true);
-                        }
-                    }
-                }
-            }
-
-            private boolean checkBufferForText() {
-                if (mOutputBuffer.indexOf(text) > -1) {
-                    return true;
-                } else {
-                    // delete all old data (except the last few chars) since they don't contain text
-                    // (presumably large chunks of data will be added at a time, so this is
-                    // sufficiently efficient.)
-                    int newStart = mOutputBuffer.length() - text.length();
-                    if (newStart > 0) {
-                        mOutputBuffer.delete(0, newStart);
-                    }
-                    return false;
-                }
-            }
-
-            @Override
-            public boolean isCancelled() {
-                return mIsCanceled.get();
-            }
-
-            @Override
-            public void flush() {
-            }
-        };
-
-        try {
-            // Wait for at most maxTimeMs for logcat to display the desired text.
-            getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
-                    receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
-        } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
-            System.err.println(e);
-        }
-    }
-
-    /**
      * Returns the bytes downloaded for the wifi transfer download tests.
      * @param requestCode the output of executeForeground() or executeBackground() to identify in
      *                    the logcat the line associated with the desired download information
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
index f5c13c2..bdf7dcc 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
@@ -374,16 +374,14 @@
                 assertNotNegative("TX bytes", bucket.getTxBytes());
                 assertNotNegative("TX packets", bucket.getTxPackets());
 
-// 10 was still too big?                // It should be safe to say # of bytes >= 10 * 10 of packets, due to headers, etc...
-                final long FACTOR = 4;
                 assertTrue(
                         String.format("# of bytes %d too small for # of packets %d",
                                 bucket.getRxBytes(), bucket.getRxPackets()),
-                        bucket.getRxBytes() >= bucket.getRxPackets() * FACTOR);
+                        bucket.getRxBytes() >= bucket.getRxPackets());
                 assertTrue(
                         String.format("# of bytes %d too small for # of packets %d",
                                 bucket.getTxBytes(), bucket.getTxPackets()),
-                        bucket.getTxBytes() >= bucket.getTxPackets() * FACTOR);
+                        bucket.getTxBytes() >= bucket.getTxPackets());
             }
         }
 
diff --git a/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java b/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java
new file mode 100644
index 0000000..640da22
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/ProcStatsProtoTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import android.service.procstats.ProcessStatsProto;
+import android.service.procstats.ProcessStatsServiceDumpProto;
+
+/**
+ * Test proto dump of procstats
+ *
+ * $ make -j32 CtsIncidentHostTestCases
+ * $ cts-tradefed run cts-dev -m CtsIncidentHostTestCases \
+ * -t com.android.server.cts.ProcStatsProtoTest
+ */
+public class ProcStatsProtoTest extends ProtoDumpTestCase {
+
+    private static final String DEVICE_SIDE_TEST_APK = "CtsProcStatsProtoApp.apk";
+    private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.procstats";
+    private static final String TEST_APP_TAG = "ProcstatsAppRunningTest";
+    private static final String TEST_APP_LOG = "Procstats app is running";
+    private static final int WAIT_MS = 1000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getDevice().executeShellCommand("dumpsys procstats --clear");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+        super.tearDown();
+    }
+
+    /**
+     * Test that procstats dump is reasonable, it installs procstats app and
+     * starts the activity, then asserts the procstats dump contains the package.
+     *
+     * @throws Exception
+     */
+    public void testDump() throws Exception {
+        installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
+        int retries = 3;
+        do {
+            getDevice().executeShellCommand(
+                "am start -n com.android.server.cts.procstats/.SimpleActivity");
+        } while (!checkLogcatForText(TEST_APP_TAG, TEST_APP_LOG, WAIT_MS) && retries-- > 0);
+
+        final ProcessStatsServiceDumpProto dump = getDump(ProcessStatsServiceDumpProto.parser(),
+                "dumpsys procstats --proto");
+
+        int N = dump.getProcstatsNow().getProcessStatsCount();
+        boolean containsTestApp = false;
+        for (int i = 0; i < N; i++) {
+            ProcessStatsProto ps = dump.getProcstatsNow().getProcessStats(i);
+            if (DEVICE_SIDE_TEST_PACKAGE.equals(ps.getProcess())) {
+                containsTestApp = true;
+            }
+        }
+
+        assertTrue(N > 0);
+        assertTrue(containsTestApp); // found test app in procstats dump
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
index 5739bf4..d273033 100644
--- a/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
+++ b/hostsidetests/incident/src/com/android/server/cts/ProtoDumpTestCase.java
@@ -17,6 +17,7 @@
 package com.android.server.cts;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.IShellOutputReceiver;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.ddmlib.testrunner.TestResult;
@@ -31,11 +32,14 @@
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
+import com.google.common.base.Charsets;
 import com.google.protobuf.InvalidProtocolBufferException;
 import com.google.protobuf.MessageLite;
 import com.google.protobuf.Parser;
 
 import java.io.FileNotFoundException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -176,4 +180,66 @@
         assertTrue("No group found for pattern '" + pattern + "'", matcher.groupCount() > 0);
         return matcher.group(1);
     }
+
+    /**
+     * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
+     * the given tag.
+     * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
+     * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
+     * Returns true means the desired log line is found.
+     */
+    protected boolean checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
+        IShellOutputReceiver receiver = new IShellOutputReceiver() {
+            private final StringBuilder mOutputBuffer = new StringBuilder();
+            private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
+
+            @Override
+            public void addOutput(byte[] data, int offset, int length) {
+                if (!isCancelled()) {
+                    synchronized (mOutputBuffer) {
+                        String s = new String(data, offset, length, Charsets.UTF_8);
+                        mOutputBuffer.append(s);
+                        if (checkBufferForText()) {
+                            mIsCanceled.set(true);
+                        }
+                    }
+                }
+            }
+
+            private boolean checkBufferForText() {
+                if (mOutputBuffer.indexOf(text) > -1) {
+                    return true;
+                } else {
+                    // delete all old data (except the last few chars) since they don't contain text
+                    // (presumably large chunks of data will be added at a time, so this is
+                    // sufficiently efficient.)
+                    int newStart = mOutputBuffer.length() - text.length();
+                    if (newStart > 0) {
+                        mOutputBuffer.delete(0, newStart);
+                    }
+                    return false;
+                }
+            }
+
+            @Override
+            public boolean isCancelled() {
+                return mIsCanceled.get();
+            }
+
+            @Override
+            public void flush() {
+            }
+        };
+
+        try {
+            // Wait for at most maxTimeMs for logcat to display the desired text.
+            getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
+                    receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
+            return receiver.isCancelled();
+        } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
+            System.err.println(e);
+        }
+        return false;
+    }
+
 }
diff --git a/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
index c534b71..c147265 100644
--- a/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
@@ -29,21 +29,20 @@
 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.internal.os.StatsdConfigProto.ValueMetric;
 import com.android.os.AtomsProto.Atom;
 import com.android.os.AtomsProto.KernelWakelockPulled;
 import com.android.os.AtomsProto.ScreenStateChanged;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
 import com.android.tradefed.log.LogUtil;
-import com.google.common.base.Charsets;
+
 import com.google.common.io.Files;
 
 import java.io.File;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Test for statsd
@@ -55,6 +54,8 @@
     private static final String TAG = "StatsdValidationTest";
 
     private static final boolean TESTS_ENABLED = false;
+    // For tests that require incidentd. Keep as true until TESTS_ENABLED is permanently enabled.
+    private static final boolean INCIDENTD_TESTS_ENABLED = true;
 
     // TODO: Use a statsd-specific app (temporarily just borrowing the batterystats app)
     private static final String DEVICE_SIDE_TEST_APK = "CtsStatsDApp.apk";
@@ -170,6 +171,7 @@
     // Also tests that anomaly detection works when spanning multiple buckets.
     public void testCountAnomalyDetection() throws Exception {
         if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
         // TODO: Don't use screen-state as the atom.
         StatsdConfig config = getDefaultConfig()
                 .addCountMetric(CountMetric.newBuilder()
@@ -214,6 +216,7 @@
     // Also tests that refractory periods in anomaly detection work.
     public void testDurationAnomalyDetection() throws Exception {
         if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
         // TODO: Do NOT use screenState for this, since screens auto-turn-off after a variable time.
         StatsdConfig config = getDefaultConfig()
                 .addDurationMetric(DurationMetric.newBuilder()
@@ -222,14 +225,6 @@
                         .setAggregationType(DurationMetric.AggregationType.SUM)
                         .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
                 )
-                .addPredicate(Predicate.newBuilder()
-                        .setName("SCREEN_IS_ON")
-                        .setSimplePredicate(SimplePredicate.newBuilder()
-                                .setStart("SCREEN_TURNED_ON")
-                                .setStop("SCREEN_TURNED_OFF")
-                                .setCountNesting(false)
-                        )
-                )
                 .addAlert(Alert.newBuilder()
                         .setName("testDurationAnomalyDetectionAlert")
                         .setMetricName("METRIC")
@@ -274,6 +269,81 @@
         assertTrue(didIncidentdFireSince(markDeviceDate));
     }
 
+    // TODO: There is no value anomaly detection code yet! So this will fail.
+    // Tests that anomaly detection for value works.
+    public void testValueAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Definitely don't use screen-state as the atom. This MUST be changed.
+        StatsdConfig config = getDefaultConfig()
+                .addValueMetric(ValueMetric.newBuilder()
+                        .setName("METRIC")
+                        .setWhat("SCREEN_TURNED_ON")
+                        .setValueField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+                )
+                .addAlert(Alert.newBuilder()
+                        .setName("testValueAnomalyDetectionAlert")
+                        .setMetricName("METRIC")
+                        .setNumberOfBuckets(4)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                        .setIncidentdDetails(Alert.IncidentdDetails.newBuilder()
+                                .addSection(-1)
+                        )
+                )
+                .build();
+        uploadConfig(config);
+
+        turnScreenOff();
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOff(); // value = STATE_OFF = 1 (probably)
+        Thread.sleep(2000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+        turnScreenOn(); // value = STATE_ON = 2 (probably)
+        Thread.sleep(2000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+    }
+
+    // Tests that anomaly detection for gauge works.
+    public void testGaugeAnomalyDetection() throws Exception {
+        if (!TESTS_ENABLED) return;
+        if (!INCIDENTD_TESTS_ENABLED) return;
+        // TODO: Definitely don't use screen-state as the atom. This MUST be changed.
+        StatsdConfig config = getDefaultConfig()
+                .addGaugeMetric(GaugeMetric.newBuilder()
+                        .setName("METRIC")
+                        .setWhat("SCREEN_TURNED_ON")
+                        .setGaugeField(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        .setBucket(Bucket.newBuilder().setBucketSizeMillis(10_000))
+                )
+                .addAlert(Alert.newBuilder()
+                        .setName("testGaugeAnomalyDetectionAlert")
+                        .setMetricName("METRIC")
+                        .setNumberOfBuckets(1)
+                        .setRefractoryPeriodSecs(20)
+                        .setTriggerIfSumGt(ScreenStateChanged.State.STATE_OFF.getNumber())
+                        .setIncidentdDetails(Alert.IncidentdDetails.newBuilder()
+                                .addSection(-1)
+                        )
+                )
+                .build();
+        uploadConfig(config);
+
+        turnScreenOff();
+        String markDeviceDate = getCurrentLogcatDate();
+        turnScreenOff(); // gauge = STATE_OFF = 1 (probably)
+        Thread.sleep(2000);
+        assertFalse(didIncidentdFireSince(markDeviceDate));
+        turnScreenOn(); // gauge = STATE_ON = 2 (probably)
+        Thread.sleep(2000);
+        assertTrue(didIncidentdFireSince(markDeviceDate));
+
+        turnScreenOff();
+    }
+
     /**
      * Determines whether logcat indicates that incidentd fired since the given device date.
      */
@@ -288,7 +358,6 @@
     }
 
     private void uploadConfig(StatsdConfig config) throws Exception {
-        LogUtil.CLog.d("uploading the config:\n" + config.toString());
         File configFile = File.createTempFile("statsdconfig", ".config");
         Files.write(config.toByteArray(), configFile);
         String remotePath = "/data/" + configFile.getName();
@@ -370,64 +439,62 @@
     private StatsdConfig.Builder getDefaultConfig() {
         StatsdConfig.Builder configBuilder = StatsdConfig.newBuilder();
         configBuilder.setName("12345");
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("SCREEN_TURNED_ON")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
-                                        .addKeyValueMatcher(KeyValueMatcher.newBuilder()
-                                                .setKeyMatcher(
-                                                        KeyMatcher.newBuilder()
-                                                                .setKey(ScreenStateChanged
-                                                                        .DISPLAY_STATE_FIELD_NUMBER)
-                                                ).setEqInt(
-                                                        ScreenStateChanged.State.STATE_ON_VALUE))));
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("SCREEN_TURNED_OFF")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
-                                        .addKeyValueMatcher(KeyValueMatcher.newBuilder()
-                                                .setKeyMatcher(
-                                                        KeyMatcher.newBuilder()
-                                                                .setKey(ScreenStateChanged
-                                                                        .DISPLAY_STATE_FIELD_NUMBER)
-                                                ).setEqInt(
-                                                        ScreenStateChanged.State.STATE_OFF_VALUE)
-                                        )));
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("UID_PROCESS_STATE_CHANGED")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER)));
-        // up to 110 is fine. 128 not good
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("KERNEL_WAKELOCK_PULLED")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.KERNEL_WAKELOCK_PULLED_FIELD_NUMBER)));
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("CPU_TIME_PER_UID_PULLED")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.CPU_TIME_PER_UID_PULLED_FIELD_NUMBER)));
-        configBuilder.addAtomMatcher(
-                AtomMatcher.newBuilder()
-                        .setName("CPU_TIME_PER_FREQ_PULLED")
-                        .setSimpleAtomMatcher(
-                                SimpleAtomMatcher.newBuilder()
-                                        .setTag(Atom.CPU_TIME_PER_FREQ_PULLED_FIELD_NUMBER)));
-        configBuilder.addPredicate(Predicate.newBuilder()
+        configBuilder
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("SCREEN_TURNED_ON")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                    .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+                        .setKeyMatcher(KeyMatcher.newBuilder()
+                            .setKey(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        )
+                        .setEqInt(ScreenStateChanged.State.STATE_ON_VALUE)
+                    )
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("SCREEN_TURNED_OFF")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+                    .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+                        .setKeyMatcher(KeyMatcher.newBuilder()
+                            .setKey(ScreenStateChanged.DISPLAY_STATE_FIELD_NUMBER)
+                        )
+                        .setEqInt(ScreenStateChanged.State.STATE_OFF_VALUE)
+                    )
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("UID_PROCESS_STATE_CHANGED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER)
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("KERNEL_WAKELOCK_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.KERNEL_WAKELOCK_PULLED_FIELD_NUMBER)
+                )
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("CPU_TIME_PER_UID_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.CPU_TIME_PER_UID_PULLED_FIELD_NUMBER))
+            )
+            .addAtomMatcher(AtomMatcher.newBuilder()
+                .setName("CPU_TIME_PER_FREQ_PULLED")
+                .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder()
+                    .setTag(Atom.CPU_TIME_PER_FREQ_PULLED_FIELD_NUMBER))
+            )
+            .addPredicate(Predicate.newBuilder()
                 .setName("SCREEN_IS_ON")
                 .setSimplePredicate(SimplePredicate.newBuilder()
-                        .setStart("SCREEN_TURNED_ON")
-                        .setStop("SCREEN_TURNED_OFF")
-                        .setCountNesting(false)));
+                    .setStart("SCREEN_TURNED_ON")
+                    .setStop("SCREEN_TURNED_OFF")
+                    .setCountNesting(false)
+                )
+            )
+        ;
         return configBuilder;
     }
 
@@ -590,64 +657,6 @@
     }
 
     /**
-     * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
-     * the given tag.
-     * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
-     * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
-     */
-    private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
-        IShellOutputReceiver receiver = new IShellOutputReceiver() {
-            private final StringBuilder mOutputBuffer = new StringBuilder();
-            private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
-
-            @Override
-            public void addOutput(byte[] data, int offset, int length) {
-                if (!isCancelled()) {
-                    synchronized (mOutputBuffer) {
-                        String s = new String(data, offset, length, Charsets.UTF_8);
-                        mOutputBuffer.append(s);
-                        if (checkBufferForText()) {
-                            mIsCanceled.set(true);
-                        }
-                    }
-                }
-            }
-
-            private boolean checkBufferForText() {
-                if (mOutputBuffer.indexOf(text) > -1) {
-                    return true;
-                } else {
-                    // delete all old data (except the last few chars) since they don't contain text
-                    // (presumably large chunks of data will be added at a time, so this is
-                    // sufficiently efficient.)
-                    int newStart = mOutputBuffer.length() - text.length();
-                    if (newStart > 0) {
-                        mOutputBuffer.delete(0, newStart);
-                    }
-                    return false;
-                }
-            }
-
-            @Override
-            public boolean isCancelled() {
-                return mIsCanceled.get();
-            }
-
-            @Override
-            public void flush() {
-            }
-        };
-
-        try {
-            // Wait for at most maxTimeMs for logcat to display the desired text.
-            getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
-                    receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
-        } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
-            System.err.println(e);
-        }
-    }
-
-    /**
      * Determines if the device has the given feature.
      * Prints a warning if its value differs from requiredAnswer.
      */
diff --git a/hostsidetests/inputmethodservice/hostside/Android.mk b/hostsidetests/inputmethodservice/hostside/Android.mk
index 19aae73..5ff22a2 100644
--- a/hostsidetests/inputmethodservice/hostside/Android.mk
+++ b/hostsidetests/inputmethodservice/hostside/Android.mk
@@ -30,7 +30,6 @@
     cts-tradefed \
     tradefed
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    cts-inputmethodservice-common-host \
-    hamcrest-library
+    cts-inputmethodservice-common-host
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index b094812..bc906a0 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -22,11 +22,10 @@
 import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_COMPONENT;
 import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
 
-import static org.hamcrest.Matchers.emptyOrNullString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
@@ -91,7 +90,8 @@
 
         // There should be a new IME that is different from the IME1
         final String newIme = shell(ShellCommandUtils.getCurrentIme());
-        assertThat(newIme, is(not(emptyOrNullString())));
+        assertNotNull(newIme);
+        assertFalse(newIme.isEmpty());
         assertNotEquals(newIme, Ime1Constants.IME_ID);
     }
 
@@ -107,7 +107,8 @@
 
         // There should be a new IME that is different from the IME1
         final String newIme = shell(ShellCommandUtils.getCurrentIme());
-        assertThat(newIme, is(not(emptyOrNullString())));
+        assertNotNull(newIme);
+        assertFalse(newIme.isEmpty());
         assertNotEquals(newIme, Ime1Constants.IME_ID);
     }
 
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 4548fb0..6093384 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -104,6 +104,8 @@
 
         <service android:name="android.app.stubs.MockService" />
 
+        <service android:name="android.app.stubs.NullService" />
+
         <activity android:name="android.app.stubs.SearchManagerStubActivity"
                 android:label="SearchManagerStubActivity">
             <intent-filter>
diff --git a/tests/app/app/src/android/app/stubs/ActionBarActivity.java b/tests/app/app/src/android/app/stubs/ActionBarActivity.java
index 5e15407..c9d38b5 100644
--- a/tests/app/app/src/android/app/stubs/ActionBarActivity.java
+++ b/tests/app/app/src/android/app/stubs/ActionBarActivity.java
@@ -20,7 +20,11 @@
 import android.os.Bundle;
 import android.view.Menu;
 
+import java.util.concurrent.CountDownLatch;
+
 public class ActionBarActivity extends Activity {
+    // Make sure that ActionBarActivity has focus before running Action Bar Tests.
+    public CountDownLatch windowFocusSignal;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -29,6 +33,7 @@
         if (bar != null) {
             bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
         }
+        windowFocusSignal = new CountDownLatch(1);
     }
 
     @Override
@@ -36,4 +41,12 @@
         getMenuInflater().inflate(R.menu.flat_menu, menu);
         return true;
     }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (hasFocus) {
+            windowFocusSignal.countDown();
+        }
+    }
 }
diff --git a/tests/app/app/src/android/app/stubs/NullService.java b/tests/app/app/src/android/app/stubs/NullService.java
new file mode 100644
index 0000000..6a1c618
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/NullService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.app.stubs;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * A Service that always returns null from onBind(Intent).
+ */
+public class NullService extends Service {
+    private static final String TAG = "NullService";
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind() returning null");
+        return null;
+    }
+
+}
diff --git a/tests/app/src/android/app/cts/ActionBarTest.java b/tests/app/src/android/app/cts/ActionBarTest.java
index 8bfaa59..2cfc970 100644
--- a/tests/app/src/android/app/cts/ActionBarTest.java
+++ b/tests/app/src/android/app/cts/ActionBarTest.java
@@ -25,6 +25,8 @@
 import android.view.KeyEvent;
 import android.view.Window;
 
+import java.util.concurrent.TimeUnit;
+
 public class ActionBarTest extends ActivityInstrumentationTestCase2<ActionBarActivity> {
 
     private ActionBarActivity mActivity;
@@ -83,13 +85,16 @@
         assertEquals(t3, mBar.getTabAt(4));
     }
 
-    public void testOptionsMenuKey() {
+    public void testOptionsMenuKey() throws Exception {
         if (!mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
             return;
         }
         final boolean menuIsVisible[] = {false};
         mActivity.getActionBar().addOnMenuVisibilityListener(
                 isVisible -> menuIsVisible[0] = isVisible);
+        // Wait here for test activity to gain focus before sending keyevent.
+        // Visibility listener needs the action bar to be visible.
+        assertTrue(mActivity.windowFocusSignal.await(1000, TimeUnit.MILLISECONDS));
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
         getInstrumentation().waitForIdleSync();
         assertTrue(menuIsVisible[0]);
diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java
index 5f8202d..d660040 100644
--- a/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/app/src/android/app/cts/ServiceTest.java
@@ -26,6 +26,7 @@
 import android.app.stubs.LocalForegroundService;
 import android.app.stubs.LocalGrantedService;
 import android.app.stubs.LocalService;
+import android.app.stubs.NullService;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -36,6 +37,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
@@ -85,6 +87,41 @@
         }
     }
 
+    private static class NullServiceConnection implements ServiceConnection {
+        boolean mNullBinding = false;
+
+        @Override public void onServiceConnected(ComponentName name, IBinder service) {}
+        @Override public void onServiceDisconnected(ComponentName name) {}
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            synchronized (this) {
+                mNullBinding = true;
+                this.notifyAll();
+            }
+        }
+
+        public void waitForNullBinding(final long timeout) {
+            long now = SystemClock.uptimeMillis();
+            final long end = now + timeout;
+            synchronized (this) {
+                while (!mNullBinding && (now < end)) {
+                    try {
+                        this.wait(end - now);
+                    } catch (InterruptedException e) {
+                    }
+                    now = SystemClock.uptimeMillis();
+                }
+            }
+        }
+
+        public boolean nullBindingReceived() {
+            synchronized (this) {
+                return mNullBinding;
+            }
+        }
+    }
+
     private class TestConnection implements ServiceConnection {
         private final boolean mExpectDisconnect;
         private final boolean mSetReporter;
@@ -831,4 +868,28 @@
             // expected
         }
     }
+
+    /**
+     * Verify that when the requested service's onBind() returns null,
+     * the connection's onNullBinding() method is invoked.
+     */
+    @MediumTest
+    public void testNullServiceBinder() throws Exception {
+        Intent intent = new Intent(mContext, NullService.class);
+        intent.setAction("testNullServiceBinder");
+        NullServiceConnection conn1 = new NullServiceConnection();
+        NullServiceConnection conn2 = new NullServiceConnection();
+        try {
+            assertTrue(mContext.bindService(intent, conn1, Context.BIND_AUTO_CREATE));
+            conn1.waitForNullBinding(DELAY);
+            assertTrue(conn1.nullBindingReceived());
+
+            assertTrue(mContext.bindService(intent, conn2, Context.BIND_AUTO_CREATE));
+            conn2.waitForNullBinding(DELAY);
+            assertTrue(conn2.nullBindingReceived());
+        } finally {
+            mContext.unbindService(conn1);
+            mContext.unbindService(conn2);
+        }
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.java
new file mode 100644
index 0000000..8569472
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationScorerTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.assertThat;
+
+import android.service.autofill.FieldsClassificationScorer;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class FieldsClassificationScorerTest {
+
+    @Test
+    public void testGetScore_nullValue() {
+        assertThat(FieldsClassificationScorer.getScore(null, "D'OH!")).isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_nonTextValue() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forToggle(true), "D'OH!"))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_nullUserData() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("D'OH!"), null))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_fullMatch() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("d'oh!"), "d'oh!"))
+                .isEqualTo(100_0000);
+    }
+
+    @Test
+    public void testGetScore_fullMatchMixedCase() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("D'OH!"), "D'oH!"))
+                .isEqualTo(100_0000);
+    }
+
+    // TODO(b/67867469): might need to change it once it supports different sizes
+    @Test
+    public void testGetScore_mismatchDifferentSizes() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("One"), "MoreThanOne"))
+                .isEqualTo(0);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("MoreThanOne"), "One"))
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void testGetScore_partialMatch() {
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "Dxxx"))
+                .isEqualTo(25_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "DUxx"))
+                .isEqualTo(50_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dude"), "DUDx"))
+                .isEqualTo(75_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("Dxxx"), "Dude"))
+                .isEqualTo(25_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("DUxx"), "Dude"))
+                .isEqualTo(50_0000);
+        assertThat(FieldsClassificationScorer.getScore(AutofillValue.forText("DUDx"), "Dude"))
+                .isEqualTo(75_0000);
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index 59e7115..e78f85c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -16,7 +16,7 @@
 package android.autofillservice.cts;
 
 import static android.autofillservice.cts.Helper.assertFillEventForContextCommitted;
-import static android.autofillservice.cts.Helper.assertFillEventForFieldsDetected;
+import static android.autofillservice.cts.Helper.assertFillEventForFieldsClassification;
 import static android.autofillservice.cts.Helper.runShellCommand;
 import static android.service.autofill.FillResponse.FLAG_TRACK_CONTEXT_COMMITED;
 
@@ -82,7 +82,7 @@
     }
 
     @Test
-    public void testFullHit() throws Exception {
+    public void testHit_oneUserData_oneDetectableField() throws Exception {
         // Set service.
         enableService();
 
@@ -105,7 +105,6 @@
         callback.assertUiUnavailableEvent(field);
 
         // Simulate user input
-        mActivity.focusCell(1, 1);
         mActivity.setText(1, 1, "fully");
 
         // Finish context.
@@ -116,7 +115,157 @@
                 InstrumentedAutoFillService.peekInstance().getFillEventHistory();
         final List<Event> events = history.getEvents();
         assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
-        assertFillEventForFieldsDetected(events.get(0), "myId", 0);
+        assertFillEventForFieldsClassification(events.get(0), fieldId, "myId", 1000000);
+    }
+
+    @Test
+    public void testHit_manyUserData_oneDetectableField_bestMatchIsFirst() throws Exception {
+        manyUserData_oneDetectableField(true);
+    }
+
+    @Test
+    public void testHit_manyUserData_oneDetectableField_bestMatchIsSecond() throws Exception {
+        manyUserData_oneDetectableField(false);
+    }
+
+    private void manyUserData_oneDetectableField(boolean firstMatch) throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        final String expectedId;
+        final String typedText;
+        if (firstMatch) {
+            expectedId = "1stId";
+            typedText = "IAM111";
+        } else {
+            expectedId = "2ndId";
+            typedText = "IAM222";
+        }
+        mActivity.getAutofillManager().setUserData(new UserData.Builder("1stId", "Iam1ST")
+                .add("2ndId", "Iam2ND").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field = mActivity.getCell(1, 1);
+        final AutofillId fieldId = field.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field);
+
+        // Simulate user input
+        mActivity.setText(1, 1, typedText);
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        // Matches 4 of 6 chars - 66.6666%
+        assertFillEventForFieldsClassification(events.get(0), fieldId, expectedId, 666666);
+    }
+
+    @Test
+    public void testHit_oneUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .setUserData(new UserData.Builder("myId", "FULLY").build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field1 = mActivity.getCell(1, 1);
+        final AutofillId fieldId1 = field1.getAutofillId();
+        final EditText field2 = mActivity.getCell(1, 2);
+        final AutofillId fieldId2 = field2.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // 100%
+        mActivity.setText(1, 2, "fooly"); // 60%
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForFieldsClassification(events.get(0),
+                new AutofillId[] { fieldId1, fieldId2 },
+                new String[] { "myId", "myId" },
+                new int[] { 1000000, 600000 });
+    }
+
+    @Test
+    public void testHit_manyUserData_manyDetectableFields() throws Exception {
+        // Set service.
+        enableService();
+
+        // Set expectations.
+        mActivity.getAutofillManager()
+                .setUserData(new UserData.Builder("myId", "FULLY")
+                        .add("otherId", "EMPTY")
+                        .build());
+        final MyAutofillCallback callback = mActivity.registerCallback();
+        final EditText field1 = mActivity.getCell(1, 1);
+        final AutofillId fieldId1 = field1.getAutofillId();
+        final EditText field2 = mActivity.getCell(1, 2);
+        final AutofillId fieldId2 = field2.getAutofillId();
+        final EditText field3 = mActivity.getCell(2, 1);
+        final AutofillId fieldId3 = field3.getAutofillId();
+        final EditText field4 = mActivity.getCell(2, 2);
+        final AutofillId fieldId4 = field4.getAutofillId();
+        sReplier.addResponse(new CannedFillResponse.Builder()
+                .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+                .setFieldClassificationIds(fieldId1, fieldId2)
+                .build());
+
+        // Trigger autofill
+        mActivity.focusCell(1, 1);
+        sReplier.getNextFillRequest();
+
+        sUiBot.assertNoDatasets();
+        callback.assertUiUnavailableEvent(field1);
+
+        // Simulate user input
+        mActivity.setText(1, 1, "fully"); // 100%
+        mActivity.setText(1, 2, "empty"); // 100%
+        mActivity.setText(2, 1, "fooly"); // 60%
+        mActivity.setText(2, 2, "emppy"); // 80%
+
+        // Finish context.
+        mActivity.getAutofillManager().commit();
+
+        // Assert results
+        final FillEventHistory history =
+                InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+        final List<Event> events = history.getEvents();
+        assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+        assertFillEventForFieldsClassification(events.get(0),
+                new AutofillId[] { fieldId1, fieldId2, fieldId3, fieldId4 },
+                new String[] { "myId", "otherId", "myId", "otherId" },
+                new int[] { 1000000, 1000000, 600000, 800000});
     }
 
     @Test
@@ -196,9 +345,9 @@
      * - Multipartition (for example, one response with FieldsDetection, others with datasets,
      *   saveinfo, and/or ignoredIds)
      * - make sure detectable fields don't trigger a new partition
-     * - test partial hit (for example, 'fool' instead of 'full'
-     * - multiple fields
-     * - multiple value
+     * v test partial hit (for example, 'fool' instead of 'full'
+     * v multiple fields
+     * v multiple value
      * - combinations of above items
      */
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index c95e74e..008e10c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -36,6 +36,8 @@
 import android.content.pm.PackageManager;
 import android.icu.util.Calendar;
 import android.os.Bundle;
+import android.service.autofill.FieldClassification;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillEventHistory;
 import android.support.annotation.NonNull;
@@ -55,6 +57,7 @@
 import java.lang.reflect.Field;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.function.Function;
 
 /**
@@ -1047,7 +1050,8 @@
     private static void assertFillEvent(@NonNull FillEventHistory.Event event,
             int eventType, @Nullable String datasetId,
             @Nullable String key, @Nullable String value,
-            @Nullable String detectedRemoteId, int detectedScore) {
+            @Nullable AutofillId[] detectedAutofillIds, @Nullable String[] detectedRemoteIds,
+            @Nullable int[] detectedScores) {
         assertThat(event).isNotNull();
         assertWithMessage("Wrong type for %s", event).that(event.getType()).isEqualTo(eventType);
         if (datasetId == null) {
@@ -1072,11 +1076,22 @@
                 .that(event.getChangedFields()).isEmpty();
         assertWithMessage("Event '%s' should not have manually-entered fields", event)
                 .that(event.getManuallyEnteredField()).isEmpty();
-        final Map<String, Integer> detectedFields = event.getFieldsClassification();
-        if (detectedRemoteId == null) {
+        final Map<AutofillId, FieldClassification> detectedFields = event.getFieldsClassification();
+        if (detectedAutofillIds == null) {
             assertThat(detectedFields).isEmpty();
         } else {
-            assertThat(detectedFields).containsExactly(detectedRemoteId, detectedScore);
+            assertThat(detectedFields).hasSize(detectedAutofillIds.length);
+            int i = 0;
+            for (Entry<AutofillId, FieldClassification> entry : detectedFields.entrySet()) {
+                assertWithMessage("Wrong field id at %s", i).that(entry.getKey())
+                        .isEqualTo(detectedAutofillIds[i]);
+                final Match topMatch = entry.getValue().getTopMatch();
+                assertWithMessage("Wrong remote id at %s", i).that(topMatch.getRemoteId())
+                        .isEqualTo(detectedRemoteIds[i]);
+                assertWithMessage("Wrong score at %s", i).that(topMatch.getScore())
+                        .isEqualTo(detectedScores[i]);
+                i++;
+            }
         }
     }
 
@@ -1089,7 +1104,7 @@
      */
     public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
             @Nullable String datasetId) {
-        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, null, null, null, -1);
+        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, null, null, null, null, null);
     }
 
     /**
@@ -1103,7 +1118,7 @@
      */
     public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
             @Nullable String datasetId, @Nullable String key, @Nullable String value) {
-        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, key, value, null, -1);
+        assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, key, value, null, null, null);
     }
 
     /**
@@ -1117,7 +1132,7 @@
      */
     public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
             @NonNull String datasetId, @NonNull String key, @NonNull String value) {
-        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null, -1);
+        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null, null, null);
     }
 
     /**
@@ -1129,7 +1144,7 @@
      */
     public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
             @NonNull String datasetId) {
-        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null, -1);
+        assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null, null, null);
     }
 
     /**
@@ -1146,7 +1161,7 @@
             @NonNull FillEventHistory.Event event,
             @Nullable String datasetId, @NonNull String key, @NonNull String value) {
         assertFillEvent(event, TYPE_DATASET_AUTHENTICATION_SELECTED, datasetId, key, value, null,
-                -1);
+                null, null);
     }
 
     /**
@@ -1161,18 +1176,27 @@
     public static void assertFillEventForAuthenticationSelected(
             @NonNull FillEventHistory.Event event,
             @Nullable String datasetId, @NonNull String key, @NonNull String value) {
-        assertFillEvent(event, TYPE_AUTHENTICATION_SELECTED, datasetId, key, value, null, -1);
+        assertFillEvent(event, TYPE_AUTHENTICATION_SELECTED, datasetId, key, value, null, null,
+                null);
     }
 
     // TODO(b/67867469): document
-    public static void assertFillEventForFieldsDetected(@NonNull FillEventHistory.Event event,
-            @NonNull String remoteId, int result) {
-        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, remoteId, result);
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull AutofillId fieldId, @NonNull String remoteId, int score) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null,
+                new AutofillId[] { fieldId }, new String[] { remoteId }, new int[] { score });
+    }
+
+    // TODO(b/67867469): document
+    public static void assertFillEventForFieldsClassification(@NonNull FillEventHistory.Event event,
+            @NonNull AutofillId[] fieldIds, @NonNull String[] remoteIds, @NonNull int[] scores) {
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, fieldIds, remoteIds,
+                scores);
     }
 
     // TODO(b/67867469): document
     public static void assertFillEventForContextCommitted(@NonNull FillEventHistory.Event event) {
-        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, null, -1);
+        assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, null, null, null);
     }
 
     private Helper() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
index e1100e8..61bcdf2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -72,6 +72,11 @@
     }
 
     @Test
+    public void testAdd_duplicatedValue() {
+        assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId2, mValue));
+    }
+
+    @Test
     public void testAdd_maximumReached() {
         // Must start from 1 because first is added on builder
         for (int i = 1; i < UserData.getMaxFieldClassificationIdsSize() - 1; i++) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index 6eab0f3..7375fd9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -69,6 +69,9 @@
         // Set service.
         enableService();
 
+        // Load WebView
+        mActivity.loadWebView();
+
         // Set expectations.
         sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 8282dc1..4063e79 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -93,7 +93,7 @@
      */
     @Test
     public void testTranslucentActivityOnTopOfHome() throws Exception {
-        if (noHomeScreen()) {
+        if (!hasHomeScreen()) {
             return;
         }
 
@@ -204,7 +204,7 @@
     private void performFinishActivityWithMoveTaskToBack(String finishPoint) throws Exception {
         // Make sure home activity is visible.
         launchHomeActivity();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true /* visible */);
         }
 
@@ -226,7 +226,7 @@
         // BroadcastActivity finishes, so homeActivity is not visible afterwards
 
         // Home must be visible.
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.waitForHomeActivityVisible();
             mAmWmState.assertHomeActivityVisible(true /* visible */);
         }
@@ -240,7 +240,7 @@
     public void testReorderToFrontBackstack() throws Exception {
         // Start with home on top
         launchHomeActivity();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true /* visible */);
         }
 
@@ -275,7 +275,7 @@
     public void testReorderToFrontChangingStack() throws Exception {
         // Start with home on top
         launchHomeActivity();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true /* visible */);
         }
 
@@ -288,7 +288,7 @@
 
         // Return home
         launchHomeActivity();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true /* visible */);
         }
         // Launch the launching activity from the alternate launching activity with reorder to
@@ -317,7 +317,7 @@
      */
     @Test
     public void testNoHistoryActivityFinishedResumedActivityNotIdle() throws Exception {
-        if (noHomeScreen()) {
+        if (!hasHomeScreen()) {
             return;
         }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 8dc396c..e382587 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -447,6 +447,10 @@
      */
     @Test
     public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+          log("Skipping test: no multi-window support");
+          return;
+        }
         requestOrientationInSplitScreen(1 /* portrait */, LANDSCAPE_ACTIVITY_NAME);
     }
 
@@ -455,6 +459,10 @@
      */
     @Test
     public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
+        if (!supportsSplitScreenMultiWindow()) {
+          log("Skipping test: no multi-window support");
+          return;
+        }
         requestOrientationInSplitScreen(0 /* landscape */, PORTRAIT_ACTIVITY_NAME);
     }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
index af88f2c..4e77ed9 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -210,7 +210,7 @@
                 TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
         assertAssistantStackExists();
         mAmWmState.waitForHomeActivityVisible();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true);
         }
 
@@ -239,7 +239,7 @@
         mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
         assertAssistantStackExists();
         mAmWmState.waitForHomeActivityVisible();
-        if (!noHomeScreen()) {
+        if (hasHomeScreen()) {
             mAmWmState.assertHomeActivityVisible(true);
         }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
index 3a73a2a..7eb3f37 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -32,6 +32,8 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.compatibility.common.util.PollingCheck;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -134,9 +136,19 @@
     public void testMaxAspectRatioResizeableActivity() throws Exception {
         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
         final float expected = getAspectRatio(context);
+        final Activity testActivity = launchActivity(mMaxAspectRatioResizeableActivity);
+        PollingCheck.waitFor(testActivity::hasWindowFocus);
+
+        Display testDisplay = testActivity.findViewById(android.R.id.content).getDisplay();
+
+        // TODO(b/69982434): Fix DisplayManager NPE when getting display from Instrumentation
+        // context, then can use DisplayManager to get the aspect ratio of the correct display.
+        if (testDisplay.getDisplayId() != Display.DEFAULT_DISPLAY) {
+            return;
+        }
 
         // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
-        runAspectRatioTest(mMaxAspectRatioResizeableActivity, actual -> {
+        runTest(testActivity, actual -> {
             if (aspectRatioEqual(expected, actual) || expected < actual) return;
             fail("actual=" + actual + " is less than expected=" + expected);
         });
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
index cf22ad1..6d1dc53 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
@@ -33,7 +33,7 @@
     void runAspectRatioTest(final ActivityTestRule activityRule,
             final AssertAspectRatioCallback callback) {
         final Activity activity = launchActivity(activityRule);
-        callback.assertAspectRatio(getAspectRatio(activity));
+        runTest(activity, callback);
         finishActivity(activityRule);
 
         // TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
@@ -48,6 +48,10 @@
 //        callback.assertAspectRatio(getAspectRatio(activity));
     }
 
+    protected void runTest(Activity activity, AssertAspectRatioCallback callback) {
+        callback.assertAspectRatio(getAspectRatio(activity));
+    }
+
      static float getAspectRatio(Context context) {
         final Display display =
                 context.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -58,7 +62,7 @@
         return longSide / shortSide;
     }
 
-    private Activity launchActivity(final ActivityTestRule activityRule) {
+    protected Activity launchActivity(final ActivityTestRule activityRule) {
         final Activity activity = activityRule.launchActivity(null);
         waitForIdle();
         return activity;
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index 4bd76f1..092eae0 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -54,7 +54,7 @@
 
     @Test
     public void testKeyguardHidesActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("TestActivity");
@@ -69,7 +69,7 @@
 
     @Test
     public void testShowWhenLockedActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedActivity");
@@ -89,7 +89,7 @@
      */
     @Test
     public void testShowWhenLockedActivity_withDialog() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedWithDialogActivity");
@@ -110,7 +110,7 @@
      */
     @Test
     public void testMultipleShowWhenLockedActivities() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedActivity");
@@ -133,7 +133,7 @@
      */
     @Test
     public void testTranslucentShowWhenLockedActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedTranslucentActivity");
@@ -153,7 +153,7 @@
      */
     @Test
     public void testTranslucentDoesntRevealBehind() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("TestActivity");
@@ -173,7 +173,7 @@
 
     @Test
     public void testDialogShowWhenLockedActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedDialogActivity");
@@ -193,7 +193,7 @@
      */
     @Test
     public void testShowWhenLockedActivityWhileSplit() throws Exception {
-        if (!isHandheld() || !supportsSplitScreenMultiWindow()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset() || !supportsSplitScreenMultiWindow()) {
             return;
         }
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
@@ -214,7 +214,7 @@
      */
     @Test
     public void testDismissKeyguardActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -229,7 +229,7 @@
 
     @Test
     public void testDismissKeyguardActivity_method() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         final String logSeparator = clearLogcat();
@@ -246,7 +246,7 @@
 
     @Test
     public void testDismissKeyguardActivity_method_notTop() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         final String logSeparator = clearLogcat();
@@ -261,7 +261,7 @@
 
     @Test
     public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         final String logSeparator = clearLogcat();
@@ -278,7 +278,7 @@
 
     @Test
     public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -295,7 +295,7 @@
 
     @Test
     public void testKeyguardLock() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -311,7 +311,7 @@
 
     @Test
     public void testUnoccludeRotationChange() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -339,7 +339,7 @@
 
     @Test
     public void testDismissKeyguardAttrActivity_method_turnScreenOn() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
@@ -359,7 +359,7 @@
 
     @Test
     public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
@@ -379,7 +379,7 @@
 
     @Test
     public void testScreenOffWhileOccludedStopsActivity() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
@@ -397,7 +397,7 @@
 
     @Test
     public void testScreenOffCausesSingleStop() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
index 2abae6a..3520f52 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -44,7 +44,7 @@
 
     @Test
     public void testUnlock() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("TestActivity");
@@ -56,7 +56,7 @@
     }
     @Test
     public void testUnlockWallpaper() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("WallpaperActivity");
@@ -68,7 +68,7 @@
     }
     @Test
     public void testOcclude() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -79,7 +79,7 @@
     }
     @Test
     public void testUnocclude() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         gotoKeyguard();
@@ -92,7 +92,7 @@
     }
     @Test
     public void testNewActivityDuringOccluded() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
         launchActivity("ShowWhenLockedActivity");
@@ -104,7 +104,7 @@
     }
     @Test
     public void testOccludeManifestAttr() throws Exception {
-         if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
              return;
          }
 
@@ -120,7 +120,7 @@
     }
     @Test
     public void testOccludeAttrRemove() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
@@ -142,7 +142,7 @@
     }
     @Test
     public void testNewActivityDuringOccludedWithAttr() throws Exception {
-        if (!isHandheld()) {
+        if (!isHandheld() || isUiModeLockedToVrHeadset()) {
             return;
         }
 
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 5e9a465..40ecfff 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -113,6 +113,11 @@
 
     private static final String DEFAULT_COMPONENT_NAME = "android.server.am";
 
+    private static final int UI_MODE_TYPE_MASK = 0x0f;
+    private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
+
+    private static Boolean sHasHomeScreen = null;
+
     // TODO: Remove this when all activity name are specified by {@link ComponentName}.
     static String componentName = DEFAULT_COMPONENT_NAME;
 
@@ -568,13 +573,43 @@
         return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
     }
 
+    // TODO: Switch to using a feature flag, when available.
+    protected boolean isUiModeLockedToVrHeadset() {
+        final String output = runCommandAndPrintOutput("dumpsys uimode");
+
+        Integer curUiMode = null;
+        Boolean uiModeLocked = null;
+        for (String line : output.split("\\n")) {
+            line = line.trim();
+            Matcher matcher = sCurrentUiModePattern.matcher(line);
+            if (matcher.find()) {
+                curUiMode = Integer.parseInt(matcher.group(1), 16);
+            }
+            matcher = sUiModeLockedPattern.matcher(line);
+            if (matcher.find()) {
+                uiModeLocked = matcher.group(1).equals("true");
+            }
+        }
+
+        boolean uiModeLockedToVrHeadset = (curUiMode != null) && (uiModeLocked != null)
+                && ((curUiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET) && uiModeLocked;
+
+        if (uiModeLockedToVrHeadset) {
+            log("UI mode is locked to VR headset");
+        }
+
+        return uiModeLockedToVrHeadset;
+    }
+
     protected boolean supportsSplitScreenMultiWindow() {
         return ActivityManager.supportsSplitScreenMultiWindow(mContext);
     }
 
-    protected boolean noHomeScreen() {
-        String output = executeShellCommand(AM_NO_HOME_SCREEN);
-        return output.startsWith("true");
+    protected boolean hasHomeScreen() {
+        if (sHasHomeScreen == null) {
+            sHasHomeScreen = !executeShellCommand(AM_NO_HOME_SCREEN).startsWith("true");
+        }
+        return sHasHomeScreen;
     }
 
     /**
@@ -1101,6 +1136,9 @@
             + " orientation=(\\d+)");
     private static final Pattern sDisplayStatePattern =
             Pattern.compile("Display Power: state=(.+)");
+    private static final Pattern sCurrentUiModePattern = Pattern.compile("mCurUiMode=0x(\\d+)");
+    private static final Pattern sUiModeLockedPattern =
+            Pattern.compile("mUiModeLocked=(true|false)");
 
     class ReportedSizes {
         int widthDp;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
index 2fda508..48fe59a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
@@ -57,6 +57,10 @@
 
     void doDockedTest(String testCase, ParentChildTest t) throws Exception {
         log("Running test docked");
+        if (!supportsSplitScreenMultiWindow()) {
+            log("Skipping test: no split multi-window support");
+            return;
+        }
         startTestCaseDocked(testCase);
         doSingleTest(t);
         stopTestCase();
diff --git a/tests/tests/appwidget/AndroidManifest.xml b/tests/tests/appwidget/AndroidManifest.xml
index 6f7d053..e7e7944 100644
--- a/tests/tests/appwidget/AndroidManifest.xml
+++ b/tests/tests/appwidget/AndroidManifest.xml
@@ -38,6 +38,30 @@
               android:resource="@xml/second_appwidget_info" />
       </receiver>
 
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider1" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature1" />
+      </receiver>
+
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider2" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature2" />
+      </receiver>
+
+      <receiver android:name="android.appwidget.cts.provider.AppWidgetProviderWithFeatures$Provider3" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/appwidget_info_with_feature3" />
+      </receiver>
+
       <service android:name="android.appwidget.cts.service.MyAppWidgetService"
           android:permission="android.permission.BIND_REMOTEVIEWS">
       </service>
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml
new file mode 100644
index 0000000..cadb283
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature1.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="reconfigurable"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml
new file mode 100644
index 0000000..89d60eb
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature2.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="hide_from_picker"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml b/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml
new file mode 100644
index 0000000..25936ab
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/appwidget_info_with_feature3.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:widgetFeatures="reconfigurable|hide_from_picker"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 747a8bd..acafd7d 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -33,6 +33,7 @@
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.appwidget.cts.provider.AppWidgetProviderCallbacks;
+import android.appwidget.cts.provider.AppWidgetProviderWithFeatures;
 import android.appwidget.cts.provider.FirstAppWidgetProvider;
 import android.appwidget.cts.provider.SecondAppWidgetProvider;
 import android.appwidget.cts.service.MyAppWidgetService;
@@ -1308,6 +1309,25 @@
         }
     }
 
+    public void testWidgetFeaturesParsed() throws Exception {
+        if (!hasAppWidgets()) {
+            return;
+        }
+        assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures);
+        String packageName = getInstrumentation().getTargetContext().getPackageName();
+
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE,
+                getProviderInfo(new ComponentName(packageName,
+                AppWidgetProviderWithFeatures.Provider1.class.getName())).widgetFeatures);
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER,
+                getProviderInfo(new ComponentName(packageName,
+                        AppWidgetProviderWithFeatures.Provider2.class.getName())).widgetFeatures);
+        assertEquals(AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
+                | AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER,
+                getProviderInfo(new ComponentName(packageName,
+                        AppWidgetProviderWithFeatures.Provider3.class.getName())).widgetFeatures);
+    }
+
     private void waitForCallCount(AtomicInteger counter, int expectedCount) {
         synchronized (mLock) {
             final long startTimeMillis = SystemClock.uptimeMillis();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java
new file mode 100644
index 0000000..d1f848a
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderWithFeatures.java
@@ -0,0 +1,45 @@
+/*
+ * 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.appwidget.cts.provider;
+
+public abstract class AppWidgetProviderWithFeatures extends StubbableAppWidgetProvider {
+    private static final Object sLock = new Object();
+
+    private static AppWidgetProviderCallbacks sCallbacks;
+
+    public static void setCallbacks(AppWidgetProviderCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected AppWidgetProviderCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setProvider(this);
+            }
+            return sCallbacks;
+        }
+    }
+
+    public static final class Provider1 extends AppWidgetProviderWithFeatures { }
+
+    public static final class Provider2 extends AppWidgetProviderWithFeatures { }
+
+    public static final class Provider3 extends AppWidgetProviderWithFeatures { }
+}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 5185fdb..73252db 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.content.pm.PackageManager;
+import android.media.AudioSystem;
 import android.media.MediaPlayer;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -59,6 +60,8 @@
     private boolean mHasVibrator;
     private boolean mUseFixedVolume;
     private boolean mIsTelevision;
+    private boolean mIsSingleVolume;
+    private boolean mSkipRingerTests;
     private Context mContext;
     private final static int ASYNC_TIMING_TOLERANCE_MS = 50;
 
@@ -77,6 +80,9 @@
         mIsTelevision = packageManager != null
                 && (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
                         || packageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
+        mIsSingleVolume = mContext.getResources().getBoolean(
+                Resources.getSystem().getIdentifier("config_single_volume", "bool", "android"));
+        mSkipRingerTests = mUseFixedVolume || mIsTelevision || mIsSingleVolume;
     }
     @Override
     protected void tearDown() throws Exception {
@@ -156,6 +162,9 @@
     }
 
     public void testMusicActive() throws Exception {
+        if (mAudioManager.isMusicActive()) {
+            return;
+        }
         MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);
         assertNotNull(mp);
         mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
@@ -353,14 +362,14 @@
         mAudioManager.setRingerMode(RINGER_MODE_SILENT);
         // AudioService#setRingerMode() has:
         // if (isTelevision) return;
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             assertEquals(RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
         } else {
             assertEquals(RINGER_MODE_SILENT, mAudioManager.getRingerMode());
         }
 
         mAudioManager.setRingerMode(RINGER_MODE_VIBRATE);
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             assertEquals(RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
         } else {
             assertEquals(mHasVibrator ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT,
@@ -369,7 +378,7 @@
     }
 
     public void testSetRingerModePolicyAccess() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         // Apps without policy access cannot change silent -> normal or silent -> vibrate.
@@ -432,7 +441,7 @@
     }
 
     public void testVolumeDndAffectedStream() throws Exception {
-        if (mUseFixedVolume || mHasVibrator || mIsTelevision) {
+        if (mHasVibrator || mSkipRingerTests) {
             return;
         }
         Utils.toggleNotificationPolicyAccess(
@@ -575,6 +584,11 @@
         mAudioManager.adjustVolume(ADJUST_RAISE, 0);
         Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
 
+        boolean isMusicPlayingBeforeTest = false;
+        if (mAudioManager.isMusicActive()) {
+            isMusicPlayingBeforeTest = true;
+        }
+
         MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);
         assertNotNull(mp);
         mp.setAudioStreamType(STREAM_MUSIC);
@@ -610,7 +624,9 @@
         mp.stop();
         mp.release();
         Thread.sleep(TIME_TO_PLAY);
-        assertFalse(mAudioManager.isMusicActive());
+        if (!isMusicPlayingBeforeTest) {
+            assertFalse(mAudioManager.isMusicActive());
+        }
     }
 
     public void testAccessibilityVolume() throws Exception {
@@ -680,7 +696,7 @@
     }
 
     public void testMuteDndAffectedStreams() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         int[] streams = { AudioManager.STREAM_RING };
@@ -754,7 +770,7 @@
     }
 
     public void testMuteDndUnaffectedStreams() throws Exception {
-        if (mUseFixedVolume  || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         int[] streams = {
@@ -840,7 +856,7 @@
     }
 
     public void testAdjustVolumeInTotalSilenceMode() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -860,7 +876,7 @@
     }
 
     public void testAdjustVolumeInAlarmsOnlyMode() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -883,7 +899,7 @@
     }
 
     public void testSetStreamVolumeInTotalSilenceMode() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
@@ -905,7 +921,7 @@
     }
 
     public void testSetStreamVolumeInAlarmsOnlyMode() throws Exception {
-        if (mUseFixedVolume || mIsTelevision) {
+        if (mSkipRingerTests) {
             return;
         }
         try {
diff --git a/tests/tests/security/res/raw/bug_65483665.mp4 b/tests/tests/security/res/raw/bug_65483665.mp4
new file mode 100644
index 0000000..105e274
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_65483665.mp4
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_69478425.mp4 b/tests/tests/security/res/raw/bug_69478425.mp4
new file mode 100644
index 0000000..b8ff0c5
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_69478425.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index bb7e455..62ca28d 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -247,6 +247,11 @@
      ***********************************************************/
 
     @SecurityTest
+    public void testStagefright_bug_65483665() throws Exception {
+        doStagefrightTest(R.raw.bug_65483665);
+    }
+
+    @SecurityTest
     public void testStagefright_cve_2017_0852_b_62815506() throws Exception {
         doStagefrightTest(R.raw.cve_2017_0852_b_62815506);
     }
@@ -257,6 +262,11 @@
      ***********************************************************/
 
     @SecurityTest
+    public void testStagefright_bug_69478425() throws Exception {
+        doStagefrightTest(R.raw.bug_69478425);
+    }
+
+    @SecurityTest
     public void testStagefright_bug_65717533() throws Exception {
         doStagefrightTest(R.raw.bug_65717533_header_corrupt);
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 79d5ec3..32be1c9 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -2100,7 +2100,7 @@
         }
         final String p = "<p style=\"height:1000px;width:1000px\">Test setInitialScale.</p>";
         final float defaultScale =
-            getInstrumentation().getTargetContext().getResources().getDisplayMetrics().density;
+            getActivity().getResources().getDisplayMetrics().density;
 
         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
                 + "</body></html>", "text/html", null);
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index b447e9d..51e6102 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -125,9 +125,9 @@
     @Before
     public void setup() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mContext = mInstrumentation.getTargetContext();
-
         final Activity activity = mActivityRule.getActivity();
+        // Always use the activity context
+        mContext = activity;
 
         PollingCheck.waitFor(activity::hasWindowFocus);
 
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java b/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java
index 64b1693..ee6dc39 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListView_ScrollTest.java
@@ -97,7 +97,7 @@
         mActivityRule.runOnUiThread(() -> mListView.setAdapter(mCountriesAdapter));
         mInstrumentation.waitForIdleSync();
 
-        mRowHeightPx = mContext.getResources().getDimensionPixelSize(R.dimen.listrow_height);
+        mRowHeightPx = activity.getResources().getDimensionPixelSize(R.dimen.listrow_height);
     }
 
     /**
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
index d3943e5..ce093e7 100644
--- a/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerTest.java
@@ -776,6 +776,12 @@
                 : (TimePicker) mActivity.findViewById(R.id.timepicker_spinner);
 
         mActivityRule.runOnUiThread(() -> {
+            /* hide one of the widgets to assure they fit onto the screen */
+            if (isClockMode) {
+                mActivity.findViewById(R.id.timepicker_spinner).setVisibility(View.GONE);
+            } else {
+                mActivity.findViewById(R.id.timepicker_clock).setVisibility(View.GONE);
+            }
             mTimePicker.setIs24HourView(is24hFormat);
             mTimePicker.setHour(initialHour);
             mTimePicker.setMinute(initialMinute);
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 4f36b8f..f20f190 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -94,6 +94,7 @@
     checkFile ${JAR_DIR}/${JAR}.jar
     JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}.jar
 done
+JAR_PATH=${JAR_PATH:1} # Strip off leading ':'
 
 OPTIONAL_JARS="
   google-tradefed