Merge "Added new tests on LoginActivityTest:" into oc-dev
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 0355cb4..23744c7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -32,7 +32,6 @@
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.util.Log;
 import android.view.View;
@@ -101,7 +100,7 @@
         tests.add(new SetModeAllTest());
         tests.add(new AllInterceptsNothingTest());
         tests.add(new DefaultOrderTest());
-        tests.add(new PrioritytOrderTest());
+        tests.add(new PriorityOrderTest());
         tests.add(new InterruptionOrderTest());
         tests.add(new AmbientBitsTest());
         tests.add(new LookupUriOrderTest());
@@ -113,13 +112,11 @@
 
     private void createChannels() {
         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
-                NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+                NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_MIN);
         mNm.createNotificationChannel(channel);
         NotificationChannel noisyChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY,
                 NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
         noisyChannel.enableVibration(true);
-        noisyChannel.setSound(
-                Settings.System.DEFAULT_RINGTONE_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT);
         mNm.createNotificationChannel(noisyChannel);
     }
 
@@ -528,7 +525,7 @@
     }
 
     // ordered by priority: B, C, A
-    protected class PrioritytOrderTest extends InteractiveTestCase {
+    protected class PriorityOrderTest extends InteractiveTestCase {
         @Override
         View inflate(ViewGroup parent) {
             return createAutoItem(parent, R.string.attention_priority_order);
@@ -585,8 +582,8 @@
         @Override
         void setUp() {
             createChannels();
-            // send B & C noisy
-            sendNotifications(SEND_B | SEND_C, MODE_NONE, false, true);
+            // send B & C noisy with contact affinity
+            sendNotifications(SEND_B | SEND_C, MODE_URI, false, true);
             status = READY;
             // wait for then to not be recently noisy any more
             delay(15000);
@@ -595,7 +592,7 @@
         @Override
         void test() {
             if (status == READY) {
-                // send A noisy
+                // send A noisy but no contact affinity
                 sendNotifications(SEND_A, MODE_NONE, false, true);
                 status = RETEST;
                 delay();
@@ -650,7 +647,8 @@
         @Override
         void setUp() {
             createChannels();
-            sendNotifications(MODE_NONE, true, false);
+            sendNotifications(SEND_B | SEND_C, MODE_NONE, true, true);
+            sendNotifications(SEND_A, MODE_NONE, true, false);
             status = READY;
             // wait for notifications to move through the system
             delay();
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index b91c264..e584bc4 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -26,7 +26,6 @@
   $(DEVICE_INFO_PACKAGE).GenericDeviceInfo \
   $(DEVICE_INFO_PACKAGE).GlesStubActivity \
   $(DEVICE_INFO_PACKAGE).GraphicsDeviceInfo \
-  $(DEVICE_INFO_PACKAGE).LibraryDeviceInfo \
   $(DEVICE_INFO_PACKAGE).LocaleDeviceInfo \
   $(DEVICE_INFO_PACKAGE).MediaDeviceInfo \
   $(DEVICE_INFO_PACKAGE).MemoryDeviceInfo \
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
deleted file mode 100644
index 792c88a..0000000
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/LibraryDeviceInfo.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.deviceinfo;
-
-import android.util.Log;
-
-import com.android.compatibility.common.util.DeviceInfoStore;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Formatter;
-
-/**
- * Library device info collector.
- */
-public final class LibraryDeviceInfo extends DeviceInfo {
-
-    private static final String TAG = "LibraryDeviceInfo";
-    private static final int BUFFER_SIZE_BYTES = 4096;
-
-    @Override
-    protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
-        collectSystemLibs(store);
-        collectVendorLibs(store);
-        collectFrameworkJars(store);
-    }
-
-    private void collectSystemLibs(DeviceInfoStore store) throws Exception {
-        store.startArray("lib");
-        collectFileDetails(store, "/system/lib", ".so");
-        store.endArray();
-    }
-
-    private void collectVendorLibs(DeviceInfoStore store) throws Exception {
-        store.startArray("vendor_lib");
-        collectFileDetails(store, "/system/vendor/lib", ".so");
-        store.endArray();
-    }
-
-    private void collectFrameworkJars(DeviceInfoStore store) throws Exception {
-        store.startArray("framework_jar");
-        collectFileDetails(store, "/system/framework", ".jar");
-        store.endArray();
-    }
-
-    private void collectFileDetails(DeviceInfoStore store, String path, String suffix)
-            throws Exception {
-        File dir = new File(path);
-        File[] files = dir.listFiles();
-        if (files == null) {
-            return;
-        }
-        for (File file : files) {
-            String name = file.getName();
-            if (file.isFile() && name.endsWith(suffix)) {
-                String sha1 = "unknown";
-                try {
-                    sha1 = getSha1sum(file);
-                } catch (IOException e) {
-                    Log.e(TAG, "Failed to hash " + file + ": ", e);
-                }
-                store.startGroup();
-                store.addResult("name", name);
-                store.addResult("sha1", sha1);
-                store.endGroup();
-            }
-        }
-    }
-
-    private static String getSha1sum(File file) throws IOException {
-        InputStream in = null;
-        try {
-            in = new FileInputStream(file);
-            return sha1(in);
-        } finally {
-            close(in);
-        }
-    }
-
-    private static void close(Closeable s) throws IOException {
-        if (s == null) {
-            return;
-        }
-        s.close();
-    }
-
-    /**
-     * @return the SHA-1 digest of input as a hex string
-     */
-    public static String sha1(InputStream input) throws IOException {
-        try {
-            return toHexString(digest(input, "sha1"));
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static byte[] digest(InputStream in, String algorithm)
-        throws NoSuchAlgorithmException, IOException {
-        MessageDigest digest = MessageDigest.getInstance(algorithm);
-        byte[] buffer = new byte[BUFFER_SIZE_BYTES];
-        while (true) {
-            int read = in.read(buffer);
-            if (read < 0) {
-                break;
-            }
-            digest.update(buffer, 0, read);
-        }
-        return digest.digest();
-    }
-
-    private static String toHexString(byte[] buffer) {
-        Formatter formatter = new Formatter();
-        try {
-            for (byte b : buffer) {
-                formatter.format("%02X", b);
-            }
-            return formatter.toString();
-        } finally {
-            formatter.close();
-        }
-    }
-}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 3a282bf..6a6c27d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -47,8 +47,13 @@
     private static final String MODE_EMULATED = "emulated";
     private static final String MODE_NONE = "none";
 
+    private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin\n";
+    private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive\n";
+
     private static final long SHUTDOWN_TIME_MS = 30 * 1000;
 
+    private String mFeatureList = null;
+
     private IAbi mAbi;
     private IBuildInfo mCtsBuild;
 
@@ -82,6 +87,22 @@
     }
 
     /**
+     * Automotive devices MUST support native FBE.
+     */
+    public void testAutomotiveNativeFbe() throws Exception {
+        if (!isSupportedDevice()) {
+            Log.v(TAG, "Device not supported; skipping test");
+            return;
+        } else if (!isAutomotiveDevice()) {
+            Log.v(TAG, "Device not automotive; skipping test");
+            return;
+        }
+
+        assertTrue("Automotive devices must support native FBE",
+            MODE_NATIVE.equals(getFbeMode()));
+    }
+
+    /**
      * If device has native FBE, verify lifecycle.
      */
     public void testDirectBootNative() throws Exception {
@@ -227,9 +248,20 @@
         return "1".equals(output);
     }
 
+    private boolean hasSystemFeature(final String feature) throws Exception {
+        if (mFeatureList == null) {
+            mFeatureList = getDevice().executeShellCommand("pm list features");
+        }
+
+        return mFeatureList.contains(feature);
+    }
+
     private boolean isSupportedDevice() throws Exception {
-        final String featureList = getDevice().executeShellCommand("pm list features");
-        return featureList.contains("feature:android.software.device_admin\n");
+        return hasSystemFeature(FEATURE_DEVICE_ADMIN);
+    }
+
+    private boolean isAutomotiveDevice() throws Exception {
+        return hasSystemFeature(FEATURE_AUTOMOTIVE);
     }
 
     private void waitForBootCompleted() throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 77513c5..7de83d6 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -110,12 +110,9 @@
         runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartEphemeral");
     }
 
-    /*
-     * Disabled pending drops of updated prebuilts
     public void testExposedSystemActivities() throws Exception {
         runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testExposedSystemActivities");
     }
-    */
 
     public void testBuildSerialUnknown() throws Exception {
         runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testBuildSerialUnknown");
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 21afece..fa8730d 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -97,9 +97,6 @@
                 ContactsContract.CommonDataKinds.Email.CONTENT_TYPE, null),
         makeIntent(Intent.ACTION_PICK, null,
                 ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_TYPE, null),
-        makeIntent(Intent.ACTION_INSERT, null, ContactsContract.Contacts.CONTENT_TYPE, null),
-        // Email
-        makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("mailto:")),
         // File Storage
         makeIntent(Intent.ACTION_OPEN_DOCUMENT, Intent.CATEGORY_OPENABLE, "*/*", null),
         makeIntent(Intent.ACTION_OPEN_DOCUMENT, null, "*/*", null),
@@ -108,13 +105,8 @@
         makeIntent(Intent.ACTION_OPEN_DOCUMENT_TREE, null, null, null),
         makeIntent(Intent.ACTION_CREATE_DOCUMENT, Intent.CATEGORY_OPENABLE, "text/plain", null),
         makeIntent(Intent.ACTION_CREATE_DOCUMENT, null, "text/plain", null),
-        // Phone call
-        makeIntent(Intent.ACTION_DIAL, null, null, Uri.parse("tel:")),
-        // SMS
-        makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("sms:")),
-        makeIntent(Intent.ACTION_SEND, null, "text/plain", Uri.parse("smsto:")),
-        // Web
-        makeIntent(Intent.ACTION_VIEW, null, "text/html", Uri.parse("https://example.com")),
+        // Framework
+        makeIntent(Intent.ACTION_CHOOSER, null, null, null),
     };
 
     private BroadcastReceiver mReceiver;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
index b8cc99d..f0b8279 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
@@ -105,68 +105,91 @@
         }
 
         // Install
+        CLog.i("Installing apk1...");
         installAppAsUser(OWNER_APK_1, getUserId());
+
+        CLog.i("Making it a device/profile owner...");
         setAsOwnerOrFail(OWNER_COMPONENT);
 
         withRetry(() -> assertServiceBound(OWNER_SERVICE));
 
         // Remove admin.
+        CLog.i("Removing admin...");
         removeAdmin(OWNER_COMPONENT, getUserId());
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
 
         // Overwrite -> update.
+        CLog.i("Re-installing apk1...");
         installAppAsUser(OWNER_APK_1, getUserId());
+
+        CLog.i("Making it a device/profile owner...");
         setAsOwnerOrFail(OWNER_COMPONENT);
         withRetry(() -> assertServiceBound(OWNER_SERVICE));
 
+        CLog.i("Installing apk2...");
         installAppAsUser(OWNER_APK_2, getUserId());
         withRetry(() -> assertServiceBound(OWNER_SERVICE)); // Should still be bound.
 
         // Service exported -> not bound.
+        CLog.i("Installing apk3...");
         installAppAsUser(OWNER_APK_3, getUserId());
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
 
         // Recover.
+        CLog.i("Installing apk2 again...");
         installAppAsUser(OWNER_APK_2, getUserId());
         withRetry(() -> assertServiceBound(OWNER_SERVICE));
 
         // Multiple service found -> not bound.
+        CLog.i("Installing apk4...");
         installAppAsUser(OWNER_APK_4, getUserId());
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
 
         // Disable service1 -> now there's only one service, so should be bound.
+        CLog.i("Running testDisableService1...");
         executeDeviceTestMethod(".ComponentController", "testDisableService1");
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
         withRetry(() -> assertServiceBound(OWNER_SERVICE2));
 
+        CLog.i("Running testDisableService2...");
         executeDeviceTestMethod(".ComponentController", "testDisableService2");
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
 
+        CLog.i("Running testEnableService1...");
         executeDeviceTestMethod(".ComponentController", "testEnableService1");
         withRetry(() -> assertServiceBound(OWNER_SERVICE));
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
 
+        CLog.i("Running testEnableService2...");
         executeDeviceTestMethod(".ComponentController", "testEnableService2");
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
 
         // Remove admin.
+        CLog.i("Removing admin again...");
         removeAdmin(OWNER_COMPONENT, getUserId());
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
 
         // Retry with package 1 and remove admin.
+        CLog.i("Installing apk1 again...");
         installAppAsUser(OWNER_APK_1, getUserId());
+
+        CLog.i("Making it a device/profile owner again...");
         setAsOwnerOrFail(OWNER_COMPONENT);
         withRetry(() -> assertServiceBound(OWNER_SERVICE));
 
+        CLog.i("Removing admin again...");
         removeAdmin(OWNER_COMPONENT, getUserId());
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
 
         // Now install package B and make it the owner.  OWNER_APK_1 still exists, but it shouldn't
         // interfere.
+        CLog.i("Installing apk B...");
         installAppAsUser(OWNER_APK_B, getUserId());
+
+        CLog.i("Making it a device/profile owner...");
         setAsOwnerOrFail(OWNER_COMPONENT_B);
         withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
         withRetry(() -> assertServiceBound(OWNER_SERVICE_B));
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index fd3e85d..8ba3064 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -387,7 +387,7 @@
     }
 
     private void checkBattery(String[] parts) {
-        assertEquals(13, parts.length);
+        assertEquals(15, parts.length);
         if (!parts[4].equals("N/A")) {
             assertInteger(parts[4]);  // startCount
         }
@@ -399,6 +399,9 @@
         long bOffReal = assertInteger(parts[10]); // batteryScreenOffRealtime
         long bOffUp = assertInteger(parts[11]); // batteryScreenOffUptime
         long bEstCap = assertInteger(parts[12]); // batteryEstimatedCapacity
+        assertInteger(parts[13]); // minLearnedBatteryCapacity
+        assertInteger(parts[14]); // maxLearnedBatteryCapacity
+
         // The device cannot be up more than there are real-world seconds.
         assertTrue("batteryRealtime must be >= batteryUptime", bReal >= bUp);
         assertTrue("totalRealtime must be >= totalUptime", tReal >= tUp);
diff --git a/hostsidetests/edi/Android.mk b/hostsidetests/edi/Android.mk
new file mode 100644
index 0000000..efa093f
--- /dev/null
+++ b/hostsidetests/edi/Android.mk
@@ -0,0 +1,32 @@
+# 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := tests
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MODULE := CtsEdiHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
new file mode 100644
index 0000000..94be6b6
--- /dev/null
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS EDI host test cases">
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsEdiHostTestCases.jar" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
new file mode 100644
index 0000000..b9f83f4
--- /dev/null
+++ b/hostsidetests/edi/src/android/edi/cts/LibraryDeviceInfo.java
@@ -0,0 +1,88 @@
+/*
+ * 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.edi.cts;
+
+import com.android.compatibility.common.util.DeviceInfo;
+import com.android.compatibility.common.util.HostInfoStore;
+import com.android.ddmlib.FileListingService.FileEntry;
+import com.android.tradefed.device.IFileEntry;
+import com.android.tradefed.device.ITestDevice;
+
+import java.util.Arrays;
+
+public class LibraryDeviceInfo extends DeviceInfo {
+
+    private ITestDevice mDevice;
+
+    @Override
+    protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+
+        mDevice = getDevice();
+
+        collectSystemLibs(store);
+        collectVendorLibs(store);
+        collectFrameworkJars(store);
+    }
+
+    private void collectSystemLibs(HostInfoStore store) throws Exception {
+        store.startArray("lib");
+        collectFileDetails(store, "/system/lib", ".so");
+        store.endArray();
+    }
+
+    private void collectVendorLibs(HostInfoStore store) throws Exception {
+        store.startArray("vendor_lib");
+        collectFileDetails(store, "/system/vendor/lib", ".so");
+        store.endArray();
+    }
+
+    private void collectFrameworkJars(HostInfoStore store) throws Exception {
+        store.startArray("framework_jar");
+        collectFileDetails(store, "/system/framework", ".jar");
+        store.endArray();
+    }
+
+    private void collectFileDetails(HostInfoStore store, String path, String suffix)
+            throws Exception {
+        IFileEntry dir = mDevice.getFileEntry(path);
+
+        if(dir == null || !dir.isDirectory()) {
+            return;
+        }
+
+        for (IFileEntry file : dir.getChildren(false)) {
+            String name = file.getName();
+            if (!file.isDirectory() && name.endsWith(suffix)) {
+                String sha1 = getSha1(file.getFullPath());
+                store.startGroup();
+                store.addResult("name", name);
+                store.addResult("sha1", sha1);
+                store.endGroup();
+            }
+        }
+    }
+
+    private String getSha1(String filePath) {
+        String sha1 = "unknown";
+        try {
+            String out = mDevice.executeShellCommand("sha1sum " + filePath);
+            sha1 = out.split(" ", 2)[0].toUpperCase();
+        } catch (Exception e) {
+            // Do nothing.
+        }
+        return sha1;
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
index 496e61a..bc28c07 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBackgroundService.java
@@ -17,6 +17,7 @@
 package com.android.server.cts.device.batterystats;
 
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
 
@@ -45,7 +46,9 @@
             Log.w(TAG, "Couldn't determine if app is in background. Proceeding with test anyway.");
         }
 
-        Log.i(TAG, "Starting action from background service");
-        doAction(this, intent.getStringExtra(KEY_ACTION));
+        String action = intent.getStringExtra(KEY_ACTION);
+        String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+        Log.i(TAG, "Starting " + action + " from background service as request " + requestCode);
+        doAction(this, action, requestCode);
     }
 }
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
index 480ca2f..3c9abda 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsBgVsFgActions.java
@@ -55,9 +55,10 @@
     public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
     public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
 
+    public static final String KEY_REQUEST_CODE = "request_code";
 
     /** Perform the action specified by the given action code (see constants above). */
-    public static void doAction(Context ctx, String actionCode) {
+    public static void doAction(Context ctx, String actionCode, String requestCode) {
         if (actionCode == null) {
             Log.e(TAG, "Intent was missing action.");
             return;
@@ -65,22 +66,22 @@
         sleep(100);
         switch(actionCode) {
             case ACTION_BLE_SCAN:
-                doBleScan(ctx);
+                doBleScan(ctx, requestCode);
                 break;
             case ACTION_JOB_SCHEDULE:
-                doScheduleJob(ctx);
+                doScheduleJob(ctx, requestCode);
                 break;
             case ACTION_SYNC:
-                doSync(ctx);
+                doSync(ctx, requestCode);
                 break;
             case ACTION_WIFI_SCAN:
-                doWifiScan(ctx);
+                doWifiScan(ctx, requestCode);
                 break;
             case ACTION_WIFI_DOWNLOAD:
-                doWifiDownload(ctx);
+                doWifiDownload(ctx, requestCode);
                 break;
             case ACTION_WIFI_UPLOAD:
-                doWifiUpload(ctx);
+                doWifiUpload(ctx, requestCode);
                 break;
             default:
                 Log.e(TAG, "Intent had invalid action");
@@ -88,7 +89,7 @@
         sleep(100);
     }
 
-    private static void doBleScan(Context ctx) {
+    private static void doBleScan(Context ctx, String requestCode) {
         BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         if (bluetoothAdapter == null) {
             Log.e(TAG, "Device does not support Bluetooth");
@@ -128,9 +129,10 @@
         bleScanner.startScan(null, scanSettings, scanCallback);
         sleep(2_000);
         bleScanner.stopScan(scanCallback);
+        tellHostActionFinished(ACTION_BLE_SCAN, requestCode);
     }
 
-    private static void doScheduleJob(Context ctx) {
+    private static void doScheduleJob(Context ctx, String requestCode) {
         final ComponentName JOB_COMPONENT_NAME =
                 new ComponentName("com.android.server.cts.device.batterystats",
                         SimpleJobService.class.getName());
@@ -148,13 +150,14 @@
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                waitForReceiver(null, 3_000, latch, null);
+                waitForReceiver(null, 60_000, latch, null);
+                tellHostActionFinished(ACTION_JOB_SCHEDULE, requestCode);
                 return null;
             }
         }.execute();
     }
 
-    private static void doSync(Context ctx) {
+    private static void doSync(Context ctx, String requestCode) {
         BatteryStatsAuthenticator.removeAllAccounts(ctx);
         final Account account = BatteryStatsAuthenticator.getTestAccount();
         // Create the test account.
@@ -185,36 +188,50 @@
                 if(ctx instanceof Activity){
                     ((Activity) ctx).finish();
                 }
+                tellHostActionFinished(ACTION_SYNC, requestCode);
             }
         }.execute();
     }
 
-    private static void doWifiScan(Context ctx) {
+    private static void doWifiScan(Context ctx, String requestCode) {
         IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
         CountDownLatch onReceiveLatch = new CountDownLatch(1);
         BroadcastReceiver receiver = registerReceiver(ctx, onReceiveLatch, intentFilter);
         ctx.getSystemService(WifiManager.class).startScan();
-        waitForReceiver(ctx, 3_000, onReceiveLatch, receiver);
+        waitForReceiver(ctx, 60_000, onReceiveLatch, receiver);
+        tellHostActionFinished(ACTION_WIFI_SCAN, requestCode);
     }
 
-    private static void doWifiDownload(Context ctx) {
+    private static void doWifiDownload(Context ctx, String requestCode) {
+        CountDownLatch latch = new CountDownLatch(1);
+
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                BatteryStatsWifiTransferTests.download();
+                BatteryStatsWifiTransferTests.download(requestCode);
+                latch.countDown();
                 return null;
             }
         }.execute();
+
+        waitForReceiver(null, 60_000, latch, null);
+        tellHostActionFinished(ACTION_WIFI_DOWNLOAD, requestCode);
     }
 
-    private static void doWifiUpload(Context ctx) {
+    private static void doWifiUpload(Context ctx, String requestCode) {
+        CountDownLatch latch = new CountDownLatch(1);
+
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 BatteryStatsWifiTransferTests.upload();
+                latch.countDown();
                 return null;
             }
         }.execute();
+
+        waitForReceiver(null, 60_000, latch, null);
+        tellHostActionFinished(ACTION_WIFI_UPLOAD, requestCode);
     }
 
     /** Register receiver to determine when given action is complete. */
@@ -258,6 +275,12 @@
         }
     }
 
+    /** Communicates to hostside (via logcat) that action has completed (regardless of success). */
+    private static void tellHostActionFinished(String actionCode, String requestCode) {
+        String s = String.format("Completed performing %s for request %s", actionCode, requestCode);
+        Log.i(TAG, s);
+    }
+
     /** Determines whether the package is running as a background process. */
     public static boolean isAppInBackground(Context context) throws ReflectiveOperationException {
         String pkgName = context.getPackageName();
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
index c41b244..fab306d 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsForegroundActivity.java
@@ -16,10 +16,9 @@
 
 package com.android.server.cts.device.batterystats;
 
-import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions
-        .ACTION_JOB_SCHEDULE;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.ACTION_SYNC;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_ACTION;
+import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.KEY_REQUEST_CODE;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.doAction;
 import static com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions.isAppInBackground;
 
@@ -53,7 +52,9 @@
         }
 
         String action = intent.getStringExtra(KEY_ACTION);
-        doAction(this, action);
+        String requestCode = intent.getStringExtra(KEY_REQUEST_CODE);
+        Log.i(TAG, "Starting " + action + " from foreground activity as request " + requestCode);
+        doAction(this, action, requestCode);
 
         // ACTION_SYNC will finish itself. Others get finished here.
         if (!ACTION_SYNC.equals(action)) {
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
index 1633301..aa5c018 100644
--- a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsWifiTransferTests.java
@@ -15,35 +15,14 @@
  */
 package com.android.server.cts.device.batterystats;
 
-import static org.junit.Assert.assertTrue;
-
-import android.app.IntentService;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.Arrays;
-import java.util.Random;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
 
 public class BatteryStatsWifiTransferTests {
     private static final String TAG = "BatteryStatsWifiTransferTests";
@@ -53,7 +32,7 @@
     /** Server to send requests to. */
     private static final String SERVER_URL = "https://developer.android.com/index.html";
 
-    public static String download() {
+    public static String download(String requestCode) {
         HttpURLConnection conn = null;
         try {
             URL url = new URL(SERVER_URL);
@@ -69,7 +48,7 @@
             while ((count = in.read(data)) != -1) {
                 total += count;
             }
-            Log.i(TAG, Integer.toString(total));
+            Log.i(TAG, String.format("request %s d=%d", requestCode, total));
         } catch (IOException e) {
             Log.i(TAG, e.toString());
             return "Caught exception";
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 758c443..ebe1996 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -15,8 +15,15 @@
  */
 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
  *
@@ -52,6 +59,9 @@
     public static final String ACTION_WIFI_DOWNLOAD = "action.wifi_download";
     public static final String ACTION_WIFI_UPLOAD = "action.wifi_upload";
 
+    public static final String KEY_REQUEST_CODE = "request_code";
+    public static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -130,14 +140,12 @@
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
         // Foreground test.
-        executeForeground(ACTION_BLE_SCAN);
-        Thread.sleep(2_500);
+        executeForeground(ACTION_BLE_SCAN, 30_000);
         assertValueRange("blem", "", 5, 1, 1); // ble_scan_count
         assertValueRange("blem", "", 6, 0, 0); // ble_scan_count_bg
 
         // Background test.
-        executeBackground(ACTION_BLE_SCAN);
-        Thread.sleep(2_500);
+        executeBackground(ACTION_BLE_SCAN, 30_000);
         assertValueRange("blem", "", 5, 2, 2); // ble_scan_count
         assertValueRange("blem", "", 6, 1, 1); // ble_scan_count_bg
 
@@ -149,14 +157,12 @@
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
         // Foreground test.
-        executeForeground(ACTION_JOB_SCHEDULE);
-        Thread.sleep(4_000);
+        executeForeground(ACTION_JOB_SCHEDULE, 60_000);
         assertValueRange("jb", "", 6, 1, 1); // count
         assertValueRange("jb", "", 8, 0, 0); // background_count
 
         // Background test.
-        executeBackground(ACTION_JOB_SCHEDULE);
-        Thread.sleep(4_000);
+        executeBackground(ACTION_JOB_SCHEDULE, 60_000);
         assertValueRange("jb", "", 6, 2, 2); // count
         assertValueRange("jb", "", 8, 1, 1); // background_count
 
@@ -168,15 +174,13 @@
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
         // Foreground test.
-        executeForeground(ACTION_SYNC);
-        Thread.sleep(3_000);
+        executeForeground(ACTION_SYNC, 60_000);
         // Allow one or two syncs in this time frame (not just one) due to unpredictable syncs.
         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 1, 2); // count
         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 0, 0); // background_count
 
         // Background test.
-        executeBackground(ACTION_SYNC);
-        Thread.sleep(3_000);
+        executeBackground(ACTION_SYNC, 60_000);
         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 2, 4); // count
         assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 8, 1, 2); // background_count
 
@@ -188,14 +192,12 @@
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
         // Foreground count test.
-        executeForeground(ACTION_WIFI_SCAN);
-        Thread.sleep(4_000);
+        executeForeground(ACTION_WIFI_SCAN, 60_000);
         assertValueRange("wfl", "", 7, 1, 1); // scan_count
         assertValueRange("wfl", "", 11, 0, 0); // scan_count_bg
 
         // Background count test.
-        executeBackground(ACTION_WIFI_SCAN);
-        Thread.sleep(6_000);
+        executeBackground(ACTION_WIFI_SCAN, 60_000);
         assertValueRange("wfl", "", 7, 2, 2); // scan_count
         assertValueRange("wfl", "", 11, 1, 1); // scan_count_bg
 
@@ -275,8 +277,8 @@
 
         long prevBytes = getLongValue(getUid(), "nt", "", 6);
 
-        executeForeground(ACTION_WIFI_DOWNLOAD);
-        long downloadedBytes = getDownloadedBytes();
+        String requestCode = executeForeground(ACTION_WIFI_DOWNLOAD, 60_000);
+        long downloadedBytes = getDownloadedBytes(requestCode);
         assertTrue(downloadedBytes > 0);
         long min = prevBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
         long max = prevBytes + downloadedBytes + FUZZ; // Add some fuzzing.
@@ -285,9 +287,8 @@
 
         // Do the background download
         long prevBgBytes = getLongValue(getUid(), "nt", "", 20);
-        executeBackground(ACTION_WIFI_DOWNLOAD);
-        Thread.sleep(4000);
-        downloadedBytes = getDownloadedBytes();
+        requestCode = executeBackground(ACTION_WIFI_DOWNLOAD, 60_000);
+        downloadedBytes = getDownloadedBytes(requestCode);
 
         long minBg = prevBgBytes + downloadedBytes + MIN_HTTP_HEADER_BYTES;
         long maxBg = prevBgBytes + downloadedBytes + FUZZ;
@@ -310,14 +311,12 @@
         batteryOnScreenOff();
         installPackage(DEVICE_SIDE_TEST_APK, true);
 
-        executeForeground(ACTION_WIFI_UPLOAD);
-        Thread.sleep(2000);
+        executeForeground(ACTION_WIFI_UPLOAD, 60_000);
         int min = MIN_HTTP_HEADER_BYTES + (2 * 1024);
         int max = min + (6 * 1024); // Add some fuzzing.
         assertValueRange("nt", "", 7, min, max); // wifi_bytes_tx
 
-        executeBackground(ACTION_WIFI_UPLOAD);
-        Thread.sleep(4000);
+        executeBackground(ACTION_WIFI_UPLOAD, 60_000);
         assertValueRange("nt", "", 21, min * 2, max * 2); // wifi_bytes_bg_tx
 
         batteryOffScreenOn();
@@ -371,15 +370,39 @@
     }
 
     /**
+     * Runs a (background) service to perform the given action, and waits for
+     * the device to report that the action has finished (via a logcat message) before returning.
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     *                     device to perform the given action. This value can be used to receive
+     *                     communications via logcat from the device about this action.
+     */
+    private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
+        String requestCode = executeBackground(actionValue);
+        String searchString = getCompletedActionString(actionValue, requestCode);
+        checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+        return requestCode;
+    }
+
+    /**
      * Runs a (background) service to perform the given action.
      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
      *                    action to perform.
+     * @return A string, representing a random integer, assigned to this particular request for the
+      *                     device to perform the given action. This value can be used to receive
+      *                     communications via logcat from the device about this action.
      */
-    private void executeBackground(String actionValue) throws Exception {
+    private String executeBackground(String actionValue) throws Exception {
         allowBackgroundServices();
+        String requestCode = Integer.toString(new Random().nextInt());
         getDevice().executeShellCommand(String.format(
-                "am startservice -n '%s' -e %s %s",
-                DEVICE_SIDE_BG_SERVICE_COMPONENT, KEY_ACTION, actionValue));
+                "am startservice -n '%s' -e %s %s -e %s %s",
+                DEVICE_SIDE_BG_SERVICE_COMPONENT,
+                KEY_ACTION, actionValue,
+                KEY_REQUEST_CODE, requestCode));
+        return requestCode;
     }
 
     /** Required to successfully start a background service from adb in O. */
