Merge "autofill cts: fix tests for ATV and sync to latest ATV UX" into pi-dev
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
index bb91c9a..a626ee4 100644
--- a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
+++ b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
@@ -18,7 +18,7 @@
 import its.target
 
 AE_FRAMES_PER_ITERATION = 8
-AE_CONVERGE_ITERATIONS = 3
+AE_CONVERGE_ITERATIONS = 5
 # AE must converge within this number of auto requests under scene1
 THRESH_AE_CONVERGE = AE_FRAMES_PER_ITERATION * AE_CONVERGE_ITERATIONS
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
index 6c00d08..1f8b024 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
@@ -73,7 +73,7 @@
     public CompatibilityTestSuite() {
         try {
             OptionSetter setter = new OptionSetter(this);
-            setter.setOptionValue("config-patterns", ".*.config");
+            setter.setOptionValue("config-patterns", ".*\\.config");
             setter.setOptionValue("skip-loading-config-jar", "true");
         } catch (ConfigurationException e) {
             // Should not happen
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
index 5b17a74..a46e83b 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BasicAdminReceiver.java
@@ -44,6 +44,7 @@
     @Override
     public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri,
             String suggestedAlias) {
+        super.onChoosePrivateKeyAlias(context, intent, uid, uri, suggestedAlias);
         if (uid != Process.myUid() || uri == null) {
             return null;
         }
@@ -52,32 +53,38 @@
 
     @Override
     public void onUserAdded(Context context, Intent intent, UserHandle userHandle) {
+        super.onUserAdded(context, intent, userHandle);
         sendUserBroadcast(context, ACTION_USER_ADDED, userHandle);
     }
 
     @Override
     public void onUserRemoved(Context context, Intent intent, UserHandle userHandle) {
+        super.onUserRemoved(context, intent, userHandle);
         sendUserBroadcast(context, ACTION_USER_REMOVED, userHandle);
     }
 
     @Override
     public void onUserStarted(Context context, Intent intent, UserHandle userHandle) {
+        super.onUserStarted(context, intent, userHandle);
         sendUserBroadcast(context, ACTION_USER_STARTED, userHandle);
     }
 
     @Override
     public void onUserStopped(Context context, Intent intent, UserHandle userHandle) {
+        super.onUserStopped(context, intent, userHandle);
         sendUserBroadcast(context, ACTION_USER_STOPPED, userHandle);
     }
 
     @Override
     public void onUserSwitched(Context context, Intent intent, UserHandle userHandle) {
+        super.onUserSwitched(context, intent, userHandle);
         sendUserBroadcast(context, ACTION_USER_SWITCHED, userHandle);
     }
 
     @Override
     public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
             int networkLogsCount) {
+        super.onNetworkLogsAvailable(context, intent, batchToken, networkLogsCount);
         // send the broadcast, the rest of the test happens in NetworkLoggingTest
         Intent batchIntent = new Intent(ACTION_NETWORK_LOGS_AVAILABLE);
         batchIntent.putExtra(EXTRA_NETWORK_LOGS_BATCH_TOKEN, batchToken);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserSessionTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserSessionTest.java
new file mode 100644
index 0000000..e86d202
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserSessionTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceowner;
+
+/** Test for user session APIs. */
+public class UserSessionTest extends BaseDeviceOwnerTest {
+
+    private static final String START_SESSION_MESSAGE = "Start session";
+    private static final String END_SESSION_MESSAGE = "End session";
+
+    public void testSetLogoutEnabled() {
+        mDevicePolicyManager.setLogoutEnabled(getWho(), true);
+        assertTrue(mDevicePolicyManager.isLogoutEnabled());
+    }
+
+    public void testSetLogoutDisabled() {
+        mDevicePolicyManager.setLogoutEnabled(getWho(), false);
+        assertFalse(mDevicePolicyManager.isLogoutEnabled());
+    }
+
+    public void testSetStartUserSessionMessage() {
+        mDevicePolicyManager.setStartUserSessionMessage(getWho(), START_SESSION_MESSAGE);
+        assertEquals(START_SESSION_MESSAGE,
+                mDevicePolicyManager.getStartUserSessionMessage(getWho()));
+    }
+
+    public void testSetEndUserSessionMessage() {
+        mDevicePolicyManager.setEndUserSessionMessage(getWho(), END_SESSION_MESSAGE);
+        assertEquals(END_SESSION_MESSAGE, mDevicePolicyManager.getEndUserSessionMessage(getWho()));
+    }
+
+    public void testClearStartUserSessionMessage() {
+        mDevicePolicyManager.setStartUserSessionMessage(getWho(), START_SESSION_MESSAGE);
+        mDevicePolicyManager.setStartUserSessionMessage(getWho(), null);
+        assertNull(mDevicePolicyManager.getStartUserSessionMessage(getWho()));
+    }
+
+
+    public void testClearEndUserSessionMessage() {
+        mDevicePolicyManager.setEndUserSessionMessage(getWho(), END_SESSION_MESSAGE);
+        mDevicePolicyManager.setEndUserSessionMessage(getWho(), null);
+        assertNull(mDevicePolicyManager.getEndUserSessionMessage(getWho()));
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index f9f1fce..25627ee 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -494,6 +494,13 @@
         }
     }
 
+    public void testUserSession() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceOwnerTest("UserSessionTest");
+    }
+
     public void testSecurityLoggingWithTwoUsers() throws Exception {
         if (!mHasFeature || !canCreateAdditionalUsers(1)) {
             return;
diff --git a/hostsidetests/incident/src/com/android/server/cts/IncidentdIsolatedTest.java b/hostsidetests/incident/src/com/android/server/cts/IncidentdIsolatedTest.java
index e4f5176..98e4b7a 100644
--- a/hostsidetests/incident/src/com/android/server/cts/IncidentdIsolatedTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/IncidentdIsolatedTest.java
@@ -34,7 +34,7 @@
         if (execCommandAndFind(CMD_TOP, SYSTEM_SERVER) != null) {
             CLog.logAndDisplay(LogLevel.INFO, "stop server");
             getDevice().executeShellCommand("stop");
-            Thread.sleep(3000); // wait for 3 seconds to stop.
+            Thread.sleep(10000); // wait for 10 seconds to stop.
             assertTrue("system_server failed to stop",
                     !execCommandAndGet(CMD_TOP).contains(SYSTEM_SERVER));
         }
diff --git a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
index a3751f5..c62d85e 100644
--- a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
@@ -27,7 +27,7 @@
         final String destArg = dest == null || dest.isEmpty() ? "" : "-p " + dest;
         final IncidentProto dump = getDump(IncidentProto.parser(), "incident " + destArg + " 2>/dev/null");
 
-        SystemPropertiesTest.verifySystemPropertiesProto(dump.getSystemProperties(), filterLevel, mCtsBuild);
+        SystemPropertiesTest.verifySystemPropertiesProto(dump.getSystemProperties(), filterLevel);
 
         StackTraceIncidentTest.verifyBackTraceProto(dump.getNativeTraces(), filterLevel);
         StackTraceIncidentTest.verifyBackTraceProto(dump.getHalTraces(), filterLevel);
diff --git a/hostsidetests/incident/src/com/android/server/cts/SystemPropertiesTest.java b/hostsidetests/incident/src/com/android/server/cts/SystemPropertiesTest.java
index 28e1886..8dc4722 100644
--- a/hostsidetests/incident/src/com/android/server/cts/SystemPropertiesTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/SystemPropertiesTest.java
@@ -24,18 +24,16 @@
 public class SystemPropertiesTest extends ProtoDumpTestCase {
     private static final String TAG = "SystemPropertiesTest";
 
-    static void verifySystemPropertiesProto(SystemPropertiesProto properties,
-            final int filterLevel, IBuildInfo ctsBuild) {
+    static void verifySystemPropertiesProto(SystemPropertiesProto properties, final int filterLevel) {
         // check local tagged field
         if (filterLevel == PRIVACY_LOCAL) {
             assertTrue(properties.getExtraPropertiesCount() > 0);
         } else {
             assertEquals(0, properties.getExtraPropertiesCount());
         }
-        // check explicit tagged field
-        assertEquals(filterLevel == PRIVACY_AUTO, properties.getDalvikVm().getHeapmaxfree().isEmpty());
-        // check automatic tagged field
-        assertEquals(ctsBuild.getBuildId(),
-            properties.getRo().getBuild().getVersion().getIncremental());
+        // check explicit tagged field, persist.sys.timezone is public prop.
+        assertEquals(filterLevel == PRIVACY_AUTO, properties.getPersist().getSysTimezone().isEmpty());
+        // check automatic tagged field, ro.build.display.id is public prop.
+        assertFalse(properties.getRo().getBuild().getDisplayId().isEmpty());
     }
 }
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index d03fc17..e25b96c 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -55,6 +55,7 @@
     $(HOST_OUT_EXECUTABLES)/checkfc \
     $(HOST_OUT_EXECUTABLES)/property_info_checker \
     $(HOST_OUT_EXECUTABLES)/searchpolicy \
+    $(HOST_OUT_EXECUTABLES)/secilc \
     $(HOST_OUT_EXECUTABLES)/sepolicy_tests \
     $(HOST_OUT_EXECUTABLES)/treble_sepolicy_tests \
     $(HOST_OUT)/lib64/libsepolwrap.$(SHAREDLIB_EXT) \
diff --git a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
index d878342..2e0d0a5 100644
--- a/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
+++ b/hostsidetests/security/src/android/cts/security/FileSystemPermissionTest.java
@@ -73,8 +73,8 @@
 
     public void testDevHwRandomPermissions() throws Exception {
         // This test asserts that, if present, /dev/hw_random must:
-        // 1. Be owned by UID root and GID system
-        // 2. Have file permissions 0440 (only readable, and only by owner and group). The reason
+        // 1. Be owned by UID root
+        // 2. Not allow any world read, write, or execute permissions. The reason
         //    for being not readable by all/other is to avoid apps reading from this device.
         //    Firstly, /dev/hw_random is not public API for apps. Secondly, apps might erroneously
         //    use the output of Hardware RNG as trusted random output. Android does not trust output
@@ -96,9 +96,9 @@
             fail("Unexpected output from " + command + ": \"" + output + "\"");
         }
         String[] outputWords = output.split("\\s");
-        assertEquals("Wrong file mode on " + HW_RNG_DEVICE, "cr--r-----", outputWords[0]);
+        assertEquals("Wrong device type on " + HW_RNG_DEVICE, "c", outputWords[0].substring(0, 1));
+        assertEquals("Wrong world file mode on " + HW_RNG_DEVICE, "---", outputWords[0].substring(7));
         assertEquals("Wrong owner of " + HW_RNG_DEVICE, "root", outputWords[2]);
-        assertEquals("Wrong group of " + HW_RNG_DEVICE, "system", outputWords[3]);
         assertEquals("Wrong device major on " + HW_RNG_DEVICE, "10,", outputWords[4]);
         assertEquals("Wrong device minor on " + HW_RNG_DEVICE, "183", outputWords[5]);
 
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 8428758..6cada58 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -52,6 +52,11 @@
 import java.util.Scanner;
 import java.util.Set;
 
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
 /**
  * Host-side SELinux tests.
  *
@@ -64,6 +69,8 @@
     private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1);
+    private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1);
+    private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1);
 
     private File sepolicyAnalyze;
     private File checkSeapp;
@@ -191,6 +198,54 @@
         return file;
     }
 
+    private static File buildSystemPolicy(ITestDevice device, Map<ITestDevice, File> cache,
+            String tmpFileName) throws Exception {
+        File builtPolicyFile;
+        synchronized (cache) {
+            builtPolicyFile = cache.get(device);
+        }
+        if (builtPolicyFile != null) {
+            return builtPolicyFile;
+        }
+
+
+        builtPolicyFile = File.createTempFile(tmpFileName, ".tmp");
+        builtPolicyFile.deleteOnExit();
+
+        File secilc = copyResourceToTempFile("/secilc");
+        secilc.setExecutable(true);
+
+        File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil");
+        systemSepolicyCilFile.deleteOnExit();
+
+        if (!device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile)) {
+            device.pullFile("/plat_sepolicy.cil", systemSepolicyCilFile);
+        }
+
+        ProcessBuilder pb = new ProcessBuilder(
+            secilc.getAbsolutePath(),
+            "-m", "-M", "true", "-c", "30",
+            "-o", builtPolicyFile.getAbsolutePath(),
+            systemSepolicyCilFile.getAbsolutePath());
+        pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
+        pb.redirectErrorStream(true);
+        Process p = pb.start();
+        p.waitFor();
+        BufferedReader result = new BufferedReader(new InputStreamReader(p.getInputStream()));
+        String line;
+        StringBuilder errorString = new StringBuilder();
+        while ((line = result.readLine()) != null) {
+            errorString.append(line);
+            errorString.append("\n");
+        }
+        assertTrue(errorString.toString(), errorString.length() == 0);
+
+        synchronized (cache) {
+            cache.put(device, builtPolicyFile);
+        }
+        return builtPolicyFile;
+    }
+
     // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
     /**
      * Returns the host-side file containing the SELinux policy of the device under test.
@@ -199,6 +254,37 @@
         return getDeviceFile(device, cachedDevicePolicyFiles, "/sys/fs/selinux/policy", "sepolicy");
     }
 
+    // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
+    /**
+     * Returns the host-side file containing the system SELinux policy of the device under test.
+     */
+    public static File getDeviceSystemPolicyFile(ITestDevice device) throws Exception {
+        return buildSystemPolicy(device, cachedDeviceSystemPolicy, "system_sepolicy");
+    }
+
+    // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
+    /**
+     * Returns the major number of sepolicy version of device's vendor implementation.
+     * TODO(b/37999212): Use VINTF object API instead of parsing vendor manifest.
+     */
+    public static int getVendorSepolicyVersion(ITestDevice device) throws Exception {
+        String deviceManifestPath =
+                (device.doesFileExist("/vendor/etc/vintf/manifest.xml")) ?
+                "/vendor/etc/vintf/manifest.xml" :
+                "/vendor/manifest.xml";
+        File vendorManifestFile = getDeviceFile(device, cachedDeviceVendorManifest,
+                deviceManifestPath, "manifest.xml");
+
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        DocumentBuilder db = dbf.newDocumentBuilder();
+        Document doc = db.parse(vendorManifestFile);
+        Element root = doc.getDocumentElement();
+        Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0);
+        Element version = (Element) sepolicy.getElementsByTagName("version").item(0);
+        String sepolicyVersion = version.getTextContent().split("\\.")[0];
+        return Integer.parseInt(sepolicyVersion);
+    }
+
     /**
      * Tests that the kernel is enforcing selinux policy globally.
      *
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 71d600a..809d682 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.os.WakeLockLevelEnum;
+import android.platform.test.annotations.RestrictedBuildTest;
 
 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
@@ -228,6 +229,7 @@
         assertTrue("found uid " + uid, found);
     }
 
+    @RestrictedBuildTest
     public void testCpuTimePerUidFreq() throws Exception {
         StatsdConfig.Builder config = getPulledConfig();
         FieldMatcher.Builder dimension = FieldMatcher.newBuilder()
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
index 75a73f2..b968811 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -29,6 +29,7 @@
     private static final int CANCEL_JOB_ID = TimingConstraintsTest.class.hashCode() + 1;
     private static final int EXPIRED_JOB_ID = TimingConstraintsTest.class.hashCode() + 2;
     private static final int UNEXPIRED_JOB_ID = TimingConstraintsTest.class.hashCode() + 3;
+    private static final int ZERO_DELAY_JOB_ID = TimingConstraintsTest.class.hashCode() + 4;
 
     public void testScheduleOnce() throws Exception {
         JobInfo oneTimeJob = new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
@@ -45,6 +46,7 @@
         JobInfo cancelJob = new JobInfo.Builder(CANCEL_JOB_ID, kJobServiceComponent)
                 .setMinimumLatency(5000L) // make sure it doesn't actually run immediately
                 .setOverrideDeadline(7000L)
+                .setRequiresDeviceIdle(true)
                 .build();
 
         kTestEnvironment.setExpectedExecutions(0);
@@ -55,6 +57,19 @@
                 kTestEnvironment.awaitTimeout());
     }
 
+    public void testExplicitZeroLatency() throws Exception {
+        JobInfo job = new JobInfo.Builder(ZERO_DELAY_JOB_ID, kJobServiceComponent)
+                .setMinimumLatency(0L)
+                .setRequiresDeviceIdle(true)
+                .setOverrideDeadline(10_000L)
+                .build();
+        kTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(job);
+        final boolean executed = kTestEnvironment.awaitExecution();
+        assertTrue("Failed to execute job with explicit zero min latency",
+                kTestEnvironment.awaitExecution());
+    }
+
     /**
      * Ensure that when a job is executed because its deadline has expired, that
      * {@link JobParameters#isOverrideDeadlineExpired()} returns the correct value.
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 50a8bcc..720a4e0 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -187,6 +187,18 @@
         assertTrue(TextUtils.equals(originalText, info.getContentDescription()));
     }
 
+    @SmallTest
+    public void testIsHeadingTakesOldApiIntoAccount() {
+        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        assertFalse(info.isHeading());
+        final CollectionItemInfo headingItemInfo = CollectionItemInfo.obtain(0, 1, 0, 1, true);
+        info.setCollectionItemInfo(headingItemInfo);
+        assertTrue(info.isHeading());
+        final CollectionItemInfo nonHeadingItemInfo = CollectionItemInfo.obtain(0, 1, 0, 1, false);
+        info.setCollectionItemInfo(nonHeadingItemInfo);
+        assertFalse(info.isHeading());
+    }
+
     /**
      * Fully populates the {@link AccessibilityNodeInfo} to marshal.
      *
diff --git a/tests/autofillservice/res/layout/fat_activity.xml b/tests/autofillservice/res/layout/fat_activity.xml
index 9b4a8b6..2efd33c 100644
--- a/tests/autofillservice/res/layout/fat_activity.xml
+++ b/tests/autofillservice/res/layout/fat_activity.xml
@@ -17,6 +17,7 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/root"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:focusable="true"
@@ -136,10 +137,10 @@
 
     </LinearLayout>
 
-    <View
+    <view class="android.autofillservice.cts.FatActivity$MyView"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:autofillHints="importantAmI">
-    </View>
+    </view>
 
 </LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FatActivity.java b/tests/autofillservice/src/android/autofillservice/cts/FatActivity.java
index b3e064e..8a53f4a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FatActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FatActivity.java
@@ -25,10 +25,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.content.Context;
 import android.os.Bundle;
+import android.util.AttributeSet;
 import android.view.View;
 import android.widget.EditText;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 
 /**
  * An activity containing mostly widgets that should be removed from an auto-fill structure to
@@ -41,6 +44,7 @@
     static final String ID_INPUT_CONTAINER = "input_container";
     static final String ID_IMAGE = "image";
     static final String ID_IMPORTANT_IMAGE = "important_image";
+    static final String ID_ROOT = "root";
 
     static final String ID_NOT_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS =
             "not_important_container_excluding_descendants";
@@ -63,6 +67,7 @@
     static final String ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_GRAND_CHILD =
             "not_important_container_mixed_descendants_grand_child";
 
+    private LinearLayout mRoot;
     private EditText mCaptcha;
     private EditText mInput;
     private ImageView mImage;
@@ -80,7 +85,7 @@
     private View mNotImportantContainerMixedDescendantsChild;
     private View mNotImportantContainerMixedDescendantsGrandChild;
 
-    private View mViewWithAutofillHints;
+    private MyView mViewWithAutofillHints;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -88,6 +93,7 @@
 
         setContentView(R.layout.fat_activity);
 
+        mRoot = findViewById(R.id.root);
         mCaptcha = findViewById(R.id.captcha);
         mInput = findViewById(R.id.input);
         mImage = findViewById(R.id.image);
@@ -114,10 +120,11 @@
         mNotImportantContainerMixedDescendantsGrandChild = findViewById(
                 R.id.not_important_container_mixed_descendants_grand_child);
 
-        mViewWithAutofillHints = findViewByAutofillHint(this, "importantAmI");
+        mViewWithAutofillHints = (MyView) findViewByAutofillHint(this, "importantAmI");
         assertThat(mViewWithAutofillHints).isNotNull();
 
         // Sanity check for importantForAutofill modes
+        assertThat(mRoot.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_AUTO);
         assertThat(mInput.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_YES);
         assertThat(mCaptcha.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_NO);
         assertThat(mImage.getImportantForAutofill()).isEqualTo(IMPORTANT_FOR_AUTOFILL_NO);
@@ -157,4 +164,18 @@
             v.visit(mInput);
         });
     }
+
+    /**
+     * Custom view that defines an autofill type so autofill hints are set on {@code ViewNode}.
+     */
+    public static class MyView extends View {
+        public MyView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public int getAutofillType() {
+            return AUTOFILL_TYPE_TEXT;
+        }
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
index 9e458fd4..28ecdf7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FatActivityTest.java
@@ -30,9 +30,11 @@
 import static android.autofillservice.cts.FatActivity.ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS;
 import static android.autofillservice.cts.FatActivity.ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_CHILD;
 import static android.autofillservice.cts.FatActivity.ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_GRAND_CHILD;
-import static android.autofillservice.cts.Helper.assertNumberOfChildren;
+import static android.autofillservice.cts.FatActivity.ID_ROOT;
+import static android.autofillservice.cts.Helper.findNodeByAutofillHint;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.Helper.findNodeByText;
+import static android.autofillservice.cts.Helper.getNumberNodes;
 import static android.autofillservice.cts.Helper.importantForAutofillAsString;
 import static android.view.View.IMPORTANT_FOR_AUTOFILL_AUTO;
 import static android.view.View.IMPORTANT_FOR_AUTOFILL_NO;
@@ -43,7 +45,6 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 
@@ -61,7 +62,7 @@
         new AutofillActivityTestRule<FatActivity>(FatActivity.class);
 
     private FatActivity mFatActivity;
-    private AssistStructure mStructure;
+    private ViewNode mRoot;
 
     @Before
     public void setActivity() {
@@ -69,7 +70,7 @@
     }
 
     @Test
-    public void testNoContainers() throws Exception {
+    public void testAutomaticRequest() throws Exception {
         // Set service.
         enableService();
 
@@ -79,13 +80,22 @@
         // Trigger auto-fill.
         mFatActivity.onInput((v) -> v.requestFocus());
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        mStructure = fillRequest.structure;
         mUiBot.assertNoDatasetsEver();
 
-        // TODO: should only have X children, but there is an extra
-        // TextView that's probably coming from the title. For now we're just ignoring it, but
-        // ideally we should change the .xml to exclude it.
-        assertNumberOfChildren(fillRequest.structure, 9);
+        mRoot = findNodeByResourceId(fillRequest.structure, ID_ROOT);
+        /*
+         * Should have 8 nodes:
+         *
+         * 1. root
+         * 2. important_image
+         * 3. label without id but marked as important
+         * 4. input_container
+         * 5. input
+         * 6. important_container_excluding_descendants
+         * 7. not_important_container_mixed_descendants_child
+         * 8. view (My View) without id but with hints
+         */
+        assertThat(getNumberNodes(mRoot)).isEqualTo(8);
 
         // Should not have ImageView...
         assertThat(findNodeByResourceId(fillRequest.structure, ID_IMAGE)).isNull();
@@ -108,28 +118,31 @@
         assertThat(input.getIdEntry()).isEqualTo(ID_INPUT);
 
         // Make sure a non-important container can exclude descendants
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_NOT_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS)).isNull();
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_NOT_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS_CHILD)).isNull();
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_NOT_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS_GRAND_CHILD)).isNull();
 
         // Make sure an important container can exclude descendants
         assertNodeExists(ID_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS,
                 IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS);
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS_CHILD)).isNull();
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_IMPORTANT_CONTAINER_EXCLUDING_DESCENDANTS_GRAND_CHILD)).isNull();
 
         // Make sure an intermediary descendant can be excluded
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS)).isNull();
         assertNodeExists(ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_CHILD,
                 IMPORTANT_FOR_AUTOFILL_YES);
-        assertThat(findNodeByResourceId(fillRequest.structure,
+        assertThat(findNodeByResourceId(mRoot,
                 ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_GRAND_CHILD)).isNull();
+
+        // Make sure entry with hints is always shown
+        assertThat(findNodeByAutofillHint(mRoot, "importantAmI")).isNotNull();
     }
 
     @Test
@@ -143,13 +156,31 @@
         // Trigger autofill.
         mFatActivity.onInput((v) -> mFatActivity.getAutofillManager().requestAutofill(v));
         final FillRequest fillRequest = sReplier.getNextFillRequest();
-        mStructure = fillRequest.structure;
         mUiBot.assertNoDatasetsEver();
 
-        // TODO: should only have X children, but there is an extra
-        // TextView that's probably coming from the title. For now we're just ignoring it, but
-        // ideally we should change the .xml to exclude it.
-        assertNumberOfChildren(fillRequest.structure, 28);
+        mRoot = findNodeByResourceId(fillRequest.structure, ID_ROOT);
+        /*
+         * Should have 18 nodes:
+         * 1. root
+         * 2. layout without id
+         * 3. label without id
+         * 4. input_container
+         * 5. input
+         * 6. captcha
+         * 7. image
+         * 8. important_image
+         * 9. not_important_container_excluding_descendants
+         * 10.not_important_container_excluding_descendants_child
+         * 11.not_important_container_excluding_descendants_grand_child
+         * 12.important_container_excluding_descendants
+         * 13.important_container_excluding_descendants_child
+         * 14.important_container_excluding_descendants_grand_child
+         * 15.not_important_container_mixed_descendants
+         * 16.not_important_container_mixed_descendants_child
+         * 17.not_important_container_mixed_descendants_grand_child
+         * 18.view (My View) without id but with hints
+         */
+        assertThat(getNumberNodes(mRoot)).isEqualTo(18);
 
         // Assert all nodes are present
         assertNodeExists(ID_IMAGE, IMPORTANT_FOR_AUTOFILL_NO);
@@ -186,15 +217,18 @@
                 IMPORTANT_FOR_AUTOFILL_YES);
         assertNodeExists(ID_NOT_IMPORTANT_CONTAINER_MIXED_DESCENDANTS_GRAND_CHILD,
                 IMPORTANT_FOR_AUTOFILL_NO);