@@ -389,26 +412,119 @@
     }
 
     /**
+     * Runs an activity (in the foreground) to perform the given action, and waits
+     * for the device to report that the action has finished (via a logcat message) before returning.
+     * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+     *                    action to perform.
+     * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     *                     device to perform the given action. This value can be used to receive
+     *                     communications via logcat from the device about this action.
+     */
+    private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
+        String requestCode = executeForeground(actionValue);
+        String searchString = getCompletedActionString(actionValue, requestCode);
+        checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+        return requestCode;
+    }
+
+    /**
      * Runs an activity (in the foreground) to perform the given action.
      * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
      *                    action to perform.
+     * @return A string, representing a random integer, assigned to this particular request for the
+     *                     device to perform the given action. This value can be used to receive
+     *                     communications via logcat from the device about this action.
      */
-    private void executeForeground(String actionValue) throws Exception {
+    private String executeForeground(String actionValue) throws Exception {
+        String requestCode = Integer.toString(new Random().nextInt());
         getDevice().executeShellCommand(String.format(
-                "am start -n '%s' -e %s %s",
-                DEVICE_SIDE_FG_ACTIVITY_COMPONENT, KEY_ACTION, actionValue));
+                "am start -n '%s' -e %s %s -e %s %s",
+                DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+                KEY_ACTION, actionValue,
+                KEY_REQUEST_CODE, requestCode));
+        return requestCode;
+    }
+
+    /**
+     * The string that will be printed in the logcat when the action completes. This needs to be
+     * identical to {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
+     */
+    private String getCompletedActionString(String actionValue, String requestCode) {
+        return String.format("Completed performing %s for request %s", actionValue, requestCode);
+    }
+
+    /**
+    * 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
      */