+        assertNode(findNodeByAutofillHint(mRoot, "importantAmI"), IMPORTANT_FOR_AUTOFILL_AUTO);
     }
 
     private ViewNode assertNodeExists(String resourceId, int expectedImportantForAutofill) {
-        final ViewNode node = findNodeByResourceId(mStructure, resourceId);
+        assertWithMessage("root node not set").that(mRoot).isNotNull();
+        final ViewNode node = findNodeByResourceId(mRoot, resourceId);
+        assertWithMessage("no node with resource id '%s'", resourceId).that(node).isNotNull();
         return assertNode(node, resourceId, expectedImportantForAutofill);
     }
 
     private ViewNode assertNodeWithTextExists(String text, int expectedImportantForAutofill) {
-        final ViewNode node = findNodeByText(mStructure, text);
+        final ViewNode node = findNodeByText(mRoot, text);
         return assertNode(node, text, expectedImportantForAutofill);
     }
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 34a2d96..0a6cfc7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -137,6 +137,10 @@
         return id.equals(node.getText());
     };
 
+    private static final NodeFilter<ViewNode> AUTOFILL_HINT_FILTER = (node, id) -> {
+        return hasHint(node.getAutofillHints(), id);
+    };
+
     private static final NodeFilter<ViewNode> WEBVIEW_FORM_FILTER = (node, id) -> {
         final String className = node.getClassName();
         if (!className.equals("android.webkit.WebView")) return false;
@@ -315,6 +319,14 @@
     }
 
     /**
+     * Gets a node given the value of its (single) autofill hint property, or {@code null} if not
+     * found.
+     */
+    static ViewNode findNodeByAutofillHint(ViewNode node, String hint) {
+        return findNodeByFilter(node, hint, AUTOFILL_HINT_FILTER);
+    }
+
+    /**
      * Gets a node given the name of its HTML INPUT tag or Android resoirce id, or {@code null} if
      * not found.
      */
@@ -644,7 +656,7 @@
     }
 
     /**
-     * Gets the total number of nodes in an structure, including all descendants.
+     * Gets the total number of nodes in an structure.
      */
     static int getNumberNodes(AssistStructure structure) {
         int count = 0;
@@ -660,7 +672,7 @@
     /**
      * Gets the total number of nodes in an node, including all descendants and the node itself.
      */
-    private static int getNumberNodes(ViewNode node) {
+    static int getNumberNodes(ViewNode node) {
         int count = 1;
         final int childrenSize = node.getChildCount();
         if (childrenSize > 0) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 430f238..81335c0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -358,8 +358,11 @@
                 blockingListenerList.add(mCameraListener);
             }
         } finally {
-            for (CameraDevice camera : cameraList) {
-                camera.close();
+            for (int i = 0; i < cameraList.size(); i++) {
+                // With conflicting devices, opening of one camera could result in the other camera
+                // being disconnected. To handle such case, reset the mock before close.
+                reset(listenerList.get(i));
+                cameraList.get(i).close();
             }
             for (BlockingStateCallback blockingListener : blockingListenerList) {
                 blockingListener.waitForState(
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index 488c0f3..6ca9601 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -102,6 +102,11 @@
 
         assertTrue("Bad timestamps", timestamp2 > timestamp);
 
+        // If EnableZsl is supported, disable ZSL in order to compare preview and still timestamps.
+        if (mStaticInfo.isEnableZslSupported()) {
+            stillCaptureRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, false);
+        }
+
         CaptureRequest capture = stillCaptureRequest.build();
         mSession.capture(capture, resultListener, mHandler);
 
diff --git a/tests/framework/base/windowmanager/Android.mk b/tests/framework/base/windowmanager/Android.mk
index af34c5d..a098701 100644
--- a/tests/framework/base/windowmanager/Android.mk
+++ b/tests/framework/base/windowmanager/Android.mk
@@ -25,6 +25,8 @@
     $(call all-named-files-under,Components.java, alertwindowapp) \
     $(call all-named-files-under,Components.java, alertwindowappsdk25)
 
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
 LOCAL_PACKAGE_NAME := CtsWindowManagerDeviceTestCases
 
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 5f08442..3168dae 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -34,7 +34,8 @@
         <activity android:name="android.server.wm.AlertWindowsAppOpsTestsActivity"/>
         <activity android:name="android.server.wm.DialogFrameTestActivity" />
         <activity android:name="android.server.wm.DisplayCutoutTests$TestActivity" />
-        <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity" />
+        <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
+            android:theme="@style/no_starting_window" />
         <activity android:name="android.server.wm.LocationInWindowTests$TestActivity" />
 
     </application>
diff --git a/tests/framework/base/windowmanager/res/values/styles.xml b/tests/framework/base/windowmanager/res/values/styles.xml
new file mode 100644
index 0000000..4e59d14
--- /dev/null
+++ b/tests/framework/base/windowmanager/res/values/styles.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowDisablePreview">true</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LocationInWindowTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LocationInWindowTests.java
index 206185e..1272237 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/LocationInWindowTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LocationInWindowTests.java
@@ -36,7 +36,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,7 +52,6 @@
 import org.junit.rules.ErrorCollector;
 import org.junit.runner.RunWith;
 
-import java.util.Arrays;
 import java.util.function.Supplier;
 
 @RunWith(AndroidJUnit4.class)
@@ -80,18 +78,18 @@
     }
 
     @Test
-    public void testLocationOnDisplay_appWindow() {
+    public void testLocationInWindow_appWindow() {
         runTest(mLayoutParams);
     }
 
     @Test
-    public void testLocationOnDisplay_appWindow_fullscreen() {
+    public void testLocationInWindow_appWindow_fullscreen() {
         mLayoutParams.flags |= LayoutParams.FLAG_FULLSCREEN;
         runTest(mLayoutParams);
     }
 
     @Test
-    public void testLocationOnDisplay_floatingWindow() {
+    public void testLocationInWindow_floatingWindow() {
         mLayoutParams.height = 100;
         mLayoutParams.width = 100;
         mLayoutParams.gravity = Gravity.CENTER;
@@ -100,13 +98,13 @@
     }
 
     @Test
-    public void testLocationOnDisplay_appWindow_displayCutoutNever() {
+    public void testLocationInWindow_appWindow_displayCutoutNever() {
         mLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
         runTest(mLayoutParams);
     }
 
     @Test
-    public void testLocationOnDisplay_appWindow_displayCutoutShortEdges() {
+    public void testLocationInWindow_appWindow_displayCutoutShortEdges() {
         mLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
         runTest(mLayoutParams);
     }
@@ -116,11 +114,11 @@
         PollingCheck.waitFor(() -> getOnMainSync(activity::isEnterAnimationComplete));
 
         runOnMainSync(() -> {
-            assertThatViewGetLocationInWindowAndSurfaceIsCorrect(activity.mView);
+            assertThatViewGetLocationInWindowIsCorrect(activity.mView);
         });
     }
 
-    private void assertThatViewGetLocationInWindowAndSurfaceIsCorrect(View v) {
+    private void assertThatViewGetLocationInWindowIsCorrect(View v) {
         final float[] expected = new float[] {0, 0};
 
         v.getMatrix().mapPoints(expected);
@@ -147,12 +145,6 @@
         return new Point(out[0], out[1]);
     }
 
-    private Point locationInSurfaceAsPoint(View v) {
-        final int[] out = new int[2];
-        v.getLocationInWindow(out);
-        return new Point(out[0], out[1]);
-    }
-
     private <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) {
         mErrorCollector.checkThat(reason, actual, matcher);
     }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
index 86879ee..7645e25 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -30,14 +30,15 @@
 import android.graphics.ColorSpace;
 import android.graphics.ImageDecoder;
 import android.os.Parcel;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.RequiresDevice;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Log;
 
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -781,6 +782,43 @@
     }
 
     @Test
+    public void testEncodeP3hardware() {
+        Bitmap b = null;
+        ImageDecoder.Source src = ImageDecoder.createSource(mResources.getAssets(),
+                "green-p3.png");
+        try {
+            b = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_HARDWARE);
+            });
+            assertNotNull(b);
+            assertEquals(Bitmap.Config.HARDWARE, b.getConfig());
+            assertEquals(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b.getColorSpace());
+        } catch (IOException e) {
+            fail("Failed with " + e);
+        }
+
+        for (Bitmap.CompressFormat format : new Bitmap.CompressFormat[] {
+                Bitmap.CompressFormat.JPEG,
+                Bitmap.CompressFormat.WEBP,
+                Bitmap.CompressFormat.PNG,
+        }) {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            assertTrue("Failed to encode 8888 to " + format, b.compress(format, 100, out));
+
+            byte[] array = out.toByteArray();
+            src = ImageDecoder.createSource(ByteBuffer.wrap(array));
+
+            try {
+                Bitmap b2 = ImageDecoder.decodeBitmap(src);
+                assertEquals("Wrong color space for " + format,
+                        ColorSpace.get(ColorSpace.Named.DISPLAY_P3), b2.getColorSpace());
+            } catch (IOException e) {
+                fail("Failed with " + e);
+            }
+        }
+    }
+
+    @Test
     @RequiresDevice // SwiftShader does not yet have support for F16 in HARDWARE b/75778024
     public void test16bitHardware() {
         // Decoding to HARDWARE may use LINEAR_EXTENDED_SRGB or SRGB, depending
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index f3c17cf..ae8b991 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -51,13 +51,14 @@
 
     @Override
     protected void tearDown() throws Exception {
+        // try/catch for every method in case the tests left the objects in various states
         if (mMp != null) {
-            mMp.stop();
-            mMp.release();
+            try { mMp.stop(); } catch (Exception e) {}
+            try { mMp.release(); } catch (Exception e) {}
             mMp = null;
         }
         if (mSp != null) {
-            mSp.release();
+            try { mSp.release(); } catch (Exception e) {}
             mSp = null;
         }
     }
@@ -205,7 +206,7 @@
                     nbActivePlayersBeforeStart + 1/*expected*/, callback.getNbConfigs());
             assertTrue("Active player, attributes not found", hasAttr(callback.getConfigs(), aa));
 
-            // stopping recording: callback is called with no match
+            // stopping playback: callback is called with no match
             callback.reset();
             mMp.pause();
             Thread.sleep(TEST_TIMING_TOLERANCE_MS);
@@ -215,7 +216,7 @@
             assertEquals("number of active players not expected after pause",
                     nbActivePlayersBeforeStart/*expected*/, callback.getNbConfigs());
 
-            // unregister callback and start recording again
+            // unregister callback and start playback again
             am.unregisterAudioPlaybackCallback(callback);
             Thread.sleep(TEST_TIMING_TOLERANCE_MS);
             callback.reset();
@@ -235,6 +236,58 @@
         }
     }
 
+    public void testCallbackMediaPlayerRelease() throws Exception {
+        final HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        final Handler h = new Handler(handlerThread.getLooper());
+
+        try {
+            AudioManager am = new AudioManager(getContext());
+            assertNotNull("Could not create AudioManager", am);
+
+            final AudioAttributes aa = (new AudioAttributes.Builder())
+                    .setUsage(TEST_USAGE)
+                    .setContentType(TEST_CONTENT)
+                    .build();
+
+            mMp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong,
+                    aa, am.generateAudioSessionId());
+
+            MyAudioPlaybackCallback callback = new MyAudioPlaybackCallback();
+            am.registerAudioPlaybackCallback(callback, h /*handler*/);
+
+            // query how many active players before starting the MediaPlayer
+            List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
+            final int nbActivePlayersBeforeStart = configs.size();
+
+            mMp.start();
+            Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+
+            assertEquals("onPlaybackConfigChanged call count not expected",
+                    1/*expected*/, callback.getCbInvocationNumber()); //only one start call
+            assertEquals("number of active players not expected",
+                    // one more player active
+                    nbActivePlayersBeforeStart + 1/*expected*/, callback.getNbConfigs());
+            assertTrue("Active player, attributes not found", hasAttr(callback.getConfigs(), aa));
+
+            // release the player without stopping or pausing it first
+            callback.reset();
+            mMp.release();
+            Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+
+            assertEquals("onPlaybackConfigChanged call count not expected after release",
+                    1/*expected*/, callback.getCbInvocationNumber());//only release call since reset
+            assertEquals("number of active players not expected after release",
+                    nbActivePlayersBeforeStart/*expected*/, callback.getNbConfigs());
+
+            am.unregisterAudioPlaybackCallback(callback);
+        } finally {
+            if (h != null) {
+                h.getLooper().quit();
+            }
+        }
+    }
+
     public void testGetterSoundPool() throws Exception {
         if (!isValidPlatform("testSoundPool")) return;
 
diff --git a/tests/tests/media/src/android/media/cts/DynamicsProcessingTest.java b/tests/tests/media/src/android/media/cts/DynamicsProcessingTest.java
index a7ad33b..60f2d21 100644
--- a/tests/tests/media/src/android/media/cts/DynamicsProcessingTest.java
+++ b/tests/tests/media/src/android/media/cts/DynamicsProcessingTest.java
@@ -24,8 +24,14 @@
 import android.media.MediaPlayer;
 import android.media.audiofx.AudioEffect;
 import android.media.audiofx.DynamicsProcessing;
+import android.media.audiofx.DynamicsProcessing.BandBase;
+import android.media.audiofx.DynamicsProcessing.BandStage;
 import android.media.audiofx.DynamicsProcessing.Channel;
 import android.media.audiofx.DynamicsProcessing.Eq;
+import android.media.audiofx.DynamicsProcessing.EqBand;
+import android.media.audiofx.DynamicsProcessing.Limiter;
+import android.media.audiofx.DynamicsProcessing.Mbc;
+import android.media.audiofx.DynamicsProcessing.MbcBand;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -35,7 +41,6 @@
     private DynamicsProcessing mDP;
 
     private static final int MIN_CHANNEL_COUNT = 1;
-    private static final int TEST_CHANNEL = 0;
     private static final float EPSILON = 0.00001f;
     private static final int DEFAULT_VARIANT =
             DynamicsProcessing.VARIANT_FAVOR_FREQUENCY_RESOLUTION;
@@ -49,6 +54,12 @@
     private static final float DEFAULT_FRAME_DURATION = 9.5f;
     private static final float DEFAULT_INPUT_GAIN = -12.5f;
 
+    private static final int TEST_CHANNEL_COUNT = 2;
+    private static final float TEST_GAIN1 = 12.1f;
+    private static final float TEST_GAIN2 = -2.8f;
+    private static final int TEST_CHANNEL_INDEX = 0;
+    private static final int TEST_BAND_INDEX = 0;
+
     //-----------------------------------------------------------------
     // DynamicsProcessing tests:
     //----------------------------------
@@ -129,7 +140,7 @@
             final int channelCount = mDP.getChannelCount();
             assertTrue("unexpected channel count", channelCount >= MIN_CHANNEL_COUNT);
 
-            Channel channel = mDP.getChannelByChannelIndex(TEST_CHANNEL);
+            Channel channel = mDP.getChannelByChannelIndex(TEST_CHANNEL_INDEX);
 
             final float inputGain = channel.getInputGain();
             assertEquals("inputGain is different", DEFAULT_INPUT_GAIN, inputGain, EPSILON);
@@ -145,7 +156,7 @@
         try {
             createDefaultEffect();
 
-            DynamicsProcessing.Eq eq = mDP.getPreEqByChannelIndex(TEST_CHANNEL);
+            DynamicsProcessing.Eq eq = mDP.getPreEqByChannelIndex(TEST_CHANNEL_INDEX);
 
             final boolean inUse = eq.isInUse();
             assertEquals("inUse is different", DEFAULT_PREEQ_IN_USE, inUse);
@@ -165,7 +176,7 @@
         try {
             createDefaultEffect();
 
-            DynamicsProcessing.Mbc mbc = mDP.getMbcByChannelIndex(TEST_CHANNEL);
+            DynamicsProcessing.Mbc mbc = mDP.getMbcByChannelIndex(TEST_CHANNEL_INDEX);
 
             final boolean inUse = mbc.isInUse();
             assertEquals("inUse is different", DEFAULT_MBC_IN_USE, inUse);
@@ -184,7 +195,7 @@
         try {
             createDefaultEffect();
 
-            DynamicsProcessing.Eq eq = mDP.getPostEqByChannelIndex(TEST_CHANNEL);
+            DynamicsProcessing.Eq eq = mDP.getPostEqByChannelIndex(TEST_CHANNEL_INDEX);
 
             boolean inUse = eq.isInUse();
             assertEquals("inUse is different", DEFAULT_POSTEQ_IN_USE, inUse);
@@ -204,7 +215,7 @@
         try {
             createDefaultEffect();
 
-            DynamicsProcessing.Limiter limiter = mDP.getLimiterByChannelIndex(TEST_CHANNEL);
+            DynamicsProcessing.Limiter limiter = mDP.getLimiterByChannelIndex(TEST_CHANNEL_INDEX);
 
             final boolean inUse = limiter.isInUse();
             assertEquals("inUse is different", DEFAULT_LIMITER_IN_USE, inUse);
@@ -214,10 +225,838 @@
     }
 
     //-----------------------------------------------------------------
-    // 2 - run and change parameters
+    // 2 - config builder tests
     //----------------------------------
 
-    //TODO: runtime change of parameters.
+    public void test2_0ConfigBasic() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        DynamicsProcessing.Config config = getBuilderWithValues().build();
+
+        assertEquals("getVariant is different", DEFAULT_VARIANT,
+                config.getVariant());
+        assertEquals("isPreEqInUse is different", DEFAULT_PREEQ_IN_USE,
+                config.isPreEqInUse());
+        assertEquals("getPreEqBandCount is different", DEFAULT_PREEQ_BAND_COUNT,
+                config.getPreEqBandCount());
+        assertEquals("isMbcInUse is different", DEFAULT_MBC_IN_USE,
+                config.isMbcInUse());
+        assertEquals("getMbcBandCount is different", DEFAULT_MBC_BAND_COUNT,
+                config.getMbcBandCount());
+        assertEquals("isPostEqInUse is different", DEFAULT_POSTEQ_IN_USE,
+                config.isPostEqInUse());
+        assertEquals("getPostEqBandCount is different", DEFAULT_POSTEQ_BAND_COUNT,
+                config.getPostEqBandCount());
+        assertEquals("isLimiterInUse is different", DEFAULT_LIMITER_IN_USE,
+                config.isLimiterInUse());
+        assertEquals("getPreferredFrameDuration is different", DEFAULT_FRAME_DURATION,
+                config.getPreferredFrameDuration());
+    }
+
+    public void test2_1ConfigChannel() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        DynamicsProcessing.Config config = getBuilderWithValues(TEST_CHANNEL_COUNT).build();
+
+        //per channel
+        Channel channel = config.getChannelByChannelIndex(TEST_CHANNEL_INDEX);
+        //change some parameters
+        channel.setInputGain(TEST_GAIN1);
+
+        //get stages from channel
+        Eq preEq = channel.getPreEq();
+        EqBand preEqBand = preEq.getBand(TEST_BAND_INDEX);
+        preEqBand.setGain(TEST_GAIN1);
+        channel.setPreEqBand(TEST_BAND_INDEX, preEqBand);
+
+        Mbc mbc = channel.getMbc();
+        MbcBand mbcBand = mbc.getBand(TEST_BAND_INDEX);
+        mbcBand.setPreGain(TEST_GAIN1);
+        channel.setMbcBand(TEST_BAND_INDEX, mbcBand);
+
+        Eq postEq = channel.getPostEq();
+        EqBand postEqBand = postEq.getBand(TEST_BAND_INDEX);
+        postEqBand.setGain(TEST_GAIN1);
+        channel.setPostEqBand(TEST_BAND_INDEX, postEqBand);
+
+        Limiter limiter = channel.getLimiter();
+        limiter.setPostGain(TEST_GAIN1);
+        channel.setLimiter(limiter);
+
+        config.setAllChannelsTo(channel);
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            Channel channelTest = new Channel(config.getChannelByChannelIndex(i));
+            assertEquals("inputGain is different in channel " + i, TEST_GAIN1,
+                    channelTest.getInputGain(), EPSILON);
+
+            EqBand preEqBandTest = new EqBand(channelTest.getPreEqBand(TEST_BAND_INDEX));
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, preEqBandTest.getGain(), EPSILON);
+
+            MbcBand mbcBandTest = new MbcBand(channelTest.getMbcBand(TEST_BAND_INDEX));
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, mbcBandTest.getPreGain(), EPSILON);
+
+            EqBand postEqBandTest = new EqBand(channelTest.getPostEqBand(TEST_BAND_INDEX));
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, postEqBandTest.getGain(), EPSILON);
+
+            Limiter limiterTest = new Limiter(channelTest.getLimiter());
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN1, limiterTest.getPostGain(), EPSILON);
+
+            ///changes per channelIndex
+            channelTest.setInputGain(TEST_GAIN2);
+            preEqBandTest.setGain(TEST_GAIN2);
+            channelTest.setPreEqBand(TEST_BAND_INDEX, preEqBandTest);
+            mbcBandTest.setPreGain(TEST_GAIN2);
+            channelTest.setMbcBand(TEST_BAND_INDEX, mbcBandTest);
+            postEqBandTest.setGain(TEST_GAIN2);
+            channelTest.setPostEqBand(TEST_BAND_INDEX, postEqBandTest);
+            limiterTest.setPostGain(TEST_GAIN2);
+            channelTest.setLimiter(limiterTest);
+            config.setChannelTo(i, channelTest);
+
+            assertEquals("inputGain is different in channel " + i, TEST_GAIN2,
+                    config.getInputGainByChannelIndex(i), EPSILON);
+
+            //get by module
+            Eq preEqTest = config.getPreEqByChannelIndex(i);
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, preEqTest.getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+
+            Mbc mbcTest = config.getMbcByChannelIndex(i);
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, mbcTest.getBand(TEST_BAND_INDEX).getPreGain(), EPSILON);
+
+            Eq postEqTest = config.getPostEqByChannelIndex(i);
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, postEqTest.getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+
+            limiterTest = config.getLimiterByChannelIndex(i);
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN2, limiterTest.getPostGain(), EPSILON);
+        }
+    }
+
+    public void test2_2ConfigChannel_perStage() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        DynamicsProcessing.Config config = getBuilderWithValues(TEST_CHANNEL_COUNT).build();
+
+        //Per Stage
+        config.setInputGainAllChannelsTo(TEST_GAIN1);
+
+        Eq preEq = config.getPreEqByChannelIndex(TEST_CHANNEL_INDEX);
+        EqBand preEqBand = preEq.getBand(TEST_BAND_INDEX);
+        preEqBand.setGain(TEST_GAIN1);
+        preEq.setBand(TEST_BAND_INDEX, preEqBand);
+        config.setPreEqAllChannelsTo(preEq);
+
+        Mbc mbc = config.getMbcByChannelIndex(TEST_CHANNEL_INDEX);
+        MbcBand mbcBand = mbc.getBand(TEST_BAND_INDEX);
+        mbcBand.setPreGain(TEST_GAIN1);
+        mbc.setBand(TEST_BAND_INDEX, mbcBand);
+        config.setMbcAllChannelsTo(mbc);
+
+        Eq postEq = config.getPostEqByChannelIndex(TEST_CHANNEL_INDEX);
+        EqBand postEqBand = postEq.getBand(TEST_BAND_INDEX);
+        postEqBand.setGain(TEST_GAIN1);
+        postEq.setBand(TEST_BAND_INDEX, postEqBand);
+        config.setPostEqAllChannelsTo(postEq);
+
+        Limiter limiter = config.getLimiterByChannelIndex(TEST_CHANNEL_INDEX);
+        limiter.setPostGain(TEST_GAIN1);
+        config.setLimiterAllChannelsTo(limiter);
+
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            Channel channelTest = config.getChannelByChannelIndex(i);
+            assertEquals("inputGain is different in channel " + i, TEST_GAIN1,
+                    channelTest.getInputGain(), EPSILON);
+
+            Eq preEqTest = new Eq(config.getPreEqByChannelIndex(i));
+            EqBand preEqBandTest = preEqTest.getBand(TEST_BAND_INDEX);
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, preEqBandTest.getGain(), EPSILON);
+
+            Mbc mbcTest = new Mbc(config.getMbcByChannelIndex(i));
+            MbcBand mbcBandTest = mbcTest.getBand(TEST_BAND_INDEX);
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, mbcBandTest.getPreGain(), EPSILON);
+
+            Eq postEqTest = new Eq(config.getPostEqByChannelIndex(i));
+            EqBand postEqBandTest = postEqTest.getBand(TEST_BAND_INDEX);
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, postEqBandTest.getGain(), EPSILON);
+
+            Limiter limiterTest = new Limiter(config.getLimiterByChannelIndex(i));
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN1, limiterTest.getPostGain(), EPSILON);
+
+            //change by Stage
+            preEqBandTest.setGain(TEST_GAIN2);
+            preEqTest.setBand(TEST_BAND_INDEX, preEqBandTest);
+            config.setPreEqByChannelIndex(i, preEqTest);
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            mbcBandTest.setPreGain(TEST_GAIN2);
+            mbcTest.setBand(TEST_BAND_INDEX, mbcBandTest);
+            config.setMbcByChannelIndex(i, mbcTest);
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            postEqBandTest.setGain(TEST_GAIN2);
+            postEqTest.setBand(TEST_BAND_INDEX, postEqBandTest);
+            config.setPostEqByChannelIndex(i, postEqTest);
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            limiterTest.setPostGain(TEST_GAIN2);
+            config.setLimiterByChannelIndex(i, limiterTest);
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN2, config.getLimiterByChannelIndex(i).getPostGain(), EPSILON);
+        }
+    }
+
+    public void test2_3ConfigChannel_perBand() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        DynamicsProcessing.Config config = getBuilderWithValues(TEST_CHANNEL_COUNT).build();
+
+        //Per Band
+        EqBand preEqBand = config.getPreEqBandByChannelIndex(TEST_CHANNEL_INDEX, TEST_BAND_INDEX);
+        preEqBand.setGain(TEST_GAIN1);
+        config.setPreEqBandAllChannelsTo(TEST_BAND_INDEX, preEqBand);
+
+        MbcBand mbcBand = config.getMbcBandByChannelIndex(TEST_CHANNEL_INDEX, TEST_BAND_INDEX);
+        mbcBand.setPreGain(TEST_GAIN1);
+        config.setMbcBandAllChannelsTo(TEST_BAND_INDEX, mbcBand);
+
+        EqBand postEqBand = config.getPostEqBandByChannelIndex(TEST_CHANNEL_INDEX, TEST_BAND_INDEX);
+        postEqBand.setGain(TEST_GAIN1);
+        config.setPostEqBandAllChannelsTo(TEST_BAND_INDEX, postEqBand);
+
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+
+            EqBand preEqBandTest = new EqBand(config.getPreEqBandByChannelIndex(i,
+                    TEST_BAND_INDEX));
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, preEqBandTest.getGain(), EPSILON);
+
+            MbcBand mbcBandTest = new MbcBand(config.getMbcBandByChannelIndex(i, TEST_BAND_INDEX));
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, mbcBandTest.getPreGain(), EPSILON);
+
+            EqBand postEqBandTest = new EqBand(config.getPostEqBandByChannelIndex(i,
+                    TEST_BAND_INDEX));
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, postEqBandTest.getGain(), EPSILON);
+
+            //change per Band
+            preEqBandTest.setGain(TEST_GAIN2);
+            config.setPreEqBandByChannelIndex(i, TEST_BAND_INDEX, preEqBandTest);
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            mbcBandTest.setPreGain(TEST_GAIN2);
+            config.setMbcBandByChannelIndex(i, TEST_BAND_INDEX, mbcBandTest);
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            postEqBandTest.setGain(TEST_GAIN2);
+            config.setPostEqBandByChannelIndex(i, TEST_BAND_INDEX, postEqBandTest);
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN2, config.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+        }
+    }
+
+    public void test2_4Channel_perStage() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+
+        Channel channel = new Channel(config.getChannelByChannelIndex(TEST_CHANNEL_INDEX));
+
+        channel.setInputGain(TEST_GAIN1);
+        assertEquals("channel gain is different", TEST_GAIN1, channel.getInputGain(), EPSILON);
+
+        //set by stage
+        Eq preEq = new Eq(channel.getPreEq());
+        EqBand preEqBand = new EqBand(preEq.getBand(TEST_BAND_INDEX));
+        preEqBand.setGain(TEST_GAIN1);
+        preEq.setBand(TEST_BAND_INDEX, preEqBand);
+        channel.setPreEq(preEq);
+        assertEquals("preEQBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getPreEq().getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+        preEqBand.setGain(TEST_GAIN2);
+        preEq.setBand(TEST_BAND_INDEX, preEqBand);
+        channel.setPreEq(preEq);
+        assertEquals("preEQBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getPreEq().getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+
+        Mbc mbc = new Mbc(channel.getMbc());
+        MbcBand mbcBand = new MbcBand(mbc.getBand(TEST_BAND_INDEX));
+        mbcBand.setPreGain(TEST_GAIN1);
+        mbc.setBand(TEST_BAND_INDEX, mbcBand);
+        channel.setMbc(mbc);
+        assertEquals("mbcBand preGain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getMbc().getBand(TEST_BAND_INDEX).getPreGain(), EPSILON);
+        mbcBand.setPreGain(TEST_GAIN2);
+        mbc.setBand(TEST_BAND_INDEX, mbcBand);
+        channel.setMbc(mbc);
+        assertEquals("mbcBand preGain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getMbc().getBand(TEST_BAND_INDEX).getPreGain(), EPSILON);
+
+        Eq postEq = new Eq(channel.getPostEq());
+        EqBand postEqBand = new EqBand(postEq.getBand(TEST_BAND_INDEX));
+        postEqBand.setGain(TEST_GAIN1);
+        postEq.setBand(TEST_BAND_INDEX, postEqBand);
+        channel.setPostEq(postEq);
+        assertEquals("postEqBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getPostEq().getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+        postEqBand.setGain(TEST_GAIN2);
+        postEq.setBand(TEST_BAND_INDEX, postEqBand);
+        channel.setPostEq(postEq);
+        assertEquals("postEQBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getPostEq().getBand(TEST_BAND_INDEX).getGain(), EPSILON);
+
+        Limiter limiter = new Limiter(channel.getLimiter());
+        limiter.setPostGain(TEST_GAIN1);
+        channel.setLimiter(limiter);
+        assertEquals("limiter gain is different",
+                TEST_GAIN1, channel.getLimiter().getPostGain(), EPSILON);
+        limiter.setPostGain(TEST_GAIN2);
+        channel.setLimiter(limiter);
+        assertEquals("limiter gain is different",
+                TEST_GAIN2, channel.getLimiter().getPostGain(), EPSILON);
+
+    }
+
+    public void test2_5Channel_perBand() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+
+        Channel channel = new Channel(config.getChannelByChannelIndex(TEST_CHANNEL_INDEX));
+
+        channel.setInputGain(TEST_GAIN1);
+        assertEquals("channel gain is different", TEST_GAIN1, channel.getInputGain(), EPSILON);
+
+        //set by band
+        EqBand preEqBand = new EqBand(channel.getPreEqBand(TEST_BAND_INDEX));
+        preEqBand.setGain(TEST_GAIN1);
+        channel.setPreEqBand(TEST_BAND_INDEX, preEqBand);
+        assertEquals("preEQBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getPreEqBand(TEST_BAND_INDEX).getGain(), EPSILON);
+        preEqBand.setGain(TEST_GAIN2);
+        channel.setPreEqBand(TEST_BAND_INDEX, preEqBand);
+        assertEquals("preEQBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getPreEqBand(TEST_BAND_INDEX).getGain(), EPSILON);
+
+        MbcBand mbcBand = new MbcBand(channel.getMbcBand(TEST_BAND_INDEX));
+        mbcBand.setPreGain(TEST_GAIN1);
+        channel.setMbcBand(TEST_BAND_INDEX, mbcBand);
+        assertEquals("mbcBand preGain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getMbcBand(TEST_BAND_INDEX).getPreGain(), EPSILON);
+        mbcBand.setPreGain(TEST_GAIN2);
+        channel.setMbcBand(TEST_BAND_INDEX, mbcBand);
+        assertEquals("mbcBand preGain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getMbcBand(TEST_BAND_INDEX).getPreGain(), EPSILON);
+
+        EqBand postEqBand = new EqBand(channel.getPostEqBand(TEST_BAND_INDEX));
+        postEqBand.setGain(TEST_GAIN1);
+        channel.setPostEqBand(TEST_BAND_INDEX, postEqBand);
+        assertEquals("postEqBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN1, channel.getPostEqBand(TEST_BAND_INDEX).getGain(), EPSILON);
+        postEqBand.setGain(TEST_GAIN2);
+        channel.setPostEqBand(TEST_BAND_INDEX, postEqBand);
+        assertEquals("postEqBand gain is different in band "+ TEST_BAND_INDEX,
+                TEST_GAIN2, channel.getPostEqBand(TEST_BAND_INDEX).getGain(), EPSILON);
+    }
+
+    public void test2_6Eq() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        final boolean inUse = true;
+        final boolean enabled = true;
+        final int bandCount = 3;
+
+        Eq eq = new Eq(inUse, enabled, bandCount);
+        assertEquals("eq inUse is different", inUse, eq.isInUse());
+        assertEquals("eq enabled is different", enabled, eq.isEnabled());
+        assertEquals("eq bandCount is different", bandCount, eq.getBandCount());
+
+        //changes
+        eq.setEnabled(!enabled);
+        assertEquals("eq enabled is different", !enabled, eq.isEnabled());
+
+        //bands
+        for (int i = 0; i < bandCount; i++) {
+            final float frequency = (i + 1) * 100.3f;
+            final float gain = (i+1) * 10.1f;
+            EqBand eqBand = new EqBand(eq.getBand(i));
+
+            eqBand.setEnabled(enabled);
+            eqBand.setCutoffFrequency(frequency);
+            eqBand.setGain(gain);
+
+            eq.setBand(i, eqBand);
+
+            //compare
+            assertEquals("eq enabled is different in band " + i, enabled,
+                    eq.getBand(i).isEnabled());
+            assertEquals("eq cutoffFrequency is different in band "+ i,
+                    frequency, eq.getBand(i).getCutoffFrequency(), EPSILON);
+            assertEquals("eq eqBand gain is different in band "+ i,
+                    gain, eq.getBand(i).getGain(), EPSILON);
+        }
+    }
+
+    public void test2_7Mbc() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final boolean inUse = true;
+        final boolean enabled = true;
+        final int bandCount = 3;
+
+        Mbc mbc = new Mbc(inUse, enabled, bandCount);
+        assertEquals("mbc inUse is different", inUse, mbc.isInUse());
+        assertEquals("mbc enabled is different", enabled, mbc.isEnabled());
+        assertEquals("mbc bandCount is different", bandCount, mbc.getBandCount());
+
+        //changes
+        mbc.setEnabled(!enabled);
+        assertEquals("enabled is different", !enabled, mbc.isEnabled());
+
+        //bands
+        for (int i = 0; i < bandCount; i++) {
+            int index = i+1;
+            final float frequency = index * 100.3f;
+            final float attackTime = index * 3.2f;
+            final float releaseTime = 2 * attackTime;
+            final float ratio = index * 1.2f;
+            final float threshold = index * (-12.8f);
+            final float kneeWidth = index * 0.3f;
+            final float noiseGateThreshold = index * (-20.1f);
+            final float expanderRatio = index * 1.1f;
+            final float preGain = index * 10.1f;
+            final float postGain = index * (-0.2f);
+            MbcBand mbcBand = new MbcBand(mbc.getBand(i));
+
+            mbcBand.setEnabled(enabled);
+            mbcBand.setCutoffFrequency(frequency);
+            mbcBand.setAttackTime(attackTime);
+            mbcBand.setReleaseTime(releaseTime);
+            mbcBand.setRatio(ratio);
+            mbcBand.setThreshold(threshold);
+            mbcBand.setKneeWidth(kneeWidth);
+            mbcBand.setNoiseGateThreshold(noiseGateThreshold);
+            mbcBand.setExpanderRatio(expanderRatio);
+            mbcBand.setPreGain(preGain);
+            mbcBand.setPostGain(postGain);
+
+            mbc.setBand(i, mbcBand);
+
+            //compare
+            assertEquals("mbc enabled is different", enabled, mbc.getBand(i).isEnabled());
+            assertEquals("mbc cutoffFrequency is different in band "+ i,
+                    frequency, mbc.getBand(i).getCutoffFrequency(), EPSILON);
+            assertEquals("mbc attackTime is different in band "+ i,
+                    attackTime, mbc.getBand(i).getAttackTime(), EPSILON);
+            assertEquals("mbc releaseTime is different in band "+ i,
+                    releaseTime, mbc.getBand(i).getReleaseTime(), EPSILON);
+            assertEquals("mbc ratio is different in band "+ i,
+                    ratio, mbc.getBand(i).getRatio(), EPSILON);
+            assertEquals("mbc threshold is different in band "+ i,
+                    threshold, mbc.getBand(i).getThreshold(), EPSILON);
+            assertEquals("mbc kneeWidth is different in band "+ i,
+                    kneeWidth, mbc.getBand(i).getKneeWidth(), EPSILON);
+            assertEquals("mbc noiseGateThreshold is different in band "+ i,
+                    noiseGateThreshold, mbc.getBand(i).getNoiseGateThreshold(), EPSILON);
+            assertEquals("mbc expanderRatio is different in band "+ i,
+                    expanderRatio, mbc.getBand(i).getExpanderRatio(), EPSILON);
+            assertEquals("mbc preGain is different in band "+ i,
+                    preGain, mbc.getBand(i).getPreGain(), EPSILON);
+            assertEquals("mbc postGain is different in band "+ i,
+                    postGain, mbc.getBand(i).getPostGain(), EPSILON);
+        }
+    }
+
+    public void test2_8Limiter() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final boolean inUse = true;
+        final boolean enabled = true;
+        final int linkGroup = 4;
+        final float attackTime = 3.2f;
+        final float releaseTime = 2 * attackTime;
+        final float ratio = 1.2f;
+        final float threshold = (-12.8f);
+        final float postGain = (-0.2f);
+
+        Limiter limiter = new Limiter(inUse, enabled, linkGroup, attackTime, releaseTime, ratio,
+                threshold, postGain);
+        assertEquals("limiter inUse is different", inUse, limiter.isInUse());
+        assertEquals("limiter enabled is different", enabled, limiter.isEnabled());
+        assertEquals("limiter linkGroup is different", linkGroup, limiter.getLinkGroup());
+
+        //defaults
+        assertEquals("limiter attackTime is different",
+                attackTime, limiter.getAttackTime(), EPSILON);
+        assertEquals("limiter releaseTime is different",
+                releaseTime, limiter.getReleaseTime(), EPSILON);
+        assertEquals("limiter ratio is different",
+                ratio, limiter.getRatio(), EPSILON);
+        assertEquals("limiter threshold is different",
+                threshold, limiter.getThreshold(), EPSILON);
+        assertEquals("limiter postGain is different",
+                postGain, limiter.getPostGain(), EPSILON);
+
+        //changes
+        final boolean newEnabled = !enabled;
+        final int newLinkGroup = 7;
+        final float newAttackTime = attackTime + 10;
+        final float newReleaseTime = releaseTime + 10;
+        final float newRatio = ratio + 2f;
+        final float newThreshold = threshold -20f;
+        final float newPostGain = postGain + 3f;
+
+        limiter.setEnabled(newEnabled);
+        limiter.setLinkGroup(newLinkGroup);
+        limiter.setAttackTime(newAttackTime);
+        limiter.setReleaseTime(newReleaseTime);
+        limiter.setRatio(newRatio);
+        limiter.setThreshold(newThreshold);
+        limiter.setPostGain(newPostGain);
+
+        assertEquals("limiter enabled is different", newEnabled, limiter.isEnabled());
+        assertEquals("limiter linkGroup is different", newLinkGroup, limiter.getLinkGroup());
+        assertEquals("limiter attackTime is different",
+                newAttackTime, limiter.getAttackTime(), EPSILON);
+        assertEquals("limiter releaseTime is different",
+                newReleaseTime, limiter.getReleaseTime(), EPSILON);
+        assertEquals("limiter ratio is different",
+                newRatio, limiter.getRatio(), EPSILON);
+        assertEquals("limiter threshold is different",
+                newThreshold, limiter.getThreshold(), EPSILON);
+        assertEquals("limiter postGain is different",
+                newPostGain, limiter.getPostGain(), EPSILON);
+    }
+
+    public void test2_9BandStage() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final boolean inUse = true;
+        final boolean enabled = true;
+        final int bandCount = 3;
+
+        BandStage bandStage = new BandStage(inUse, enabled, bandCount);
+        assertEquals("bandStage inUse is different", inUse, bandStage.isInUse());
+        assertEquals("bandStage enabled is different", enabled, bandStage.isEnabled());
+        assertEquals("bandStage bandCount is different", bandCount, bandStage.getBandCount());
+
+        //change
+        bandStage.setEnabled(!enabled);
+        assertEquals("bandStage enabled is different", !enabled, bandStage.isEnabled());
+    }
+
+    public void test2_10Stage() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final boolean inUse = true;
+        final boolean enabled = true;
+        final int bandCount = 3;
+
+        DynamicsProcessing.Stage stage = new DynamicsProcessing.Stage(inUse, enabled);
+        assertEquals("stage inUse is different", inUse, stage.isInUse());
+        assertEquals("stage enabled is different", enabled, stage.isEnabled());
+
+        //change
+        stage.setEnabled(!enabled);
+        assertEquals("stage enabled is different", !enabled, stage.isEnabled());
+    }
+
+    public void test2_11BandBase() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final boolean enabled = true;
+        final float frequency = 100.3f;
+
+        BandBase bandBase = new BandBase(enabled, frequency);
+
+        assertEquals("bandBase enabled is different", enabled, bandBase.isEnabled());
+        assertEquals("bandBase cutoffFrequency is different",
+                frequency, bandBase.getCutoffFrequency(), EPSILON);
+
+        //change
+        final float newFrequency = frequency + 10f;
+        bandBase.setEnabled(!enabled);
+        bandBase.setCutoffFrequency(newFrequency);
+        assertEquals("bandBase enabled is different", !enabled, bandBase.isEnabled());
+        assertEquals("bandBase cutoffFrequency is different",
+                newFrequency, bandBase.getCutoffFrequency(), EPSILON);
+    }
+    //-----------------------------------------------------------------
+    // 3 - Builder
+    //----------------------------------
+
+    public void test3_0Builder_stagesAllChannels() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+        DynamicsProcessing.Config.Builder builder = getBuilderWithValues(TEST_CHANNEL_COUNT);
+
+        //get Stages, apply all channels
+        Eq preEq = new Eq(config.getPreEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand preEqBand = new EqBand(preEq.getBand(TEST_BAND_INDEX));
+        preEqBand.setGain(TEST_GAIN1);
+        preEq.setBand(TEST_BAND_INDEX, preEqBand);
+        builder.setPreEqAllChannelsTo(preEq);
+
+        Mbc mbc = new Mbc(config.getMbcByChannelIndex(TEST_CHANNEL_INDEX));
+        MbcBand mbcBand = new MbcBand(mbc.getBand(TEST_BAND_INDEX));
+        mbcBand.setPreGain(TEST_GAIN1);
+        mbc.setBand(TEST_BAND_INDEX, mbcBand);
+        builder.setMbcAllChannelsTo(mbc);
+
+        Eq postEq = new Eq(config.getPostEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand postEqBand = new EqBand(postEq.getBand(TEST_BAND_INDEX));
+        postEqBand.setGain(TEST_GAIN1);
+        postEq.setBand(TEST_BAND_INDEX, postEqBand);
+        builder.setPostEqAllChannelsTo(postEq);
+
+        Limiter limiter = new Limiter(config.getLimiterByChannelIndex(TEST_CHANNEL_INDEX));
+        limiter.setPostGain(TEST_GAIN1);
+        builder.setLimiterAllChannelsTo(limiter);
+
+        //build and compare
+        DynamicsProcessing.Config newConfig = builder.build();
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN1, newConfig.getLimiterByChannelIndex(i).getPostGain(), EPSILON);
+        }
+    }
+
+    public void test3_1Builder_stagesByChannelIndex() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+        DynamicsProcessing.Config.Builder builder = getBuilderWithValues(TEST_CHANNEL_COUNT);
+
+        Eq preEq = new Eq(config.getPreEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand preEqBand = new EqBand(preEq.getBand(TEST_BAND_INDEX));
+
+        Mbc mbc = new Mbc(config.getMbcByChannelIndex(TEST_CHANNEL_INDEX));
+        MbcBand mbcBand = new MbcBand(mbc.getBand(TEST_BAND_INDEX));
+
+        Eq postEq = new Eq(config.getPostEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand postEqBand = new EqBand(postEq.getBand(TEST_BAND_INDEX));
+
+        Limiter limiter = new Limiter(config.getLimiterByChannelIndex(TEST_CHANNEL_INDEX));
+
+        //get Stages, apply per channel
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            float gain = i % 2 == 0 ? TEST_GAIN1 : TEST_GAIN2;
+
+            preEqBand.setGain(gain);
+            preEq.setBand(TEST_BAND_INDEX, preEqBand);
+            builder.setPreEqByChannelIndex(i, preEq);
+
+            mbcBand.setPreGain(gain);
+            mbc.setBand(TEST_BAND_INDEX, mbcBand);
+            builder.setMbcByChannelIndex(i, mbc);
+
+            postEqBand.setGain(gain);
+            postEq.setBand(TEST_BAND_INDEX, postEqBand);
+            builder.setPostEqByChannelIndex(i,postEq);
+
+            limiter.setPostGain(gain);
+            builder.setLimiterByChannelIndex(i, limiter);
+        }
+        //build and compare
+        DynamicsProcessing.Config newConfig = builder.build();
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            float expectedGain = i % 2 == 0 ? TEST_GAIN1 : TEST_GAIN2;
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("limiter gain is different in channel " + i,
+                    expectedGain, newConfig.getLimiterByChannelIndex(i).getPostGain(), EPSILON);
+        }
+    }
+
+    public void test3_2Builder_setAllChannelsTo() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+        DynamicsProcessing.Config.Builder builder = getBuilderWithValues(TEST_CHANNEL_COUNT);
+
+        Channel channel = new Channel(config.getChannelByChannelIndex(TEST_CHANNEL_INDEX));
+
+        //get Stages, apply all channels
+        Eq preEq = new Eq(config.getPreEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand preEqBand = new EqBand(preEq.getBand(TEST_BAND_INDEX));
+        preEqBand.setGain(TEST_GAIN1);
+        preEq.setBand(TEST_BAND_INDEX, preEqBand);
+        channel.setPreEq(preEq);
+
+        Mbc mbc = new Mbc(config.getMbcByChannelIndex(TEST_CHANNEL_INDEX));
+        MbcBand mbcBand = new MbcBand(mbc.getBand(TEST_BAND_INDEX));
+        mbcBand.setPreGain(TEST_GAIN1);
+        mbc.setBand(TEST_BAND_INDEX, mbcBand);
+        channel.setMbc(mbc);
+
+        Eq postEq = new Eq(config.getPostEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand postEqBand = new EqBand(postEq.getBand(TEST_BAND_INDEX));
+        postEqBand.setGain(TEST_GAIN1);
+        postEq.setBand(TEST_BAND_INDEX, postEqBand);
+        channel.setPostEq(postEq);
+
+        Limiter limiter = new Limiter(config.getLimiterByChannelIndex(TEST_CHANNEL_INDEX));
+        limiter.setPostGain(TEST_GAIN1);
+        channel.setLimiter(limiter);
+
+        builder.setAllChannelsTo(channel);
+        //build and compare
+        DynamicsProcessing.Config newConfig = builder.build();
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    TEST_GAIN1, newConfig.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("limiter gain is different in channel " + i,
+                    TEST_GAIN1, newConfig.getLimiterByChannelIndex(i).getPostGain(), EPSILON);
+        }
+    }
+
+    public void test3_3Builder_setChannelTo() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        DynamicsProcessing.Config config = getBuilderWithValues(MIN_CHANNEL_COUNT).build();
+        DynamicsProcessing.Config.Builder builder = getBuilderWithValues(TEST_CHANNEL_COUNT);
+
+        Channel channel = new Channel(config.getChannelByChannelIndex(TEST_CHANNEL_INDEX));
+
+        Eq preEq = new Eq(config.getPreEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand preEqBand = new EqBand(preEq.getBand(TEST_BAND_INDEX));
+
+        Mbc mbc = new Mbc(config.getMbcByChannelIndex(TEST_CHANNEL_INDEX));
+        MbcBand mbcBand = new MbcBand(mbc.getBand(TEST_BAND_INDEX));
+
+        Eq postEq = new Eq(config.getPostEqByChannelIndex(TEST_CHANNEL_INDEX));
+        EqBand postEqBand = new EqBand(postEq.getBand(TEST_BAND_INDEX));
+
+        Limiter limiter = new Limiter(config.getLimiterByChannelIndex(TEST_CHANNEL_INDEX));
+
+        //get Stages, apply per channel
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            float gain = i % 2 == 0 ? TEST_GAIN1 : TEST_GAIN2;
+
+            preEqBand.setGain(gain);
+            preEq.setBand(TEST_BAND_INDEX, preEqBand);
+            channel.setPreEq(preEq);
+
+            mbcBand.setPreGain(gain);
+            mbc.setBand(TEST_BAND_INDEX, mbcBand);
+            channel.setMbc(mbc);
+
+            postEqBand.setGain(gain);
+            postEq.setBand(TEST_BAND_INDEX, postEqBand);
+            channel.setPostEq(postEq);
+
+            limiter.setPostGain(gain);
+            channel.setLimiter(limiter);
+
+            builder.setChannelTo(i, channel);
+        }
+        //build and compare
+        DynamicsProcessing.Config newConfig = builder.build();
+        for (int i = 0; i < TEST_CHANNEL_COUNT; i++) {
+            float expectedGain = i % 2 == 0 ? TEST_GAIN1 : TEST_GAIN2;
+            assertEquals("preEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getPreEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("mbcBand preGain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getMbcBandByChannelIndex(i, TEST_BAND_INDEX).getPreGain(),
+                    EPSILON);
+
+            assertEquals("postEQBand gain is different in channel " + i + " band "+ TEST_BAND_INDEX,
+                    expectedGain,
+                    newConfig.getPostEqBandByChannelIndex(i, TEST_BAND_INDEX).getGain(),
+                    EPSILON);
+
+            assertEquals("limiter gain is different in channel " + i,
+                    expectedGain, newConfig.getLimiterByChannelIndex(i).getPostGain(), EPSILON);
+        }
+    }
     //-----------------------------------------------------------------
     // private methods
     //----------------------------------