-    private long getDownloadedBytes() throws Exception {
+    private long getDownloadedBytes(String requestCode) throws Exception {
         String log = getDevice().executeShellCommand(
-                "logcat -d -s BatteryStatsWifiTransferTests -e '\\d+'");
+                String.format("logcat -d -s BatteryStatsWifiTransferTests -e 'request %s d=\\d+'",
+                        requestCode));
         String[] lines = log.split("\n");
         long size = 0;
         for (int i = lines.length - 1; i >= 0; i--) {
-            String[] parts = lines[i].split(":");
+            String[] parts = lines[i].split("d=");
             String num = parts[parts.length - 1].trim();
             if (num.matches("\\d+")) {
                 size = Integer.parseInt(num);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index f406735..3f87165 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -30,6 +30,12 @@
                 android:supportsPictureInPicture="true"
                 android:exported="true"
         />
+        <activity android:name=".TestActivityWithSameAffinity"
+                android:resizeableActivity="true"
+                android:supportsPictureInPicture="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.PipActivitySameAffinity"
+        />
         <activity android:name=".TranslucentTestActivity"
                 android:resizeableActivity="true"
                 android:supportsPictureInPicture="true"
@@ -103,7 +109,20 @@
                   android:exported="true"
                   android:taskAffinity="nobody.but.PipActivity2"
         />
-
+        <activity android:name=".PipOnStopActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.PipOnStopActivity"
+        />
+        <activity android:name=".PipActivityWithSameAffinity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:taskAffinity="nobody.but.PipActivitySameAffinity"
+        />
         <activity android:name=".AlwaysFocusablePipActivity"
                   android:theme="@style/Theme.Transparent"
                   android:resizeableActivity="false"
@@ -134,19 +153,12 @@
                   android:exported="true"
         />
         <activity android:name=".LaunchImeWithPipActivity"
-            android:resizeableActivity="false"
-            android:supportsPictureInPicture="true"
-            androidprv:alwaysFocusable="true"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:exported="true"
-            android:windowSoftInputMode="stateAlwaysVisible"
-        />
-        <activity android:name=".PipOnStopActivity"
-            android:resizeableActivity="false"
-            android:supportsPictureInPicture="true"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:exported="true"
-            android:taskAffinity="nobody.but.PipOnStopActivity"
+                  android:resizeableActivity="false"
+                  android:supportsPictureInPicture="true"
+                  androidprv:alwaysFocusable="true"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+                  android:exported="true"
+                  android:windowSoftInputMode="stateAlwaysVisible"
         />
         <activity android:name=".FreeformActivity"
                   android:resizeableActivity="true"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
new file mode 100644
index 0000000..e97dc0e
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.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 android.server.cts;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+
+/**
+ * An activity that can enter PiP with a fixed affinity to match
+ * {@link TestActivityWithSameAffinity}.
+ */
+public class PipActivityWithSameAffinity extends Activity {
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
new file mode 100644
index 0000000..d835da7
--- /dev/null
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivityWithSameAffinity extends TestActivity {
+
+    private static final String TAG = TestActivityWithSameAffinity.class.getSimpleName();
+
+    // Starts the activity (component name) provided by the value at the end of onCreate
+    private static final String EXTRA_START_ACTIVITY = "start_activity";
+    // Finishes the activity at the end of onResume (after EXTRA_START_ACTIVITY is handled)
+    private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // Launch a new activity if requested
+        String launchActivityComponent = getIntent().getStringExtra(EXTRA_START_ACTIVITY);
+        if (launchActivityComponent != null) {
+            Intent launchIntent = new Intent();
+            launchIntent.setComponent(ComponentName.unflattenFromString(launchActivityComponent));
+            startActivity(launchIntent);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // Finish self if requested
+        if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+            finish();
+        }
+    }
+
+    @Override
+    protected String getTag() {
+        return TAG;
+    }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
index 8374f7c..c04ef90 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
@@ -102,6 +102,9 @@
             case "destroy_display":
                 destroyVirtualDisplays();
                 break;
+            case "resize_display":
+                resizeDisplay();
+                break;
         }
     }
 
@@ -195,4 +198,12 @@
     @Override
     public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
     }
+
+    /** Resize virtual display to half of the surface frame size. */
+    private void resizeDisplay() {
+        final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0];
+        final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder();
+        vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2,
+                surfaceHolder.getSurfaceFrame().height() / 2, vd.density);
+    }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
index 9ad9d29..dc37d9b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
@@ -410,7 +410,7 @@
      * on the primary display. It should land on the primary display and dismiss docked stack.
      */
     public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