@@ -260,11 +1099,11 @@
         createDynamicsProcessingWithConfig(session, config);
     }
 
-    private DynamicsProcessing.Config.Builder getBuilder() {
+    private DynamicsProcessing.Config.Builder getBuilder(int channelCount) {
         //simple config
         DynamicsProcessing.Config.Builder builder = new DynamicsProcessing.Config.Builder(
                 DEFAULT_VARIANT /* variant */,
-                MIN_CHANNEL_COUNT /* channels */,
+                channelCount/* channels */,
                 DEFAULT_PREEQ_IN_USE /*enable preEQ*/,
                 DEFAULT_PREEQ_BAND_COUNT /*preEq bands*/,
                 DEFAULT_MBC_IN_USE /*enable mbc*/,
@@ -276,13 +1115,18 @@
         return builder;
     }
 
-    private DynamicsProcessing.Config.Builder getBuilderWithValues() {
+    private DynamicsProcessing.Config.Builder getBuilderWithValues(int channelCount) {
         //simple config
-        DynamicsProcessing.Config.Builder builder = getBuilder();
+        DynamicsProcessing.Config.Builder builder = getBuilder(channelCount);
 
         //Set Defaults
         builder.setPreferredFrameDuration(DEFAULT_FRAME_DURATION);
         builder.setInputGainAllChannelsTo(DEFAULT_INPUT_GAIN);
         return builder;
     }
+
+    private DynamicsProcessing.Config.Builder getBuilderWithValues() {
+        return getBuilderWithValues(MIN_CHANNEL_COUNT);
+    }
+
 }
\ No newline at end of file
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index 7427096..1e6f05e 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -723,8 +723,14 @@
               mGLVersion / 10, mGLVersion % 10);
         return false;
     }
-    if (desc.format == GL_RGBA16F && !HasGLExtension("GL_EXT_color_buffer_float")) {
-        ALOGI("Test skipped: GL_RGBA16F requires GL_EXT_color_buffer_float");
+    if (desc.format == GL_RGB10_A2 && mGLVersion < 30) {
+        ALOGI("Test skipped: GL_RGB10_A2 requires GL ES 3.0, found %d.%d",
+              mGLVersion / 10, mGLVersion % 10);
+        return false;
+    }
+    if (desc.format == GL_RGBA16F &&
+        (!HasGLExtension("GL_EXT_color_buffer_float") && mGLVersion < 32)) {
+        ALOGI("Test skipped: GL_RGBA16F requires GL_EXT_color_buffer_float or GL ES 3.2");
         return false;
     }
     // Nonzero stride indicates that desc.format should be interpreted as a GL format
diff --git a/tests/tests/os/AndroidTest.xml b/tests/tests/os/AndroidTest.xml
index b66c292..8f8d997 100644
--- a/tests/tests/os/AndroidTest.xml
+++ b/tests/tests/os/AndroidTest.xml
@@ -23,5 +23,11 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.os.cts" />
         <option name="runtime-hint" value="3m15s" />
+        <!-- Do not disable hidden-api-checks for this test. This test includes
+             StrictModeTest which relies on hidden API checks being enabled in
+             order to function properly. Any APIs used by this test should be
+             added to the light grey list, or made @TestApi instead.
+        <option name="hidden-api-checks" value="false" />
+        -->
     </test>
 </configuration>
diff --git a/tests/tests/permission/src/android/permission/cts/ObserveAppUsagePermissionTest.java b/tests/tests/permission/src/android/permission/cts/ObserveAppUsagePermissionTest.java
new file mode 100644
index 0000000..720797e
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/ObserveAppUsagePermissionTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts;
+
+import static android.Manifest.permission.OBSERVE_APP_USAGE;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ObserveAppUsagePermissionTest {
+
+    private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() {
+        mPackageManager = InstrumentationRegistry.getTargetContext().getPackageManager();
+    }
+
+    @Test
+    public void testNumberOfAppsWithPermission() {
+        final List<PackageInfo> packagesWithPerm = mPackageManager.getPackagesHoldingPermissions(
+                new String[]{OBSERVE_APP_USAGE}, 0);
+        assertTrue("At most one app can hold the permission " + OBSERVE_APP_USAGE
+                + ", but found more: " + packagesWithPerm, packagesWithPerm.size() <= 1);
+    }
+}
diff --git a/tests/tests/security/res/raw/cve_2017_13309_client.bks b/tests/tests/security/res/raw/cve_2017_13309_client.bks
new file mode 100644
index 0000000..6f450d3
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13309_client.bks
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13309_server.bks b/tests/tests/security/res/raw/cve_2017_13309_server.bks
new file mode 100644
index 0000000..384c4be
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13309_server.bks
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2017_13309_trustedcert.bks b/tests/tests/security/res/raw/cve_2017_13309_trustedcert.bks
new file mode 100644
index 0000000..539d36e
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2017_13309_trustedcert.bks
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java b/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java
new file mode 100644
index 0000000..8f6477e
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/SSLConscryptPlainTextExposureTest.java
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2018 The AndroCid 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.security.cts;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.platform.test.annotations.SecurityTest;
+import android.test.InstrumentationTestCase;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Formatter;
+import java.util.Iterator;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+
+public class SSLConscryptPlainTextExposureTest extends InstrumentationTestCase {
+
+  private TestSSLServer mTestSSLServer;
+  private TestSSLConnection mTestSSLClient;
+  public static Context context;
+  public static String output = "";
+  private final String pattern = ".*PLAIN TEXT EXPOSED.*";
+
+  public void test_android_CVE_2017_13309() {
+
+    context = getInstrumentation().getContext();
+    mTestSSLServer = new TestSSLServer();
+
+    try {
+      Thread.sleep(1000);
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+    mTestSSLClient = new TestSSLConnection();
+    mTestSSLClient.StartConnect();
+    assertFalse("Pattern found",
+        Pattern.compile(pattern,
+          Pattern.DOTALL).matcher(output).matches());
+  }
+
+  public static InputStream getResource(final int resource){
+    return SSLConscryptPlainTextExposureTest.context.getResources().openRawResource(resource);
+  }
+
+  public static void setOutput(String data){
+    SSLConscryptPlainTextExposureTest.output = data;
+  }
+}
+
+
+class TestSSLConnection {
+
+  public SSLContext sslc;
+
+  public SocketChannel socketChannel;
+  public SSLEngine clientEngine;
+  public String remoteAddress = "127.0.0.1";
+  public int port = 9000;
+  public ByteBuffer[] dataOutAppBuffers = new ByteBuffer[3];
+  public ByteBuffer dataOutNetBuffer;
+  public ByteBuffer hsInAppBuffer, hsInNetBuffer, hsOutAppBuffer, hsOutNetBuffer;
+  public boolean isHandshaked = false;
+  public ExecutorService executor = Executors.newSingleThreadExecutor();
+  public InputStream clientKey = null;
+  public InputStream trustedCert = null;
+
+  public void StartConnect() {
+    KeyStore ks = null;
+
+    clientKey = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_client);
+    trustedCert = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_trustedcert);
+
+    try {
+      ks = KeyStore.getInstance(KeyStore.getDefaultType());
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+    KeyStore ts = null;
+    try {
+      ts = KeyStore.getInstance(KeyStore.getDefaultType());
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      ks.load(clientKey, "pocclient".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    try {
+      ts.load(trustedCert, "trusted".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    KeyManagerFactory kmf = null;
+    try {
+      kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      kmf.init(ks, "keypass".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    TrustManagerFactory tmf = null;
+    try {
+      tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    try {
+      tmf.init(ts);
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+
+    SSLContext sslCtx = null;
+    try {
+      sslCtx = SSLContext.getInstance("TLSv1.2");
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+    } catch (KeyManagementException e) {
+      e.printStackTrace();
+    }
+
+    sslc = sslCtx;
+
+    clientEngine = sslc.createSSLEngine(remoteAddress, port);
+    clientEngine.setUseClientMode(true);
+    SSLSession session = clientEngine.getSession();
+
+    hsOutAppBuffer = ByteBuffer.allocate(4096);
+    hsOutNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
+    hsInAppBuffer = ByteBuffer.allocate(4096);
+    hsInNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
+    dataOutNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
+
+    try {
+      socketChannel = SocketChannel.open();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    try {
+      socketChannel.configureBlocking(false);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    try {
+      socketChannel.connect(new InetSocketAddress(remoteAddress, port));
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      while(!socketChannel.finishConnect()) {
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      clientEngine.beginHandshake();
+    } catch (SSLException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      isHandshaked = doHandshake(socketChannel, clientEngine);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    if(isHandshaked) {
+      dataOutAppBuffers[0] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
+      dataOutAppBuffers[1] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
+      dataOutAppBuffers[2] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
+
+      while(dataOutAppBuffers[0].hasRemaining() || dataOutAppBuffers[1].hasRemaining() || dataOutAppBuffers[2].hasRemaining()) {
+        dataOutNetBuffer.clear();
+        SSLEngineResult result = null;
+        try {
+          result = clientEngine.wrap(dataOutAppBuffers, 0, 3, dataOutNetBuffer);
+        } catch (SSLException e) {
+          e.printStackTrace();
+        }
+        switch(result.getStatus()) {
+          case OK:
+            dataOutNetBuffer.flip();
+            String outbuff = new String("");
+            Formatter formatter = new Formatter();
+            for(int i = 0; i < dataOutNetBuffer.limit(); i++) {
+              outbuff += formatter.format("%02x ", dataOutNetBuffer.get(i)).toString();
+            }
+            String output = new String(dataOutNetBuffer.array());
+            SSLConscryptPlainTextExposureTest.setOutput(output);
+            break;
+          case BUFFER_OVERFLOW:
+            dataOutNetBuffer = enlargePacketBuffer(clientEngine, dataOutNetBuffer);
+            break;
+          case BUFFER_UNDERFLOW:
+            try {
+              throw new SSLException("Buffer underflow in sending data");
+            } catch (SSLException e) {
+              e.printStackTrace();
+            }
+          case CLOSED:
+            try {
+              closeConnection(socketChannel, clientEngine);
+            } catch (IOException e) {
+              e.printStackTrace();
+            }
+            return;
+          default:
+            throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
+        }
+      }
+    }
+
+    try {
+      clientKey.close();
+    } catch (IOException e){
+      e.printStackTrace();
+    }
+
+    try{
+      trustedCert.close();
+    } catch (IOException e){
+      e.printStackTrace();
+    }
+
+    try {
+      shutdown();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  public void shutdown() throws IOException {
+    closeConnection(socketChannel, clientEngine);
+    executor.shutdown();
+  }
+
+  public void closeConnection(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    engine.closeOutbound();
+    doHandshake(socketChannel, engine);
+    socketChannel.close();
+  }
+
+  public boolean doHandshake(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    SSLEngineResult result;
+    SSLEngineResult.HandshakeStatus handshakeStatus;
+    int appBufferSize = engine.getSession().getApplicationBufferSize();
+
+    ByteBuffer srcAppData = ByteBuffer.allocate(appBufferSize);
+    ByteBuffer dstAppData = ByteBuffer.allocate(appBufferSize);
+
+    srcAppData.clear();
+    dstAppData.clear();
+
+    handshakeStatus = engine.getHandshakeStatus();
+    while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
+        && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+      switch(handshakeStatus) {
+        case NEED_UNWRAP:
+          if(socketChannel.read(hsInNetBuffer) < 0) {
+            if(engine.isInboundDone() && engine.isOutboundDone()) {
+              return false;
+            }
+            try {
+              engine.closeInbound();
+            } catch (SSLException e) {
+              Log.e("poc-test","Forced to close inbound. No proper SSL/TLS close notification message from peer");
+            }
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          hsInNetBuffer.flip();
+          try {
+            result = engine.unwrap(hsInNetBuffer, hsInAppBuffer);
+            hsInNetBuffer.compact();
+            handshakeStatus = result.getHandshakeStatus();
+          } catch (SSLException sslException) {
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          switch(result.getStatus()) {
+            case OK:
+              break;
+            case BUFFER_OVERFLOW:
+              hsInAppBuffer = enlargeApplicationBuffer(engine, hsInAppBuffer);
+              break;
+            case BUFFER_UNDERFLOW:
+              hsInNetBuffer = handleBufferUnderflow(engine, hsInNetBuffer);
+              break;
+            case CLOSED:
+              if (engine.isOutboundDone()) {
+                return false;
+              } else {
+                engine.closeOutbound();
+                handshakeStatus = engine.getHandshakeStatus();
+                break;
+              }
+            default:
+              throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
+          }
+        case NEED_WRAP:
+          hsOutNetBuffer.clear();
+          try {
+            result = engine.wrap(hsOutAppBuffer, hsOutNetBuffer);
+            handshakeStatus = result.getHandshakeStatus();
+          } catch (SSLException sslException) {
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          switch(result.getStatus()) {
+            case OK:
+              hsOutNetBuffer.flip();
+              while(hsOutNetBuffer.hasRemaining()) {
+                socketChannel.write(hsOutNetBuffer);
+              }
+              break;
+            case BUFFER_OVERFLOW:
+              hsOutNetBuffer = enlargePacketBuffer(engine, hsOutNetBuffer);
+              break;
+            case BUFFER_UNDERFLOW:
+              throw new SSLException("Buffer underflow in handshake and wrap");
+            case CLOSED:
+              try {
+                hsOutNetBuffer.flip();
+                while(hsOutNetBuffer.hasRemaining()) {
+                  socketChannel.write(hsOutNetBuffer);
+                }
+                hsInNetBuffer.clear();
+              } catch (Exception e) {
+                handshakeStatus = engine.getHandshakeStatus();
+              }
+              break;
+            default:
+              throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
+          }
+          break;
+        case NEED_TASK:
+          Runnable task;
+          while((task = engine.getDelegatedTask()) != null) {
+            executor.execute(task);
+          }
+          handshakeStatus = engine.getHandshakeStatus();
+          break;
+        case FINISHED:
+          break;
+        case NOT_HANDSHAKING:
+          break;
+        default:
+          throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
+      }
+        }
+    return true;
+  }
+
+  public ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
+    return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
+  }
+
+  public ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
+    return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
+  }
+
+  public ByteBuffer enlargeBuffer(ByteBuffer buffer, int bufferSize) {
+    if(bufferSize > buffer.capacity()) {
+      buffer = ByteBuffer.allocate(bufferSize);
+    }
+    else {
+      buffer = ByteBuffer.allocate(buffer.capacity() * 2);
+    }
+    return buffer;
+  }
+  public ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
+    if(engine.getSession().getPacketBufferSize() < buffer.limit()) {
+      return buffer;
+    }
+    else {
+      ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
+      buffer.flip();
+      replaceBuffer.put(buffer);
+      return replaceBuffer;
+    }
+  }
+}
+
+class TestSSLServer {
+
+  public ServerRunnable serverRunnable;
+  Thread server;
+
+  public TestSSLServer() {
+    serverRunnable = new ServerRunnable();
+    server = new Thread(serverRunnable);
+    server.start();
+
+    try{
+      Thread.sleep(1000);
+    }catch(InterruptedException e){
+      e.printStackTrace();
+    }
+  }
+
+  protected void onCancelled(String result) {
+    serverRunnable.stop();
+  }
+}
+
+class ServerRunnable implements Runnable {
+
+  SSLServer server;
+  InputStream serverKey = null;
+  InputStream trustedCert = null;
+
+  public void run() {
+    try {
+      server = new SSLServer();
+      server.runServer();
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  public void stop() {
+    server.stopServer();
+  }
+}
+
+class SSLServer {
+  public SSLContext serverContext;
+
+  public ByteBuffer hsInAppBuffer, hsInNetBuffer, hsOutAppBuffer, hsOutNetBuffer;
+  public ByteBuffer dataInAppBuffer, dataInNetBuffer;
+
+  final String hostAddress = "127.0.0.1";
+  public int port = 9000;
+  public boolean bActive = false;
+
+  public Selector selector;
+  InputStream serverKey = null;
+  InputStream trustedCert = null;
+  public ExecutorService executor = Executors.newSingleThreadExecutor();
+
+  public void stopServer() {
+    bActive = false;
+    executor.shutdown();
+    selector.wakeup();
+  }
+
+  public void runServer() {
+    KeyStore ks = null;
+
+    serverKey = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_server);
+    trustedCert = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_trustedcert);
+
+    try {
+      ks = KeyStore.getInstance(KeyStore.getDefaultType());
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+    KeyStore ts = null;
+    try {
+      ts = KeyStore.getInstance(KeyStore.getDefaultType());
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      ks.load(serverKey, "pocserver".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    try {
+      ts.load(trustedCert, "trusted".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    KeyManagerFactory kmf = null;
+    try {
+      kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    try {
+      kmf.init(ks, "keypass".toCharArray());
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+
+    TrustManagerFactory tmf = null;
+    try {
+      tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+    try {
+      tmf.init(ts);
+    } catch (KeyStoreException e) {
+      e.printStackTrace();
+    }
+
+    SSLContext sslCtx = null;
+    try {
+      sslCtx = SSLContext.getInstance("TLSv1.2");
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+
+    try {
+      sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
+    } catch (KeyManagementException e) {
+      e.printStackTrace();
+    }
+
+    serverContext = sslCtx;
+
+    SSLSession dummySession = serverContext.createSSLEngine().getSession();
+
+    hsInAppBuffer = ByteBuffer.allocate(4096);
+    hsInNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
+    hsOutAppBuffer = ByteBuffer.allocate(4096);
+    hsOutNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
+    dataInAppBuffer = ByteBuffer.allocate(4096);
+    dataInNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
+    dummySession.invalidate();
+
+    try {
+      selector = SelectorProvider.provider().openSelector();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    ServerSocketChannel serverSocketChannel = null;
+    try {
+      serverSocketChannel = ServerSocketChannel.open();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    try {
+      serverSocketChannel.configureBlocking(false);
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    try {
+      serverSocketChannel.socket().bind(new InetSocketAddress(hostAddress, port));
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    try {
+      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
+    } catch (ClosedChannelException e) {
+      e.printStackTrace();
+    }
+
+    bActive = true;
+
+    while(bActive) {
+      try {
+        selector.select();
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+      Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
+      while (selectedKeys.hasNext()) {
+        SelectionKey key = selectedKeys.next();
+        selectedKeys.remove();
+        if (!key.isValid()) {
+          continue;
+        }
+        if (key.isAcceptable()) {
+          try {
+            accept(key);
+          } catch (Exception e) {
+            e.printStackTrace();
+          }
+        } else if (key.isReadable()) {
+          try {
+            read((SocketChannel) key.channel(), (SSLEngine) key.attachment());
+          } catch (IOException e) {
+            e.printStackTrace();
+          }
+        }
+      }
+    }
+  }
+
+  public void accept(SelectionKey key) throws Exception{
+    SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();
+    socketChannel.configureBlocking(false);
+
+    SSLEngine engine = serverContext.createSSLEngine();
+    engine.setUseClientMode(false);
+    engine.beginHandshake();
+
+    socketChannel.register(selector, SelectionKey.OP_READ, engine);
+
+    if(doHandshake(socketChannel, engine)) {
+      socketChannel.register(selector, SelectionKey.OP_READ, engine);
+    }
+    else {
+      socketChannel.close();
+    }
+  }
+
+  public boolean doHandshake(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    SSLEngineResult result = null;
+    SSLEngineResult.HandshakeStatus handshakeStatus;
+    int appBufferSize = engine.getSession().getApplicationBufferSize();
+
+    ByteBuffer srcAppData = ByteBuffer.allocate(appBufferSize);
+    ByteBuffer dstAppData = ByteBuffer.allocate(appBufferSize);
+
+    srcAppData.clear();
+    dstAppData.clear();
+
+    handshakeStatus = engine.getHandshakeStatus();
+    while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
+        && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+
+      switch(handshakeStatus) {
+        case NEED_UNWRAP:
+          if(socketChannel.read(hsInNetBuffer) < 0) {
+            if(engine.isInboundDone() && engine.isOutboundDone()) {
+              return false;
+            }
+            try {
+              engine.closeInbound();
+            } catch (SSLException e) {
+              Log.e("server-poc-test","Forced to close inbound. No proper SSL/TLS close notification message from peer");
+            }
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          hsInNetBuffer.flip();
+          try {
+            result = engine.unwrap(hsInNetBuffer, hsInAppBuffer);
+            hsInNetBuffer.compact();
+            handshakeStatus = result.getHandshakeStatus();
+          } catch (SSLException sslException) {
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          switch(result.getStatus()) {
+            case OK:
+              break;
+            case BUFFER_OVERFLOW:
+              hsInAppBuffer = enlargeApplicationBuffer(engine, hsInAppBuffer);
+              break;
+            case BUFFER_UNDERFLOW:
+              hsInNetBuffer = handleBufferUnderflow(engine, hsInNetBuffer);
+              break;
+            case CLOSED:
+              if (engine.isOutboundDone()) {
+                return false;
+              } else {
+                engine.closeOutbound();
+                handshakeStatus = engine.getHandshakeStatus();
+                break;
+              }
+            default:
+              throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
+          }
+        case NEED_WRAP:
+          hsOutNetBuffer.clear();
+          try {
+            result = engine.wrap(hsOutAppBuffer, hsOutNetBuffer);
+            handshakeStatus = result.getHandshakeStatus();
+          } catch (SSLException sslException) {
+            engine.closeOutbound();
+            handshakeStatus = engine.getHandshakeStatus();
+            break;
+          }
+          switch(result.getStatus()) {
+            case OK:
+              hsOutNetBuffer.flip();
+              while(hsOutNetBuffer.hasRemaining()) {
+                socketChannel.write(hsOutNetBuffer);
+              }
+              break;
+            case BUFFER_OVERFLOW:
+              hsOutNetBuffer = enlargePacketBuffer(engine, hsOutNetBuffer);
+              break;
+            case BUFFER_UNDERFLOW:
+              throw new SSLException("Buffer underflow in handshake and wrap");
+            case CLOSED:
+              try {
+                hsOutNetBuffer.flip();
+                while(hsOutNetBuffer.hasRemaining()) {
+                  socketChannel.write(hsOutNetBuffer);
+                }
+                hsInNetBuffer.clear();
+              } catch (Exception e) {
+                handshakeStatus = engine.getHandshakeStatus();
+              }
+              break;
+            default:
+              throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
+          }
+          break;
+        case NEED_TASK:
+          Runnable task;
+          while((task = engine.getDelegatedTask()) != null) {
+            executor.execute(task);
+          }
+          handshakeStatus = engine.getHandshakeStatus();
+          break;
+        case FINISHED:
+          break;
+        case NOT_HANDSHAKING:
+          break;
+        default:
+          throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
+      }
+        }
+    return true;
+  }
+
+  public ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
+    return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
+  }
+
+  public ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
+    return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
+  }
+
+  public ByteBuffer enlargeBuffer(ByteBuffer buffer, int bufferSize) {
+    if(bufferSize > buffer.capacity()) {
+      buffer = ByteBuffer.allocate(bufferSize);
+    }
+    else {
+      buffer = ByteBuffer.allocate(buffer.capacity() * 2);
+    }
+    return buffer;
+  }
+  public ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
+    if(engine.getSession().getPacketBufferSize() < buffer.limit()) {
+      return buffer;
+    }
+    else {
+      ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
+      buffer.flip();
+      replaceBuffer.put(buffer);
+      return replaceBuffer;
+    }
+  }
+
+  public void read(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    dataInNetBuffer.clear();
+    int bytesRead = socketChannel.read(dataInNetBuffer);
+    if(bytesRead > 0) {
+      dataInNetBuffer.flip();
+      while(dataInNetBuffer.hasRemaining()) {
+        dataInAppBuffer.clear();
+        SSLEngineResult result = engine.unwrap(dataInNetBuffer, dataInAppBuffer);
+        switch(result.getStatus()) {
+          case OK:
+            dataInAppBuffer.flip();
+            break;
+          case BUFFER_OVERFLOW:
+            dataInAppBuffer = enlargeApplicationBuffer(engine, dataInAppBuffer);
+            break;
+          case BUFFER_UNDERFLOW:
+            dataInNetBuffer = handleBufferUnderflow(engine, dataInNetBuffer);
+            break;
+          case CLOSED:
+            closeConnection(socketChannel, engine);
+            return;
+          default:
+            throw new IllegalStateException("invalid SSL status: " + result.getStatus());
+        }
+      }
+    }
+    else if(bytesRead < 0) {
+      handleEndOfStream(socketChannel, engine);
+    }
+  }
+
+  public void handleEndOfStream(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    try {
+      engine.closeInbound();
+    }
+    catch (Exception e) {
+      Log.e("server-poc-test", "Close inbound forced");
+    }
+    closeConnection(socketChannel, engine);
+  }
+
+  public void closeConnection(SocketChannel socketChannel, SSLEngine engine) throws IOException {
+    try{
+      serverKey.close();
+    } catch (IOException e){
+      e.printStackTrace();
+    }
+
+    try {
+      trustedCert.close();
+    } catch (IOException e){
+      e.printStackTrace();
+    }
+
+    engine.closeOutbound();
+    doHandshake(socketChannel, engine);
+    socketChannel.close();
+  }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java
index 0b708d4..ecd8dfc 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShapeTests.java
@@ -35,6 +35,6 @@
                 .addLayout(R.layout.frame_layout,
                         view -> view.setBackgroundResource(R.drawable.dashed_oval))
                 .runWithVerifier(new GoldenImageVerifier(getActivity(),
-                        R.drawable.golden_dashed_oval, new MSSIMComparer(0.99)));
+                        R.drawable.golden_dashed_oval, new MSSIMComparer(0.90)));
     }
 }
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index f27c81d..3607e57 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -35,10 +35,13 @@
  * Neverallow Rules SELinux tests.
  */
 public class SELinuxNeverallowRulesTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
+    private static final int P_SEPOLICY_VERSION = 28;
     private File sepolicyAnalyze;
     private File devicePolicyFile;
+    private File deviceSystemPolicyFile;
 
     private IBuildInfo mBuild;
+    private int mVendorSepolicyVersion = -1;
 
     /**
      * A reference to the device under test.
@@ -69,6 +72,14 @@
         sepolicyAnalyze.setExecutable(true);
 
         devicePolicyFile = android.security.cts.SELinuxHostTest.getDevicePolicyFile(mDevice);
+        deviceSystemPolicyFile =
+                android.security.cts.SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
+
+        // Caching this variable to save time.
+        if (mVendorSepolicyVersion == -1) {
+            mVendorSepolicyVersion =
+                    android.security.cts.SELinuxHostTest.getVendorSepolicyVersion(mDevice);
+        }
     }
 
     private boolean isFullTrebleDevice() throws Exception {
@@ -100,9 +111,15 @@
             return;
         }
 
+        // If vendor sepolicy version is behind platform's, only test against platform policy.
+        File policyFile =
+                (mVendorSepolicyVersion < P_SEPOLICY_VERSION) ?
+                deviceSystemPolicyFile :
+                devicePolicyFile;
+
         /* run sepolicy-analyze neverallow check on policy file using given neverallow rules */
         ProcessBuilder pb = new ProcessBuilder(sepolicyAnalyze.getAbsolutePath(),
-                devicePolicyFile.getAbsolutePath(), "neverallow", "-w", "-n",
+                policyFile.getAbsolutePath(), "neverallow", "-w", "-n",
                 neverallowRule);
         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
         pb.redirectErrorStream(true);