+        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
 
         // Start launching activity.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
@@ -540,10 +540,9 @@
         if (!supportsMultiDisplay()) { return; }
 
         // Start launching activity.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
+        launchActivity(LAUNCHING_ACTIVITY);
         // Create new virtual display.
-        final DisplayState newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
 
         // Launch activity on secondary display from the app on primary display.
         getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
@@ -569,28 +568,21 @@
         if (!supportsMultiDisplay()) { return; }
 
         // Start launching activity.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
+        launchActivity(LAUNCHING_ACTIVITY);
         // Create new virtual display.
-        final DisplayState newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
         // Launch activity on primary display and check if it doesn't affect activity on secondary
         // display.
         getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
-        mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME,
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
     }
 
@@ -606,46 +598,54 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                 VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertFocusedStack("Focus must remain on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+        ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
+        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
-        mAmWmState.assertNotFocusedStack("Focused stack must be on secondary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+        assertEquals("Focused stack must be on secondary display",
+                newDisplay.mDisplayId, focusedStack.mDisplayId);
 
         // Move activity from secondary display to primary.
-        moveActivityToStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
+        moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+        mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
         mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
-        mAmWmState.assertFocusedStack("Focus must return to primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+        assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
     }
 
     /**
      * Tests launching activities on secondary display and then removing it to see if stack focus
      * is moved correctly.
-     * This version launches virtual display creator to fullscreen stack.
+     * This version launches virtual display creator to fullscreen stack in split-screen.
      */
     @Presubmit
     public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
+        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
 
         // Start launching activity into docked stack.
         launchActivityInDockStack(LAUNCHING_ACTIVITY);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
-        tryCreatingAndRemovingDisplayWithActivity();
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                FULLSCREEN_WORKSPACE_STACK_ID);
     }
 
     /**
      * Tests launching activities on secondary display and then removing it to see if stack focus
      * is moved correctly.
-     * This version launches virtual display creator to docked stack.
+     * This version launches virtual display creator to docked stack in split-screen.
      */
     public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
-        if (!supportsMultiDisplay()) { return; }
+        if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
 
         // Setup split-screen.
         launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
@@ -654,21 +654,45 @@
         launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
         mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
-        tryCreatingAndRemovingDisplayWithActivity();
+        tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+                FULLSCREEN_WORKSPACE_STACK_ID);
     }
 
     /**
-     * Create a virtual display to side from LaunchingActivity, launch a test activity there,
-     * destroy the display and check if test activity is moved to fullscreen stack.
+     * Tests launching activities on secondary display and then removing it to see if stack focus
+     * is moved correctly.
+     * This version works without split-screen.
      */
-    private void tryCreatingAndRemovingDisplayWithActivity() throws Exception {
+    public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+        if (!supportsMultiDisplay()) { return; }
+
+        // Start an activity on default display to determine default stack.
+        launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+        // Finish probing activity.
+        executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+
+        tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
+    }
+
+    /**
+     * Create a virtual display, launch a test activity there, destroy the display and check if test
+     * activity is moved to a stack on the default display.
+     */
+    private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
+            throws Exception {
         // Create new virtual display.
-        final DisplayState newDisplay = new VirtualDisplayBuilder(this)
-                .setLaunchInSplitScreen(true)
-                .setPublicDisplay(true)
-                .build();
+        final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
+                .setPublicDisplay(true);
+        if (splitScreen) {
+            builder.setLaunchInSplitScreen(true);
+        }
+        final DisplayState newDisplay = builder.build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+        if (splitScreen) {
+            mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+        }
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
@@ -679,15 +703,17 @@
 
         // Destroy virtual display.
         destroyVirtualDisplays();
-        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
         mAmWmState.assertSanity();
         mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
 
         // Check if the focus is switched back to primary display.
         mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-        mAmWmState.assertFocusedStack("Fullscreen stack must be focused after display removed",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+        mAmWmState.assertFocusedStack(
+                "Default stack on primary display must be focused after display removed",
+                defaultStackId);
+        mAmWmState.assertFocusedActivity(
+                "Focus must be switched back to activity on primary display",
                 TEST_ACTIVITY_NAME);
     }
 
@@ -698,15 +724,10 @@
     public void testStackFocusSwitchOnStackEmptied() throws Exception {
         if (!supportsMultiDisplay()) { return; }
 
-        // Start launching activity.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
         // Create new virtual display.
-        final DisplayState newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+        final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
@@ -721,9 +742,8 @@
 
         // Unlock and check if the focus is switched back to primary display.
         wakeUpAndUnlockDevice();
-        mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+        mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
+        mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
                 VIRTUAL_DISPLAY_ACTIVITY);
@@ -767,16 +787,20 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                 VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertFocusedStack("Focus must remain on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
                 TEST_ACTIVITY_NAME);
         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        assertTrue("Focused stack must be on secondary display",
-                FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+                focusedStack.mDisplayId);
 
         // Launch other activity with different uid and check it is launched on dynamic stack on
         // secondary display.
@@ -801,8 +825,11 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                 VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertFocusedStack("Focus must remain on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         // Launch activity with different uid on secondary display.
         final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -814,8 +841,9 @@
         mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
                 SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        assertTrue("Focused stack must be on secondary display",
-                FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+                focusedStack.mDisplayId);
 
         // Launch another activity with third different uid from app on secondary display and check
         // it is launched on secondary display.
@@ -877,8 +905,11 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                 VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertFocusedStack("Focus must remain on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         // Launch other activity with different uid on secondary display.
         final String startCmd =  "am start -n " + SECOND_PACKAGE_NAME + "/." + SECOND_ACTIVITY_NAME;
@@ -890,8 +921,9 @@
         mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
                 SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        assertTrue("Focused stack must be on secondary display",
-                FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+                focusedStack.mDisplayId);
 
         // Check that owner uid can launch its own activity on secondary display.
         final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
@@ -917,16 +949,20 @@
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
         mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
                 VIRTUAL_DISPLAY_ACTIVITY);
-        mAmWmState.assertFocusedStack("Focus must remain on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+        assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         // Launch activity on new secondary display.
         launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
         mAmWmState.assertFocusedActivity("Focus must be on secondary display",
                 TEST_ACTIVITY_NAME);
         final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
-        assertTrue("Focused stack must be on secondary display",
-                FULLSCREEN_WORKSPACE_STACK_ID != externalFocusedStackId);
+        focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+        assertEquals("Focused stack must be on secondary display", newDisplay.mDisplayId,
+                focusedStack.mDisplayId);
 
         final String logSeparator = clearLogcat();
 
@@ -1052,15 +1088,9 @@
     public void testDisplayResize() throws Exception {
         if (!supportsMultiDisplay()) { return; }
 
-        // Start launching activity.
-        launchActivityInDockStack(LAUNCHING_ACTIVITY);
-
-        mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY, DOCKED_STACK_ID);
         // Create new virtual display.
-        final DisplayState newDisplay =
-                new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
+        final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
 
         // Launch a resizeable activity on new secondary display.
         final String initialLogSeparator = clearLogcat();
@@ -1072,14 +1102,10 @@
         // Grab reported sizes and compute new with slight size change.
         final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
                 initialLogSeparator);
-        final Rectangle initialBounds
-                = mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds();
-        final Rectangle newBounds = new Rectangle(initialBounds.x, initialBounds.y,
-                initialBounds.width + SIZE_VALUE_SHIFT, initialBounds.height + SIZE_VALUE_SHIFT);
 
         // Resize the docked stack, so that activity with virtual display will also be resized.
         final String logSeparator = clearLogcat();
-        resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+        executeShellCommand(getResizeVirtualDisplayCommand());
 
         mAmWmState.waitForWithAmState(mDevice, amState -> {
             try {
@@ -1091,16 +1117,8 @@
             }
         }, "Wait for the configuration change to happen and for activity to be resumed.");
 
-        mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY,
+        mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
                 VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
-        mAmWmState.assertDockedTaskBounds(newBounds.width, newBounds.height,
-                LAUNCHING_ACTIVITY);
-        mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
-        mAmWmState.assertContainsStack("Must contain fullscreen stack",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        assertEquals(new Rectangle(0, 0, newBounds.width, newBounds.height),
-                mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
-        mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
         mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
 
@@ -1112,12 +1130,8 @@
                 logSeparator);
         assertTrue(updatedSize.widthDp <= initialSize.widthDp);
         assertTrue(updatedSize.heightDp <= initialSize.heightDp);
-        assertTrue(updatedSize.displayWidth <= initialSize.displayWidth);
-        assertTrue(updatedSize.displayHeight <= initialSize.displayHeight);
-        final boolean widthUpdated = updatedSize.metricsWidth < initialSize.metricsWidth;
-        final boolean heightUpdated = updatedSize.metricsHeight < initialSize.metricsHeight;
-        assertTrue("Either width or height must be updated after split-screen resize",
-                widthUpdated ^ heightUpdated);
+        assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+        assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
     }
 
     /** Read the number of configuration changes sent to activity from logs. */
@@ -1174,7 +1188,7 @@
         // Check that task has moved from primary display to secondary.
         final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
                 .getTasks().size();
-        mAmWmState.assertEquals("Task number in fullscreen stack must be decremented.", taskNum - 1,
+        mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
                 taskNumFinal);
         final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
                 .getTasks().size();
@@ -1246,12 +1260,15 @@
                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
 
         // Check that second activity gets launched on the default display
-        final ActivityManagerState.ActivityStack fullscreenStack =
-                mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+        final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+                DEFAULT_DISPLAY_ID);
+        final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+                mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
         assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(ALT_LAUNCHING_ACTIVITY), fullscreenStack.mResumedActivity);
+                getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+                defaultDisplayFrontStack.mResumedActivity);
         mAmWmState.assertFocusedStack("Focus must be on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+                defaultDisplayFrontStackId);
 
         executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
         mAmWmState.waitForFocusedStack(mDevice, frontStackId);
@@ -1293,13 +1310,14 @@
         mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
                 null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
 
-        // Check that the second activity is launched onto the fullscreen stack
-        final ActivityManagerState.ActivityStack fullscreenStack =
-                mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
+        // Check that the second activity is launched on the default display
+        final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+        final ActivityManagerState.ActivityStack focusedStack
+                = mAmWmState.getAmState().getStackById(focusedStackId);
         assertEquals("Activity launched on default display must be resumed",
-                getActivityComponentName(TEST_ACTIVITY_NAME), fullscreenStack.mResumedActivity);
-        mAmWmState.assertFocusedStack("Focus must be on primary display",
-                FULLSCREEN_WORKSPACE_STACK_ID);
+                getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+        assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+                focusedStack.mDisplayId);
 
         executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
                 + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
@@ -1793,6 +1811,11 @@
                 " --es command destroy_display";
     }
 
+    private static String getResizeVirtualDisplayCommand() {
+        return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+                " --es command resize_display";
+    }
+
     /** Checks if the device supports multi-display. */
     private boolean supportsMultiDisplay() throws Exception {
         return hasDeviceFeature("android.software.activities_on_secondary_displays");
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 65f507e..37c9d93 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -35,11 +35,13 @@
  */
 public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
     private static final String TEST_ACTIVITY = "TestActivity";
+    private static final String TEST_ACTIVITY_WITH_SAME_AFFINITY = "TestActivityWithSameAffinity";
     private static final String TRANSLUCENT_TEST_ACTIVITY = "TranslucentTestActivity";
     private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
     private static final String RESUME_WHILE_PAUSING_ACTIVITY = "ResumeWhilePausingActivity";
     private static final String PIP_ACTIVITY = "PipActivity";
     private static final String PIP_ACTIVITY2 = "PipActivity2";
+    private static final String PIP_ACTIVITY_WITH_SAME_AFFINITY = "PipActivityWithSameAffinity";
     private static final String ALWAYS_FOCUSABLE_PIP_ACTIVITY = "AlwaysFocusablePipActivity";
     private static final String LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY =
             "LaunchIntoPinnedStackPipActivity";
@@ -914,6 +916,81 @@
         assertTrue(lifecycleCounts.mPauseCount == 0);
     }
 
+    public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+        // affinity
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+        assertPinnedStackExists();
+
+        // Launch the root activity again...
+        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+        // ...and ensure that the root activity task is found and reused, and that the pinned stack
+        // is unaffected
+        assertPinnedStackExists();
+        mAmWmState.assertFocusedActivity("Expected root activity focused",
+                TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
+    }
+
+    public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch a fullscreen activity which will launch a PiP activity in a new task with the same
+        // affinity, and also launch another activity in the same task, while finishing itself. As
+        // a result, the task will not have a component matching the same activity as what it was
+        // started with
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
+                EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
+                EXTRA_FINISH_SELF_ON_RESUME, "true");
+        mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+        launchActivityInStack(PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+        assertPinnedStackExists();
+
+        // Launch the root activity again...
+        int rootActivityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+
+        // ...and ensure that even while matching purely by task affinity, the root activity task is
+        // found and reused, and that the pinned stack is unaffected
+        assertPinnedStackExists();
+        mAmWmState.assertFocusedActivity("Expected root activity focused", TEST_ACTIVITY);
+        assertTrue(rootActivityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                TEST_ACTIVITY).mTaskId);
+    }
+
+    public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch an activity into the pinned stack with a fixed affinity
+        launchActivityInStack(TEST_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID,
+                EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
+                EXTRA_FINISH_SELF_ON_RESUME, "true");
+        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+        assertPinnedStackExists();
+
+        // Launch the root activity again, of the matching task and ensure that we expand to
+        // fullscreen
+        int activityTaskId = mAmWmState.getAmState().getTaskByActivityName(
+                PIP_ACTIVITY).mTaskId;
+        launchHomeActivity();
+        launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+        mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+        assertPinnedStackDoesNotExist();
+        assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
+                PIP_ACTIVITY).mTaskId);
+    }
+
     /**
      * Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
      * that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
diff --git a/tests/fragment/src/android/fragment/cts/TransitionFragment.java b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
index fbe2250..17363ac 100644
--- a/tests/fragment/src/android/fragment/cts/TransitionFragment.java
+++ b/tests/fragment/src/android/fragment/cts/TransitionFragment.java
@@ -71,7 +71,7 @@
     }
 
     void waitForTransition() throws InterruptedException {
-        verify(mListener, within(300)).onTransitionEnd(any());
+        verify(mListener, within(500)).onTransitionEnd(any());
         reset(mListener);
     }
 
diff --git a/tests/sensor/jni/SensorTestCases.cpp b/tests/sensor/jni/SensorTestCases.cpp
index f86f262..43e07a4 100644
--- a/tests/sensor/jni/SensorTestCases.cpp
+++ b/tests/sensor/jni/SensorTestCases.cpp
@@ -135,7 +135,7 @@
 // Test sensor direct report functionality
 void SensorTest::testDirectReport(JNIEnv* env, int32_t sensorType, int32_t channelType, int32_t rateLevel) {
     constexpr size_t kEventSize = sizeof(ASensorEvent);
-    constexpr size_t kNEvent = 500;
+    constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
     constexpr size_t kMemSize = kEventSize * kNEvent;
 
     // value check criterion
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
index 6c2462f..78ac59d 100644
--- a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -65,7 +65,8 @@
     private static final int TEST_RUN_TIME_PERIOD_MILLISEC = 5000;
     private static final int ALLOWED_SENSOR_INIT_TIME_MILLISEC = 500;
     private static final int SENSORS_EVENT_SIZE = 104;
-    private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
+    private static final int SENSORS_EVENT_COUNT = 10240; // 800Hz * 2.2 * 5 sec + extra
+    private static final int SHARED_MEMORY_SIZE = SENSORS_EVENT_COUNT * SENSORS_EVENT_SIZE;
     private static final float MERCY_FACTOR = 0.1f;
 
     private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer,
diff --git a/tests/tests/content/res/font/broken_xmlfont.xml b/tests/tests/content/res/font/broken_xmlfont.xml
new file mode 100644
index 0000000..be30f2f
--- /dev/null
+++ b/tests/tests/content/res/font/broken_xmlfont.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+  <font android:fontStyle="normal" android:fontWeight="400" android:font="@font/brokenfont" />
+</font-family>
diff --git a/tests/tests/content/res/font/brokenfont.ttf b/tests/tests/content/res/font/brokenfont.ttf
new file mode 100644
index 0000000..12b2713
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/brokenfont_source.ttx b/tests/tests/content/res/font/brokenfont_source.ttx
new file mode 100644
index 0000000..34217b4
--- /dev/null
+++ b/tests/tests/content/res/font/brokenfont_source.ttx
@@ -0,0 +1,177 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon May 1 00:00:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="a" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Broken Font
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Broken Font
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      BrokenFont-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Broken Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Broken Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      BrokenFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/tests/content/res/font/invalid_xmlfont_nosource.xml b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
new file mode 100644
index 0000000..6b70222
--- /dev/null
+++ b/tests/tests/content/res/font/invalid_xmlfont_nosource.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+  <!-- missing the android:font entry -->
+  <font android:fontStyle="normal" android:fontWeight="400" />
+</font-family>
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 89ea29b..be15e57 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -814,6 +814,30 @@
         } catch (RuntimeException e) {
             // pass
         }
+
+        try {
+            mResources.getFont(R.font.invalid_xmlfont_nosource);
+            fail();
+        } catch (RuntimeException e) {
+            // pass
+        }
+
+    }
+
+    public void testGetFont_brokenFontFiles() {
+        try {
+            mResources.getFont(R.font.brokenfont);
+            fail();
+        } catch (RuntimeException e) {
+            // pass
+        }
+
+        try {
+            mResources.getFont(R.font.broken_xmlfont);
+            fail();
+        } catch (RuntimeException e) {
+            // pass
+        }
     }
 
     public void testGetFont_fontFileIsCached() {
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 3059d71..af59cbf 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -40,6 +40,9 @@
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
+# Enforce public / test api only
+LOCAL_SDK_VERSION := test_current
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 52db21f..b36c23a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1153,6 +1153,13 @@
     }
 
     @Test
+    public void testClipOutPath() {
+        final Path p = new Path();
+        p.addRect(new RectF(5, 5, 10, 10), Direction.CW);
+        assertTrue(mCanvas.clipOutPath(p));
+    }
+
+    @Test
     public void testClipInversePath() {
         final Path p = new Path();
         p.addRoundRect(new RectF(0, 0, 10, 10), 0.5f, 0.5f, Direction.CW);
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
index 37b434e..c57682f 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
@@ -57,6 +57,58 @@
 
 }
 
+
+// Test creating a default stream with specific devices
+void runtest_aaudio_devices(int32_t deviceId, bool expectFail) {
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream = nullptr;
+
+    // Use an AAudioStreamBuilder to define the stream.
+    aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder);
+    ASSERT_EQ(AAUDIO_OK, result);
+    ASSERT_NE(nullptr, aaudioBuilder);
+
+    AAudioStreamBuilder_setDeviceId(aaudioBuilder,deviceId);
+
+    // Create an AAudioStream using the Builder.
+    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+    if (expectFail) {
+        ASSERT_NE(AAUDIO_OK, result);
+        ASSERT_EQ(nullptr, aaudioStream);
+    } else {
+        // Pass or fail is OK. Just don't crash.
+        ASSERT_TRUE(((result < 0) && (aaudioStream == nullptr))
+                    || ((result == AAUDIO_OK) && (aaudioStream != nullptr)));
+    }
+
+    // Cleanup
+    EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
+    if (aaudioStream != nullptr) {
+        AAudioStream_close(aaudioStream);
+    }
+}
+
+TEST(test_aaudio, aaudio_stream_device_unspecified) {
+    runtest_aaudio_devices(AAUDIO_DEVICE_UNSPECIFIED, false);
+}
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_absurd) {
+    runtest_aaudio_devices(19736459, true);
+}
+*/
+/* FIXME review
+TEST(test_aaudio, aaudio_stream_device_reasonable) {
+    runtest_aaudio_devices(1, false);
+}
+*/
+
+/* FIXME - why can we open this device? What is an illegal deviceId?
+TEST(test_aaudio, aaudio_stream_device_negative) {
+    runtest_aaudio_devices(-765, true);
+}
+*/
+
 // Test creating a default stream with everything unspecified.
 TEST(test_aaudio, aaudio_stream_unspecified) {
     AAudioStreamBuilder *aaudioBuilder = nullptr;
@@ -120,7 +172,12 @@
     AAudioStreamBuilder_setBufferCapacityInFrames(aaudioBuilder, 2000);
 
     // Create an AAudioStream using the Builder.
-    ASSERT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+    if (requestedSharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE
+        && result != AAUDIO_OK) {
+        return; // EXCLUSIVE just may not be available. Should not crash.
+    }
+    ASSERT_EQ(AAUDIO_OK, result);
     EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_delete(aaudioBuilder));
 
     EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, AAudioStream_getState(aaudioStream));
@@ -184,15 +241,14 @@
         // Write some data while we are running. Read counter should be advancing.
         writeLoops = 1 * actualSampleRate / framesPerBurst; // 1 second
         ASSERT_LT(2, writeLoops); // detect absurdly high framesPerBurst
-        timeoutNanos = 10 * NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+        timeoutNanos = 100 * (NANOS_PER_SECOND * framesPerBurst / actualSampleRate); // N bursts
         framesWritten = 1;
         aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
         aaudioFramesRead1 = aaudioFramesRead;
         int64_t beginTime = getNanoseconds(CLOCK_MONOTONIC);
         do {
             framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
-            ASSERT_GE(framesWritten, 0);
-            ASSERT_LE(framesWritten, framesPerBurst);
+            ASSERT_EQ(framesWritten, framesPerBurst);
 
             framesTotal += framesWritten;
             aaudioFramesWritten = AAudioStream_getFramesWritten(aaudioStream);
@@ -211,8 +267,8 @@
         aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
         int64_t endTime = getNanoseconds(CLOCK_MONOTONIC);
         ASSERT_GT(aaudioFramesRead2, 0);
-        ASSERT_GT(aaudioFramesRead2, aaudioFramesRead1);
-        ASSERT_LE(aaudioFramesRead2, aaudioFramesWritten);
+        EXPECT_GT(aaudioFramesRead2, aaudioFramesRead1);
+
 
         // TODO why is AudioTrack path so inaccurate?
         const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
@@ -236,15 +292,16 @@
     aaudioFramesRead = AAudioStream_getFramesRead(aaudioStream);
     ASSERT_GE(aaudioFramesRead, aaudioFramesRead2); // monotonic increase
 
-    // Use this to sleep by waiting for something that won't happen.
-    AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_PAUSED, &state, timeoutNanos);
+    // Use this to sleep by waiting for a state that won't happen.
+    timeoutNanos = 100 * NANOS_PER_MILLISECOND;
+    AAudioStream_waitForStateChange(aaudioStream, AAUDIO_STREAM_STATE_OPEN, &state, timeoutNanos);
     aaudioFramesRead2 = AAudioStream_getFramesRead(aaudioStream);
     EXPECT_EQ(aaudioFramesRead, aaudioFramesRead2);
 
     // ------------------- TEST FLUSH -----------------
     // Prime the buffer.
     timeoutNanos = 0;
-    writeLoops = 100;
+    writeLoops = 1000;
     do {
         framesWritten = AAudioStream_write(aaudioStream, data, framesPerBurst, timeoutNanos);
         framesTotal += framesWritten;
@@ -282,12 +339,10 @@
     runtest_aaudio_stream(AAUDIO_SHARING_MODE_SHARED);
 }
 
-/* TODO Enable exclusive mode test.
-// Test Writing to an AAudioStream using EXCLUSIVE sharing mode.
+// Test Writing to an AAudioStream using EXCLUSIVE sharing mode. It may fail gracefully.
 TEST(test_aaudio, aaudio_stream_exclusive) {
     runtest_aaudio_stream(AAUDIO_SHARING_MODE_EXCLUSIVE);
 }
-*/
 
 int main(int argc, char **argv) {
     testing::InitGoogleTest(&argc, argv);
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
index 2ec1e1c..3786967 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
@@ -18,17 +18,55 @@
 #define LOG_TAG "AAudioTest"
 
 #include <gtest/gtest.h>
+#include <atomic>
 #include <utils/Log.h>
 
 #include <aaudio/AAudio.h>
 #include "test_aaudio.h"
 
 typedef struct AAudioCallbackTestData {
-    int32_t callbackCount;
     int32_t expectedFramesPerCallback;
     int32_t actualFramesPerCallback;
+    int32_t minLatency;
+    int32_t maxLatency;
+    std::atomic<aaudio_result_t> callbackError;
+    std::atomic<int32_t> callbackCount;
 } AAudioCallbackTestData;
 
+static int32_t measureLatency(AAudioStream *stream) {
+    int64_t presentationTime = 0;
+    int64_t presentationPosition = 0;
+    int64_t now = getNanoseconds();
+    int32_t sampleRate = AAudioStream_getSampleRate(stream);
+    int64_t framesWritten = AAudioStream_getFramesWritten(stream);
+    aaudio_result_t result = AAudioStream_getTimestamp(stream,
+                                                       CLOCK_MONOTONIC,
+                                                       &presentationPosition,
+                                                       &presentationTime);
+    if (result < 0) {
+        return result;
+    }
+    // Calculate when the last frame written would be played.
+    int64_t deltaFrames = framesWritten - presentationPosition;
+    EXPECT_GE(framesWritten, presentationPosition);
+    int64_t calculatedDeltaNanos = deltaFrames * NANOS_PER_SECOND / sampleRate;
+    int64_t calculatedTimeNanos = presentationTime +  calculatedDeltaNanos;
+    int64_t latencyNanos = calculatedTimeNanos - now;
+    int32_t latencyMillis = (int32_t) ((latencyNanos + NANOS_PER_MILLISECOND - 1)
+                            / NANOS_PER_MILLISECOND);
+    return latencyMillis;
+}
+
+static void MyErrorCallbackProc(
+        AAudioStream *stream,
+        void *userData,
+        aaudio_result_t error) {
+    (void) stream;
+    AAudioCallbackTestData *myData = (AAudioCallbackTestData *) userData;
+    myData->callbackError = error;
+
+}
+
 // Callback function that fills the audio output buffer.
 static aaudio_data_callback_result_t MyDataCallbackProc(
         AAudioStream *stream,
@@ -54,6 +92,17 @@
         float *floatData = (float *) audioData;
         for (int i = 0; i < numSamples; i++) *floatData++ = 0.0f;
     }
+
+    int32_t latency = measureLatency(stream);
+    if (latency > 0) {
+        if (latency < myData->minLatency) {
+            myData->minLatency = latency;
+        }
+        if (latency > myData->maxLatency) {
+            myData->maxLatency = latency;
+        }
+    }
+
     myData->callbackCount++;
     return AAUDIO_CALLBACK_RESULT_CONTINUE;
 }
@@ -61,7 +110,7 @@
 // Test Writing to an AAudioStream using a Callback
 void runtest_aaudio_callback(aaudio_sharing_mode_t requestedSharingMode,
                              int32_t framesPerDataCallback) {
-    AAudioCallbackTestData myTestData = { 0, 0, 0 };
+    AAudioCallbackTestData myTestData;
     const int32_t requestedSampleRate = 48000;
     const int32_t requestedSamplesPerFrame = 2;
     const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
@@ -79,6 +128,10 @@
     AAudioStream *stream = nullptr;
 
     aaudio_result_t result = AAUDIO_OK;
+    myTestData.callbackCount.store(0);
+    myTestData.callbackError = AAUDIO_OK;
+    myTestData.actualFramesPerCallback = 0;
+    myTestData.expectedFramesPerCallback = 0;
 
     // Use an AAudioStreamBuilder to define the stream.
     result = AAudio_createStreamBuilder(&builder);
@@ -93,6 +146,7 @@
     AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
     AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2000);
 
+    AAudioStreamBuilder_setErrorCallback(builder, MyErrorCallbackProc, &myTestData);
     AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &myTestData);
     if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
         AAudioStreamBuilder_setFramesPerDataCallback(builder, framesPerDataCallback);
@@ -118,8 +172,8 @@
 
     actualDataFormat = AAudioStream_getFormat(stream);
 
-    // TODO test this on full build
-    // ASSERT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
+    // TODO Why does getDeviceId() always return 0?
+    // EXPECT_NE(AAUDIO_DEVICE_UNSPECIFIED, AAudioStream_getDeviceId(stream));
 
     framesPerBurst = AAudioStream_getFramesPerBurst(stream);
     ASSERT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
@@ -136,18 +190,27 @@
     // Start/stop more than once to see if it fails after the first time.
     // Write some data and measure the rate to see if the timing is OK.
     for (int loopIndex = 0; loopIndex < 2; loopIndex++) {
+
         myTestData.callbackCount = 0;
+        myTestData.minLatency = INT32_MAX;
+        myTestData.maxLatency = 0;
+        myTestData.callbackCount.store(0);
+
         myTestData.expectedFramesPerCallback = actualFramesPerDataCallback;
 
         // Start and wait for server to respond.
         ASSERT_EQ(AAUDIO_OK, AAudioStream_requestStart(stream));
+
         ASSERT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(stream,
                                                              AAUDIO_STREAM_STATE_STARTING,
                                                              &state,
                                                              DEFAULT_STATE_TIMEOUT));
         EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
 
-        sleep(2);
+        sleep(2); // let the stream run
+
+        ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+        ASSERT_GT(myTestData.callbackCount.load(), 10);
 
         // For more coverage, alternate pausing and stopping.
         if ((loopIndex & 1) == 0) {
@@ -168,29 +231,34 @@
             EXPECT_EQ(AAUDIO_STREAM_STATE_STOPPED, state);
         }
 
-        int32_t oldCallbackCount = myTestData.callbackCount;
+        int32_t oldCallbackCount = myTestData.callbackCount.load();
         EXPECT_GT(oldCallbackCount, 10);
         sleep(1);
-        EXPECT_EQ(oldCallbackCount, myTestData.callbackCount); // expect not advancing
+        EXPECT_EQ(oldCallbackCount, myTestData.callbackCount.load()); // expect not advancing
 
         if (framesPerDataCallback != AAUDIO_UNSPECIFIED) {
             ASSERT_EQ(framesPerDataCallback, myTestData.actualFramesPerCallback);
         }
+
+        EXPECT_GE(myTestData.minLatency, 1);   // Absurdly low
+        EXPECT_LE(myTestData.maxLatency, 200); // Absurdly high, should be < 30
+        //printf("latency: %d, %d\n", myTestData.minLatency, myTestData.maxLatency);
     }
 
+    ASSERT_EQ(myTestData.callbackError.load(), AAUDIO_OK);
+
     EXPECT_EQ(AAUDIO_OK, AAudioStream_close(stream));
 }
 
 // Test Using an AAudioStream callback in SHARED mode.
-
 TEST(test_aaudio, aaudio_callback_shared_unspecified) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
+    runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, AAUDIO_UNSPECIFIED);
 }
 
 TEST(test_aaudio, aaudio_callback_shared_109) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
+    runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 109); // arbitrary prime number < 192
 }
 
 TEST(test_aaudio, aaudio_callback_shared_223) {
-runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
+    runtest_aaudio_callback(AAUDIO_SHARING_MODE_SHARED, 223); // arbitrary prime number > 192
 }
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
index 8599d48..6662b72 100644
--- a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
@@ -1,7 +1,7 @@
 /*
  * Copyright 2017 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
+ * Licensed under the Apache License, Version 2.0 (the "License", ENUM_CANNOT_CHANGE);
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
@@ -25,53 +25,55 @@
 // Make sure enums do not change value.
 TEST(test_aaudio_misc, aaudio_freeze_enums) {
 
-    ASSERT_EQ(0, AAUDIO_DIRECTION_OUTPUT);
-    ASSERT_EQ(1, AAUDIO_DIRECTION_INPUT);
+#define ENUM_CANNOT_CHANGE "enum in API cannot change"
 
-    ASSERT_EQ(-1, AAUDIO_FORMAT_INVALID);
-    ASSERT_EQ(0, AAUDIO_FORMAT_UNSPECIFIED);
-    ASSERT_EQ(1, AAUDIO_FORMAT_PCM_I16);
-    ASSERT_EQ(2, AAUDIO_FORMAT_PCM_FLOAT);
+    static_assert(0 == AAUDIO_DIRECTION_OUTPUT, ENUM_CANNOT_CHANGE);
+    static_assert(1 == AAUDIO_DIRECTION_INPUT, ENUM_CANNOT_CHANGE);
 
-    ASSERT_EQ(0, AAUDIO_OK);
-    ASSERT_EQ(-900, AAUDIO_ERROR_BASE);
-    ASSERT_EQ(-899, AAUDIO_ERROR_DISCONNECTED);
-    ASSERT_EQ(-898, AAUDIO_ERROR_ILLEGAL_ARGUMENT);
-    ASSERT_EQ(-897, AAUDIO_ERROR_INCOMPATIBLE);
-    ASSERT_EQ(-896, AAUDIO_ERROR_INTERNAL);
-    ASSERT_EQ(-895, AAUDIO_ERROR_INVALID_STATE);
-    ASSERT_EQ(-894, AAUDIO_ERROR_UNEXPECTED_STATE);
-    ASSERT_EQ(-893, AAUDIO_ERROR_UNEXPECTED_VALUE);
-    ASSERT_EQ(-892, AAUDIO_ERROR_INVALID_HANDLE);
-    ASSERT_EQ(-891, AAUDIO_ERROR_INVALID_QUERY);
-    ASSERT_EQ(-890, AAUDIO_ERROR_UNIMPLEMENTED);
-    ASSERT_EQ(-889, AAUDIO_ERROR_UNAVAILABLE);
-    ASSERT_EQ(-888, AAUDIO_ERROR_NO_FREE_HANDLES);
-    ASSERT_EQ(-887, AAUDIO_ERROR_NO_MEMORY);
-    ASSERT_EQ(-886, AAUDIO_ERROR_NULL);
-    ASSERT_EQ(-885, AAUDIO_ERROR_TIMEOUT);
-    ASSERT_EQ(-884, AAUDIO_ERROR_WOULD_BLOCK);
-    ASSERT_EQ(-883, AAUDIO_ERROR_INVALID_FORMAT);
-    ASSERT_EQ(-882, AAUDIO_ERROR_OUT_OF_RANGE);
-    ASSERT_EQ(-881, AAUDIO_ERROR_NO_SERVICE);
+    static_assert(-1 == AAUDIO_FORMAT_INVALID, ENUM_CANNOT_CHANGE);
+    static_assert(0 == AAUDIO_FORMAT_UNSPECIFIED, ENUM_CANNOT_CHANGE);
+    static_assert(1 == AAUDIO_FORMAT_PCM_I16, ENUM_CANNOT_CHANGE);
+    static_assert(2 == AAUDIO_FORMAT_PCM_FLOAT, ENUM_CANNOT_CHANGE);
 
-    ASSERT_EQ(0, AAUDIO_STREAM_STATE_UNINITIALIZED);
-    ASSERT_EQ(1, AAUDIO_STREAM_STATE_UNKNOWN);
-    ASSERT_EQ(2, AAUDIO_STREAM_STATE_OPEN);
-    ASSERT_EQ(3, AAUDIO_STREAM_STATE_STARTING);
-    ASSERT_EQ(4, AAUDIO_STREAM_STATE_STARTED);
-    ASSERT_EQ(5, AAUDIO_STREAM_STATE_PAUSING);
-    ASSERT_EQ(6, AAUDIO_STREAM_STATE_PAUSED);
-    ASSERT_EQ(7, AAUDIO_STREAM_STATE_FLUSHING);
-    ASSERT_EQ(8, AAUDIO_STREAM_STATE_FLUSHED);
-    ASSERT_EQ(9, AAUDIO_STREAM_STATE_STOPPING);
-    ASSERT_EQ(10, AAUDIO_STREAM_STATE_STOPPED);
-    ASSERT_EQ(11, AAUDIO_STREAM_STATE_CLOSING);
-    ASSERT_EQ(12, AAUDIO_STREAM_STATE_CLOSED);
+    static_assert(0 == AAUDIO_OK, ENUM_CANNOT_CHANGE);
+    static_assert(-900 == AAUDIO_ERROR_BASE, ENUM_CANNOT_CHANGE);
+    static_assert(-899 == AAUDIO_ERROR_DISCONNECTED, ENUM_CANNOT_CHANGE);
+    static_assert(-898 == AAUDIO_ERROR_ILLEGAL_ARGUMENT, ENUM_CANNOT_CHANGE);
+    static_assert(-897 == AAUDIO_ERROR_INCOMPATIBLE, ENUM_CANNOT_CHANGE);
+    static_assert(-896 == AAUDIO_ERROR_INTERNAL, ENUM_CANNOT_CHANGE);
+    static_assert(-895 == AAUDIO_ERROR_INVALID_STATE, ENUM_CANNOT_CHANGE);
+    static_assert(-894 == AAUDIO_ERROR_UNEXPECTED_STATE, ENUM_CANNOT_CHANGE);
+    static_assert(-893 == AAUDIO_ERROR_UNEXPECTED_VALUE, ENUM_CANNOT_CHANGE);
+    static_assert(-892 == AAUDIO_ERROR_INVALID_HANDLE, ENUM_CANNOT_CHANGE);
+    static_assert(-891 == AAUDIO_ERROR_INVALID_QUERY, ENUM_CANNOT_CHANGE);
+    static_assert(-890 == AAUDIO_ERROR_UNIMPLEMENTED, ENUM_CANNOT_CHANGE);
+    static_assert(-889 == AAUDIO_ERROR_UNAVAILABLE, ENUM_CANNOT_CHANGE);
+    static_assert(-888 == AAUDIO_ERROR_NO_FREE_HANDLES, ENUM_CANNOT_CHANGE);
+    static_assert(-887 == AAUDIO_ERROR_NO_MEMORY, ENUM_CANNOT_CHANGE);
+    static_assert(-886 == AAUDIO_ERROR_NULL, ENUM_CANNOT_CHANGE);
+    static_assert(-885 == AAUDIO_ERROR_TIMEOUT, ENUM_CANNOT_CHANGE);
+    static_assert(-884 == AAUDIO_ERROR_WOULD_BLOCK, ENUM_CANNOT_CHANGE);
+    static_assert(-883 == AAUDIO_ERROR_INVALID_FORMAT, ENUM_CANNOT_CHANGE);
+    static_assert(-882 == AAUDIO_ERROR_OUT_OF_RANGE, ENUM_CANNOT_CHANGE);
+    static_assert(-881 == AAUDIO_ERROR_NO_SERVICE, ENUM_CANNOT_CHANGE);
 
-    ASSERT_EQ(0, AAUDIO_SHARING_MODE_EXCLUSIVE);
-    ASSERT_EQ(1, AAUDIO_SHARING_MODE_SHARED);
+    static_assert(0 == AAUDIO_STREAM_STATE_UNINITIALIZED, ENUM_CANNOT_CHANGE);
+    static_assert(1 == AAUDIO_STREAM_STATE_UNKNOWN, ENUM_CANNOT_CHANGE);
+    static_assert(2 == AAUDIO_STREAM_STATE_OPEN, ENUM_CANNOT_CHANGE);
+    static_assert(3 == AAUDIO_STREAM_STATE_STARTING, ENUM_CANNOT_CHANGE);
+    static_assert(4 == AAUDIO_STREAM_STATE_STARTED, ENUM_CANNOT_CHANGE);
+    static_assert(5 == AAUDIO_STREAM_STATE_PAUSING, ENUM_CANNOT_CHANGE);
+    static_assert(6 == AAUDIO_STREAM_STATE_PAUSED, ENUM_CANNOT_CHANGE);
+    static_assert(7 == AAUDIO_STREAM_STATE_FLUSHING, ENUM_CANNOT_CHANGE);
+    static_assert(8 == AAUDIO_STREAM_STATE_FLUSHED, ENUM_CANNOT_CHANGE);
+    static_assert(9 == AAUDIO_STREAM_STATE_STOPPING, ENUM_CANNOT_CHANGE);
+    static_assert(10 == AAUDIO_STREAM_STATE_STOPPED, ENUM_CANNOT_CHANGE);
+    static_assert(11 == AAUDIO_STREAM_STATE_CLOSING, ENUM_CANNOT_CHANGE);
+    static_assert(12 == AAUDIO_STREAM_STATE_CLOSED, ENUM_CANNOT_CHANGE);
 
-    ASSERT_EQ(0, AAUDIO_CALLBACK_RESULT_CONTINUE);
-    ASSERT_EQ(1, AAUDIO_CALLBACK_RESULT_STOP);
+    static_assert(0 == AAUDIO_SHARING_MODE_EXCLUSIVE, ENUM_CANNOT_CHANGE);
+    static_assert(1 == AAUDIO_SHARING_MODE_SHARED, ENUM_CANNOT_CHANGE);
+
+    static_assert(0 == AAUDIO_CALLBACK_RESULT_CONTINUE, ENUM_CANNOT_CHANGE);
+    static_assert(1 == AAUDIO_CALLBACK_RESULT_STOP, ENUM_CANNOT_CHANGE);
 }
diff --git a/tests/tests/text/src/android/text/format/cts/FormatterTest.java b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
index fa11023..8c7fdc9 100644
--- a/tests/tests/text/src/android/text/format/cts/FormatterTest.java
+++ b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
@@ -19,6 +19,8 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -29,6 +31,7 @@
 
 import java.math.BigDecimal;
 import java.math.MathContext;
+import java.util.Locale;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -38,9 +41,12 @@
         // test null Context
         assertEquals("", Formatter.formatFileSize(null, 0));
 
-        MathContext mc = MathContext.DECIMAL64;
-        BigDecimal bd = new BigDecimal((long) 1000, mc);
-        Context context = InstrumentationRegistry.getTargetContext();
+        final MathContext mc = MathContext.DECIMAL64;
+        final BigDecimal bd = new BigDecimal((long) 1000, mc);
+        final Configuration config = new Configuration();
+        config.setLocales(new LocaleList(Locale.US));
+        final Context context =
+                InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
 
         // test different long values with various length
         assertEquals("0 B", Formatter.formatFileSize(context, 0));
@@ -53,6 +59,8 @@
         assertEquals("0.90 kB", Formatter.formatFileSize(context, 901));
 
         assertEquals("1.00 kB", Formatter.formatFileSize(context, bd.pow(1).longValue()));
+        assertEquals("1.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 3 / 2));
+        assertEquals("12.50 kB", Formatter.formatFileSize(context, bd.pow(1).longValue() * 25 / 2));
 
         assertEquals("1.00 MB", Formatter.formatFileSize(context, bd.pow(2).longValue()));
 
@@ -69,6 +77,48 @@
     }
 
     @Test
+    public void testFormatShortFileSize() {
+        // test null Context
+        assertEquals("", Formatter.formatFileSize(null, 0));
+
+        final MathContext mc = MathContext.DECIMAL64;
+        final BigDecimal bd = new BigDecimal((long) 1000, mc);
+        final Configuration config = new Configuration();
+        config.setLocales(new LocaleList(Locale.US));
+        final Context context =
+                InstrumentationRegistry.getTargetContext().createConfigurationContext(config);
+
+        // test different long values with various length
+        assertEquals("0 B", Formatter.formatShortFileSize(context, 0));
+        assertEquals("1 B", Formatter.formatShortFileSize(context, 1));
+        assertEquals("9 B", Formatter.formatShortFileSize(context, 9));
+        assertEquals("10 B", Formatter.formatShortFileSize(context, 10));
+        assertEquals("99 B", Formatter.formatShortFileSize(context, 99));
+        assertEquals("100 B", Formatter.formatShortFileSize(context, 100));
+        assertEquals("900 B", Formatter.formatShortFileSize(context, 900));
+        assertEquals("0.90 kB", Formatter.formatShortFileSize(context, 901));
+
+        assertEquals("1.0 kB", Formatter.formatShortFileSize(context, bd.pow(1).longValue()));
+        assertEquals("1.5 kB", Formatter.formatShortFileSize(context,
+                bd.pow(1).longValue() * 3 / 2));
+        assertEquals("13 kB", Formatter.formatShortFileSize(context,
+                bd.pow(1).longValue() * 25 / 2));
+
+        assertEquals("1.0 MB", Formatter.formatShortFileSize(context, bd.pow(2).longValue()));
+
+        assertEquals("1.0 GB", Formatter.formatShortFileSize(context, bd.pow(3).longValue()));
+
+        assertEquals("1.0 TB", Formatter.formatShortFileSize(context, bd.pow(4).longValue()));
+
+        assertEquals("1.0 PB", Formatter.formatShortFileSize(context, bd.pow(5).longValue()));
+
+        assertEquals("1000 PB", Formatter.formatShortFileSize(context, bd.pow(6).longValue()));
+
+        // test Negative value
+        assertEquals("-1 B", Formatter.formatShortFileSize(context, -1));
+    }
+
+    @Test
     public void testFormatIpAddress() {
         assertEquals("1.0.168.192", Formatter.formatIpAddress(0xC0A80001));
         assertEquals("1.0.0.127", Formatter.formatIpAddress(0x7F000001));
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index 7c44c77..e085a29 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -1995,6 +1995,54 @@
     }
 
     @Test
+    public void testGetJulianMondayFromWeeksSinceEpoch() {
+        final int mondayBeforeEpoch = Time.MONDAY_BEFORE_JULIAN_EPOCH;
+        assertEquals(mondayBeforeEpoch, Time.getJulianMondayFromWeeksSinceEpoch(0));
+        assertEquals(mondayBeforeEpoch + 7, Time.getJulianMondayFromWeeksSinceEpoch(1));
+        assertEquals(mondayBeforeEpoch + 14, Time.getJulianMondayFromWeeksSinceEpoch(2));
+        assertEquals(mondayBeforeEpoch - 7, Time.getJulianMondayFromWeeksSinceEpoch(-1));
+    }
+
+    @Test
+    public void testGetWeeksSinceEpochFromJulianDay() {
+        final int epoch = Time.EPOCH_JULIAN_DAY;  // a Thursday
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SUNDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.MONDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.TUESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.WEDNESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.THURSDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.FRIDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epoch, Time.SATURDAY));
+
+        final int epochFriday = epoch + 1;
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SUNDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.MONDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.TUESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.WEDNESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.THURSDAY));
+        assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.FRIDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochFriday, Time.SATURDAY));
+
+        final int epochSaturday = epoch + 2;
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SUNDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.MONDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.TUESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.WEDNESDAY));
+        assertEquals(0, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.THURSDAY));
+        assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.FRIDAY));
+        assertEquals(1, Time.getWeeksSinceEpochFromJulianDay(epochSaturday, Time.SATURDAY));
+
+        final int tenWeeksLater = epochSaturday + 10 * 7;
+        assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SUNDAY));
+        assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.MONDAY));
+        assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.TUESDAY));
+        assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.WEDNESDAY));
+        assertEquals(10, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.THURSDAY));
+        assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.FRIDAY));
+        assertEquals(11, Time.getWeeksSinceEpochFromJulianDay(tenWeeksLater, Time.SATURDAY));
+    }
+
+    @Test
     public void testNormalize_utc() {
         Time t = new Time(Time.TIMEZONE_UTC);
         Time expected = new Time(Time.TIMEZONE_UTC);
diff --git a/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
new file mode 100644
index 0000000..76dcbc5
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/pathclippingtest_torus.png
Binary files differ
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index ae4fee1..d78972f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -29,6 +29,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.uirendering.cts.R;
 import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
 import android.uirendering.cts.testinfrastructure.ActivityTestBase;
 import android.uirendering.cts.testinfrastructure.CanvasClient;
@@ -71,18 +72,38 @@
         canvas.restore();
     };
 
+    // draw circle with hole in it, by path operations + path clipping
+    static final CanvasClient sTorusClipOutCanvasClient = (canvas, width, height) -> {
+        canvas.save();
+
+        Path path1 = new Path();
+        path1.addCircle(30, 30, 50, Path.Direction.CW);
+
+        Path path2 = new Path();
+        path2.addCircle(30, 30, 30, Path.Direction.CW);
+
+        canvas.clipPath(path1);
+        canvas.clipOutPath(path2);
+        canvas.drawColor(Color.BLUE);
+
+        canvas.restore();
+    };
+
     @Test
     public void testCircleWithCircle() {
         createTest()
                 .addCanvasClient("TorusDraw", sTorusDrawCanvasClient, false)
                 .addCanvasClient("TorusClip", sTorusClipCanvasClient)
-                .runWithComparer(new MSSIMComparer(0.90));
+                .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
+                .runWithVerifier(new GoldenImageVerifier(getActivity(),
+                        R.drawable.pathclippingtest_torus, new MSSIMComparer(0.95)));
     }
 
     @Test
     public void testCircleWithPoints() {
         createTest()
                 .addCanvasClient("TorusClip", sTorusClipCanvasClient)
+                .addCanvasClient("TorusClipOut", sTorusClipOutCanvasClient)
                 .runWithVerifier(new SamplePointVerifier(
                         new Point[] {
                                 // inside of circle
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 94c981f..d6a196a 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -127,6 +127,7 @@
                     TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888);
             Rect srcRect = new Rect(testOffset.x, testOffset.y,
                     testOffset.x + TEST_WIDTH, testOffset.y + TEST_HEIGHT);
+            Log.d("UiRendering", "capturing screenshot of " + srcRect.toShortString());
             int copyResult = copy.request(getActivity().getWindow(), srcRect, dest);
             Assert.assertEquals(PixelCopy.SUCCESS, copyResult);
             return dest;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 3b57af2..2b8f3b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -209,6 +209,7 @@
         @Override
         public void onDraw() {
             mView.post(() -> {
+                Log.d("UiRendering", "notifying capture");
                 mView.getViewTreeObserver().removeOnDrawListener(this);
                 synchronized (mLock) {
                     mViewWrapper.getLocationOnScreen(mLocationOnScreen);
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 8d56087..a8015ab 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Rect;
@@ -244,6 +245,19 @@
         assertTrue(nextFocus == mBottomRight || nextFocus == mBottomLeft);
     }
 
+    @Test
+    public void testChainVisibility() {
+        mBottomRight.setNextFocusForwardId(mBottomLeft.getId());
+        mBottomLeft.setNextFocusForwardId(mTopRight.getId());
+        mBottomLeft.setVisibility(View.INVISIBLE);
+        View next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+        assertSame(mTopRight, next);
+
+        mBottomLeft.setNextFocusForwardId(View.NO_ID);
+        next = mFocusFinder.findNextFocus(mLayout, mBottomRight, View.FOCUS_FORWARD);
+        assertSame(mTopLeft, next);
+    }
+
     private void verifyNextCluster(View currentCluster, int direction, View expectedNextCluster) {
         View actualNextCluster = mFocusFinder.findNextKeyboardNavigationCluster(
                 mLayout, currentCluster, direction);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index c75f2e4..b40b2f1 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2808,12 +2808,18 @@
         mInstrumentation.waitForIdleSync();
         assertTrue(mTextView.isFocused());
 
-        // Arrows should not cause focus to leave the textfield
+        // Pure-keyboard arrows should not cause focus to leave the textfield
         CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP);
         mInstrumentation.waitForIdleSync();
         assertTrue(mTextView.isFocused());
 
-        CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN);
+        // Non-pure-keyboard arrows, however, should.
+        int dpadRemote = InputDevice.SOURCE_DPAD | InputDevice.SOURCE_KEYBOARD;
+        sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_UP, dpadRemote);
+        mInstrumentation.waitForIdleSync();
+        assertFalse(mTextView.isFocused());
+
+        sendSourceKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_DPAD_DOWN, dpadRemote);
         mInstrumentation.waitForIdleSync();
         assertTrue(mTextView.isFocused());
 
@@ -2823,6 +2829,16 @@
         assertFalse(mTextView.isFocused());
     }
 
+    private void sendSourceKeyDownUp(Instrumentation instrumentation, View targetView, int key,
+            int source) {
+        KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, key);
+        event.setSource(source);
+        CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+        event = new KeyEvent(KeyEvent.ACTION_UP, key);
+        event.setSource(source);
+        CtsKeyEventUtil.sendKey(instrumentation, targetView, event);
+    }
+
     @Test
     public void testSetIncludeFontPadding() throws Throwable {
         mTextView = findTextView(R.id.textview_text);
@@ -3536,6 +3552,7 @@
     public void testGetOffsetForPositionSingleLineLtr() throws Throwable {
         // asserts getOffsetPosition returns correct values for a single line LTR text
         final String text = "aaaaa";
+
         mActivityRule.runOnUiThread(() -> {
             mTextView = new TextView(mActivity);
             mTextView.setText(text);
@@ -3559,17 +3576,21 @@
         mActivityRule.runOnUiThread(() -> mActivity.setContentView(layout));
         mInstrumentation.waitForIdleSync();
 
+        final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+        final int paddingTop = mTextView.getTotalPaddingTop();
+        final int paddingLeft = mTextView.getTotalPaddingLeft();
+
         final int firstOffset = 0;
         final int lastOffset = text.length() - 1;
         final int midOffset = text.length() / 2;
 
         // left edge of view
         float x = 0f;
-        float y = mTextView.getHeight() / 2f;
+        float y = mTextView.getHeight() / 2f + paddingTop;
         assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
 
         // right edge of text
-        x = mTextView.getLayout().getLineWidth(0) - 1f;
+        x = mTextView.getLayout().getLineWidth(0) + paddingLeft - halfCharWidth;
         assertEquals(lastOffset, mTextView.getOffsetForPosition(x, y));
 
         // right edge of view
@@ -3581,7 +3602,7 @@
         assertEquals(firstOffset, mTextView.getOffsetForPosition(x, y));
 
         // horizontal center of text
-        x = (float) Math.floor(mTextView.getLayout().getLineWidth(0) / 2f + 0.5f);
+        x = mTextView.getLayout().getLineWidth(0) / 2f + paddingLeft - halfCharWidth;
         assertEquals(midOffset, mTextView.getOffsetForPosition(x, y));
     }
 
@@ -3615,9 +3636,13 @@
         final Rect lineBounds = new Rect();
         mTextView.getLayout().getLineBounds(0, lineBounds);
 
+        final float halfCharWidth = (float) Math.ceil(mTextView.getPaint().measureText("a") / 2f);
+        final int paddingTop = mTextView.getTotalPaddingTop();
+        final int paddingLeft = mTextView.getTotalPaddingLeft();
+
         // left edge of view at first line
         float x = 0f;
-        float y = lineBounds.height() / 2f;
+        float y = lineBounds.height() / 2f + paddingTop;
         assertEquals(0, mTextView.getOffsetForPosition(x, y));
 
         // right edge of view at first line
@@ -3626,14 +3651,14 @@
 
         // update lineBounds to be the second line
         mTextView.getLayout().getLineBounds(1, lineBounds);
-        y = lineBounds.top + lineBounds.height() / 2;
+        y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
 
         // left edge of view at second line
         x = 0f;
         assertEquals(line.length(), mTextView.getOffsetForPosition(x, y));
 
         // right edge of text at second line
-        x = mTextView.getLayout().getLineWidth(1) - 1f;
+        x = mTextView.getLayout().getLineWidth(1) + paddingLeft - halfCharWidth;
         assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
 
         // right edge of view at second line
@@ -3641,7 +3666,7 @@
         assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
 
         // horizontal center of text at second line
-        x = (float) Math.floor(mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+        x = mTextView.getLayout().getLineWidth(1) / 2f + paddingLeft - halfCharWidth;
         // second line mid offset should not include next line, therefore subtract one
         assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
     }
@@ -3676,9 +3701,14 @@
         final Rect lineBounds = new Rect();
         mTextView.getLayout().getLineBounds(0, lineBounds);
 
+        final float halfCharWidth = (float) Math.ceil(
+                mTextView.getPaint().measureText("\u0635") / 2f);
+        final int paddingTop = mTextView.getTotalPaddingTop();
+        final int paddingRight = mTextView.getTotalPaddingRight();
+
         // right edge of view at first line
         float x = mTextView.getWidth() - 1f;
-        float y = lineBounds.height() / 2f;
+        float y = lineBounds.height() / 2f + paddingTop;
         assertEquals(0, mTextView.getOffsetForPosition(x, y));
 
         // left edge of view at first line
@@ -3687,7 +3717,7 @@
 
         // update lineBounds to be the second line
         mTextView.getLayout().getLineBounds(1, lineBounds);
-        y = lineBounds.top + lineBounds.height() / 2f;
+        y = lineBounds.top + lineBounds.height() / 2f + paddingTop;
 
         // right edge of view at second line
         x = mTextView.getWidth() - 1f;
@@ -3697,13 +3727,14 @@
         x = 0f;
         assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
 
-        // right edge of text at second line
-        x = mTextView.getWidth() - mTextView.getLayout().getLineWidth(1) + 1f;
+        // left edge of text at second line
+        x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) + paddingRight
+                - halfCharWidth);
         assertEquals(line.length() + line.length() - 1, mTextView.getOffsetForPosition(x, y));
 
         // horizontal center of text at second line
-        x = mTextView.getWidth() - (float) Math.floor(
-                mTextView.getLayout().getLineWidth(1) / 2f + 0.5f);
+        x = mTextView.getWidth() - (mTextView.getLayout().getLineWidth(1) / 2f + paddingRight
+                - halfCharWidth);
         // second line mid offset should not include next line, therefore subtract one
         assertEquals(line.length() + (line.length() - 1) / 2, mTextView.getOffsetForPosition(x, y));
     }
@@ -7309,12 +7340,17 @@
         PollingCheck.waitFor(() -> mTextView.getSelectionStart() == SMARTSELECT_START
                 && mTextView.getSelectionEnd() == SMARTSELECT_END);
 
-        // Click to reset selection. Expect selection of original selection.
+        // Tap to reset selection. Expect tapped word to be selected.
+        startIndex = text.indexOf("Filip");
+        endIndex = startIndex + "Filip".length();
+        offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex);
         emulateClickOnView(mTextView, offset.x, offset.y);
-        PollingCheck.waitFor(() -> mTextView.getSelectionStart() == startIndex
-                && mTextView.getSelectionEnd() == endIndex);
+        final int selStart = startIndex;
+        final int selEnd = endIndex;
+        PollingCheck.waitFor(() -> mTextView.getSelectionStart() == selStart
+                && mTextView.getSelectionEnd() == selEnd);
 
-        // Click one more time to dismiss the selection.
+        // Tap one more time to dismiss the selection.
         emulateClickOnView(mTextView, offset.x, offset.y);
         assertFalse(mTextView.hasSelection());
     }