Merge "Add more role manager tests." into rvc-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index a5bde79..0a0226f 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -3577,8 +3577,6 @@
             <meta-data android:name="test_category" android:value="@string/test_category_tv"/>
             <meta-data android:name="test_required_features"
                        android:value="android.software.leanback"/>
-            <meta-data android:name="test_required_configs"
-                       android:value="config_hdmi_source"/>
         </activity>
 
 
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 663b54e..51a4810 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -4562,6 +4562,9 @@
 
     <!-- HDR Capabilities test -->
     <string name="tv_hdr_capabilities_test">HDR Capabilities Test</string>
+    <string name="tv_hdr_capabilities_test_step_hdr_display">HDR Display</string>
+    <string name="tv_hdr_capabilities_test_step_no_display">No Display</string>
+    <string name="tv_hdr_capabilities_test_step_non_hdr_display">Non HDR Display</string>
     <string name="tv_hdr_capabilities_test_info">This test checks if
         Display.getHdrCapabilities correctly reports the HDR capabilities of the display.
     </string>
@@ -4595,7 +4598,12 @@
     <string name="tv_display_modes_connect_1080p_display">
         Connect a 1080p display and press the "%s" button, below.
     </string>
-
+    <string name="tv_panel_display_modes_reported_are_supported">
+        The supported display modes are:\n%s\n\nAre all of the above display modes supported by the hardware?
+    </string>
+    <string name="tv_panel_display_modes_supported_are_reported">
+        Are there other modes which are supported by the hardware, but are not listed above?
+    </string>
     <string name="overlay_view_text">Overlay View Dummy Text</string>
     <string name="custom_rating">Example of input app specific custom rating.</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 4c6a270..1c9fc0f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -110,7 +110,7 @@
      * Call this to create a test step where the test automatically evaluates whether
      * an expected condition is satisfied.
      */
-    protected View createAutoItem(int stringId) {
+    public View createAutoItem(int stringId) {
         View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
         TextView instructions = (TextView) item.findViewById(R.id.instructions);
         instructions.setText(stringId);
@@ -119,9 +119,21 @@
     }
 
     /**
+     * Call this to create a test step where the test automatically evaluates whether
+     * an expected condition is satisfied.
+     */
+    public View createAutoItem(CharSequence instructionCharSequence) {
+        View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
+        TextView instructions = (TextView) item.findViewById(R.id.instructions);
+        instructions.setText(instructionCharSequence);
+        mItemList.addView(item);
+        return item;
+    }
+
+    /**
      * Call this to create alternative choice for the previous test step.
      */
-    protected View createButtonItem(int buttonTextId, View.OnClickListener l) {
+    public View createButtonItem(int buttonTextId, View.OnClickListener l) {
         View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
         Button button = (Button) item.findViewById(R.id.user_action_button);
         button.setVisibility(View.VISIBLE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.java
new file mode 100644
index 0000000..6327d68
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.verifier.tv;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TvUtil {
+    public static final String LOG_TAG = "TvUtil";
+
+    public static boolean isHdmiSourceDevice() {
+        final int DEVICE_TYPE_HDMI_SOURCE = 4;
+        try {
+            return getHdmiDeviceType().contains(DEVICE_TYPE_HDMI_SOURCE);
+        } catch (Exception exception) {
+            Log.e(LOG_TAG, "Exception while looking up HDMI device type.", exception);
+        }
+        return false;
+    }
+
+    public static List<Integer> getHdmiDeviceType()
+            throws InvocationTargetException, IllegalAccessException, ClassNotFoundException,
+            NoSuchMethodException {
+        Method getStringMethod =
+                ClassLoader.getSystemClassLoader()
+                        .loadClass("android.os.SystemProperties")
+                        .getMethod("get", String.class);
+        String deviceTypesStr = (String) getStringMethod.invoke(null, "ro.hdmi.device_type");
+        if (deviceTypesStr.equals("")) {
+            return new ArrayList<>();
+        }
+        return Arrays.stream(deviceTypesStr.split(","))
+                .map(Integer::parseInt)
+                .collect(Collectors.toList());
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
index 37c33a3..03c1930 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
@@ -18,28 +18,29 @@
 
 import android.view.View;
 
+import androidx.annotation.StringRes;
+
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.tv.TvAppVerifierActivity;
 
 /**
- *  Encapsulates the logic of an asynchronous test step, which displays a human instructions and a
- *  button to start the test. For synchronous steps see {@link SyncTestStep}.
+ * Encapsulates the logic of an asynchronous test step, which displays human instructions and a
+ * button to start the test. For synchronous steps see {@link SyncTestStep}.
  */
-public abstract class AsyncTestStep extends TestStepBase {
+public abstract class AsyncTestStep extends OneButtonTestStep {
 
-    public AsyncTestStep(TvAppVerifierActivity context) {
-        super(context);
+    public AsyncTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+            String instructionText, @StringRes int buttonStringId) {
+        super(context, stepNameStringId, instructionText, buttonStringId);
     }
 
-    /**
-     * Runs the test logic, when finished calls {@link AsyncTestStep#done()}.
-     */
+    /** Runs the test logic, when finished calls {@link AsyncTestStep#done()}. */
     public abstract void runTestAsync();
 
     @Override
     protected void onButtonClickRunTest() {
         // Disable the button, so the user can't run it twice.
-        disableButton();
+        disableInteractivity();
         showLoadingSpinner();
         runTestAsync();
     }
@@ -51,12 +52,12 @@
     }
 
     private void showLoadingSpinner() {
-        View spinner = mViewItem.findViewById(R.id.loadingSpinner);
+        View spinner = mButtonView.findViewById(R.id.loadingSpinner);
         spinner.setVisibility(View.VISIBLE);
     }
 
     private void hideLoadingSpinner() {
-        View spinner = mViewItem.findViewById(R.id.loadingSpinner);
+        View spinner = mButtonView.findViewById(R.id.loadingSpinner);
         spinner.setVisibility(View.INVISIBLE);
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
index 7123be7..b86c58b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.verifier.tv.display;
 
+import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.view.Display;
 
 import androidx.annotation.StringRes;
@@ -34,12 +36,12 @@
 /**
  * Test to verify the HDR Capabilities API is correctly implemented.
  *
- * This test checks if
- * <a href="https://developer.android.com/reference/android/view/Display.html#isHdr()">Display.isHdr()</a>
- * and
- * <a href="https://developer.android.com/reference/android/view/Display.html#getHdrCapabilities()">Display.getHdrCapabilities()</a>
- * return correct results when 1. HDR Display is connected, 2. non-HDR
- * Display is connected and 3. no display is connected.
+ * <p>This test checks if <a
+ * href="https://developer.android.com/reference/android/view/Display.html#isHdr()">Display.isHdr()</a>
+ * and <a
+ * href="https://developer.android.com/reference/android/view/Display.html#getHdrCapabilities()">Display.getHdrCapabilities()</a>
+ * return correct results when 1. HDR Display is connected, 2. non-HDR Display is connected and 3.
+ * no display is connected.
  */
 public class DisplayHdrCapabilitiesTestActivity extends TvAppVerifierActivity {
     private static final float MAX_EXPECTED_LUMINANCE = 10_000f;
@@ -49,8 +51,8 @@
 
     @Override
     protected void setInfoResources() {
-        setInfoResources(R.string.tv_hdr_capabilities_test,
-                R.string.tv_hdr_capabilities_test_info, -1);
+        setInfoResources(
+                R.string.tv_hdr_capabilities_test, R.string.tv_hdr_capabilities_test_info, -1);
     }
 
     @Override
@@ -72,32 +74,28 @@
     private static class NonHdrDisplayTestStep extends SyncTestStep {
 
         public NonHdrDisplayTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_hdr_capabilities_test_step_non_hdr_display,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(R.string.tv_hdr_connect_no_hdr_display,
-                    mContext.getString(getButtonStringId()));
+        private static String getInstructionText(Context context) {
+            return context.getString(
+                    R.string.tv_hdr_connect_no_hdr_display, context.getString(getButtonStringId()));
         }
 
-        @Override
-        protected String getStepName() {
-            return "Non HDR Display";
-        }
-
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTest() {
-            Display display = mContext.getWindowManager().getDefaultDisplay();
-            getAsserter()
-                    .withMessage("Display.isHdr()")
-                    .that(display.isHdr())
-                    .isFalse();
+            DisplayManager displayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isFalse();
             getAsserter()
                     .withMessage("Display.getHdrCapabilities()")
                     .that(display.getHdrCapabilities().getSupportedHdrTypes())
@@ -108,33 +106,29 @@
     private static class HdrDisplayTestStep extends SyncTestStep {
 
         public HdrDisplayTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_hdr_capabilities_test_step_hdr_display,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(R.string.tv_hdr_connect_hdr_display,
-                    mContext.getString(getButtonStringId()));
+        private static String getInstructionText(Context context) {
+            return context.getString(
+                    R.string.tv_hdr_connect_hdr_display, context.getString(getButtonStringId()));
         }
 
-        @Override
-        protected String getStepName() {
-            return "HDR Display";
-        }
-
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTest() {
-            Display display = mContext.getWindowManager().getDefaultDisplay();
+            DisplayManager displayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
 
-            getAsserter()
-                    .withMessage("Display.isHdr()")
-                    .that(display.isHdr())
-                    .isTrue();
+            getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isTrue();
 
             Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
 
@@ -144,12 +138,13 @@
             getAsserter()
                     .withMessage("Display.getHdrCapabilities().getSupportedTypes()")
                     .that(supportedHdrTypes)
-                    .isEqualTo(new int[]{
-                        Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION,
-                        Display.HdrCapabilities.HDR_TYPE_HDR10,
-                        Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS,
-                        Display.HdrCapabilities.HDR_TYPE_HLG
-                    });
+                    .isEqualTo(
+                            new int[] {
+                                Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION,
+                                Display.HdrCapabilities.HDR_TYPE_HDR10,
+                                Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS,
+                                Display.HdrCapabilities.HDR_TYPE_HLG
+                            });
 
             float maxLuminance = hdrCapabilities.getDesiredMaxLuminance();
             getAsserter()
@@ -172,41 +167,44 @@
 
     private static class NoDisplayTestStep extends AsyncTestStep {
         public NoDisplayTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_hdr_capabilities_test_step_no_display,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(R.string.tv_hdr_disconnect_display,
-                    mContext.getString(getButtonStringId()),
+        private static String getInstructionText(Context context) {
+            return context.getString(
+                    R.string.tv_hdr_disconnect_display,
+                    context.getString(getButtonStringId()),
                     DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
-                    DISPLAY_DISCONNECT_WAIT_TIME_SECONDS+1);
+                    DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
         }
 
-        @Override
-        protected String getStepName() {
-            return "No Display";
-        }
-
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTestAsync() {
             // Wait for the user to disconnect the display.
-            mContext.getPostTarget().postDelayed(() -> {
-                try {
-                    // Verify the display APIs do not crash when the display is disconnected
-                    Display display = mContext.getWindowManager().getDefaultDisplay();
-                    display.isHdr();
-                    display.getHdrCapabilities();
-                } catch (Exception e) {
-                    getAsserter().fail(Throwables.getStackTraceAsString(e));
-                }
-                done();
-            }, Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis());
+            final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
+            mContext.getPostTarget().postDelayed(this::runTest, delay);
+        }
+
+        private void runTest() {
+            try {
+                // Verify the display APIs do not crash when the display is disconnected
+                DisplayManager displayManager =
+                        (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+                Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+                display.isHdr();
+                display.getHdrCapabilities();
+            } catch (Exception e) {
+                getAsserter().fail(Throwables.getStackTraceAsString(e));
+            }
+            done();
         }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
index c9b4de1..71cea14 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
@@ -25,6 +25,7 @@
 
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.tv.TvAppVerifierActivity;
+import com.android.cts.verifier.tv.TvUtil;
 
 import com.google.common.base.Throwables;
 import com.google.common.truth.Correspondence;
@@ -35,13 +36,15 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.annotation.Nullable;
 
 /**
  * Test for verifying that the platform correctly reports display resolution and refresh rate. More
- * specifically Display.getMode() and Display.getSupportedModes() APIs are tested against reference
- * displays.
+ * specifically Display.getMode() and Display.getSupportedModes() APIs are tested. In the case for
+ * set-top boxes and TV dongles they are tested against reference displays. For TV panels they are
+ * tested against the hardware capabilities of the device.
  */
 public class DisplayModesTestActivity extends TvAppVerifierActivity {
     private static final int DISPLAY_DISCONNECT_WAIT_TIME_SECONDS = 5;
@@ -64,7 +67,6 @@
             };
 
     private TestSequence mTestSequence;
-    private DisplayManager mDisplayManager;
 
     @Override
     protected void setInfoResources() {
@@ -74,15 +76,21 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
     }
 
     @Override
     protected void createTestItems() {
         List<TestStepBase> testSteps = new ArrayList<>();
-        testSteps.add(new NoDisplayTestStep(this));
-        testSteps.add(new Display2160pTestStep(this));
-        testSteps.add(new Display1080pTestStep(this));
+        if (TvUtil.isHdmiSourceDevice()) {
+            // The device is a set-top box or a TV dongle
+            testSteps.add(new NoDisplayTestStep(this));
+            testSteps.add(new Display2160pTestStep(this));
+            testSteps.add(new Display1080pTestStep(this));
+        } else {
+            // The device is a TV Panel
+            testSteps.add(new TvPanelReportedModesAreSupportedTestStep(this));
+            testSteps.add(new TvPanelSupportedModesAreReportedTestStep(this));
+        }
         mTestSequence = new TestSequence(this, testSteps);
         mTestSequence.init();
     }
@@ -92,101 +100,101 @@
         return mTestSequence.getFailureDetails();
     }
 
-    private class NoDisplayTestStep extends AsyncTestStep {
+    private static class NoDisplayTestStep extends AsyncTestStep {
         public NoDisplayTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_display_modes_test_step_no_display,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getStepName() {
-            return mContext.getString(R.string.tv_display_modes_test_step_no_display);
-        }
-
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(
+        private static String getInstructionText(Context context) {
+            return context.getString(
                     R.string.tv_display_modes_disconnect_display,
-                    mContext.getString(getButtonStringId()),
+                    context.getString(getButtonStringId()),
                     DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
                     DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
         }
 
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTestAsync() {
-            mContext.getPostTarget()
-                    .postDelayed(
-                            () -> {
-                                try {
-                                    // Verify the display APIs do not crash when the display is
-                                    // disconnected
-                                    Display display =
-                                            mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-                                    display.getMode();
-                                    display.getSupportedModes();
-                                } catch (Exception e) {
-                                    getAsserter().fail(Throwables.getStackTraceAsString(e));
-                                }
-                                done();
-                            },
-                            Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis());
+            final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
+            mContext.getPostTarget().postDelayed(this::runTest, delay);
+        }
+
+        private void runTest() {
+            try {
+                // Verify the display APIs do not crash when the display is disconnected
+                DisplayManager displayManager =
+                        (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+                Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+                display.getMode();
+                display.getSupportedModes();
+            } catch (Exception e) {
+                getAsserter().fail(Throwables.getStackTraceAsString(e));
+            }
+            done();
         }
     }
 
-    private class Display2160pTestStep extends SyncTestStep {
+    private static class Display2160pTestStep extends SyncTestStep {
         public Display2160pTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_display_modes_test_step_2160p,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getStepName() {
-            return mContext.getString(R.string.tv_display_modes_test_step_2160p);
-        }
-
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(
+        private static String getInstructionText(Context context) {
+            return context.getString(
                     R.string.tv_display_modes_connect_2160p_display,
-                    mContext.getString(getButtonStringId()));
+                    context.getString(getButtonStringId()));
         }
 
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTest() {
-            Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            DisplayManager displayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
             getAsserter()
                     .withMessage("Display.getMode()")
                     .about(MODE_SUBJECT_FACTORY)
                     .that(display.getMode())
-                    .isEquivalentTo(new Mode(3840, 2160, 60f), REFRESH_RATE_PRECISION);
+                    .isEquivalentToAnyOf(
+                            REFRESH_RATE_PRECISION,
+                            new Mode(3840, 2160, 60f),
+                            new Mode(3840, 2160, 50f));
 
-             Mode[] expected2160pSupportedModes = new Mode[]{
-                    new Mode(720, 480, 60f),
-                    new Mode(720, 576, 50f),
-                    // 720p modes
-                    new Mode(1280, 720, 50f),
-                    new Mode(1280, 720, 60f),
-                    // 1080p modes
-                    new Mode(1920, 1080, 24f),
-                    new Mode(1920, 1080, 25f),
-                    new Mode(1920, 1080, 30f),
-                    new Mode(1920, 1080, 50f),
-                    new Mode(1920, 1080, 60f),
-                    // 2160p modes
-                    new Mode(3840, 2160, 24f),
-                    new Mode(3840, 2160, 25f),
-                    new Mode(3840, 2160, 30f),
-                    new Mode(3840, 2160, 50f),
-                    new Mode(3840, 2160, 60f)
-            };
+            Mode[] expected2160pSupportedModes =
+                    new Mode[] {
+                        new Mode(720, 480, 60f),
+                        new Mode(720, 576, 50f),
+                        // 720p modes
+                        new Mode(1280, 720, 50f),
+                        new Mode(1280, 720, 60f),
+                        // 1080p modes
+                        new Mode(1920, 1080, 24f),
+                        new Mode(1920, 1080, 25f),
+                        new Mode(1920, 1080, 30f),
+                        new Mode(1920, 1080, 50f),
+                        new Mode(1920, 1080, 60f),
+                        // 2160p modes
+                        new Mode(3840, 2160, 24f),
+                        new Mode(3840, 2160, 25f),
+                        new Mode(3840, 2160, 30f),
+                        new Mode(3840, 2160, 50f),
+                        new Mode(3840, 2160, 60f)
+                    };
             getAsserter()
                     .withMessage("Display.getSupportedModes()")
                     .that(Arrays.asList(display.getSupportedModes()))
@@ -195,51 +203,54 @@
         }
     }
 
-    private class Display1080pTestStep extends SyncTestStep {
+    private static class Display1080pTestStep extends SyncTestStep {
         public Display1080pTestStep(TvAppVerifierActivity context) {
-            super(context);
+            super(
+                    context,
+                    R.string.tv_display_modes_test_step_1080p,
+                    getInstructionText(context),
+                    getButtonStringId());
         }
 
-        @Override
-        protected String getStepName() {
-            return mContext.getString(R.string.tv_display_modes_test_step_1080p);
-        }
-
-        @Override
-        protected String getInstructionText() {
-            return mContext.getString(
+        private static String getInstructionText(Context context) {
+            return context.getString(
                     R.string.tv_display_modes_connect_1080p_display,
-                    mContext.getString(getButtonStringId()));
+                    context.getString(getButtonStringId()));
         }
 
-        @Override
-        protected @StringRes int getButtonStringId() {
+        private static @StringRes int getButtonStringId() {
             return R.string.tv_start_test;
         }
 
         @Override
         public void runTest() {
-            Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            DisplayManager displayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
 
             getAsserter()
                     .withMessage("Display.getMode()")
                     .about(MODE_SUBJECT_FACTORY)
                     .that(display.getMode())
-                    .isEquivalentTo(new Mode(1920, 1080, 60f), REFRESH_RATE_PRECISION);
+                    .isEquivalentToAnyOf(
+                            REFRESH_RATE_PRECISION,
+                            new Mode(1920, 1080, 60f),
+                            new Mode(1920, 1080, 50f));
 
-            final Mode[] expected1080pSupportedModes = new Mode[]{
-                    new Mode(720, 480, 60f),
-                    new Mode(720, 576, 50f),
-                    // 720p modes
-                    new Mode(1280, 720, 50f),
-                    new Mode(1280, 720, 60f),
-                    // 1080p modes
-                    new Mode(1920, 1080, 24f),
-                    new Mode(1920, 1080, 25f),
-                    new Mode(1920, 1080, 30f),
-                    new Mode(1920, 1080, 50f),
-                    new Mode(1920, 1080, 60f),
-            };
+            final Mode[] expected1080pSupportedModes =
+                    new Mode[] {
+                        new Mode(720, 480, 60f),
+                        new Mode(720, 576, 50f),
+                        // 720p modes
+                        new Mode(1280, 720, 50f),
+                        new Mode(1280, 720, 60f),
+                        // 1080p modes
+                        new Mode(1920, 1080, 24f),
+                        new Mode(1920, 1080, 25f),
+                        new Mode(1920, 1080, 30f),
+                        new Mode(1920, 1080, 50f),
+                        new Mode(1920, 1080, 60f),
+                    };
             getAsserter()
                     .withMessage("Display.getSupportedModes()")
                     .that(Arrays.asList(display.getSupportedModes()))
@@ -248,6 +259,35 @@
         }
     }
 
+    private static class TvPanelReportedModesAreSupportedTestStep extends YesNoTestStep {
+        public TvPanelReportedModesAreSupportedTestStep(TvAppVerifierActivity context) {
+            super(context, getInstructionText(context));
+        }
+
+        private static String getInstructionText(Context context) {
+            DisplayManager displayManager =
+                    (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+            Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+            String supportedModes =
+                    Arrays.stream(display.getSupportedModes())
+                            .map(DisplayModesTestActivity::formatDisplayMode)
+                            .collect(Collectors.joining("\n"));
+
+            return context.getString(
+                    R.string.tv_panel_display_modes_reported_are_supported, supportedModes);
+        }
+    }
+
+    private static class TvPanelSupportedModesAreReportedTestStep extends YesNoTestStep {
+        public TvPanelSupportedModesAreReportedTestStep(TvAppVerifierActivity context) {
+            super(context, getInstructionText(context));
+        }
+
+        private static String getInstructionText(Context context) {
+            return context.getString(R.string.tv_panel_display_modes_supported_are_reported);
+        }
+    }
+
     // We use a custom Mode class since the constructors of Display.Mode are hidden. Additionally,
     // we want to use fuzzy comparision for frame rates which is not used in Display.Mode.equals().
     private static class Mode {
@@ -269,7 +309,7 @@
 
         @Override
         public String toString() {
-            return String.format("%dx%d %.2f Hz", mWidth, mHeight, mRefreshRate);
+            return formatDisplayMode(mWidth, mHeight, mRefreshRate);
         }
     }
 
@@ -278,10 +318,22 @@
             super(failureMetadata, subject);
         }
 
-        public void isEquivalentTo(Mode mode, float refreshRatePrecision) {
-            if (!mode.isEquivalent(actual(), refreshRatePrecision)) {
-                failWithActual("expected", mode);
+        public void isEquivalentToAnyOf(final float refreshRatePrecision, Mode... modes) {
+            boolean found =
+                    Arrays.stream(modes)
+                            .anyMatch(mode -> mode.isEquivalent(actual(), refreshRatePrecision));
+            if (!found) {
+                failWithActual("expected any of", Arrays.toString(modes));
             }
         }
     }
+
+    private static String formatDisplayMode(Display.Mode mode) {
+        return formatDisplayMode(
+                mode.getPhysicalWidth(), mode.getPhysicalHeight(), mode.getRefreshRate());
+    }
+
+    private static String formatDisplayMode(int width, int height, float refreshRate) {
+        return String.format("%dx%d %.2f Hz", width, height, refreshRate);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
new file mode 100644
index 0000000..c73fd53
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 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.verifier.tv.display;
+
+import android.view.View;
+
+import androidx.annotation.StringRes;
+
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+/** Test step containing instruction to the user and a button. */
+public abstract class OneButtonTestStep extends TestStepBase {
+
+    protected View mButtonView;
+
+    @StringRes
+    private int mButtonStringId;
+
+    @StringRes
+    private int mStepNameStringId;
+
+    /**
+     * Constructs a test step containing instruction to the user and a button.
+     *
+     * @param context The test activity which this test step is part of.
+     * @param instructionText The text of the test instruction visible to the user.
+     * @param stepNameStringId Id of a string resource containing human readable name of this step
+     *                         to be used  in logs.
+     * @param buttonStringId Id of a string resource containing the text of the button.
+     */
+    public OneButtonTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+            String instructionText, @StringRes int buttonStringId) {
+        super(context, instructionText);
+        mStepNameStringId = stepNameStringId;
+        mButtonStringId = buttonStringId;
+    }
+
+    @Override
+    public void createUiElements() {
+        super.createUiElements();
+        mButtonView =
+                mContext.createButtonItem(
+                        mButtonStringId,
+                        (View view) -> {
+                            String stepName = mContext.getString(mStepNameStringId);
+                            appendInfoDetails("Running test step %s...", stepName);
+                            onButtonClickRunTest();
+                        });
+    }
+
+    @Override
+    public void enableInteractivity() {
+        TvAppVerifierActivity.setButtonEnabled(mButtonView, true);
+    }
+
+    @Override
+    public void disableInteractivity() {
+        TvAppVerifierActivity.setButtonEnabled(mButtonView, false);
+    }
+
+    /** Test logic to be executed when the button is pressed. */
+    protected abstract void onButtonClickRunTest();
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
index 0c4181a..a1736fb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
@@ -16,15 +16,18 @@
 
 package com.android.cts.verifier.tv.display;
 
+import androidx.annotation.StringRes;
+
 import com.android.cts.verifier.tv.TvAppVerifierActivity;
 
 /**
- * Encapsulates the logic of a synchronously running test step, which displays a human instructions
+ * Encapsulates the logic of a synchronously running test step, which displays human instructions
  * and a button to start the test. For asynchronous steps see {@link AsyncTestStep}.
  */
-public abstract class SyncTestStep extends TestStepBase {
-    public SyncTestStep(TvAppVerifierActivity context) {
-        super(context);
+public abstract class SyncTestStep extends OneButtonTestStep {
+    public SyncTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+            String instructionText, @StringRes int buttonStringId) {
+        super(context, stepNameStringId, instructionText, buttonStringId);
     }
 
     public abstract void runTest();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
index e80828a..7815dcf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
@@ -40,7 +40,7 @@
         // After a step is completed we enable the button of the next step.
         for (int i = 0; i < steps.size() - 1; i++) {
             final int next = i + 1;
-            steps.get(i).setOnDoneListener(() -> steps.get(next).enableButton());
+            steps.get(i).setOnDoneListener(() -> steps.get(next).enableInteractivity());
         }
 
         // When the last step is done, mark the sequence as done.
@@ -48,7 +48,7 @@
                 .setOnDoneListener(() -> onAllStepsDone());
 
         // Enable the button of the first test step so the user can start it.
-        steps.get(0).enableButton();
+        steps.get(0).enableInteractivity();
     }
 
     public String getFailureDetails() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
index ae2a65b..050c549 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
@@ -19,74 +19,56 @@
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.annotation.StringRes;
-
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.tv.TvAppVerifierActivity;
 
 import com.google.common.truth.FailureStrategy;
 import com.google.common.truth.StandardSubjectBuilder;
 
-import java.util.Arrays;
-
-/**
- * Encapsulates the logic of a test step, which displays a human instructions and a button to start
- * the test.
- */
+/** Encapsulates the logic of a test step, which displays human instructions. */
 public abstract class TestStepBase {
-    final protected TvAppVerifierActivity mContext;
-    protected View mViewItem;
+    protected final TvAppVerifierActivity mContext;
+
     private boolean mHasPassed;
     private Runnable mOnDoneListener;
     private String mFailureDetails;
     private StandardSubjectBuilder mAsserter;
+    private View mInstructionView;
+    private String mInstructionText;
 
     /**
      * Constructs a test step containing instruction to the user and a button.
      *
      * @param context The test activity which this test step is part of.
+     * @param instructionText The text of the test instruction visible to the user.
      */
-    public TestStepBase(TvAppVerifierActivity context) {
+    public TestStepBase(TvAppVerifierActivity context, String instructionText) {
         this.mContext = context;
 
-        FailureStrategy failureStrategy = assertionError -> {
-            appendFailureDetails(assertionError.getMessage());
-            mHasPassed = false;
-        };
+        FailureStrategy failureStrategy =
+                assertionError -> {
+                    appendFailureDetails(assertionError.getMessage());
+                    mHasPassed = false;
+                };
         mAsserter = StandardSubjectBuilder.forCustomFailureStrategy(failureStrategy);
         mHasPassed = true;
+        mInstructionText = instructionText;
     }
 
     public boolean hasPassed() {
         return mHasPassed;
     }
 
-    /**
-     * Creates the View for this test step in the context {@link TvAppVerifierActivity}.
-     */
+    /** Creates the View for this test step in the context {@link TvAppVerifierActivity}. */
     public void createUiElements() {
-        mViewItem = mContext.createUserItem(
-                getInstructionText(),
-                getButtonStringId(),
-                (View view) -> {
-                    appendInfoDetails("Running test step %s...", getStepName());
-                    onButtonClickRunTest();
-                });
+        mInstructionView = mContext.createAutoItem(mInstructionText);
     }
 
-    /**
-     * Enables the button of this test step.
-     */
-    public void enableButton() {
-        TvAppVerifierActivity.setButtonEnabled(mViewItem, true);
-    }
+    /** Enables interactivity for this test step - for example, it enables buttons. */
+    public abstract void enableInteractivity();
 
-    /**
-     * Disables the button of this test step.
-     */
-    public void disableButton() {
-        TvAppVerifierActivity.setButtonEnabled(mViewItem, false);
-    }
+    /** Disables interactivity for this test step - for example, it disables buttons. */
+    public abstract void disableInteractivity();
 
     public void setOnDoneListener(Runnable listener) {
         mOnDoneListener = listener;
@@ -96,25 +78,8 @@
         return mFailureDetails;
     }
 
-    /**
-     * Human readable name of this test step to be output to logs.
-     */
-    protected abstract String getStepName();
-
-    protected abstract void onButtonClickRunTest();
-
-    /**
-     * Returns the text of the test instruction visible to the user.
-     */
-    protected abstract String getInstructionText();
-
-    /**
-     * Returns id of string resource containing the text of the button.
-     */
-    protected abstract @StringRes int getButtonStringId();
-
     protected void done() {
-        TvAppVerifierActivity.setPassState(mViewItem, mHasPassed);
+        TvAppVerifierActivity.setPassState(mInstructionView, mHasPassed);
         if (mOnDoneListener != null) {
             mOnDoneListener.run();
         }
@@ -133,7 +98,8 @@
     protected void appendFailureDetails(String failure) {
         String details = String.format("Failure: %s", failure);
         appendDetails(details);
-        appendMessageToView(mViewItem, details);
+
+        appendMessageToView(mInstructionView, details);
     }
 
     protected void appendDetails(String details) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
new file mode 100644
index 0000000..0281252
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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.verifier.tv.display;
+
+import android.view.View;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+/**
+ * Encapsulates the logic of a test step, which displays human instructions for a manual test and
+ * two buttons - Yes and No, which respectively set the test in passing and failing state.
+ */
+public abstract class YesNoTestStep extends TestStepBase {
+    private View yesButton;
+    private View noButton;
+
+    /**
+     * Constructs a test step containing human instructions for a manual test and two buttons -
+     * Yes and No.
+     *
+     * @param context The test activity which this test step is part of.
+     * @param instructionText The text of the test instruction visible to the user.
+     */
+    public YesNoTestStep(TvAppVerifierActivity context, String instructionText) {
+        super(context, instructionText);
+    }
+
+    @Override
+    public void createUiElements() {
+        super.createUiElements();
+        yesButton =
+                mContext.createButtonItem(
+                        R.string.tv_yes,
+                        (View view) -> {
+                            disableInteractivity();
+                            // do nothing so the test will pass
+                            done();
+                        });
+        noButton =
+                mContext.createButtonItem(
+                        R.string.tv_no,
+                        (View view) -> {
+                            disableInteractivity();
+                            getAsserter().fail();
+                            done();
+                        });
+    }
+
+    @Override
+    public void enableInteractivity() {
+        TvAppVerifierActivity.setButtonEnabled(yesButton, true);
+        TvAppVerifierActivity.setButtonEnabled(noButton, true);
+    }
+
+    @Override
+    public void disableInteractivity() {
+        TvAppVerifierActivity.setButtonEnabled(yesButton, false);
+        TvAppVerifierActivity.setButtonEnabled(noButton, false);
+    }
+}
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps
new file mode 100644
index 0000000..509ea3b
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8 b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem
new file mode 100644
index 0000000..f8e5e65
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD
+VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X
+7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN
+IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD
+VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq
+hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh
+ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk b/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk
new file mode 100644
index 0000000..789a022
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk b/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk
deleted file mode 100644
index f06a60d..0000000
--- a/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 23fcb2a..05a76b4 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -714,6 +714,27 @@
                 "testGetSigningCertificatesShowsAll");
     }
 
+    public void testInstallV3KeyRotationGetApkContentsSigners() throws Exception {
+        // The GET_SIGNING_CERTIFICATES flag results in a PackageInfo object returned with a
+        // SigningInfo instance that can be used to query all certificates in the lineage or only
+        // the current signer(s) via getApkContentsSigners. This test verifies when a V3 signed
+        // package with a rotated key is queried getApkContentsSigners only returns the current
+        // signer.
+        installApkFromBuild("v3-ec-p256-with-por_1_2-default-caps.apk");
+        Utils.runDeviceTests(
+                getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
+                "testGetApkContentsSignersShowsCurrent");
+    }
+
+    public void testInstallV2MultipleSignersGetApkContentsSigners() throws Exception {
+        // Similar to the above test, but verifies when an APK is signed with two V2 signers
+        // getApkContentsSigners returns both of the V2 signers.
+        installApkFromBuild("v1v2-ec-p256-two-signers-targetSdk-30.apk");
+        Utils.runDeviceTests(
+                getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
+                "testGetApkContentsSignersShowsMultipleSigners");
+    }
+
     public void testInstallV3KeyRotationHasSigningCertificate() throws Exception {
         // tests that hasSigningCertificate() recognizes past and current signing certs
         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
@@ -764,16 +785,17 @@
         assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1_P_and_2_Qplus.apk");
     }
 
-    public void testInstallTargetRWithV1Signer() throws Exception {
-        // An app targeting R must have at least a V2 signature; this test verifies that an app
-        // targeting R with only a V1 signature fails to install.
-        assertInstallFails("v1-only-target-r.apk");
+    public void testInstallTargetSdk30WithV1Signers() throws Exception {
+        // An app targeting SDK version >= 30 must have at least a V2 signature; this test verifies
+        // an app targeting SDK version 30 with only a V1 signature fails to install.
+        assertInstallFails("v1-ec-p256-two-signers-targetSdk-30.apk");
     }
 
-    public void testInstallTargetRWithV2V2Signers() throws Exception {
-        // An app targeting R must have at least a V2 signature; this test verifies that an app
-        // targeting R with both a V1 and V2 signature installs successfully.
-        assertInstallSucceeds("v1v2-target-r.apk");
+    public void testInstallTargetSdk30WithV1V2Signers() throws Exception {
+        // An app targeting SDK version >= 30 must have at least a V2 signature; this test verifies
+        // that an app targeting SDK version 30 with both a V1 and V2 signature installs
+        // successfully.
+        installApkFromBuild("v1v2-ec-p256-two-signers-targetSdk-30.apk");
     }
 
     public void testInstallV4WithV2Signer() throws Exception {
@@ -894,10 +916,14 @@
     }
 
     private void installDeviceTestPkg() throws Exception {
+        installApkFromBuild(DEVICE_TESTS_APK);
+    }
+
+    private void installApkFromBuild(String apkName) throws Exception {
         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
-        File apk = buildHelper.getTestFile(DEVICE_TESTS_APK);
-        String result = getDevice().installPackage(apk, true);
-        assertNull("failed to install " + DEVICE_TESTS_APK + ", Reason: " + result, result);
+        File apk = buildHelper.getTestFile(apkName);
+        String result = getDevice().installPackage(apk, true, INSTALL_ARG_FORCE_QUERYABLE);
+        assertNull("failed to install " + apkName + ", Reason: " + result, result);
     }
 
     private String installPackageFromResource(String apkFilenameInResources, boolean ephemeral)
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
index f787cc9..1a46d6a 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
@@ -19,9 +19,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.fail;
 import static org.testng.Assert.expectThrows;
 
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -60,6 +65,15 @@
         });
         assertThat(exception.getMessage()).contains(JAVA_FILE_NOT_FOUND_MSG);
         assertThat(exception.getMessage()).doesNotContain(JAVA_FILE_PERMISSION_DENIED_MSG);
+
+        // Try to create a directory here, and it should return permission denied not directory
+        // exists.
+        try {
+            Os.mkdir(path, 0700);
+            fail("Should not able to mkdir() on " + path);
+        } catch (ErrnoException e) {
+            assertEquals(e.errno, OsConstants.EACCES);
+        }
     }
 
     public static void assertDirIsAccessible(String path) {
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
index 0b45764..ae419b6 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_MODULE := CtsCorruptApkTests_Unaligned_Q
 LOCAL_SRC_FILES := unaligned_Q.apk
 LOCAL_MODULE_CLASS := APPS
+LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_SUFFIX := .apk
 LOCAL_CERTIFICATE := PRESIGNED
 LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_Q.apk
@@ -30,8 +31,9 @@
 LOCAL_MODULE := CtsCorruptApkTests_Unaligned_R
 LOCAL_SRC_FILES := unaligned_R.apk
 LOCAL_MODULE_CLASS := APPS
+LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_SUFFIX := .apk
 LOCAL_CERTIFICATE := PRESIGNED
 LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_R.apk
 LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
-include $(BUILD_PREBUILT)
\ No newline at end of file
+include $(BUILD_PREBUILT)
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index 1e57012..7d94aed 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -216,6 +216,20 @@
         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
         final UserHandle user = android.os.Process.myUserHandle();
 
+        // Since scoped storage, apps can't access the package-specific Android/
+        // directories anymore. So we compute the current size, and assume the
+        // delta is in Android/*, which unfortunately may have data from
+        // test APKs that aren't cleaned up properly.
+        //
+        // Then, when we compute the new size and compare it with the stats,
+        // we expect the same delta
+        final long manualSizeBefore = getSizeManual(
+                Environment.getExternalStorageDirectory(), true);
+        final long statsSizeBefore = stats.queryExternalStatsForUser(
+                UUID_DEFAULT, user).getTotalBytes();
+
+        final long deltaBefore = statsSizeBefore - manualSizeBefore;
+
         useSpace(getContext());
 
         final File top = Environment.getExternalStorageDirectory();
@@ -240,7 +254,8 @@
         manualSize += getSizeManual(getContext().getExternalCacheDir(), true);
         final long statsSize = stats.queryExternalStatsForUser(UUID_DEFAULT, user).getTotalBytes();
 
-        assertMostlyEquals(manualSize, statsSize);
+        final long deltaAfter = statsSize - manualSize;
+        assertMostlyEquals(deltaBefore, deltaAfter);
     }
 
     public void testVerifyCategory() throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
index 4fe98ec..2f06395 100644
--- a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
+++ b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
@@ -19,12 +19,13 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
-import android.util.Log;
 import android.test.AndroidTestCase;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.lang.Override;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * On-device tests for APK Signature Scheme v3 based signing certificate rotation
@@ -83,6 +84,38 @@
             + "a477acbe6eecc8d575511b2b77a3551f040ccf21792f74cd95c84e3ff87ad03851db81d164c836830e31"
             + "8208a52dcdc77e376f73b96d";
 
+    // These are the hex encodings of the der form of the pkgsigverify/ec-p256[_2] certs used to
+    // sign APKs that are part of this test.
+    private static final String EC_P256_FIRST_CERT_HEX =
+            "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a86"
+                    + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+                    + "3136303333313134353830365a170d3433303831373134353830365a3012"
+                    + "3110300e06035504030c0765632d703235363059301306072a8648ce3d02"
+                    + "0106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2b"
+                    + "a0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991e"
+                    + "f0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e"
+                    + "04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d"
+                    + "23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c06"
+                    + "03551d13040530030101ff300a06082a8648ce3d04030203490030460221"
+                    + "00f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16"
+                    + "db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0f"
+                    + "dbb8042cb655aadd";
+
+    private static final String EC_P256_SECOND_CERT_HEX =
+            "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a86"
+                    + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+                    + "3138303731333137343135315a170d3238303731303137343135315a3014"
+                    + "3112301006035504030c0965632d703235365f323059301306072a8648ce"
+                    + "3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da9"
+                    + "3d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc"
+                    + "271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d060355"
+                    + "1d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603"
+                    + "551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc830"
+                    + "0c0603551d13040530030101ff300a06082a8648ce3d0403020348003045"
+                    + "02202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb"
+                    + "99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda1"
+                    + "00a6fe1a2ab19ff09e";
+
     public void testHasPerm() throws Exception {
         PackageManager pm = getContext().getPackageManager();
         assertTrue(PERMISSION_NAME + " not granted to " + COMPANION_PKG,
@@ -141,6 +174,31 @@
                 + SECOND_CERT_HEX, matchedSecond);
     }
 
+    public void testGetApkContentsSignersShowsCurrent() throws Exception {
+        // The SigningInfo instance returned from GET_SIGNING_CERTIFICATES provides an option to
+        // obtain only the current signer through getApkContentsSigners.
+        PackageManager pm = getContext().getPackageManager();
+        PackageInfo pi = pm.getPackageInfo(PKG, PackageManager.GET_SIGNING_CERTIFICATES);
+        assertNotNull("Failed to get signatures in Package Info of " + PKG, pi.signingInfo);
+        assertFalse("Multiple signing certificates found in signing certificate history for " + PKG,
+                pi.signingInfo.hasMultipleSigners());
+        assertExpectedSignatures(pi.signingInfo.getApkContentsSigners(), EC_P256_SECOND_CERT_HEX);
+    }
+
+    public void testGetApkContentsSignersShowsMultipleSigners() throws Exception {
+        // Similar to the test above when GET_SIGNING_CERTIFICATES is used to obtain the signers
+        // getApkContentSigners should return all of the current signatures when there are multiple
+        // V1 / V2 signers.
+        PackageManager pm = getContext().getPackageManager();
+        PackageInfo pi = pm.getPackageInfo(PKG, PackageManager.GET_SIGNING_CERTIFICATES);
+        assertNotNull("Failed to get signatures in PackageInfo of " + PKG,
+                pi.signingInfo);
+        assertTrue("Multiple signing certificates should have been reported for " + PKG,
+                pi.signingInfo.hasMultipleSigners());
+        assertExpectedSignatures(pi.signingInfo.getApkContentsSigners(), EC_P256_FIRST_CERT_HEX,
+                EC_P256_SECOND_CERT_HEX);
+    }
+
     public void testHasSigningCertificate() throws Exception {
         // make sure that hasSigningCertificate() reports that both certificates in the signing
         // history are present
@@ -226,4 +284,20 @@
         messageDigest.update(data);
         return messageDigest.digest();
     }
+
+    private static void assertExpectedSignatures(Signature[] signatures,
+            String... expectedSignatures) throws Exception {
+        int numSigners = signatures.length;
+        assertEquals("An unexpected number of signatures was returned, expected "
+                        + expectedSignatures.length + ", but received " + signatures.length,
+                expectedSignatures.length, numSigners);
+        Set<String> expectedSignatureSet = new HashSet<>(Arrays.asList(expectedSignatures));
+        for (int i = 0; i < numSigners; i++) {
+            String reportedCert = signatures[i].toCharsString();
+            // Remove the reported certificate from the set to ensure duplicates are not matched.
+            if (!expectedSignatureSet.remove(reportedCert)) {
+                fail("Received an unexpected signature during the test: " + reportedCert);
+            }
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
new file mode 100644
index 0000000..e037d67
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+cert_dir := cts/hostsidetests/appsecurity/certs/pkgsigverify
+
+# This is the default test package signed with the default key.
+include $(LOCAL_PATH)/base.mk
+LOCAL_PACKAGE_NAME := CtsPkgInstallTinyApp
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+# This is the test package signed using the V1/V2 signature schemes with
+# two signers targeting SDK version 30 with sandbox version 1. From this
+# package the v1-ec-p256-two-signers-targetSdk-30.apk is created with the
+# following command:
+# apksigner sign --in v1v2-ec-p256-two-signers-targetSdk-30.apk --out
+# v1-ec-p256-two-signers-targetSdk-30.apk --cert ec-p256.x509.pem --key
+# ec-p256.pk8 --next-signer --cert ec-p256_2.x509.pem --key ec-p256_2.pk8
+# --v2-signing-enabled false --v3-signing-enabled false --v4-signing-enabled false
+include $(LOCAL_PATH)/base.mk
+LOCAL_SDK_VERSION := 30
+LOCAL_MANIFEST_FILE := AndroidManifest-sandbox-v1.xml
+LOCAL_PACKAGE_NAME := v1v2-ec-p256-two-signers-targetSdk-30
+LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
+LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256_2
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+# This is the test package signed using the V3 signature scheme with
+# a rotated key and one signer in the lineage with default capabilities.
+include $(LOCAL_PATH)/base.mk
+LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-default-caps
+LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
+LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
+LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-default-caps
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+cert_dir :=
+
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml
new file mode 100644
index 0000000..8ca35572
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.appsecurity.cts.tinyapp"
+        android:versionCode="10"
+        android:versionName="1.0"
+        android:targetSandboxVersion="1">
+    <application android:label="@string/app_name">
+        <activity
+                android:name=".MainActivity"
+                android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/base.mk b/hostsidetests/appsecurity/test-apps/tinyapp/base.mk
new file mode 100644
index 0000000..3c4581f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/base.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2020 Google Inc.
+#
+# 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.
+#
+
+# Base setup that can be included by all builds of this package.
+include $(CLEAR_VARS)
+LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
+LOCAL_SDK_VERSION := current
+
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
index 1753d75..3b493f4 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
+import android.net.Network;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
@@ -50,6 +51,7 @@
     private static final String METERED_DATA_APP_MAIN_ACTIVITY
             = METERED_DATA_APP_PKG + ".MainActivity";
 
+    private static final long WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC = 10;
     private static final long WAIT_FOR_NETWORK_INFO_TIMEOUT_SEC = 8;
 
     private static final int NUM_TRIES_METERED_STATUS_CHECK = 20;
@@ -151,34 +153,40 @@
     }
 
     private void setMeteredNetwork() throws Exception {
+        final int oldNetId = getActiveNetworkNetId();
+        final boolean oldMeteredState = mCm.isActiveNetworkMetered();
         final NetworkInfo networkInfo = mCm.getActiveNetworkInfo();
         if (networkInfo == null) {
             fail("Active network is not available");
-        } else if (mCm.isActiveNetworkMetered()) {
-            Log.i(TAG, "Active network already metered: " + networkInfo);
-            return;
         } else if (networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
             fail("Active network doesn't support setting metered status: " + networkInfo);
         }
-        final String netId = setWifiMeteredStatus(true);
+        final String ssid = setWifiMeteredStatus(true);
 
         // Set flag so status is reverted on resetMeteredNetwork();
-        mMeteredWifi = netId;
+        mMeteredWifi = ssid;
+
+        // When transitioning from unmetered to metered, the network stack will discconect
+        // the current WiFi connection and reconnect it. In this case we need to wait for
+        // the new network to come up.
+        if (!oldMeteredState) {
+            waitForReconnection(oldNetId);
+        }
         // Sanity check.
-        assertWifiMeteredStatus(netId, true);
+        assertWifiMeteredStatus(ssid, true);
         assertActiveNetworkMetered(true);
     }
 
     private void resetMeteredNetwork() throws Exception {
         if (mMeteredWifi != null) {
             Log.i(TAG, "Resetting metered status for netId=" + mMeteredWifi);
-            setWifiMeteredStatus(mMeteredWifi, false);
-            assertWifiMeteredStatus(mMeteredWifi, false);
+            setWifiMeteredStatus(mMeteredWifi, /* default meteredness */ null);
+            assertWifiMeteredStatus(mMeteredWifi, /* default meteredness */ null);
             assertActiveNetworkMetered(false);
         }
     }
 
-    private String setWifiMeteredStatus(boolean metered) throws Exception {
+    private String setWifiMeteredStatus(Boolean metered) throws Exception {
         final String ssid = mWm.getConnectionInfo().getSSID();
         assertNotNull("null SSID", ssid);
         final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
@@ -187,14 +195,15 @@
         return netId;
     }
 
-    private void setWifiMeteredStatus(String netId, boolean metered) throws Exception {
-        Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
-        executeCmd("cmd netpolicy set metered-network " + netId + " " + metered);
+    private void setWifiMeteredStatus(String ssid, Boolean metered) throws Exception {
+        Log.i(TAG, "Setting wi-fi network " + ssid + " metered status to " + metered);
+        executeCmd("cmd netpolicy set metered-network " + ssid + " " +
+                (metered != null ? metered.toString() : "undefined"));
     }
 
-    private void assertWifiMeteredStatus(String netId, boolean metered) throws Exception {
+    private void assertWifiMeteredStatus(String ssid, Boolean metered) throws Exception {
         final String cmd = "cmd netpolicy list wifi-networks";
-        final String expectedResult = netId + ";" + metered;
+        final String expectedResult = ssid + ";" + (metered != null ? metered.toString() : "none");
         String cmdResult = null;
         for (int i = 0; i < NUM_TRIES_METERED_STATUS_CHECK; ++i) {
             cmdResult = executeCmd(cmd);
@@ -226,4 +235,27 @@
         Log.i(TAG, "Cmd '" + cmd + "' result: " + result);
         return result;
     }
+
+    private int getActiveNetworkNetId() {
+        Network network = mCm.getActiveNetwork();
+        if (network == null) {
+            return 0;
+        }
+        return network.getNetId();
+    }
+
+    private void waitForReconnection(int oldNetId) throws InterruptedException {
+        long pollingDeadline = System.currentTimeMillis()
+                + WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC * 1000;
+        int latestNetId;
+        do {
+            Thread.sleep(1000);
+            if (System.currentTimeMillis() >= pollingDeadline) {
+                fail("Timeout waiting for network reconnection");
+            }
+            latestNetId = getActiveNetworkNetId();
+            // NetId will be 0 while old network is disconnected but new network
+            // has not come up yet.
+        } while (latestNetId == 0 || latestNetId == oldNetId);
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
index a61a6e2..6ca7bd3 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.testng.Assert.assertThrows;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.hardware.camera2.CameraManager;
@@ -94,7 +96,6 @@
     private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
             Sets.newSet(
                     UserManager.DISALLOW_CONFIG_DATE_TIME,
-                    UserManager.DISALLOW_ADD_USER,
                     UserManager.DISALLOW_BLUETOOTH,
                     UserManager.DISALLOW_BLUETOOTH_SHARING,
                     UserManager.DISALLOW_CONFIG_BLUETOOTH,
@@ -138,6 +139,25 @@
         assertThat(restrictions.get(restriction)).isNull();
     }
 
+    public void testUnableToAddAndClearBaseUserRestrictions_onParent() {
+        testUnableToAddBaseUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+        testUnableToClearBaseUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+        testUnableToAddBaseUserRestriction(UserManager.DISALLOW_ADD_USER);
+        testUnableToClearBaseUserRestriction(UserManager.DISALLOW_ADD_USER);
+    }
+
+    private void testUnableToAddBaseUserRestriction(String restriction) {
+        assertThrows(UnsupportedOperationException.class,
+                () -> mParentDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                        restriction));
+    }
+
+    private void testUnableToClearBaseUserRestriction(String restriction) {
+        assertThrows(UnsupportedOperationException.class,
+                () -> mParentDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+                        restriction));
+    }
+
     private void checkCanOpenCamera(boolean canOpen) throws Exception {
         // If the device does not support a camera it will return an empty camera ID list.
         if (mCameraManager.getCameraIdList() == null
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
index caa52d6..04eebca 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
@@ -61,25 +61,4 @@
         assertThat(
                 mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME)).isFalse();
     }
-
-    public void testAddUserRestrictionDisallowAddUser_onParent() {
-        DevicePolicyManager parentDevicePolicyManager =
-                mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
-        assertNotNull(parentDevicePolicyManager);
-
-        parentDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
-                UserManager.DISALLOW_ADD_USER);
-    }
-
-    public void testHasUserRestrictionDisallowAddUser() {
-        assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).isTrue();
-    }
-
-    public void testClearUserRestrictionDisallowAddUser() {
-        DevicePolicyManager parentDevicePolicyManager =
-                mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
-
-        parentDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
-                UserManager.DISALLOW_ADD_USER);
-    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 2c28f1d..7f6398c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -112,6 +112,15 @@
     }
 
     @Test
+    public void testCannotAddSecondaryUser() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        failToCreateUser();
+    }
+
+    @Test
     public void testCanRelinquishControlOverDevice() throws Exception {
         if (!mHasFeature) {
             return;
@@ -181,18 +190,16 @@
 
     @Test
     public void testUserRestrictionsSetOnParentAreNotPersisted() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+        if (!mHasFeature) {
             return;
         }
-        int secondaryUserId = createUser();
-        setPoAsUser(secondaryUserId);
-
+        installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
                 "testAddUserRestrictionDisallowConfigDateTime_onParent", mUserId);
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
                 "testHasUserRestrictionDisallowConfigDateTime", mUserId);
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testHasUserRestrictionDisallowConfigDateTime", secondaryUserId);
+                "testHasUserRestrictionDisallowConfigDateTime", mPrimaryUserId);
         removeOrgOwnedProfile();
         assertHasNoUser(mUserId);
 
@@ -201,29 +208,7 @@
 
         // User restrictions are not persist after organization-owned profile owner is removed
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testUserRestrictionDisallowConfigDateTimeIsNotPersisted", secondaryUserId);
-    }
-
-    @Test
-    public void testUserRestrictionsSetOnParentAreEnforced() throws Exception {
-        if (!mHasFeature || !canCreateAdditionalUsers(1)) {
-            return;
-        }
-        int userId = createUser();
-        removeUser(userId);
-
-        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testAddUserRestrictionDisallowAddUser_onParent", mUserId);
-        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testHasUserRestrictionDisallowAddUser", mUserId);
-
-        // Make sure the user restriction is enforced
-        failToCreateUser();
-
-        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testHasUserRestrictionDisallowAddUser", mUserId);
-        runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
-                "testClearUserRestrictionDisallowAddUser", mUserId);
+                "testUserRestrictionDisallowConfigDateTimeIsNotPersisted", mPrimaryUserId);
     }
 
     @Test
diff --git a/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java b/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
index b7771bc..4e52a6e 100644
--- a/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
+++ b/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
@@ -36,10 +36,6 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
-/**
- * Used by NetstatsIncidentTest.  Makes some network requests so "dumpsys netstats" will have
- * something to show.
- */
 @RunWith(AndroidJUnit4.class)
 public class ProcStatsTest {
     private static final String TAG = "ProcStatsTest";
diff --git a/hostsidetests/incident/apps/netstatsapp/Android.bp b/hostsidetests/incident/apps/netstatsapp/Android.bp
deleted file mode 100644
index ef69c0b..0000000
--- a/hostsidetests/incident/apps/netstatsapp/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-android_test_helper_app {
-    name: "CtsNetStatsApp",
-    defaults: ["cts_defaults"],
-    srcs: ["src/**/*.java"],
-    libs: [
-        "android.test.runner.stubs",
-        "junit",
-    ],
-    static_libs: [
-        "ctstestrunner-axt",
-        "compatibility-device-util-axt",
-        "androidx.legacy_legacy-support-v4",
-    ],
-    sdk_version: "test_current",
-    // tag this module as a cts test artifact
-    test_suites: [
-        "cts",
-        "vts10",
-        "general-tests",
-    ],
-}
diff --git a/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
deleted file mode 100644
index df045fd..0000000
--- a/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.server.cts.netstats" >
-
-    <uses-permission android:name="android.permission.INTERNET" />
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-            android:targetPackage="com.android.server.cts.netstats" />
-</manifest>
diff --git a/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java b/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
deleted file mode 100644
index e9ec71e..0000000
--- a/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.cts.netstats;
-
-import android.net.TrafficStats;
-import android.util.Log;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.URL;
-
-import javax.net.ssl.HttpsURLConnection;
-
-/**
- * Used by NetstatsIncidentTest.  Makes some network requests so "dumpsys netstats" will have
- * something to show.
- */
-@RunWith(AndroidJUnit4.class)
-public class NetstatsDeviceTest {
-    private static final String TAG = "NetstatsDeviceTest";
-
-    private static final int NET_TAG = 123123123;
-
-    @Test
-    public void testDoNetworkWithoutTagging() throws Exception {
-        Log.i(TAG, "testDoNetworkWithoutTagging");
-
-        makeNetworkRequest();
-    }
-
-    @Test
-    public void testDoNetworkWithTagging() throws Exception {
-        Log.i(TAG, "testDoNetworkWithTagging");
-
-        TrafficStats.getAndSetThreadStatsTag(NET_TAG);
-        makeNetworkRequest();
-    }
-
-    private void makeNetworkRequest() throws Exception {
-        final URL url = new URL("https://www.android.com/");
-        final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
-        HttpsURLConnection.setFollowRedirects(true);
-        try {
-            final int status = urlConnection.getResponseCode();
-
-            Log.i(TAG, "Response code from " + url + ": " + status);
-
-            // Doesn't matter what response code we got.  We touched the network, which is enough.
-        } finally {
-            urlConnection.disconnect();
-        }
-    }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
index 58784e9..460181d0 100644
--- a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
@@ -40,8 +40,6 @@
             FingerprintIncidentTest.verifyFingerprintServiceDumpProto(dump.getFingerprint(), filterLevel);
         }
 
-        NetstatsIncidentTest.verifyNetworkStatsServiceDumpProto(dump.getNetstats(), filterLevel);
-
         SettingsIncidentTest.verifySettingsServiceDumpProto(dump.getSettings(), filterLevel);
 
         NotificationIncidentTest.verifyNotificationServiceDumpProto(dump.getNotification(), filterLevel);
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
deleted file mode 100644
index 13c5803..0000000
--- a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.cts;
-
-import android.service.NetworkIdentityProto;
-import android.service.NetworkInterfaceProto;
-import android.service.NetworkStatsCollectionKeyProto;
-import android.service.NetworkStatsCollectionStatsProto;
-import android.service.NetworkStatsHistoryBucketProto;
-import android.service.NetworkStatsHistoryProto;
-import android.service.NetworkStatsRecorderProto;
-import android.service.NetworkStatsServiceDumpProto;
-
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * Test for "dumpsys netstats --proto"
- *
- * Note most of the logic here is just heuristics.
- *
- * Usage:
-
-  cts-tradefed run cts --skip-device-info --skip-preconditions \
-      --skip-system-status-check \
-       com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker \
-       -a armeabi-v7a -m CtsIncidentHostTestCases -t com.android.server.cts.NetstatsIncidentTest
-
- */
-public class NetstatsIncidentTest extends ProtoDumpTestCase {
-    private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
-    private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
-    private static final String FEATURE_WIFI = "android.hardware.wifi";
-
-    @Override
-    protected void tearDown() throws Exception {
-        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
-
-        super.tearDown();
-    }
-
-
-    private void assertPositive(String name, long value) {
-        if (value > 0) return;
-        fail(name + " expected to be positive, but was: " + value);
-    }
-
-    private void assertNotNegative(String name, long value) {
-        if (value >= 0) return;
-        fail(name + " expected to be zero or positive, but was: " + value);
-    }
-
-    private void assertGreaterOrEqual(long greater, long lesser) {
-        assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
-                greater >= lesser);
-    }
-
-    /**
-     * Parse the output of "dumpsys netstats --proto" and make sure all the values are probable.
-     */
-    public void testSanityCheck() throws Exception {
-
-        final long st = System.currentTimeMillis();
-
-        installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
-
-        // Find the package UID.
-        final int uid = Integer.parseInt(execCommandAndGetFirstGroup(
-                "dumpsys package " + DEVICE_SIDE_TEST_PACKAGE, "userId=(\\d+)"));
-
-        CLog.i("Start time: " + st);
-        CLog.i("App UID: " + uid);
-
-        // Run the device side test which makes some network requests.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, null, null);
-
-        // Make some more activity.
-        getDevice().executeShellCommand("ping -s 100 -c 10 -i 0  www.android.com");
-
-        // Force refresh the output.
-        getDevice().executeShellCommand("dumpsys netstats --poll");
-
-        NetworkStatsServiceDumpProto dump = getDump(NetworkStatsServiceDumpProto.parser(),
-                "dumpsys netstats --proto");
-
-        CLog.d("First dump:\n" + dump.toString());
-
-        // Basic sanity check.
-        checkInterfaces(dump.getActiveInterfacesList());
-        checkInterfaces(dump.getActiveUidInterfacesList());
-
-        checkStats(dump.getDevStats(), /*withUid=*/ false, /*withTag=*/ false);
-        checkStats(dump.getXtStats(), /*withUid=*/ false, /*withTag=*/ false);
-        checkStats(dump.getUidStats(), /*withUid=*/ true, /*withTag=*/ false);
-        checkStats(dump.getUidTagStats(), /*withUid=*/ true, /*withTag=*/ true);
-
-        // Remember the original values.
-        final Predicate<NetworkStatsCollectionKeyProto> uidFilt = key -> key.getUid() == uid;
-        final Predicate<NetworkStatsCollectionKeyProto> tagFilt =
-                key -> (key.getTag() == 123123123) && (key.getUid() == uid);
-
-        final long devRxPackets = sum(dump.getDevStats(), st, b -> b.getRxPackets());
-        final long devRxBytes = sum(dump.getDevStats(), st, b -> b.getRxBytes());
-        final long devTxPackets = sum(dump.getDevStats(), st, b -> b.getTxPackets());
-        final long devTxBytes = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
-        final long xtRxPackets = sum(dump.getXtStats(), st, b -> b.getRxPackets());
-        final long xtRxBytes = sum(dump.getXtStats(), st, b -> b.getRxBytes());
-        final long xtTxPackets = sum(dump.getXtStats(), st, b -> b.getTxPackets());
-        final long xtTxBytes = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
-        final long uidRxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
-        final long uidRxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
-        final long uidTxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
-        final long uidTxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
-        final long tagRxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
-        final long tagRxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
-        final long tagTxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
-        final long tagTxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
-        // Run again to make some more activity.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
-                "com.android.server.cts.netstats.NetstatsDeviceTest",
-                "testDoNetworkWithoutTagging");
-
-        getDevice().executeShellCommand("dumpsys netstats --poll");
-        dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
-
-        CLog.d("Second dump:\n" + dump.toString());
-
-        final long devRxPackets2 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
-        final long devRxBytes2 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
-        final long devTxPackets2 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
-        final long devTxBytes2 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
-        final long xtRxPackets2 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
-        final long xtRxBytes2 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
-        final long xtTxPackets2 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
-        final long xtTxBytes2 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
-        final long uidRxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
-        final long uidRxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
-        final long uidTxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
-        final long uidTxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
-        final long tagRxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
-        final long tagRxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
-        final long tagTxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
-        final long tagTxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
-        // At least 1 packet, 100 bytes sent.
-        assertGreaterOrEqual(uidTxPackets2, uidTxPackets + 1);
-        assertGreaterOrEqual(uidTxBytes2, uidTxBytes + 100);
-
-//        assertGreaterOrEqual(tagTxPackets2, tagTxPackets + 1);
-//        assertGreaterOrEqual(tagTxBytes2, tagTxBytes + 100);
-
-        // At least 2 packets, 100 bytes sent.
-        assertGreaterOrEqual(uidRxPackets2, uidRxPackets + 2);
-        assertGreaterOrEqual(uidRxBytes2, uidRxBytes + 100);
-
-//        assertGreaterOrEqual(tagRxPackets2, tagRxPackets + 2);
-//        assertGreaterOrEqual(tagRxBytes2, tagRxBytes + 100);
-
-        // Run again to make some more activity.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
-                "com.android.server.cts.netstats.NetstatsDeviceTest",
-                "testDoNetworkWithTagging");
-
-        getDevice().executeShellCommand("dumpsys netstats --poll");
-        dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
-
-        CLog.d("Second dump:\n" + dump.toString());
-
-        final long devRxPackets3 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
-        final long devRxBytes3 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
-        final long devTxPackets3 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
-        final long devTxBytes3 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
-        final long xtRxPackets3 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
-        final long xtRxBytes3 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
-        final long xtTxPackets3 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
-        final long xtTxBytes3 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
-        final long uidRxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
-        final long uidRxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
-        final long uidTxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
-        final long uidTxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
-        final long tagRxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
-        final long tagRxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
-        final long tagTxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
-        final long tagTxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
-        // At least 1 packet, 100 bytes sent.
-        assertGreaterOrEqual(uidTxPackets3, uidTxPackets2 + 1);
-        assertGreaterOrEqual(uidTxBytes3, uidTxBytes2 + 100);
-
-        assertGreaterOrEqual(tagTxPackets3, tagTxPackets2 + 1);
-        assertGreaterOrEqual(tagTxBytes3, tagTxBytes2 + 100);
-
-        // At least 2 packets, 100 bytes sent.
-        assertGreaterOrEqual(uidRxPackets3, uidRxPackets2 + 2);
-        assertGreaterOrEqual(uidRxBytes3, uidRxBytes2 + 100);
-
-        assertGreaterOrEqual(tagRxPackets3, tagRxPackets2 + 2);
-        assertGreaterOrEqual(tagRxBytes3, tagRxBytes2 + 100);
-    }
-
-    private long sum(NetworkStatsRecorderProto recorder,
-            long startTime,
-            Function<NetworkStatsHistoryBucketProto, Long> func) {
-        return sum(recorder, startTime, key -> true, func);
-    }
-
-    private long sum(NetworkStatsRecorderProto recorder,
-            long startTime,
-            Predicate<NetworkStatsCollectionKeyProto> filter,
-            Function<NetworkStatsHistoryBucketProto, Long> func) {
-
-        long total = 0;
-        for (NetworkStatsCollectionStatsProto stats
-                : recorder.getCompleteHistory().getStatsList()) {
-            if (!filter.test(stats.getKey())) {
-                continue;
-            }
-            for (NetworkStatsHistoryBucketProto bucket : stats.getHistory().getBucketsList()) {
-                if (startTime < bucket.getBucketStartMs()) {
-                    continue;
-                }
-                total += func.apply(bucket);
-            }
-        }
-        return total;
-    }
-
-    private void checkInterfaces(List<NetworkInterfaceProto> interfaces) throws Exception{
-        /* Example:
-    active_interfaces=[
-      NetworkInterfaceProto {
-        interface=wlan0
-        identities=NetworkIdentitySetProto {
-          identities=[
-            NetworkIdentityProto {
-              type=1
-              subscriber_id=
-              network_id="wifiap"
-              roaming=false
-              metered=false
-            }
-          ]
-        }
-      }
-    ]
-         */
-        assertTrue("There must be at least one network device",
-                interfaces.size() > 0);
-
-        boolean allRoaming = true;
-        boolean allMetered = true;
-
-        for (NetworkInterfaceProto iface : interfaces) {
-            assertTrue("Missing interface name", !iface.getInterface().isEmpty());
-
-            assertPositive("# identities", iface.getIdentities().getIdentitiesList().size());
-
-            for (NetworkIdentityProto iden : iface.getIdentities().getIdentitiesList()) {
-                allRoaming &= iden.getRoaming();
-                allMetered &= iden.getMetered();
-
-                // TODO Can we check the other fields too?  type, subscriber_id, and network_id.
-            }
-        }
-        assertFalse("There must be at least one non-roaming interface during CTS", allRoaming);
-        if (hasWiFiFeature()) {
-            assertFalse("There must be at least one non-metered interface during CTS", allMetered);
-        }
-    }
-
-    private void checkStats(NetworkStatsRecorderProto recorder, boolean withUid, boolean withTag) {
-        /*
-         * Example:
-    dev_stats=NetworkStatsRecorderProto {
-      pending_total_bytes=136
-      complete_history=NetworkStatsCollectionProto {
-        stats=[
-          NetworkStatsCollectionStatsProto {
-            key=NetworkStatsCollectionKeyProto {
-              identity=NetworkIdentitySetProto {
-                identities=[
-                  NetworkIdentityProto {
-                    type=1
-                    subscriber_id=
-                    network_id="wifiap"
-                    roaming=false
-                    metered=false
-                  }
-                ]
-              }
-              uid=-1
-              set=-1
-              tag=0
-            }
-            history=NetworkStatsHistoryProto {
-              bucket_duration_ms=3600000
-              buckets=[
-                NetworkStatsHistoryBucketProto {
-                  bucket_start_ms=2273694336
-                  rx_bytes=2142
-                  rx_packets=10
-                  tx_bytes=1568
-                  tx_packets=12
-                  operations=0
-                }
-                NetworkStatsHistoryBucketProto {
-                  bucket_start_ms=3196682880
-                  rx_bytes=2092039
-                  rx_packets=1987
-                  tx_bytes=236735
-                  tx_packets=1750
-                  operations=0
-                }
-         */
-
-        assertNotNegative("Pending bytes", recorder.getPendingTotalBytes());
-
-        for (NetworkStatsCollectionStatsProto stats : recorder.getCompleteHistory().getStatsList()) {
-
-            final NetworkStatsCollectionKeyProto key = stats.getKey();
-
-            // TODO Check the key.
-
-            final NetworkStatsHistoryProto hist = stats.getHistory();
-
-            assertPositive("duration", hist.getBucketDurationMs());
-
-            // Subtract one hour from duration to compensate for possible DTS.
-            final long minInterval = hist.getBucketDurationMs() - (60 * 60 * 1000);
-
-            NetworkStatsHistoryBucketProto prev = null;
-            for (NetworkStatsHistoryBucketProto bucket : hist.getBucketsList()) {
-
-                // Make sure the start time is increasing by at least the "duration",
-                // except we subtract duration from one our to compensate possible DTS.
-
-                if (prev != null) {
-                    assertTrue(
-                            String.format("Last start=%d, current start=%d, diff=%d, duration=%d",
-                                    prev.getBucketStartMs(), bucket.getBucketStartMs(),
-                                    (bucket.getBucketStartMs() - prev.getBucketStartMs()),
-                                    minInterval),
-                            (bucket.getBucketStartMs() - prev.getBucketStartMs()) >=
-                                    minInterval);
-                }
-                assertNotNegative("RX bytes", bucket.getRxBytes());
-                assertNotNegative("RX packets", bucket.getRxPackets());
-                assertNotNegative("TX bytes", bucket.getTxBytes());
-                assertNotNegative("TX packets", bucket.getTxPackets());
-
-                assertTrue(
-                        String.format("# of bytes %d too small for # of packets %d",
-                                bucket.getRxBytes(), bucket.getRxPackets()),
-                        bucket.getRxBytes() >= bucket.getRxPackets());
-                assertTrue(
-                        String.format("# of bytes %d too small for # of packets %d",
-                                bucket.getTxBytes(), bucket.getTxPackets()),
-                        bucket.getTxBytes() >= bucket.getTxPackets());
-            }
-        }
-
-        // TODO Make sure test app's UID actually shows up.
-    }
-
-    private boolean hasWiFiFeature() throws Exception {
-        final String commandOutput = getDevice().executeShellCommand("pm list features");
-        return commandOutput.contains(FEATURE_WIFI);
-    }
-
-    // Currently only verifies that privacy filtering is done properly.
-    static void verifyNetworkStatsServiceDumpProto(NetworkStatsServiceDumpProto dump, final int filterLevel) throws Exception {
-        if (filterLevel == PRIVACY_AUTO) {
-            for (NetworkInterfaceProto nip : dump.getActiveInterfacesList()) {
-                verifyNetworkInterfaceProto(nip, filterLevel);
-            }
-            for (NetworkInterfaceProto nip : dump.getActiveUidInterfacesList()) {
-                verifyNetworkInterfaceProto(nip, filterLevel);
-            }
-        }
-    }
-
-    private static void verifyNetworkInterfaceProto(NetworkInterfaceProto nip, final int filterLevel) throws Exception {
-        for (NetworkIdentityProto ni : nip.getIdentities().getIdentitiesList()) {
-            if (filterLevel == PRIVACY_AUTO) {
-                assertTrue(ni.getSubscriberId().isEmpty());
-                assertTrue(ni.getNetworkId().isEmpty());
-            }
-        }
-    }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
index 9c9d11e..6ea48fe 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
@@ -23,9 +23,9 @@
 
 /** Test for "dumpsys package --proto" */
 public class PackageIncidentTest extends ProtoDumpTestCase {
-    // Use the test apk from the NetstatsIncidentTest
-    private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
-    private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
+    // Use the test apk from the BatteryStatsIncidentTest
+    private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk";
+    private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.device.batterystats";
 
     @Override
     protected void tearDown() throws Exception {
diff --git a/hostsidetests/incrementalinstall/Android.bp b/hostsidetests/incrementalinstall/Android.bp
index fbc9dba..64c15aa 100644
--- a/hostsidetests/incrementalinstall/Android.bp
+++ b/hostsidetests/incrementalinstall/Android.bp
@@ -27,5 +27,11 @@
     test_suites: [
         "cts",
     ],
-    data: [":IncrementalTestAppRule"],
+    data: [
+        ":IncrementalTestAppDynamicAsset",
+        ":IncrementalTestAppDynamicCode",
+        ":IncrementalTestAppCompressedNativeLib",
+        ":IncrementalTestAppUncompressedNativeLib",
+    ],
+
 }
diff --git a/hostsidetests/incrementalinstall/app/Android.bp b/hostsidetests/incrementalinstall/app/Android.bp
index 132ea73..483011e 100644
--- a/hostsidetests/incrementalinstall/app/Android.bp
+++ b/hostsidetests/incrementalinstall/app/Android.bp
@@ -80,29 +80,3 @@
     aapt_include_all_resources: true,
     manifest: "AndroidManifestV2.xml",
 }
-
-genrule {
-  // copy apks and id sig to gendir.
-  name: "IncrementalTestAppRule",
-  srcs: [":IncrementalTestApp",
-         ":IncrementalTestApp2_v1",
-         ":IncrementalTestApp2_v2",
-         ":IncrementalTestAppDynamicAsset",
-         ":IncrementalTestAppDynamicCode",
-         ":IncrementalTestAppCompressedNativeLib",
-         ":IncrementalTestAppUncompressedNativeLib",],
-  out: ["IncrementalTestApp.apk.idsig",
-        "IncrementalTestApp2_v1.apk.idsig",
-        "IncrementalTestApp2_v2.apk.idsig",
-        "IncrementalTestAppDynamicAsset.apk", "IncrementalTestAppDynamicAsset.apk.idsig",
-        "IncrementalTestAppDynamicCode.apk", "IncrementalTestAppDynamicCode.apk.idsig",
-        "IncrementalTestAppCompressedNativeLib.apk", "IncrementalTestAppCompressedNativeLib.apk.idsig",
-        "IncrementalTestAppUncompressedNativeLib.apk", "IncrementalTestAppUncompressedNativeLib.apk.idsig",],
-  cmd: "cp $(locations :IncrementalTestApp) $(genDir)" +
-       " && cp $(locations :IncrementalTestApp2_v1) $(genDir)" +
-       " && cp $(locations :IncrementalTestApp2_v2) $(genDir)" +
-       " && cp $(locations :IncrementalTestAppDynamicAsset) $(genDir)" +
-       " && cp $(locations :IncrementalTestAppDynamicCode) $(genDir)" +
-       " && cp $(locations :IncrementalTestAppCompressedNativeLib) $(genDir)" +
-       " && cp $(locations :IncrementalTestAppUncompressedNativeLib) $(genDir)",
-  }
\ No newline at end of file
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
index a820ae5..5aafdf0 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -25,4 +25,5 @@
     String getRestrictBackgroundStatus();
     void sendNotification(int notificationId, String notificationType);
     void registerNetworkCallback(in INetworkCallback cb);
+    void unregisterNetworkCallback();
 }
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
index 740ec26..2048bab 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
@@ -17,9 +17,11 @@
 package com.android.cts.net.hostside;
 
 import android.net.Network;
+import android.net.NetworkCapabilities;
 
 interface INetworkCallback {
     void onBlockedStatusChanged(in Network network, boolean blocked);
     void onAvailable(in Network network);
     void onLost(in Network network);
+    void onCapabilitiesChanged(in Network network, in NetworkCapabilities cap);
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 51bdf8e..5fe4573 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -50,7 +50,7 @@
     public final void tearDown() throws Exception {
         super.tearDown();
 
-        turnBatteryOff();
+        executeSilentShellCommand("cmd battery reset");
         setAppIdle(false);
     }
 
@@ -131,11 +131,11 @@
     @RequiredProperties({BATTERY_SAVER_MODE})
     @Test
     public void testAppIdleNetworkAccess_whenCharging() throws Exception {
-        // Check that idle app doesn't get network when charging
+        // Check that app is paroled when charging
         setAppIdle(true);
         assertBackgroundNetworkAccess(false);
         turnBatteryOff();
-        assertBackgroundNetworkAccess(false);
+        assertBackgroundNetworkAccess(true);
         turnBatteryOn();
         assertBackgroundNetworkAccess(false);
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 1db0417..2072db3 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -700,6 +700,10 @@
         mServiceClient.registerNetworkCallback(cb);
     }
 
+    protected void unregisterNetworkCallback() throws Exception {
+        mServiceClient.unregisterNetworkCallback();
+    }
+
     /**
      * Registers a {@link NotificationListenerService} implementation that will execute the
      * notification actions right after the notification is sent.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 3ee7b99..6546e26 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -100,4 +100,8 @@
     public void registerNetworkCallback(INetworkCallback cb) throws RemoteException {
         mService.registerNetworkCallback(cb);
     }
+
+    public void unregisterNetworkCallback() throws RemoteException {
+        mService.unregisterNetworkCallback();
+    }
 }
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index ed397b9..ec884d0 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,18 +16,23 @@
 
 package com.android.cts.net.hostside;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
 import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
 import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.net.Network;
+import android.net.NetworkCapabilities;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Objects;
@@ -35,15 +40,18 @@
 import java.util.concurrent.TimeUnit;
 
 public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
-
     private Network mNetwork;
     private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+    @Rule
+    public final MeterednessConfigurationRule mMeterednessConfiguration
+            = new MeterednessConfigurationRule();
 
     enum CallbackState {
         NONE,
         AVAILABLE,
         LOST,
-        BLOCKED_STATUS
+        BLOCKED_STATUS,
+        CAPABILITIES
     }
 
     private static class CallbackInfo {
@@ -75,7 +83,7 @@
     }
 
     private class TestNetworkCallback extends INetworkCallback.Stub {
-        private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+        private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
 
         private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
 
@@ -117,12 +125,21 @@
             setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
         }
 
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+            setLastCallback(CallbackState.CAPABILITIES, network, cap);
+        }
+
         public void expectLostCallback(Network expectedNetwork) {
             expectCallback(CallbackState.LOST, expectedNetwork, null);
         }
 
-        public void expectAvailableCallback(Network expectedNetwork) {
-            expectCallback(CallbackState.AVAILABLE, expectedNetwork, null);
+        public Network expectAvailableCallbackAndGetNetwork() {
+            final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+            if (cb.state != CallbackState.AVAILABLE) {
+                fail("Network is not available");
+            }
+            return cb.network;
         }
 
         public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
@@ -130,15 +147,28 @@
                     expectBlocked);
         }
 
-        void assertNoCallback() {
-            CallbackInfo cb = null;
-            try {
-                cb = mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
-                // Expected.
-            }
-            if (cb != null) {
-                assertNull("Unexpected callback: " + cb, cb);
+        public void waitBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+            final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
+            do {
+                final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
+                if (cb.state == CallbackState.BLOCKED_STATUS) {
+                    assertEquals(expectBlocked, (Boolean) cb.arg);
+                    return;
+                }
+            } while (System.currentTimeMillis() <= deadline);
+            fail("Didn't receive onBlockedStatusChanged()");
+        }
+
+        public void expectCapabilitiesCallback(Network expectedNetwork, boolean hasCapability,
+                int capability) {
+            final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+            final NetworkCapabilities cap = (NetworkCapabilities) cb.arg;
+            assertEquals(expectedNetwork, cb.network);
+            assertEquals(CallbackState.CAPABILITIES, cb.state);
+            if (hasCapability) {
+                assertTrue(cap.hasCapability(capability));
+            } else {
+                assertFalse(cap.hasCapability(capability));
             }
         }
     }
@@ -147,13 +177,28 @@
     public void setUp() throws Exception {
         super.setUp();
 
-        mNetwork = mCm.getActiveNetwork();
-
         registerBroadcastReceiver();
 
         removeRestrictBackgroundWhitelist(mUid);
         removeRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(0);
+
+        // Initial state
+        setBatterySaverMode(false);
+        setRestrictBackground(false);
+
+        // Make wifi a metered network.
+        mMeterednessConfiguration.configureNetworkMeteredness(true);
+
+        // Register callback
+        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+        // Once the wifi is marked as metered, the wifi will reconnect. Wait for onAvailable()
+        // callback to ensure wifi is connected before the test and store the default network.
+        mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
+        // Check that the network is metered.
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, false /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
     }
 
     @After
@@ -162,102 +207,80 @@
 
         setRestrictBackground(false);
         setBatterySaverMode(false);
+        unregisterNetworkCallback();
     }
 
     @RequiredProperties({DATA_SAVER_MODE})
     @Test
     public void testOnBlockedStatusChanged_dataSaver() throws Exception {
-        // Initial state
-        setBatterySaverMode(false);
-        setRestrictBackground(false);
-
-        final MeterednessConfigurationRule meterednessConfiguration
-                = new MeterednessConfigurationRule();
-        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            // Register callback
-            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-            mTestNetworkCallback.expectAvailableCallback(mNetwork);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
             // Enable restrict background
             setRestrictBackground(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Add to whitelist
             addRestrictBackgroundWhitelist(mUid);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
 
             // Remove from whitelist
             removeRestrictBackgroundWhitelist(mUid);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
 
         // Set to non-metered network
-        meterednessConfiguration.configureNetworkMeteredness(false);
+        mMeterednessConfiguration.configureNetworkMeteredness(false);
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
         try {
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
 
             // Disable restrict background, should not trigger callback
             setRestrictBackground(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.assertNoCallback();
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
     @RequiredProperties({BATTERY_SAVER_MODE})
     @Test
     public void testOnBlockedStatusChanged_powerSaver() throws Exception {
-        // Set initial state.
-        setBatterySaverMode(false);
-        setRestrictBackground(false);
-
-        final MeterednessConfigurationRule meterednessConfiguration
-                = new MeterednessConfigurationRule();
-        meterednessConfiguration.configureNetworkMeteredness(true);
         try {
-            // Register callback
-            registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
-            mTestNetworkCallback.expectAvailableCallback(mNetwork);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
             // Enable Power Saver
             setBatterySaverMode(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Disable Power Saver
             setBatterySaverMode(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
 
         // Set to non-metered network
-        meterednessConfiguration.configureNetworkMeteredness(false);
+        mMeterednessConfiguration.configureNetworkMeteredness(false);
+        mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+                NET_CAPABILITY_NOT_METERED);
         try {
-            mTestNetworkCallback.assertNoCallback();
-
             // Enable Power Saver
             setBatterySaverMode(true);
             assertBackgroundNetworkAccess(false);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
 
             // Disable Power Saver
             setBatterySaverMode(false);
             assertBackgroundNetworkAccess(true);
-            mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+            mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
         } finally {
-            meterednessConfiguration.resetNetworkMeteredness();
+            mMeterednessConfiguration.resetNetworkMeteredness();
         }
     }
 
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
index ec536af..590e17e 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -127,6 +127,16 @@
                         unregisterNetworkCallback();
                     }
                 }
+
+                @Override
+                public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+                    try {
+                        cb.onCapabilitiesChanged(network, cap);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Cannot send onCapabilitiesChanged: " + e);
+                        unregisterNetworkCallback();
+                    }
+                }
             };
             mCm.registerNetworkCallback(makeWifiNetworkRequest(), mNetworkCallback);
             try {
@@ -135,17 +145,21 @@
                 unregisterNetworkCallback();
             }
         }
-      };
 
-    private void unregisterNetworkCallback() {
-        Log.d(TAG, "unregistering network callback");
-        mCm.unregisterNetworkCallback(mNetworkCallback);
-        mNetworkCallback = null;
-    }
+        @Override
+        public void unregisterNetworkCallback() {
+            Log.d(TAG, "unregistering network callback");
+            if (mNetworkCallback != null) {
+                mCm.unregisterNetworkCallback(mNetworkCallback);
+                mNetworkCallback = null;
+            }
+        }
+      };
 
     private NetworkRequest makeWifiNetworkRequest() {
         return new NetworkRequest.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                 .build();
     }
 
diff --git a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
index dfce7da..b0facec 100644
--- a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
+++ b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
@@ -24,6 +24,9 @@
 public class NetworkPolicyTestsPreparer implements ITargetPreparer {
     private ITestDevice mDevice;
     private String mOriginalAppStandbyEnabled;
+    private String mOriginalBatteryStatsConstants;
+    private final static String KEY_STABLE_CHARGING_DELAY_MS = "battery_charged_delay_ms";
+    private final static int DESIRED_STABLE_CHARGING_DELAY_MS = 0;
 
     @Override
     public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
@@ -31,12 +34,18 @@
         mOriginalAppStandbyEnabled = getAppStandbyEnabled();
         setAppStandbyEnabled("1");
         LogUtil.CLog.d("Original app_standby_enabled: " + mOriginalAppStandbyEnabled);
+
+        mOriginalBatteryStatsConstants = getBatteryStatsConstants();
+        setBatteryStatsConstants(
+                KEY_STABLE_CHARGING_DELAY_MS + "=" + DESIRED_STABLE_CHARGING_DELAY_MS);
+        LogUtil.CLog.d("Original battery_saver_constants: " + mOriginalBatteryStatsConstants);
     }
 
     @Override
     public void tearDown(TestInformation testInformation, Throwable e)
             throws DeviceNotAvailableException {
         setAppStandbyEnabled(mOriginalAppStandbyEnabled);
+        setBatteryStatsConstants(mOriginalBatteryStatsConstants);
     }
 
     private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
@@ -51,6 +60,15 @@
         return executeCmd("settings get global app_standby_enabled").trim();
     }
 
+    private void setBatteryStatsConstants(String batteryStatsConstants)
+            throws DeviceNotAvailableException {
+        executeCmd("settings put global battery_stats_constants \"" + batteryStatsConstants + "\"");
+    }
+
+    private String getBatteryStatsConstants() throws DeviceNotAvailableException {
+        return executeCmd("settings get global battery_stats_constants");
+    }
+
     private String executeCmd(String cmd) throws DeviceNotAvailableException {
         final String output = mDevice.executeShellCommand(cmd).trim();
         LogUtil.CLog.d("Output for '%s': %s", cmd, output);
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 9f8ec2f..9e68c8c 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -306,20 +306,44 @@
     }
 
     @Test
+    public void testAbandonStagedApkBeforeReady_CommitAndAbandon() throws Exception {
+        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        abandonSession(sessionId);
+        assertThat(getStagedSessionInfo(sessionId)).isNull();
+    }
+
+    @Test
+    public void testAbandonStagedApkBeforeReady_VerifyPostReboot() throws Exception {
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+    }
+
+    @Test
+    public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception {
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
+        abandonSession(sessionId);
+        stageSingleApk(TestApp.Apex2).assertSuccessful();
+    }
+
+    @Test
+    public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
+        assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+        // Using an apex in hopes that pre-reboot verification will take longer to complete
+        // and we will manage to abandon it before session becomes ready.
+        int sessionId = stageMultipleApks(TestApp.A1, TestApp.Apex2).assertSuccessful()
+                .getSessionId();
+        abandonSession(sessionId);
+        assertThat(getStagedSessionInfo(sessionId)).isNull();
+        assertNoSessionUpdatedBroadcastSent();
+    }
+
+    @Test
     public void testGetActiveStagedSessions() throws Exception {
         PackageInstaller packageInstaller = getPackageInstaller();
         int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(firstSessionId);
         int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(secondSessionId);
         List<Integer> stagedSessionIds = packageInstaller.getActiveStagedSessions()
                 .stream().map(s -> s.getSessionId()).collect(Collectors.toList());
         assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId);
@@ -341,11 +365,6 @@
     public void testIsStagedSessionActive() throws Exception {
         PackageInstaller packageInstaller = getPackageInstaller();
         int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(sessionId);
 
         List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions();
         boolean activeStagedSessionFound = false;
@@ -377,17 +396,8 @@
     public void testGetActiveStagedSessions_MultiApkSession() throws Exception {
         int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
                 .assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(firstSessionId);
         int secondSessionId = stageMultipleApks(TestApp.C1)
                 .assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        waitForIsReadyBroadcast(secondSessionId);
         List<Integer> stagedSessionIds = getPackageInstaller().getActiveStagedSessions()
                 .stream().map(s -> s.getSessionId()).collect(Collectors.toList());
         assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId);
@@ -873,59 +883,33 @@
     // Should fail to stage multiple sessions when check-point is not available
     @Test
     public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception {
-        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        stageSingleApk(TestApp.A1).assertSuccessful();
         StageSessionResult failedSessionResult = stageSingleApk(TestApp.B1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "Cannot stage multiple sessions without checkpoint support");
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(sessionId);
     }
 
     @Test
     public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception {
-        int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+        stageSingleApk(TestApp.A1).assertSuccessful();
         StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "has been staged already by session");
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(sessionId);
     }
 
     @Test
     public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()
             throws Exception {
-        int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful()
-                .getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(firstSessionId);
-        int secondSessionId = stageSingleApk(TestApp.C1).assertSuccessful().getSessionId();
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(secondSessionId);
+        stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful();
+        stageSingleApk(TestApp.C1).assertSuccessful();
     }
 
     @Test
     public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception {
-        int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful().getSessionId();
+        stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful();
         StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A2, TestApp.C1);
         assertThat(failedSessionResult.getErrorMessage()).contains(
                 "has been staged already by session");
-        // Currently abandoning a session before pre-reboot verification finishes might result in
-        // a system_server crash. Before that issue is resolved we need to manually wait for
-        // pre-reboot verification to finish before abandoning sessions.
-        // TODO(b/145925842): remove following two lines after fixing the bug.
-        waitForIsReadyBroadcast(sessionId);
     }
 
     // Should succeed in installing multiple staged sessions together
@@ -1173,7 +1157,8 @@
         // Commit the session (this will start the installation workflow).
         Log.i(TAG, "Committing session for apk: " + testApp);
         commitSession(sessionId);
-        return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
+        return new StageSessionResult(sessionId,
+                LocalIntentSender.getIntentSenderResult(sessionId));
     }
 
     private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception {
@@ -1368,6 +1353,13 @@
         assertThat(info).isNull();
     }
 
+    private void assertNoSessionUpdatedBroadcastSent() throws Exception {
+        PackageInstaller.SessionInfo info =
+                SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(10,
+                        TimeUnit.SECONDS);
+        assertThat(info).isNull();
+    }
+
     @Test
     public void isCheckpointSupported() {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index d5b1a8e..2369a2d 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -125,6 +125,25 @@
 
     @Test
     @LargeTest
+    public void testAbandonStagedApkBeforeReady() throws Exception {
+        runPhase("testAbandonStagedApkBeforeReady_CommitAndAbandon");
+        getDevice().reboot();
+        runPhase("testAbandonStagedApkBeforeReady_VerifyPostReboot");
+    }
+
+    @Test
+    public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception {
+        runPhase("testStageAnotherSessionImmediatelyAfterAbandon");
+    }
+
+    @Test
+    public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
+        assumeTrue(isUpdatingApexSupported());
+        runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon");
+    }
+
+    @Test
+    @LargeTest
     public void testInstallMultipleStagedApks() throws Exception {
         assumeSystemUser();
 
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 9742ee3..25727f1 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -62,6 +62,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -85,6 +86,7 @@
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
 
 public class AtomTests {
     private static final String TAG = AtomTests.class.getSimpleName();
@@ -226,7 +228,68 @@
         performBleScan(scanSettings, Arrays.asList(scanFilter.build()), true);
     }
 
-    private static void performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters, boolean waitForResult) {
+    @Test
+    public void testBleScanInterrupted() throws Exception {
+        performBleAction((bluetoothAdapter, bleScanner) -> {
+            ScanSettings scanSettings = new ScanSettings.Builder()
+                    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+            ScanCallback scanCallback = new ScanCallback() {
+                @Override
+                public void onScanResult(int callbackType, ScanResult result) {
+                    Log.v(TAG, "called onScanResult");
+                }
+                @Override
+                public void onScanFailed(int errorCode) {
+                    Log.v(TAG, "called onScanFailed");
+                }
+                @Override
+                public void onBatchScanResults(List<ScanResult> results) {
+                    Log.v(TAG, "called onBatchScanResults");
+                }
+            };
+
+            int uid = Process.myUid();
+            int whatAtomId = 9_999;
+
+            // Change state to State.ON.
+            bleScanner.startScan(null, scanSettings, scanCallback);
+            sleep(500);
+            writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+            writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+            bluetoothAdapter.disable();
+            sleep(500);
+
+            // Trigger State.RESET so that new state is State.OFF.
+            if (!bluetoothAdapter.enable()) {
+                Log.e(TAG, "Could not enable bluetooth to trigger state reset");
+                return;
+            }
+            sleep(2_000); // Wait for Bluetooth to fully turn on.
+            writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+            writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+            writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+        });
+    }
+
+    private static void writeSliceByBleScanStateChangedAtom(int atomId, int firstUid,
+                                                            boolean field2, boolean field3,
+                                                            boolean field4) {
+        final StatsEvent.Builder builder = StatsEvent.newBuilder()
+                .setAtomId(atomId)
+                .writeAttributionChain(new int[] {firstUid}, new String[] {"tag1"})
+                .writeBoolean(field2)
+                .writeBoolean(field3)
+                .writeBoolean(field4)
+                .usePooledBuffer();
+
+        StatsLog.write(builder.build());
+    }
+
+    /**
+     * Set up BluetoothLeScanner and perform the action in the callback.
+     * Restore Bluetooth to original state afterwards.
+     **/
+    private static void performBleAction(BiConsumer<BluetoothAdapter, BluetoothLeScanner> actions) {
         BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         if (bluetoothAdapter == null) {
             Log.e(TAG, "Device does not support Bluetooth");
@@ -238,7 +301,7 @@
                 Log.e(TAG, "Bluetooth is not enabled");
                 return;
             }
-            sleep(8_000);
+            sleep(2_000); // Wait for Bluetooth to fully turn on.
             bluetoothEnabledByTest = true;
         }
         BluetoothLeScanner bleScanner = bluetoothAdapter.getBluetoothLeScanner();
@@ -247,38 +310,45 @@
             return;
         }
 
-        CountDownLatch resultsLatch = new CountDownLatch(1);
-        ScanCallback scanCallback = new ScanCallback() {
-            @Override
-            public void onScanResult(int callbackType, ScanResult result) {
-                Log.v(TAG, "called onScanResult");
-                resultsLatch.countDown();
-            }
-            @Override
-            public void onScanFailed(int errorCode) {
-                Log.v(TAG, "called onScanFailed");
-            }
-            @Override
-            public void onBatchScanResults(List<ScanResult> results) {
-                Log.v(TAG, "called onBatchScanResults");
-                resultsLatch.countDown();
-            }
-        };
+        actions.accept(bluetoothAdapter, bleScanner);
 
-        bleScanner.startScan(scanFilters, scanSettings, scanCallback);
-        if (waitForResult) {
-            waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
-        } else {
-            sleep(2_000);
-        }
-        bleScanner.stopScan(scanCallback);
-
-        // Restore adapter state at end of test
+        // Restore adapter state
         if (bluetoothEnabledByTest) {
             bluetoothAdapter.disable();
         }
     }
 
+
+    private static void performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters, boolean waitForResult) {
+        performBleAction((bluetoothAdapter, bleScanner) -> {
+            CountDownLatch resultsLatch = new CountDownLatch(1);
+            ScanCallback scanCallback = new ScanCallback() {
+                @Override
+                public void onScanResult(int callbackType, ScanResult result) {
+                    Log.v(TAG, "called onScanResult");
+                    resultsLatch.countDown();
+                }
+                @Override
+                public void onScanFailed(int errorCode) {
+                    Log.v(TAG, "called onScanFailed");
+                }
+                @Override
+                public void onBatchScanResults(List<ScanResult> results) {
+                    Log.v(TAG, "called onBatchScanResults");
+                    resultsLatch.countDown();
+                }
+            };
+
+            bleScanner.startScan(scanFilters, scanSettings, scanCallback);
+            if (waitForResult) {
+                waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
+            } else {
+                sleep(2_000);
+            }
+            bleScanner.stopScan(scanCallback);
+        });
+    }
+
     @Test
     public void testCameraState() throws Exception {
         Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index b455c36..fb08d2d 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -131,7 +131,7 @@
         Thread.sleep(WAIT_TIME_SHORT);
 
         executeBackgroundService(ACTION_LMK);
-        Thread.sleep(5_000);
+        Thread.sleep(15_000);
 
         // Sorted list of events in order in which they occurred.
         List<EventMetricData> data = getEventMetricDataList();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 4eeb74c..aaf94af 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -21,8 +21,12 @@
 import android.cts.statsd.atom.DeviceAtomTestCase;
 
 import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.Position;
 import com.android.os.AtomsProto.Atom;
 import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.os.AtomsProto.AttributionNode;
+import com.android.os.AtomsProto.BleScanStateChanged;
 import com.android.os.StatsLog;
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
@@ -296,7 +300,7 @@
         assertThat(bucketInfo.getCount()).isEqualTo(1);
         assertWithMessage("First report's bucket should be less than 1 day")
                 .that(bucketInfo.getEndBucketElapsedNanos())
-                .isLessThan(bucketInfo.getStartBucketElapsedNanos() + 
+                .isLessThan(bucketInfo.getStartBucketElapsedNanos() +
                         1_000_000_000L * 60L * 60L * 24L);
 
         //Second report should have a count of 2.
@@ -307,4 +311,118 @@
         }
         assertThat(totalCount).isEqualTo(2);
     }
+
+    public void testSlicedStateCountMetric() throws Exception {
+        if (statsdDisabled()) {
+            return;
+        }
+        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
+
+        int whatMatcherId = 3;
+        int stateId = 4;
+
+        // Atom 9999 {
+        //     repeated AttributionNode attribution_node = 1;
+        //     optional bool is_filtered = 2;
+        //     optional bool is_first_match = 3;
+        //     optional bool is_opportunistic = 4;
+        // }
+        int whatAtomId = 9_999;
+
+        StatsdConfigProto.AtomMatcher whatMatcher =
+                MetricsUtils.getAtomMatcher(whatAtomId)
+                        .setId(whatMatcherId)
+                        .build();
+
+        StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder()
+            .setId(stateId)
+            .setAtomId(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER)
+            .build();
+
+        StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder()
+            .setStateAtomId(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER)
+            .setFieldsInWhat(FieldMatcher.newBuilder()
+                    .setField(whatAtomId)
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(1)
+                            .setPosition(Position.FIRST)
+                            .addChild(FieldMatcher.newBuilder()
+                                    .setField(AttributionNode.UID_FIELD_NUMBER)
+                            )
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(2)
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(3)
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(4)
+                    )
+            )
+            .setFieldsInState(FieldMatcher.newBuilder()
+                    .setField(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER)
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(BleScanStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
+                            .setPosition(Position.FIRST)
+                            .addChild(FieldMatcher.newBuilder()
+                                    .setField(AttributionNode.UID_FIELD_NUMBER)
+                            )
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(BleScanStateChanged.IS_FILTERED_FIELD_NUMBER)
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(BleScanStateChanged.IS_FIRST_MATCH_FIELD_NUMBER)
+                    )
+                    .addChild(FieldMatcher.newBuilder()
+                            .setField(BleScanStateChanged.IS_OPPORTUNISTIC_FIELD_NUMBER)
+                    )
+            )
+            .build();
+
+        StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+                .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
+                    .setId(MetricsUtils.COUNT_METRIC_ID)
+                    .setBucket(StatsdConfigProto.TimeUnit.CTS)
+                    .setWhat(whatMatcherId)
+                    .addSliceByState(stateId)
+                    .addStateLink(stateLink)
+                )
+                .addAtomMatcher(whatMatcher)
+                .addState(state);
+        uploadConfig(builder);
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBleScanInterrupted");
+
+        StatsLogReport metricReport = getStatsLogReport();
+        LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
+        assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+        assertThat(metricReport.hasCountMetrics()).isTrue();
+
+        StatsLogReport.CountMetricDataWrapper dataWrapper = metricReport.getCountMetrics();
+        assertThat(dataWrapper.getDataCount()).isEqualTo(2);
+
+        CountMetricData data = dataWrapper.getData(0);
+        assertThat(data.getSliceByStateCount()).isEqualTo(1);
+        assertThat(data.getSliceByState(0).getAtomId())
+                .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER);
+        assertThat(data.getSliceByState(0).getValue())
+                .isEqualTo(BleScanStateChanged.State.OFF.ordinal());
+        long totalCount = data.getBucketInfoList().stream()
+                .mapToLong(CountBucketInfo::getCount)
+                .sum();
+        assertThat(totalCount).isEqualTo(3);
+
+        data = dataWrapper.getData(1);
+        assertThat(data.getSliceByStateCount()).isEqualTo(1);
+        assertThat(data.getSliceByState(0).getAtomId())
+                .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER);
+        assertThat(data.getSliceByState(0).getValue())
+                .isEqualTo(BleScanStateChanged.State.ON.ordinal());
+        totalCount = data.getBucketInfoList().stream()
+                .mapToLong(CountBucketInfo::getCount)
+                .sum();
+        assertThat(totalCount).isEqualTo(2);
+    }
 }
diff --git a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
index 31401ca..ba980fb 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
@@ -159,6 +159,12 @@
         while (output.length > startIndex) {
             assertThat(output.length).isAtLeast(startIndex + sizetBytes);
             int dataLength = readSizetFromByteArray(output, startIndex);
+            if (dataLength == 0) {
+                // We have received a heartbeat from statsd. This heartbeat isn't accompanied by any
+                // atoms so return to top of while loop.
+                startIndex += sizetBytes;
+                continue;
+            }
             assertThat(output.length).isAtLeast(startIndex + sizetBytes + dataLength);
 
             ShellDataProto.ShellData data = null;
diff --git a/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
index cf2abe8..4560ab1 100644
--- a/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
+++ b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
@@ -63,6 +63,21 @@
         return intent;
     }
 
+    /**
+     * Returns an Intent that targets the given {@code sessionId}, while discarding others.
+     */
+    public static Intent getIntentSenderResult(int sessionId) throws InterruptedException {
+        while (true) {
+            Intent intent = sIntentSenderResults.take();
+            if (intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) == sessionId) {
+                Log.i(TAG, "Taking intent " + prettyPrint(intent));
+                return intent;
+            } else {
+                Log.i(TAG, "Discarding intent " + prettyPrint(intent));
+            }
+        }
+    }
+
     private static String prettyPrint(Intent intent) {
         int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
         int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index eaea69b..faeb8f9 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -497,6 +497,9 @@
 
             if (enable) {
                 SystemUtil.runShellCommand("svc wifi enable");
+                //noinspection deprecation
+                SystemUtil.runWithShellPermissionIdentity(wm::reconnect,
+                        android.Manifest.permission.NETWORK_SETTINGS);
             } else {
                 SystemUtil.runShellCommand("svc wifi disable");
             }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index c3220ad..cfb04b4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -17,7 +17,6 @@
 import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
 
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.accessibilityservice.AccessibilityService;
@@ -31,7 +30,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.text.TextUtils;
@@ -93,7 +91,12 @@
         ActivityLauncher activityLauncher = new ActivityLauncher() {
             @Override
             Activity launchActivity() {
-                return instrumentation.startActivitySync(intent, options.toBundle());
+                uiAutomation.adoptShellPermissionIdentity();
+                try {
+                    return instrumentation.startActivitySync(intent, options.toBundle());
+                } finally {
+                    uiAutomation.dropShellPermissionIdentity();
+                }
             }
         };
         return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index 6b11ba6..c49e89a 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -1145,6 +1145,7 @@
     private static class Monitor {
         static final String WAIT_FOR_EARLY_ANR = "Waiting after early ANR...  available commands:";
         static final String WAIT_FOR_ANR = "Waiting after ANR...  available commands:";
+        static final String WAIT_FOR_CRASHED = "Waiting after crash...  available commands:";
         static final String CMD_CONTINUE = "c";
         static final String CMD_KILL = "k";
 
@@ -1194,6 +1195,13 @@
                     String line = mPendingLines.remove(0);
                     if (TextUtils.equals(line, expected)) {
                         break;
+                    } else if (TextUtils.equals(line, WAIT_FOR_EARLY_ANR)
+                            || TextUtils.equals(line, WAIT_FOR_ANR)
+                            || TextUtils.equals(line, WAIT_FOR_CRASHED)) {
+                        // If we are getting any of the unexpected state,
+                        // for example, get a crash while waiting for an ANR,
+                        // it could be from another unrelated process, kill it directly.
+                        sendCommand(CMD_KILL);
                     }
                 }
             }
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index 25ec9a3..1d443f6 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -34,6 +34,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.time.LocalTime;
 
 public class UiModeManagerTest extends AndroidTestCase {
     private static final String TAG = "UiModeManagerTest";
@@ -84,6 +85,28 @@
         }
     }
 
+    public void testSetAndGetCustomTimeStart() {
+        LocalTime time = mUiModeManager.getCustomNightModeStart();
+        // decrease time
+        LocalTime timeNew = LocalTime.of(
+                (time.getHour() + 1) % 12,
+                (time.getMinute() + 30) % 60);
+        setStartTime(timeNew);
+        assertNotSame(time, timeNew);
+        assertEquals(timeNew, mUiModeManager.getCustomNightModeStart());
+    }
+
+    public void testSetAndGetCustomTimeEnd() {
+        LocalTime time = mUiModeManager.getCustomNightModeEnd();
+        // decrease time
+        LocalTime timeNew = LocalTime.of(
+                (time.getHour() + 1) % 12,
+                (time.getMinute() + 30) % 60);
+        setEndTime(timeNew);
+        assertNotSame(time, timeNew);
+        assertEquals(timeNew, mUiModeManager.getCustomNightModeEnd());
+    }
+
     public void testNightModeYesPersisted() throws InterruptedException {
         // Reset the mode to no if it is set to another value
         setNightMode(UiModeManager.MODE_NIGHT_NO);
@@ -327,7 +350,6 @@
     }
 
     private void setNightMode(int mode) {
-        final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         String modeString = "unknown";
         switch (mode) {
             case UiModeManager.MODE_NIGHT_AUTO:
@@ -341,6 +363,21 @@
                 break;
         }
         final String command = " cmd uimode night " + modeString;
+        applyCommand(command);
+    }
+
+    private void setStartTime(LocalTime t) {
+        final String command = " cmd uimode time start " + t.toString();
+        applyCommand(command);
+    }
+
+    private void setEndTime(LocalTime t) {
+        final String command = " cmd uimode time end " + t.toString();
+        applyCommand(command);
+    }
+
+    private void applyCommand(String command) {
+        final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
             Assert.assertNotNull("Failed to execute shell command: " + command, fd);
             // Wait for the command to finish by reading until EOF
@@ -356,4 +393,5 @@
             uiAutomation.destroy();
         }
     }
+
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 65df6fb..5b1a86e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -210,7 +210,6 @@
             }
         };
 
-        @ClassRule
         public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule(
                 InstrumentationRegistry.getTargetContext(),
                 InstrumentationRegistry.getInstrumentation().getUiAutomation(),
@@ -248,6 +247,9 @@
                 // test being ran and finishes dangling activities at the end
                 .around(mTestWatcher)
                 //
+                // sMockImeSessionRule make sure MockImeSession.create() is used to launch mock IME
+                .around(sMockImeSessionRule)
+                //
                 // mLoggingRule wraps the test but doesn't interfere with it
                 .around(mLoggingRule)
                 //
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 19223b0..5fe5d2f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -56,8 +56,10 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -79,6 +81,7 @@
     private Handler mHandler;
     private BlockingStateCallback mCameraListener;
     private CameraErrorCollector mCollector;
+    private Set<Set<String>> mConcurrentCameraIdCombinations;
 
     /** Load validation jni on initialization. */
     static {
@@ -106,6 +109,8 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mCollector = new CameraErrorCollector();
+        mConcurrentCameraIdCombinations =
+                CameraTestUtils.getConcurrentCameraIds(mCameraManager, mAdoptShellPerm);
     }
 
     @Override
@@ -160,10 +165,12 @@
          * must be matched system features.
          */
         boolean externalCameraConnected = false;
+        Map<String, Integer> lensFacingMap = new HashMap<String, Integer>();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
             assertNotNull("Can't get camera characteristics for camera " + ids[i], props);
             Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING);
+            lensFacingMap.put(ids[i], lensFacing);
             assertNotNull("Can't get lens facing info", lensFacing);
             if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
                 assertTrue("System doesn't have front camera feature",
@@ -198,6 +205,38 @@
             || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
             || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)
             || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
+
+        boolean frontBackAdvertised =
+                mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT);
+
+        boolean frontBackCombinationFound = false;
+        // Go through all combinations and see that at least one combination has a front + back
+        // camera.
+        for (Set<String> cameraIdCombination : mConcurrentCameraIdCombinations) {
+            boolean frontFacingFound = false, backFacingFound = false;
+            for (String cameraId : cameraIdCombination) {
+                Integer lensFacing = lensFacingMap.get(cameraId);
+                if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
+                    frontFacingFound = true;
+                } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+                    backFacingFound = true;
+                }
+                if (frontFacingFound && backFacingFound) {
+                    frontBackCombinationFound = true;
+                    break;
+                }
+            }
+            if (frontBackCombinationFound) {
+                break;
+            }
+        }
+
+        if(mCameraIdsUnderTest.length > 0) {
+            assertTrue("System camera feature FEATURE_CAMERA_CONCURRENT = " + frontBackAdvertised +
+                    " and device actually having a front back combination which can operate " +
+                    "concurrently = " + frontBackCombinationFound +  " do not match",
+                    frontBackAdvertised == frontBackCombinationFound);
+        }
     }
 
     // Test: that properties can be queried from each device, without exceptions.
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index b940aae..9425c2d 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -48,6 +48,13 @@
         <option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk"/>
         <option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk"/>
     </target_preparer>
+    <!-- Necessary for Automotive devices - no-op for other Android verticals -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="settings put secure android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER 0" />
+        <option name="teardown-command"
+                value="settings put secure android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER 1" />
+    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="android.server.wm.cts"/>
         <option name="runtime-hint" value="1h"/>
diff --git a/tests/framework/base/windowmanager/backgroundactivity/OWNERS b/tests/framework/base/windowmanager/backgroundactivity/OWNERS
new file mode 100644
index 0000000..fcaa235
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/OWNERS
@@ -0,0 +1,2 @@
+alanstokes@google.com
+brufino@google.com
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index 09c8fc2..19ba5c0 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -454,10 +454,12 @@
         // Press home key to ensure stopAppSwitches is called because the last-stop-app-switch-time
         // is a criteria of allowing background start.
         pressHomeButton();
+        // Waiting for home visible before resuming app switches to make sure the part that sets the
+        // stop-app-switches time from pressHomeButton() doesn't race with resumeAppSwitches()
+        mWmState.waitForHomeActivityVisible();
         // Resume the stopped state (it won't affect last-stop-app-switch-time) so we don't need to
         // wait extra time to prevent the next launch from being delayed.
         resumeAppSwitches();
-        mWmState.waitForHomeActivityVisible();
     }
 
     private void assertTaskStack(ComponentName[] expectedComponents,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
index 1b6aa86..9f71d26 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
@@ -22,10 +22,14 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assume.assumeTrue;
 
 import android.app.DreamManager;
 import android.content.ComponentName;
 import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
 
 import androidx.test.filters.FlakyTest;
 
@@ -79,7 +83,7 @@
         });
     }
 
-    private void assertDreamActivityIsGone() {
+    private void assertDreamActivityGone() {
         mWmState.computeState();
         assertTrue(!mWmState.containsWindow(getWindowName(mDreamActivityName))
                    && !mWmState.containsActivity(mDreamActivityName));
@@ -90,16 +94,18 @@
         setActiveDream(TEST_DREAM_SERVICE);
 
         startDream(TEST_DREAM_SERVICE);
-        mWmState.waitForValidState(mDreamActivityName);
-        mWmState.assertVisibility(mDreamActivityName, true);
-        mWmState.assertHomeActivityVisible(false);
+        waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+                "Dream activity should be the top resumed activity");
+        mWmState.waitForValidState(mWmState.getHomeActivityName());
+        mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
 
         assertTrue(getIsDreaming());
 
         stopDream();
         mWmState.waitAndAssertActivityRemoved(mDreamActivityName);
 
-        mWmState.assertHomeActivityVisible(true);
+        waitAndAssertTopResumedActivity(mWmState.getHomeActivityName(), DEFAULT_DISPLAY,
+                "Home activity should show when dream is stopped");
     }
 
     @Test
@@ -107,14 +113,17 @@
         setActiveDream(TEST_DREAM_SERVICE);
 
         startDream(TEST_DREAM_SERVICE);
-        mWmState.waitForValidState(mDreamActivityName);
+        waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+                "Dream activity should be the top resumed activity");
+        mWmState.waitForValidState(mWmState.getHomeActivityName());
+        mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
         assertTrue(getIsDreaming());
 
         stopDream();
 
         Thread.sleep(ACTIVITY_STOP_TIMEOUT);
 
-        assertDreamActivityIsGone();
+        assertDreamActivityGone();
         assertFalse(getIsDreaming());
     }
 
@@ -123,15 +132,32 @@
         setActiveDream(TEST_STUBBORN_DREAM_SERVICE);
 
         startDream(TEST_STUBBORN_DREAM_SERVICE);
-        mWmState.waitForValidState(mDreamActivityName);
-        mWmState.assertVisibility(mDreamActivityName, true);
-        mWmState.assertHomeActivityVisible(false);
+        waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+                "Dream activity should be the top resumed activity");
+        mWmState.waitForValidState(mWmState.getHomeActivityName());
+        mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
 
         stopDream();
 
         Thread.sleep(ACTIVITY_FORCE_STOP_TIMEOUT);
 
-        assertDreamActivityIsGone();
+        assertDreamActivityGone();
         assertFalse(getIsDreaming());
+        waitAndAssertTopResumedActivity(mWmState.getHomeActivityName(), DEFAULT_DISPLAY,
+                "Home activity should show when dream is stopped");
+    }
+
+    @Test
+    public void testDreamNotFinishAfterRotation() {
+        assumeTrue("Skipping test: no rotation support", supportsRotation());
+
+        final RotationSession rotationSession = createManagedRotationSession();
+        rotationSession.set(Surface.ROTATION_0);
+        setActiveDream(TEST_DREAM_SERVICE);
+        startDream(TEST_DREAM_SERVICE);
+        rotationSession.set(Surface.ROTATION_90);
+
+        waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+                "Dream activity should be the top resumed activity");
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
index c699460..115310f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
@@ -33,9 +33,12 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -52,13 +55,15 @@
 
 import com.android.compatibility.common.util.CtsTouchUtils;
 import com.android.compatibility.common.util.PollingCheck;
+
 import com.android.cts.mockime.ImeEventStream;
 import com.android.cts.mockime.MockImeSession;
 
-import java.util.concurrent.TimeUnit;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:KeyguardLockedTests
@@ -104,8 +109,14 @@
             keyguardLock.disableKeyguard();
 
             lockScreenSession.setLockCredential();
+            lockScreenSession.gotoKeyguard();
+
             mWmState.waitForKeyguardShowingAndNotOccluded();
             mWmState.assertKeyguardShowingAndNotOccluded();
+            assertTrue(mKeyguardManager.isKeyguardLocked());
+            assertTrue(mKeyguardManager.isDeviceLocked());
+            assertTrue(mKeyguardManager.isDeviceSecure());
+            assertTrue(mKeyguardManager.isKeyguardSecure());
         } finally {
             keyguardLock.reenableKeyguard();
         }
@@ -183,6 +194,9 @@
 
     @Test
     public void testDismissKeyguardActivity_method_cancelled() {
+        // Pressing the back button does not cancel Keyguard in AAOS.
+        assumeFalse(isCar());
+
         final LockScreenSession lockScreenSession = createManagedLockScreenSession();
         lockScreenSession.setLockCredential();
         separateTestJournal();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
index 538ae84..474d9d2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
@@ -190,6 +190,12 @@
         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
         mInstrumentation.waitForIdleSync();
 
+        // If we don't support hardware acceleration on the main activity the embedded
+        // view also won't be.
+        if (!mSurfaceView.isHardwareAccelerated()) {
+            return;
+        }
+
         assertTrue(mEmbeddedView.isHardwareAccelerated());
     }
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index a9ecab2..51645f1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
@@ -46,7 +46,6 @@
 import android.content.Intent;
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
@@ -73,7 +72,6 @@
         mUseTaskOrganizer = false;
     }
 
-    @FlakyTest(bugId = 137329632)
     @Test
     public void testResumedWhenRecreatedFromInNonFocusedStack() throws Exception {
         // Launch first activity
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 279f74d..8a6ba7c 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -987,6 +987,10 @@
         return hasDeviceFeature(FEATURE_WATCH);
     }
 
+    protected boolean isCar() {
+        return hasDeviceFeature(FEATURE_AUTOMOTIVE);
+    }
+
     protected boolean isTablet() {
         // Larger than approx 7" tablets
         return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 2d3a344..23e4dd8 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -562,9 +562,7 @@
             CtsTouchUtils.emulateTapOnViewCenter(instrumentation, null, editText);
             TestUtils.waitOnMainUntil(() -> editTextHasWindowFocus.get()
                     && !popupTextHasWindowFocus.get(), TIMEOUT);
-            // Expect there is no "onStartInput" when window focus back to activity's EditText.
-            // Since the EditText still has view focus and served by InputMethodManager.
-            notExpectEvent(stream, editorMatcher("onStartInput", marker), NOT_EXPECT_TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
             expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
         }
     }
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
index 3a57360..b817a2c 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
@@ -21,7 +21,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
 import android.app.Service;
@@ -131,12 +130,6 @@
                 150, 150, pos.x, pos.y,
                 TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE,
                 PixelFormat.OPAQUE);
-        // Currently SOFT_INPUT_STATE_UNSPECIFIED isn't appropriate for CTS test because there is no
-        // clear spec about how it behaves.  In order to make our tests deterministic, currently we
-        // must use SOFT_INPUT_STATE_HIDDEN to make sure soft-keyboard will hide after navigating
-        // forward to next window.
-        // TODO(Bug 77152727): Remove the following code once we define how
-        params.softInputMode = SOFT_INPUT_STATE_HIDDEN;
         wm.addView(editText, params);
         return editText;
     }
diff --git a/tests/media/AndroidManifest.xml b/tests/media/AndroidManifest.xml
index 7cd34c5..3ccd92e 100644
--- a/tests/media/AndroidManifest.xml
+++ b/tests/media/AndroidManifest.xml
@@ -28,6 +28,7 @@
         android:requestLegacyExternalStorage="true"
         android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
+        <activity android:name="android.mediav2.cts.CodecTestActivity" />
     </application>
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="android.mediav2.cts"
diff --git a/tests/media/jni/Android.bp b/tests/media/jni/Android.bp
index 0db7dad..1e13378 100644
--- a/tests/media/jni/Android.bp
+++ b/tests/media/jni/Android.bp
@@ -15,7 +15,7 @@
 cc_test_library {
     name: "libctsmediav2muxer_jni",
     srcs: [
-        "NativeMediaConstants.cpp",
+        "NativeMediaCommon.cpp",
         "NativeMuxerTest.cpp",
         "NativeMuxerUnitTest.cpp",
     ],
@@ -37,7 +37,7 @@
 cc_test_library {
     name: "libctsmediav2extractor_jni",
     srcs: [
-        "NativeMediaConstants.cpp",
+        "NativeMediaCommon.cpp",
         "NativeExtractorTest.cpp",
         "NativeExtractorUnitTest.cpp",
     ],
@@ -59,15 +59,17 @@
 cc_test_library {
     name: "libctsmediav2codec_jni",
     srcs: [
-        "NativeMediaConstants.cpp",
+        "NativeMediaCommon.cpp",
         "NativeCodecDecoderTest.cpp",
         "NativeCodecEncoderTest.cpp",
         "NativeCodecTestBase.cpp",
         "NativeCodecUnitTest.cpp",
+        "NativeCodecEncoderSurfaceTest.cpp",
     ],
     shared_libs: [
         "libmediandk",
         "liblog",
+        "libandroid",
     ],
     include_dirs: [
         "frameworks/av/media/ndk/include/media",
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index fb9c66d..d5eb85b 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -17,7 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NativeCodecDecoderTest"
 #include <log/log.h>
-
+#include <android/native_window_jni.h>
 #include <NdkMediaExtractor.h>
 #include <jni.h>
 #include <sys/stat.h>
@@ -26,7 +26,7 @@
 #include <fstream>
 
 #include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
 
 class CodecDecoderTest final : public CodecTestBase {
   private:
@@ -37,6 +37,7 @@
     AMediaFormat* mInpDecDupFormat;
     std::vector<std::pair<void*, size_t>> mCsdBuffers;
     int mCurrCsdIdx;
+    ANativeWindow* mWindow;
 
     void setUpAudioReference(const char* refFile);
     void deleteReference();
@@ -52,7 +53,7 @@
                         OutputManager* ref, int64_t pts, SeekMode mode);
 
   public:
-    explicit CodecDecoderTest(const char* mime);
+    explicit CodecDecoderTest(const char* mime, ANativeWindow* window);
     ~CodecDecoderTest();
 
     bool testSimpleDecode(const char* decoder, const char* testFile, const char* refFile,
@@ -62,14 +63,15 @@
     bool testSimpleDecodeQueueCSD(const char* decoder, const char* testFile);
 };
 
-CodecDecoderTest::CodecDecoderTest(const char* mime)
+CodecDecoderTest::CodecDecoderTest(const char* mime, ANativeWindow* window)
     : CodecTestBase(mime),
       mRefData(nullptr),
       mRefLength(0),
       mExtractor(nullptr),
       mInpDecFormat(nullptr),
       mInpDecDupFormat(nullptr),
-      mCurrCsdIdx(0) {}
+      mCurrCsdIdx(0),
+      mWindow{window} {}
 
 CodecDecoderTest::~CodecDecoderTest() {
     deleteReference();
@@ -151,7 +153,7 @@
     resetContext(isAsync, signalEOSWithLastFrame);
     CHECK_STATUS(mAsyncHandle.setCallBack(mCodec, isAsync),
                  "AMediaCodec_setAsyncNotifyCallback failed");
-    CHECK_STATUS(AMediaCodec_configure(mCodec, format, nullptr, nullptr,
+    CHECK_STATUS(AMediaCodec_configure(mCodec, format, mWindow, nullptr,
                                        isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
                  "AMediaCodec_configure failed");
     return true;
@@ -229,7 +231,7 @@
     }
     ALOGV("output: id: %zu  size: %d  pts: %d  flags: %d", bufferIndex, info->size,
           (int)info->presentationTimeUs, info->flags);
-    CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false),
+    CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, mWindow != nullptr),
                  "AMediaCodec_releaseOutputBuffer failed");
     return !hasSeenError();
 }
@@ -261,7 +263,7 @@
 
 bool CodecDecoderTest::decodeToMemory(const char* decoder, AMediaFormat* format, int frameLimit,
                                       OutputManager* ref, int64_t pts, SeekMode mode) {
-    mSaveToMem = true;
+    mSaveToMem = (mWindow == nullptr);
     mOutputBuff = ref;
     AMediaExtractor_seekTo(mExtractor, pts, mode);
     mCodec = AMediaCodec_createCodecByName(decoder);
@@ -285,7 +287,7 @@
                                         const char* refFile, float rmsError) {
     bool isPass = true;
     if (!setUpExtractor(testFile)) return false;
-    mSaveToMem = true;
+    mSaveToMem = (mWindow == nullptr);
     auto ref = &mRefBuff;
     auto test = &mTestBuff;
     const bool boolStates[]{true, false};
@@ -451,7 +453,7 @@
         if (mIsCodecInAsyncMode) {
             CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
         }
-        mSaveToMem = true;
+        mSaveToMem = (mWindow == nullptr);
         test->reset();
         AMediaExtractor_seekTo(mExtractor, pts, mode);
         if (!doWork(INT32_MAX)) return false;
@@ -504,7 +506,7 @@
 bool CodecDecoderTest::testOnlyEos(const char* decoder, const char* testFile) {
     bool isPass = true;
     if (!setUpExtractor(testFile)) return false;
-    mSaveToMem = true;
+    mSaveToMem = (mWindow == nullptr);
     auto ref = &mRefBuff;
     auto test = &mTestBuff;
     const bool boolStates[]{true, false};
@@ -638,16 +640,22 @@
     return isPass;
 }
 
-static jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jstring jMime,
-                                       jstring jtestFile, jstring jrefFile, jfloat jrmsError) {
+static jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
+                                       jstring jMime, jstring jtestFile, jstring jrefFile,
+                                       jfloat jrmsError) {
     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
     const char* cMime = env->GetStringUTFChars(jMime, nullptr);
     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
     const char* cRefFile = env->GetStringUTFChars(jrefFile, nullptr);
     float cRmsError = jrmsError;
-    auto codecDecoderTest = new CodecDecoderTest(cMime);
+    ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
+    auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
     bool isPass = codecDecoderTest->testSimpleDecode(cDecoder, cTestFile, cRefFile, cRmsError);
     delete codecDecoderTest;
+    if (window) {
+        ANativeWindow_release(window);
+        window = nullptr;
+    }
     env->ReleaseStringUTFChars(jDecoder, cDecoder);
     env->ReleaseStringUTFChars(jMime, cMime);
     env->ReleaseStringUTFChars(jtestFile, cTestFile);
@@ -660,7 +668,7 @@
     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
     const char* cMime = env->GetStringUTFChars(jMime, nullptr);
     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
-    auto codecDecoderTest = new CodecDecoderTest(cMime);
+    auto* codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
     bool isPass = codecDecoderTest->testOnlyEos(cDecoder, cTestFile);
     delete codecDecoderTest;
     env->ReleaseStringUTFChars(jDecoder, cDecoder);
@@ -669,14 +677,19 @@
     return static_cast<jboolean>(isPass);
 }
 
-static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jstring jMime,
-                                jstring jtestFile) {
+static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
+                                jstring jMime, jstring jtestFile) {
     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
     const char* cMime = env->GetStringUTFChars(jMime, nullptr);
     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
-    auto codecDecoderTest = new CodecDecoderTest(cMime);
+    ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
+    auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
     bool isPass = codecDecoderTest->testFlush(cDecoder, cTestFile);
     delete codecDecoderTest;
+    if (window) {
+        ANativeWindow_release(window);
+        window = nullptr;
+    }
     env->ReleaseStringUTFChars(jDecoder, cDecoder);
     env->ReleaseStringUTFChars(jMime, cMime);
     env->ReleaseStringUTFChars(jtestFile, cTestFile);
@@ -688,7 +701,7 @@
     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
     const char* cMime = env->GetStringUTFChars(jMime, nullptr);
     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
-    auto codecDecoderTest = new CodecDecoderTest(cMime);
+    auto codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
     bool isPass = codecDecoderTest->testSimpleDecodeQueueCSD(cDecoder, cTestFile);
     delete codecDecoderTest;
     env->ReleaseStringUTFChars(jDecoder, cDecoder);
@@ -700,11 +713,13 @@
 int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env) {
     const JNINativeMethod methodTable[] = {
             {"nativeTestSimpleDecode",
-             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;F)Z",
+             "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
+             "lang/String;F)Z",
              (void*)nativeTestSimpleDecode},
             {"nativeTestOnlyEos", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
              (void*)nativeTestOnlyEos},
-            {"nativeTestFlush", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+            {"nativeTestFlush",
+             "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
              (void*)nativeTestFlush},
             {"nativeTestSimpleDecodeQueueCSD",
              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
@@ -713,3 +728,17 @@
     jclass c = env->FindClass("android/mediav2/cts/CodecDecoderTest");
     return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
 }
+
+int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env) {
+    const JNINativeMethod methodTable[] = {
+            {"nativeTestSimpleDecode",
+             "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
+             "lang/String;F)Z",
+             (void*)nativeTestSimpleDecode},
+            {"nativeTestFlush",
+             "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
+             (void*)nativeTestFlush},
+    };
+    jclass c = env->FindClass("android/mediav2/cts/CodecDecoderSurfaceTest");
+    return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
diff --git a/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp b/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp
new file mode 100644
index 0000000..2c76eb1
--- /dev/null
+++ b/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeCodecEncoderSurfaceTest"
+#include <log/log.h>
+#include <android/native_window_jni.h>
+#include <NdkMediaExtractor.h>
+#include <NdkMediaMuxer.h>
+#include <jni.h>
+#include <sys/stat.h>
+
+#include "NativeCodecTestBase.h"
+#include "NativeMediaCommon.h"
+
+class CodecEncoderSurfaceTest {
+  private:
+    const long kQDeQTimeOutUs = 5000;
+    const char* mMime;
+    ANativeWindow* mWindow;
+    AMediaExtractor* mExtractor;
+    AMediaFormat* mDecFormat;
+    AMediaFormat* mEncFormat;
+    AMediaMuxer* mMuxer;
+    AMediaCodec* mDecoder;
+    AMediaCodec* mEncoder;
+    CodecAsyncHandler mAsyncHandleDecoder;
+    CodecAsyncHandler mAsyncHandleEncoder;
+    bool mIsCodecInAsyncMode;
+    bool mSawDecInputEOS;
+    bool mSawDecOutputEOS;
+    bool mSawEncOutputEOS;
+    bool mSignalEOSWithLastFrame;
+    int mDecInputCount;
+    int mDecOutputCount;
+    int mEncOutputCount;
+    int mEncBitrate;
+    int mEncFramerate;
+    int mMaxBFrames;
+    int mMuxTrackID;
+
+    OutputManager* mOutputBuff;
+    OutputManager mRefBuff;
+    OutputManager mTestBuff;
+    bool mSaveToMem;
+
+    bool setUpExtractor(const char* srcPath);
+    void deleteExtractor();
+    bool configureCodec(bool isAsync, bool signalEOSWithLastFrame);
+    void resetContext(bool isAsync, bool signalEOSWithLastFrame);
+    void setUpEncoderFormat();
+    bool enqueueDecoderInput(size_t bufferIndex);
+    bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo);
+    bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info);
+    bool tryEncoderOutput(long timeOutUs);
+    bool waitForAllEncoderOutputs();
+    bool queueEOS();
+    bool enqueueDecoderEOS(size_t bufferIndex);
+    bool doWork(int frameLimit);
+    bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); }
+
+  public:
+    CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate);
+    ~CodecEncoderSurfaceTest();
+
+    bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath,
+                          const char* muxOutPath);
+};
+
+CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate)
+    : mMime{mime}, mEncBitrate{bitrate}, mEncFramerate{framerate} {
+    mWindow = nullptr;
+    mExtractor = nullptr;
+    mDecFormat = nullptr;
+    mEncFormat = nullptr;
+    mMuxer = nullptr;
+    mDecoder = nullptr;
+    mEncoder = nullptr;
+    resetContext(false, false);
+    mMaxBFrames = 0;
+    mMuxTrackID = -1;
+}
+
+CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() {
+    deleteExtractor();
+    if (mWindow) {
+        ANativeWindow_release(mWindow);
+        mWindow = nullptr;
+    }
+    if (mEncFormat) {
+        AMediaFormat_delete(mEncFormat);
+        mEncFormat = nullptr;
+    }
+    if (mMuxer) {
+        AMediaMuxer_delete(mMuxer);
+        mMuxer = nullptr;
+    }
+    if (mDecoder) {
+        AMediaCodec_delete(mDecoder);
+        mDecoder = nullptr;
+    }
+    if (mEncoder) {
+        AMediaCodec_delete(mEncoder);
+        mEncoder = nullptr;
+    }
+}
+
+bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile) {
+    FILE* fp = fopen(srcFile, "rbe");
+    struct stat buf {};
+    if (fp && !fstat(fileno(fp), &buf)) {
+        deleteExtractor();
+        mExtractor = AMediaExtractor_new();
+        media_status_t res =
+                AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
+        if (res != AMEDIA_OK) {
+            deleteExtractor();
+        } else {
+            for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
+                 trackID++) {
+                AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
+                const char* mime = nullptr;
+                AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+                if (mime && strncmp(mime, "video/", strlen("video/")) == 0) {
+                    AMediaExtractor_selectTrack(mExtractor, trackID);
+                    AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
+                                          COLOR_FormatYUV420Flexible);
+                    mDecFormat = currFormat;
+                    break;
+                }
+                AMediaFormat_delete(currFormat);
+            }
+        }
+    }
+    if (fp) fclose(fp);
+    return mDecFormat != nullptr;
+}
+
+void CodecEncoderSurfaceTest::deleteExtractor() {
+    if (mExtractor) {
+        AMediaExtractor_delete(mExtractor);
+        mExtractor = nullptr;
+    }
+    if (mDecFormat) {
+        AMediaFormat_delete(mDecFormat);
+        mDecFormat = nullptr;
+    }
+}
+
+bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame) {
+    resetContext(isAsync, signalEOSWithLastFrame);
+    CHECK_STATUS(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync),
+                 "AMediaCodec_setAsyncNotifyCallback failed");
+    CHECK_STATUS(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr,
+                                       AMEDIACODEC_CONFIGURE_FLAG_ENCODE),
+                 "AMediaCodec_configure failed");
+    CHECK_STATUS(AMediaCodec_createInputSurface(mEncoder, &mWindow),
+                 "AMediaCodec_createInputSurface failed");
+    CHECK_STATUS(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync),
+                 "AMediaCodec_setAsyncNotifyCallback failed");
+    CHECK_STATUS(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0),
+                 "AMediaCodec_configure failed");
+    return !hasSeenError();
+}
+
+void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
+    mAsyncHandleDecoder.resetContext();
+    mAsyncHandleEncoder.resetContext();
+    mIsCodecInAsyncMode = isAsync;
+    mSawDecInputEOS = false;
+    mSawDecOutputEOS = false;
+    mSawEncOutputEOS = false;
+    mSignalEOSWithLastFrame = signalEOSWithLastFrame;
+    mDecInputCount = 0;
+    mDecOutputCount = 0;
+    mEncOutputCount = 0;
+}
+
+void CodecEncoderSurfaceTest::setUpEncoderFormat() {
+    if (mEncFormat) AMediaFormat_delete(mEncFormat);
+    mEncFormat = AMediaFormat_new();
+    int width, height;
+    AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
+    AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
+    AMediaFormat_setString(mEncFormat, AMEDIAFORMAT_KEY_MIME, mMime);
+    AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_WIDTH, width);
+    AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_HEIGHT, height);
+    AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_BIT_RATE, mEncBitrate);
+    AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_FRAME_RATE, mEncFramerate);
+    AMediaFormat_setInt32(mEncFormat, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES, mMaxBFrames);
+    AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatSurface);
+    AMediaFormat_setFloat(mEncFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F);
+}
+
+bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) {
+    if (!hasSeenError() && !mSawDecInputEOS) {
+        CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0,
+                                                  AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
+                     "Queued Decoder End of Stream Failed");
+        mSawDecInputEOS = true;
+        ALOGV("Queued Decoder End of Stream");
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) {
+    if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
+        return enqueueDecoderEOS(bufferIndex);
+    } else {
+        uint32_t flags = 0;
+        size_t bufSize = 0;
+        uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize);
+        if (buf == nullptr) {
+            ALOGE("AMediaCodec_getInputBuffer failed");
+            return false;
+        }
+        ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
+        int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
+        if (size > bufSize) {
+            ALOGE("extractor sample size exceeds codec input buffer size %zu %zu", size, bufSize);
+            return false;
+        }
+        if (size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize)) {
+            ALOGE("AMediaExtractor_readSampleData failed");
+            return false;
+        }
+        if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
+            flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
+            mSawDecInputEOS = true;
+        }
+        CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags),
+                     "AMediaCodec_queueInputBuffer failed");
+        ALOGV("input: id: %zu  size: %zu  pts: %d  flags: %d", bufferIndex, size, (int)pts, flags);
+        if (size > 0) {
+            mOutputBuff->saveInPTS(pts);
+            mDecInputCount++;
+        }
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex,
+                                                   AMediaCodecBufferInfo* bufferInfo) {
+    if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
+        mSawDecOutputEOS = true;
+    }
+    if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
+        mDecOutputCount++;
+    }
+    ALOGV("output: id: %zu  size: %d  pts: %d  flags: %d", bufferIndex, bufferInfo->size,
+          (int)bufferInfo->presentationTimeUs, bufferInfo->flags);
+    CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr),
+                 "AMediaCodec_releaseOutputBuffer failed");
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex,
+                                                   AMediaCodecBufferInfo* info) {
+    if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
+        mSawEncOutputEOS = true;
+    }
+    if (info->size > 0) {
+        size_t buffSize;
+        uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize);
+        if (mSaveToMem) {
+            mOutputBuff->saveToMemory(buf, info);
+        }
+        if (mMuxer != nullptr) {
+            if (mMuxTrackID == -1) {
+                mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder));
+                CHECK_STATUS(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed");
+            }
+            CHECK_STATUS(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info),
+                         "AMediaMuxer_writeSampleData failed");
+        }
+        if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputBuff->saveOutPTS(info->presentationTimeUs);
+            mEncOutputCount++;
+        }
+    }
+    ALOGV("output: id: %zu  size: %d  pts: %d  flags: %d", bufferIndex, info->size,
+          (int)info->presentationTimeUs, info->flags);
+    CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false),
+                 "AMediaCodec_releaseOutputBuffer failed");
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) {
+    if (mIsCodecInAsyncMode) {
+        if (!hasSeenError() && !mSawEncOutputEOS) {
+            callbackObject element = mAsyncHandleEncoder.getOutput();
+            if (element.bufferIndex >= 0) {
+                if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
+            }
+        }
+    } else {
+        AMediaCodecBufferInfo outInfo;
+        if (!mSawEncOutputEOS) {
+            int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs);
+            if (bufferID >= 0) {
+                if (!dequeueEncoderOutput(bufferID, &outInfo)) return false;
+            } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+            } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+            } else {
+                ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)bufferID);
+                return false;
+            }
+        }
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() {
+    if (mIsCodecInAsyncMode) {
+        while (!hasSeenError() && !mSawEncOutputEOS) {
+            if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
+        }
+    } else {
+        while (!mSawEncOutputEOS) {
+            if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
+        }
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::queueEOS() {
+    if (mIsCodecInAsyncMode) {
+        if (!hasSeenError() && !mSawDecInputEOS) {
+            callbackObject element = mAsyncHandleDecoder.getInput();
+            if (element.bufferIndex >= 0) {
+                if (!enqueueDecoderEOS(element.bufferIndex)) return false;
+            }
+        }
+    } else {
+        if (!mSawDecInputEOS) {
+            int bufferIndex = AMediaCodec_dequeueInputBuffer(mDecoder, -1);
+            if (bufferIndex >= 0) {
+                if (!enqueueDecoderEOS(bufferIndex)) return false;
+            } else {
+                ALOGE("unexpected return value from *_dequeueInputBufferBuffer: %d",
+                      (int)bufferIndex);
+                return false;
+            }
+        }
+    }
+
+    if (mIsCodecInAsyncMode) {
+        // output processing after queuing EOS is done in waitForAllOutputs()
+        while (!hasSeenError() && !mSawDecOutputEOS) {
+            callbackObject element = mAsyncHandleDecoder.getOutput();
+            if (element.bufferIndex >= 0) {
+                if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
+            }
+            if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                if (!tryEncoderOutput(-1)) return false;
+            }
+        }
+    } else {
+        AMediaCodecBufferInfo outInfo;
+        // output processing after queuing EOS is done in waitForAllOutputs()
+        while (!mSawDecOutputEOS) {
+            if (!mSawDecOutputEOS) {
+                ssize_t oBufferID =
+                        AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
+                if (oBufferID >= 0) {
+                    if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
+                } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+                } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+                } else {
+                    ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)oBufferID);
+                    return false;
+                }
+            }
+            if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                if (!tryEncoderOutput(-1)) return false;
+            }
+        }
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::doWork(int frameLimit) {
+    int frameCnt = 0;
+    if (mIsCodecInAsyncMode) {
+        // output processing after queuing EOS is done in waitForAllOutputs()
+        while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
+            callbackObject element = mAsyncHandleDecoder.getWork();
+            if (element.bufferIndex >= 0) {
+                if (element.isInput) {
+                    if (!enqueueDecoderInput(element.bufferIndex)) return false;
+                    frameCnt++;
+                } else {
+                    if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
+                        return false;
+                    }
+                }
+            }
+            // check decoder EOS
+            if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+            // encoder output
+            // TODO: remove fixed constant and change it according to encoder latency
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                if (!tryEncoderOutput(-1)) return false;
+            }
+        }
+    } else {
+        AMediaCodecBufferInfo outInfo;
+        // output processing after queuing EOS is done in waitForAllOutputs()
+        while (!mSawDecInputEOS && frameCnt < frameLimit) {
+            ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
+            if (oBufferID >= 0) {
+                if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
+            } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+            } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+            } else {
+                ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)oBufferID);
+                return false;
+            }
+            ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
+            if (iBufferId >= 0) {
+                if (!enqueueDecoderInput(iBufferId)) return false;
+                frameCnt++;
+            } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            } else {
+                ALOGE("unexpected return value from *_dequeueInputBuffer: %d", (int)iBufferId);
+                return false;
+            }
+            if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+            // TODO: remove fixed constant and change it according to encoder latency
+            if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+                if (!tryEncoderOutput(-1)) return false;
+            }
+        }
+    }
+    return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder,
+                                               const char* srcPath, const char* muxOutPath) {
+    bool isPass = true;
+    if (!setUpExtractor(srcPath)) {
+        ALOGE("setUpExtractor failed");
+        return false;
+    }
+    setUpEncoderFormat();
+    bool muxOutput = true;
+
+    /* TODO(b/149027258) */
+    if (true) mSaveToMem = false;
+    else mSaveToMem = true;
+    auto ref = &mRefBuff;
+    auto test = &mTestBuff;
+    int loopCounter = 0;
+    const bool boolStates[]{true, false};
+    for (bool isAsync : boolStates) {
+        if (!isPass) break;
+        AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
+        mOutputBuff = loopCounter == 0 ? ref : test;
+        mOutputBuff->reset();
+
+        /* TODO(b/147348711) */
+        /* Instead of create and delete codec at every iteration, we would like to create
+         * once and use it for all iterations and delete before exiting */
+        mEncoder = AMediaCodec_createCodecByName(encoder);
+        mDecoder = AMediaCodec_createCodecByName(decoder);
+        if (!mDecoder || !mEncoder) {
+            ALOGE("unable to create media codec by name %s or %s", encoder, decoder);
+            isPass = false;
+            continue;
+        }
+
+        FILE* ofp = nullptr;
+        if (muxOutput && loopCounter == 0) {
+            int muxerFormat = 0;
+            if (!strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP8) ||
+                !strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP9)) {
+                muxerFormat = OUTPUT_FORMAT_WEBM;
+            } else {
+                muxerFormat = OUTPUT_FORMAT_MPEG_4;
+            }
+            ofp = fopen(muxOutPath, "wbe+");
+            if (ofp) {
+                mMuxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)muxerFormat);
+            }
+        }
+        if (!configureCodec(mIsCodecInAsyncMode, mSignalEOSWithLastFrame)) return false;
+        CHECK_STATUS(AMediaCodec_start(mEncoder), "AMediaCodec_start failed");
+        CHECK_STATUS(AMediaCodec_start(mDecoder), "AMediaCodec_start failed");
+        if (!doWork(INT32_MAX)) return false;
+        if (!queueEOS()) return false;
+        if (!waitForAllEncoderOutputs()) return false;
+        if (muxOutput) {
+            if (mMuxer != nullptr) {
+                CHECK_STATUS(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed");
+                mMuxTrackID = -1;
+                CHECK_STATUS(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed");
+                mMuxer = nullptr;
+            }
+            if (ofp) fclose(ofp);
+        }
+        CHECK_STATUS(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed");
+        CHECK_STATUS(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed");
+        char log[1000];
+        snprintf(log, sizeof(log), "format: %s \n codec: %s, file: %s, mode: %s:: ",
+                 AMediaFormat_toString(mEncFormat), encoder, srcPath, (isAsync ? "async" : "sync"));
+        CHECK_ERR((hasSeenError()), log, "has seen error", isPass);
+        CHECK_ERR((0 == mDecInputCount), log, "no input sent", isPass);
+        CHECK_ERR((0 == mDecOutputCount), log, "no decoder output received", isPass);
+        CHECK_ERR((0 == mEncOutputCount), log, "no encoder output received", isPass);
+        CHECK_ERR((mDecInputCount != mDecOutputCount), log, "decoder input count != output count",
+                  isPass);
+        /* TODO(b/153127506)
+         *  Currently disabling all encoder output checks. Added checks only for encoder timeStamp
+         *  is in increasing order or not.
+         *  Once issue is fixed remove increasing timestamp check and enable encoder checks.
+         */
+        /*CHECK_ERR((mEncOutputCount != mDecOutputCount), log,
+                  "encoder output count != decoder output count", isPass);
+        CHECK_ERR((loopCounter != 0 && !ref->equals(test)), log, "output is flaky", isPass);
+        CHECK_ERR((loopCounter == 0 && !ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)),
+                  log, "input pts list and output pts list are not identical", isPass);*/
+        CHECK_ERR(loopCounter == 0 && (!ref->isPtsStrictlyIncreasing(INT32_MIN)), log,
+                  "Ref pts is not strictly increasing", isPass);
+        CHECK_ERR(loopCounter != 0 && (!test->isPtsStrictlyIncreasing(INT32_MIN)), log,
+                  "Test pts is not strictly increasing", isPass);
+
+        loopCounter++;
+        ANativeWindow_release(mWindow);
+        mWindow = nullptr;
+        CHECK_STATUS(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed");
+        mEncoder = nullptr;
+        CHECK_STATUS(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed");
+        mDecoder = nullptr;
+    }
+    return isPass;
+}
+
+static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder,
+                                       jstring jMime, jstring jtestFile, jstring jmuxFile,
+                                       jint jBitrate, jint jFramerate) {
+    const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
+    const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
+    const char* cMime = env->GetStringUTFChars(jMime, nullptr);
+    const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
+    const char* cMuxFile = env->GetStringUTFChars(jmuxFile, nullptr);
+    auto codecEncoderSurfaceTest =
+            new CodecEncoderSurfaceTest(cMime, (int)jBitrate, (int)jFramerate);
+    bool isPass =
+            codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile, cMuxFile);
+    delete codecEncoderSurfaceTest;
+    env->ReleaseStringUTFChars(jEncoder, cEncoder);
+    env->ReleaseStringUTFChars(jDecoder, cDecoder);
+    env->ReleaseStringUTFChars(jMime, cMime);
+    env->ReleaseStringUTFChars(jtestFile, cTestFile);
+    env->ReleaseStringUTFChars(jmuxFile, cMuxFile);
+    return static_cast<jboolean>(isPass);
+}
+
+int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) {
+    const JNINativeMethod methodTable[] = {
+            {"nativeTestSimpleEncode",
+             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
+             "String;II)Z",
+             (void*)nativeTestSimpleEncode},
+    };
+    jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest");
+    return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
diff --git a/tests/media/jni/NativeCodecEncoderTest.cpp b/tests/media/jni/NativeCodecEncoderTest.cpp
index 626c4307..032bd6c 100644
--- a/tests/media/jni/NativeCodecEncoderTest.cpp
+++ b/tests/media/jni/NativeCodecEncoderTest.cpp
@@ -22,7 +22,7 @@
 #include <sys/stat.h>
 
 #include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
 
 class CodecEncoderTest final : CodecTestBase {
   private:
@@ -1030,6 +1030,8 @@
 
 extern int registerAndroidMediaV2CtsCodecUnitTest(JNIEnv* env);
 extern int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env);
+extern int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env);
+extern int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env);
 
 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
     JNIEnv* env;
@@ -1037,5 +1039,7 @@
     if (registerAndroidMediaV2CtsCodecUnitTest(env) != JNI_OK) return JNI_ERR;
     if (registerAndroidMediaV2CtsEncoderTest(env) != JNI_OK) return JNI_ERR;
     if (registerAndroidMediaV2CtsDecoderTest(env) != JNI_OK) return JNI_ERR;
+    if (registerAndroidMediaV2CtsDecoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
+    if (registerAndroidMediaV2CtsEncoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
     return JNI_VERSION_1_6;
-}
\ No newline at end of file
+}
diff --git a/tests/media/jni/NativeCodecUnitTest.cpp b/tests/media/jni/NativeCodecUnitTest.cpp
index dc06169..3709172 100644
--- a/tests/media/jni/NativeCodecUnitTest.cpp
+++ b/tests/media/jni/NativeCodecUnitTest.cpp
@@ -24,7 +24,7 @@
 #include <thread>
 
 #include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
 
 class NativeCodecUnitTest final : CodecTestBase {
   private:
diff --git a/tests/media/jni/NativeExtractorTest.cpp b/tests/media/jni/NativeExtractorTest.cpp
index 60df304..596026f 100644
--- a/tests/media/jni/NativeExtractorTest.cpp
+++ b/tests/media/jni/NativeExtractorTest.cpp
@@ -25,6 +25,8 @@
 #include <cstdlib>
 #include <random>
 
+#include "NativeMediaCommon.h"
+
 static bool isExtractorOKonEOS(AMediaExtractor* extractor) {
     return AMediaExtractor_getSampleTrackIndex(extractor) < 0 &&
            AMediaExtractor_getSampleSize(extractor) < 0 &&
@@ -51,6 +53,7 @@
     bool hasTestMime = AMediaFormat_getString(testFormat, AMEDIAFORMAT_KEY_MIME, &testMime);
 
     if (!hasRefMime || !hasTestMime || strcmp(refMime, testMime) != 0) return false;
+    if (!isCSDIdentical(refFormat, testFormat)) return false;
     if (!strncmp(refMime, "audio/", strlen("audio/"))) {
         int32_t refSampleRate, testSampleRate, refNumChannels, testNumChannels;
         bool hasRefSampleRate =
@@ -150,6 +153,11 @@
                     areTracksIdentical = false;
                     break;
                 }
+                if (memcmp(refBuffer, testBuffer, refSz)) {
+                    ALOGD("Mime: %s Mismatch in sample data", refMime);
+                    areTracksIdentical = false;
+                    break;
+                }
                 bool haveRefSamples = AMediaExtractor_advance(refExtractor);
                 bool haveTestSamples = AMediaExtractor_advance(testExtractor);
                 if (haveRefSamples != haveTestSamples) {
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
new file mode 100644
index 0000000..0debeac
--- /dev/null
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMediaCommon"
+#include <log/log.h>
+
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+#include "NativeMediaCommon.h"
+/* TODO(b/153592281)
+ * Note: constants used by the native media tests but not available in media ndk api
+ */
+const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
+const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
+
+/* TODO(b/153592281) */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
+
+bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
+    const char* mime;
+    AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+    /* TODO(b/154177490) */
+    if ((strcmp(mime, AMEDIA_MIMETYPE_VIDEO_VP9) == 0) ||
+        (strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AV1) == 0)) {
+        return true;
+    }
+    for (int i = 0;; i++) {
+        std::pair<void*, size_t> refCsd;
+        std::pair<void*, size_t> testCsd;
+        char name[16];
+        snprintf(name, sizeof(name), "csd-%d", i);
+        bool hasRefCSD = AMediaFormat_getBuffer(refFormat, name, &refCsd.first, &refCsd.second);
+        bool hasTestCSD = AMediaFormat_getBuffer(testFormat, name, &testCsd.first, &testCsd.second);
+        if (hasRefCSD != hasTestCSD) {
+            ALOGW("mismatch, ref fmt has CSD %d, test fmt has CSD %d", hasRefCSD, hasTestCSD);
+            return false;
+        }
+        if (hasRefCSD) {
+            if (refCsd.second != testCsd.second) {
+                ALOGW("ref/test %s buffer sizes are not identical %zu/%zu", name, refCsd.second,
+                      testCsd.second);
+                return false;
+            }
+            if (memcmp(refCsd.first, testCsd.first, refCsd.second)) {
+                ALOGW("ref/test %s buffers are not identical", name);
+                return false;
+            }
+        } else break;
+    }
+    return true;
+}
\ No newline at end of file
diff --git a/tests/media/jni/NativeMediaConstants.h b/tests/media/jni/NativeMediaCommon.h
similarity index 85%
rename from tests/media/jni/NativeMediaConstants.h
rename to tests/media/jni/NativeMediaCommon.h
index 4b60361..f5f8fb2 100644
--- a/tests/media/jni/NativeMediaConstants.h
+++ b/tests/media/jni/NativeMediaCommon.h
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
-#define MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+#ifndef MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
+#define MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
+
+#include <NdkMediaFormat.h>
 
 extern const char* AMEDIA_MIMETYPE_VIDEO_VP8;
 extern const char* AMEDIA_MIMETYPE_VIDEO_VP9;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AV1;
 extern const char* AMEDIA_MIMETYPE_VIDEO_AVC;
 extern const char* AMEDIA_MIMETYPE_VIDEO_HEVC;
 extern const char* AMEDIA_MIMETYPE_VIDEO_MPEG4;
@@ -45,6 +48,7 @@
 // from MediaCodecConstants.h (are these going to be deprecated)
 constexpr int COLOR_FormatYUV420SemiPlanar = 21;
 constexpr int COLOR_FormatYUV420Flexible = 0x7F420888;
+constexpr int COLOR_FormatSurface = 0x7f000789;
 
 // constants not defined in NDK
 extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
@@ -55,4 +59,7 @@
 
 static const int kBitrateModeConstant = 2;
 
-#endif  // MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+// common utility functions
+bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat);
+
+#endif  // MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
diff --git a/tests/media/jni/NativeMediaConstants.cpp b/tests/media/jni/NativeMediaConstants.cpp
deleted file mode 100644
index e1ab6da..0000000
--- a/tests/media/jni/NativeMediaConstants.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "NativeMediaConstants.h"
-
-/* TODO(b/153592281)
- * Note: constants used by the native media tests but not available in media ndk api
- */
-const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
-const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
-const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
-const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
-const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
-const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
-
-const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
-const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
-const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
-const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
-const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
-
-/* TODO(b/153592281) */
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
-const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
index 8d73431..9e759bc 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -32,7 +32,7 @@
 #include <map>
 #include <vector>
 
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
 
 /**
  * MuxerNativeTestHelper breaks a media file to elements that a muxer can use to rebuild its clone.
@@ -277,8 +277,9 @@
             const char* thatMime = nullptr;
             AMediaFormat_getString(thatFormat, AMEDIAFORMAT_KEY_MIME, &thatMime);
             if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
+                if (!isCSDIdentical(thisFormat, thatFormat)) continue;
                 if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
-                    int flagsDiff = 0, sizeDiff = 0, tsDiff = 0;
+                    int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
                     for (int k = 0; k < mBufferInfo[i].size(); k++) {
                         AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
                         AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
@@ -287,17 +288,21 @@
                         }
                         if (thisInfo->size != thatInfo->size) {
                             sizeDiff++;
+                        } else if (memcmp(mBuffer + thisInfo->offset,
+                                          that->mBuffer + thatInfo->offset, thisInfo->size)) {
+                            buffDiff++;
                         }
                         if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
                             STTS_TOLERANCE) {
                             tsDiff++;
                         }
                     }
-                    if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0)
+                    if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0 && buffDiff == 0)
                         break;
                     else {
-                        ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff %d",
-                              thisMime, (int)mBufferInfo[i].size(), flagsDiff, sizeDiff, tsDiff);
+                        ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff "
+                              "%d, buffDiff %d", thisMime, (int)mBufferInfo[i].size(), flagsDiff,
+                              sizeDiff, tsDiff, buffDiff);
                     }
                 }
             }
diff --git a/tests/media/jni/NativeMuxerUnitTest.cpp b/tests/media/jni/NativeMuxerUnitTest.cpp
index a08c921..78af59d 100644
--- a/tests/media/jni/NativeMuxerUnitTest.cpp
+++ b/tests/media/jni/NativeMuxerUnitTest.cpp
@@ -32,7 +32,7 @@
 #include <map>
 #include <vector>
 
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
 
 static media_status_t insertPerFrameSubtitles(AMediaMuxer* muxer, long pts, size_t trackID) {
     const char* greeting = "hello world";
diff --git a/tests/media/res/layout/media_decoder_surface_layout.xml b/tests/media/res/layout/media_decoder_surface_layout.xml
new file mode 100644
index 0000000..bff09bb
--- /dev/null
+++ b/tests/media/res/layout/media_decoder_surface_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <SurfaceView android:id="@+id/surface"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+    </SurfaceView>
+</LinearLayout>
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
new file mode 100644
index 0000000..5d1bc41
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2020 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.mediav2.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class CodecDecoderSurfaceTest extends CodecTestBase {
+    private static final String LOG_TAG = CodecDecoderSurfaceTest.class.getSimpleName();
+
+    private final String mMime;
+    private final String mTestFile;
+    private final String mReconfigFile;
+
+    private final ArrayList<ByteBuffer> mCsdBuffers;
+    private int mCurrCsdIdx;
+
+    private MediaExtractor mExtractor;
+    private SurfaceView mSurfaceView;
+
+    public CodecDecoderSurfaceTest(String mime, String testFile, String reconfigFile) {
+        mMime = mime;
+        mTestFile = testFile;
+        mReconfigFile = reconfigFile;
+        mCsdBuffers = new ArrayList<>();
+        mAsyncHandle = new CodecAsyncHandler();
+        mIsAudio = mMime.startsWith("audio/");
+    }
+
+    private void setScreenParams(int width, int height, boolean noStretch) {
+        ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
+        final DisplayMetrics dm = mActivityRule.getActivity().getResources().getDisplayMetrics();
+        if (noStretch && width <= dm.widthPixels && height <= dm.heightPixels) {
+            lp.width = width;
+            lp.height = height;
+        } else {
+            int a = dm.widthPixels * height / width;
+            if (a <= dm.heightPixels) {
+                lp.width = dm.widthPixels;
+                lp.height = a;
+            } else {
+                lp.width = dm.heightPixels * width / height;
+                lp.height = dm.heightPixels;
+            }
+        }
+        assertTrue(lp.width <= dm.widthPixels);
+        assertTrue(lp.height <= dm.heightPixels);
+        mActivityRule.getActivity().runOnUiThread(() -> mSurfaceView.setLayoutParams(lp));
+    }
+
+    private MediaFormat setUpSource(String srcFile) throws IOException {
+        mExtractor = new MediaExtractor();
+        mExtractor.setDataSource(mInpPrefix + srcFile);
+        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+            MediaFormat format = mExtractor.getTrackFormat(trackID);
+            if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
+                mExtractor.selectTrack(trackID);
+                if (!mIsAudio) {
+                    // COLOR_FormatYUV420Flexible by default should be supported by all components
+                    // This call shouldn't effect configure() call for any codec
+                    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+                }
+                return format;
+            }
+        }
+        fail("No track with mime: " + mMime + " found in file: " + srcFile);
+        return null;
+    }
+
+    private void enqueueCodecConfig(int bufferIndex) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
+        inputBuffer.put((ByteBuffer) csdBuffer.rewind());
+        mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
+                MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
+        }
+    }
+
+    private void queueCodecConfig() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
+                 mCurrCsdIdx++) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+                if (element != null) {
+                    enqueueCodecConfig(element.first);
+                }
+            }
+        } else {
+            for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
+                enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
+            }
+        }
+    }
+
+    void enqueueInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+            mExtractor.readSampleData(inputBuffer, 0);
+            int size = (int) mExtractor.getSampleSize();
+            long pts = mExtractor.getSampleTime();
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+            }
+            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                mSawInputEOS = true;
+            }
+            if (ENABLE_LOGS) {
+                Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+                        " flags: " + codecFlags);
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+            if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
+                    MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+                mOutputBuff.saveInPTS(pts);
+                mInputCount++;
+            }
+        }
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+                    info.size + " timestamp: " + info.presentationTimeUs);
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputBuff.saveOutPTS(info.presentationTimeUs);
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, mSurface != null);
+    }
+
+    private void decodeAndSavePts(String file, String decoder, long pts, int mode, int frameLimit)
+            throws IOException, InterruptedException {
+        mOutputBuff = new OutputManager();
+        mCodec = MediaCodec.createByCodecName(decoder);
+        MediaFormat format = setUpSource(file);
+        configureCodec(format, false, true, false);
+        mCodec.start();
+        mExtractor.seekTo(pts, mode);
+        doWork(frameLimit);
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        mExtractor.release();
+    }
+
+    @Rule
+    public ActivityTestRule<CodecTestActivity> mActivityRule =
+            new ActivityTestRule<>(CodecTestActivity.class);
+
+    public void setUpSurface() {
+        CodecTestActivity activity = mActivityRule.getActivity();
+        mSurfaceView = activity.findViewById(R.id.surface);
+        mSurface = mSurfaceView.getHolder().getSurface();
+    }
+
+    public void tearDownSurface() {
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+    }
+
+    @Parameterized.Parameters(name = "{index}({0})")
+    public static Collection<Object[]> input() {
+        Set<String> list = new HashSet<>();
+        if (isHandheld() || isTv() || isAutomotive()) {
+            // sec 2.2.2, 2.3.2, 2.5.2
+            list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
+            list.add(MediaFormat.MIMETYPE_VIDEO_H263);
+            list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+            list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+        }
+        if (isHandheld()) {
+            // sec 2.2.2
+            list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+        }
+        if (isTv()) {
+            // sec 2.3.2
+            list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+        }
+        ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
+        final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+                {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4",
+                        "bbb_520x390_1mbps_30fps_mpeg2.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4",
+                        "bbb_520x390_1mbps_30fps_avc.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_360x640_768kbps_30fps_avc.mp4",
+                        "bbb_520x390_1mbps_30fps_avc.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_160x1024_1500kbps_30fps_avc.mp4",
+                        "bbb_520x390_1mbps_30fps_avc.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x120_1500kbps_30fps_avc.mp4",
+                        "bbb_340x280_768kbps_30fps_avc.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4",
+                        "bbb_340x280_768kbps_30fps_hevc.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4",
+                        "bbb_176x144_192kbps_15fps_mpeg4.mp4"},
+                {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp",
+                        "bbb_176x144_192kbps_10fps_h263.3gp"},
+                {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm",
+                        "bbb_520x390_1mbps_30fps_vp8.webm"},
+                {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm",
+                        "bbb_520x390_1mbps_30fps_vp9.webm"},
+                {MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
+                        "bbb_520x390_1mbps_30fps_av1.mp4"},
+        });
+        return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, false);
+    }
+
+    /**
+     * Tests decoder for codec is in sync and async mode with surface.
+     * In these scenarios, Timestamp and it's ordering is verified.
+     */
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testSimpleDecodeToSurface() throws IOException, InterruptedException {
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        boolean[] boolStates = {true, false};
+        OutputManager ref;
+        OutputManager test = new OutputManager();
+        final long pts = 0;
+        final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+        for (String decoder : listOfDecoders) {
+            decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+            ref = mOutputBuff;
+            assertTrue("input pts list and output pts list are not identical",
+                    ref.isOutPtsListIdenticalToInpPtsList(false));
+            MediaFormat format = setUpSource(mTestFile);
+            mCodec = MediaCodec.createByCodecName(decoder);
+            setUpSurface();
+            setScreenParams(getWidth(format), getHeight(format), true);
+            for (boolean isAsync : boolStates) {
+                String log = String.format("codec: %s, file: %s, mode: %s:: ", decoder, mTestFile,
+                        (isAsync ? "async" : "sync"));
+                mOutputBuff = test;
+                mOutputBuff.reset();
+                mExtractor.seekTo(pts, mode);
+                configureCodec(format, isAsync, true, false);
+                mCodec.start();
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                /* TODO(b/147348711) */
+                if (false) mCodec.stop();
+                else mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " +
+                        mInputCount, mInputCount == mOutputCount);
+                assertTrue(log + "decoder output is flaky", ref.equals(test));
+            }
+            mCodec.release();
+            mExtractor.release();
+            mSurface = null;
+        }
+        tearDownSurface();
+    }
+
+    /**
+     * Tests flush when codec is in sync and async mode with surface. In these scenarios,
+     * Timestamp and the ordering is verified.
+     */
+    @Ignore("TODO(b/147576107)")
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testFlush() throws IOException, InterruptedException {
+        MediaFormat format = setUpSource(mTestFile);
+        mExtractor.release();
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        mCsdBuffers.clear();
+        for (int i = 0; ; i++) {
+            String csdKey = "csd-" + i;
+            if (format.containsKey(csdKey)) {
+                mCsdBuffers.add(format.getByteBuffer(csdKey));
+            } else break;
+        }
+        final long pts = 500000;
+        final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+        boolean[] boolStates = {true, false};
+        OutputManager test = new OutputManager();
+        for (String decoder : listOfDecoders) {
+            decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+            OutputManager ref = mOutputBuff;
+            assertTrue("input pts list and output pts list are not identical",
+                    ref.isOutPtsListIdenticalToInpPtsList(false));
+            mOutputBuff = test;
+            setUpSource(mTestFile);
+            mCodec = MediaCodec.createByCodecName(decoder);
+            setUpSurface();
+            setScreenParams(getWidth(format), getHeight(format), false);
+            for (boolean isAsync : boolStates) {
+                String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+                        mTestFile, (isAsync ? "async" : "sync"));
+                mExtractor.seekTo(0, mode);
+                configureCodec(format, isAsync, true, false);
+                mCodec.start();
+
+                /* test flush in running state before queuing input */
+                flushCodec();
+                if (mIsCodecInAsyncMode) mCodec.start();
+                queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
+
+                doWork(1);
+                flushCodec();
+                if (mIsCodecInAsyncMode) mCodec.start();
+                queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
+
+                mExtractor.seekTo(0, mode);
+                test.reset();
+                doWork(23);
+                assertTrue(log + " pts is not strictly increasing",
+                        test.isPtsStrictlyIncreasing(mPrevOutputPts));
+
+                /* test flush in running state */
+                flushCodec();
+                if (mIsCodecInAsyncMode) mCodec.start();
+                test.reset();
+                mExtractor.seekTo(pts, mode);
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
+                        + mInputCount, mInputCount == mOutputCount);
+                assertTrue(log + "decoder output is flaky", ref.equals(test));
+
+                /* test flush in eos state */
+                flushCodec();
+                if (mIsCodecInAsyncMode) mCodec.start();
+                test.reset();
+                mExtractor.seekTo(pts, mode);
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                /* TODO(b/147348711) */
+                if (false) mCodec.stop();
+                else mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
+                        + mInputCount, mInputCount == mOutputCount);
+                assertTrue(log + "decoder output is flaky", ref.equals(test));
+            }
+            mCodec.release();
+            mExtractor.release();
+            mSurface = null;
+        }
+        tearDownSurface();
+    }
+
+    /**
+     * Tests reconfigure when codec is in sync and async mode with surface. In these scenarios,
+     * Timestamp and the ordering is verified.
+     */
+    @Ignore("TODO(b/148523403)")
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testReconfigure() throws IOException, InterruptedException {
+        MediaFormat format = setUpSource(mTestFile);
+        mExtractor.release();
+        MediaFormat newFormat = setUpSource(mReconfigFile);
+        mExtractor.release();
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        final long pts = 500000;
+        final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+        boolean[] boolStates = {true, false};
+        OutputManager test = new OutputManager();
+        for (String decoder : listOfDecoders) {
+            decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+            OutputManager ref = mOutputBuff;
+            decodeAndSavePts(mReconfigFile, decoder, pts, mode, Integer.MAX_VALUE);
+            OutputManager configRef = mOutputBuff;
+            assertTrue("input pts list and reference pts list are not identical",
+                    ref.isOutPtsListIdenticalToInpPtsList(false));
+            assertTrue("input pts list and reconfig ref output pts list are not identical",
+                    configRef.isOutPtsListIdenticalToInpPtsList(false));
+            mOutputBuff = test;
+            mCodec = MediaCodec.createByCodecName(decoder);
+            setUpSurface();
+            setScreenParams(getWidth(format), getHeight(format), false);
+            for (boolean isAsync : boolStates) {
+                setUpSource(mTestFile);
+                String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+                        mTestFile, (isAsync ? "async" : "sync"));
+                mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+                configureCodec(format, isAsync, true, false);
+
+                /* test reconfigure in stopped state */
+                reConfigureCodec(format, !isAsync, false, false);
+                mCodec.start();
+
+                /* test reconfigure in running state before queuing input */
+                reConfigureCodec(format, !isAsync, false, false);
+                mCodec.start();
+                doWork(23);
+
+                /* test reconfigure codec in running state */
+                reConfigureCodec(format, isAsync, true, false);
+                mCodec.start();
+                test.reset();
+                mExtractor.seekTo(pts, mode);
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                /* TODO(b/147348711) */
+                if (false) mCodec.stop();
+                else mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                if (!mIsAudio) {
+                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
+                            " / " + mInputCount, mInputCount == mOutputCount);
+                }
+                assertTrue(log + "decoder output is flaky", ref.equals(test));
+
+                /* test reconfigure codec at eos state */
+                reConfigureCodec(format, !isAsync, false, false);
+                mCodec.start();
+                test.reset();
+                mExtractor.seekTo(pts, mode);
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                /* TODO(b/147348711) */
+                if (false) mCodec.stop();
+                else mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                if (!mIsAudio) {
+                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
+                            " / " + mInputCount, mInputCount == mOutputCount);
+                }
+                assertTrue(log + "decoder output is flaky", ref.equals(test));
+                mExtractor.release();
+
+                /* test reconfigure codec for new file */
+                setUpSource(mReconfigFile);
+                log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+                        mReconfigFile, (isAsync ? "async" : "sync"));
+                setScreenParams(getWidth(newFormat), getHeight(newFormat), true);
+                reConfigureCodec(newFormat, isAsync, false, false);
+                mCodec.start();
+                test.reset();
+                mExtractor.seekTo(pts, mode);
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                /* TODO(b/147348711) */
+                if (false) mCodec.stop();
+                else mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                assertTrue(log + "no input sent", 0 != mInputCount);
+                assertTrue(log + "output received", 0 != mOutputCount);
+                if (!mIsAudio) {
+                    assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
+                            " / " + mInputCount, mInputCount == mOutputCount);
+                }
+                assertTrue(log + "decoder output is flaky", configRef.equals(test));
+                mExtractor.release();
+            }
+            mCodec.release();
+            mSurface = null;
+        }
+        tearDownSurface();
+    }
+
+    private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
+            String testFile, String refFile, float rmsError);
+
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testSimpleDecodeToSurfaceNative() throws IOException {
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        MediaFormat format = setUpSource(mTestFile);
+        mExtractor.release();
+        setUpSurface();
+        setScreenParams(getWidth(format), getHeight(format), false);
+        for (String decoder : listOfDecoders) {
+            assertTrue(nativeTestSimpleDecode(decoder, mSurface, mMime, mInpPrefix + mTestFile,
+                    mInpPrefix + mReconfigFile, -1.0f));
+        }
+        tearDownSurface();
+    }
+
+    private native boolean nativeTestFlush(String decoder, Surface surface, String mime,
+            String testFile);
+
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testFlushNative() throws IOException {
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+        if (listOfDecoders.isEmpty()) {
+            fail("no suitable codecs found for mime: " + mMime);
+        }
+        MediaFormat format = setUpSource(mTestFile);
+        mExtractor.release();
+        setUpSurface();
+        setScreenParams(getWidth(format), getHeight(format), true);
+        for (String decoder : listOfDecoders) {
+            assertTrue(nativeTestFlush(decoder, mSurface, mMime, mInpPrefix + mTestFile));
+        }
+        tearDownSurface();
+    }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index b4e3b2d..ca05d0e 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -24,6 +24,7 @@
 import android.os.PersistableBundle;
 import android.util.Log;
 import android.util.Pair;
+import android.view.Surface;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
@@ -43,7 +44,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
 import static org.junit.Assert.assertTrue;
@@ -331,23 +334,37 @@
 
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() {
-        final ArrayList<String> cddRequiredMimeList =
-                new ArrayList<>(Arrays.asList(
-                        MediaFormat.MIMETYPE_AUDIO_MPEG,
-                        MediaFormat.MIMETYPE_AUDIO_AAC,
-                        MediaFormat.MIMETYPE_AUDIO_FLAC,
-                        MediaFormat.MIMETYPE_AUDIO_VORBIS,
-                        MediaFormat.MIMETYPE_AUDIO_OPUS,
-                        MediaFormat.MIMETYPE_AUDIO_RAW,
-                        MediaFormat.MIMETYPE_AUDIO_AMR_NB,
-                        MediaFormat.MIMETYPE_AUDIO_AMR_WB,
-                        MediaFormat.MIMETYPE_VIDEO_MPEG4,
-                        MediaFormat.MIMETYPE_VIDEO_H263,
-                        MediaFormat.MIMETYPE_VIDEO_AVC,
-                        MediaFormat.MIMETYPE_VIDEO_HEVC,
-                        MediaFormat.MIMETYPE_VIDEO_VP8,
-                        MediaFormat.MIMETYPE_VIDEO_VP9));
-        if (isTv()) cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+        Set<String> list = new HashSet<>();
+        if (hasAudioOutput()) {
+            // sec 5.1.2
+            list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+            list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
+            list.add(MediaFormat.MIMETYPE_AUDIO_MPEG);
+            list.add(MediaFormat.MIMETYPE_AUDIO_VORBIS);
+            list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
+            list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
+        }
+        if (isHandheld() || isTv() || isAutomotive()) {
+            // sec 2.2.2, 2.3.2, 2.5.2
+            list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
+            list.add(MediaFormat.MIMETYPE_VIDEO_H263);
+            list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+            list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+        }
+        if (isHandheld()) {
+            // sec 2.2.2
+            list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+            list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+            list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+        }
+        if (isTv()) {
+            // sec 2.3.2
+            list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+        }
+        ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
                 {MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3",
                         "bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3",
@@ -502,8 +519,8 @@
         mExtractor.release();
     }
 
-    private native boolean nativeTestSimpleDecode(String decoder, String mime, String testFile,
-            String refFile, float rmsError);
+    private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
+            String testFile, String refFile, float rmsError);
 
 
     @LargeTest
@@ -514,7 +531,7 @@
             fail("no suitable codecs found for mime: " + mMime);
         }
         for (String decoder : listOfDecoders) {
-            assertTrue(nativeTestSimpleDecode(decoder, mMime, mInpPrefix + mTestFile,
+            assertTrue(nativeTestSimpleDecode(decoder, null, mMime, mInpPrefix + mTestFile,
                     mInpPrefix + mRefFile, mRmsError));
         }
     }
@@ -644,7 +661,8 @@
         }
     }
 
-    private native boolean nativeTestFlush(String decoder, String mime, String testFile);
+    private native boolean nativeTestFlush(String decoder, Surface surface, String mime,
+            String testFile);
 
     @Ignore("TODO(b/147576107)")
     @LargeTest
@@ -655,7 +673,7 @@
             fail("no suitable codecs found for mime: " + mMime);
         }
         for (String decoder : listOfDecoders) {
-            assertTrue(nativeTestFlush(decoder, mMime, mInpPrefix + mTestFile));
+            assertTrue(nativeTestFlush(decoder, null, mMime, mInpPrefix + mTestFile));
         }
     }
 
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index f528065..d6caa62 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -38,12 +38,10 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -101,14 +99,12 @@
 
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() {
-        final ArrayList<String> cddRequiredMimeList =
-                new ArrayList<>(Arrays.asList(
-                        MediaFormat.MIMETYPE_VIDEO_MPEG4,
-                        MediaFormat.MIMETYPE_VIDEO_H263,
-                        MediaFormat.MIMETYPE_VIDEO_AVC,
-                        MediaFormat.MIMETYPE_VIDEO_HEVC,
-                        MediaFormat.MIMETYPE_VIDEO_VP8,
-                        MediaFormat.MIMETYPE_VIDEO_VP9));
+        ArrayList<String> cddRequiredMimeList = new ArrayList<>();
+        if (CodecTestBase.isHandheld() || CodecTestBase.isTv() || CodecTestBase.isAutomotive()) {
+            // sec 2.2.2, 2.3.2, 2.5.2
+            cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+            cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+        }
         final Object[][] exhaustiveArgsList = new Object[][]{
                 // Video - CodecMime, test file, bit rate, frame rate
                 {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp", 128000, 15},
@@ -133,6 +129,14 @@
                     }
                 }
             }
+            // TODO(b/154423708): add checks for video o/p port and display length >= 2.5"
+            /* sec 5.2: device implementations include an embedded screen display with the diagonal
+            length of at least 2.5inches or include a video output port or declare the support of a
+            camera */
+            if (CodecTestBase.hasCamera() && !mimes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) &&
+                    !mimes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
+                fail("device must support at least one of VP8 or AVC video encoders");
+            }
             for (String mime : cddRequiredMimeList) {
                 if (!mimes.contains(mime)) {
                     fail("no codec found for mime " + mime + " as required by cdd");
@@ -546,5 +550,34 @@
         mDecoder.release();
         mExtractor.release();
     }
+
+    private native boolean nativeTestSimpleEncode(String encoder, String decoder, String mime,
+            String testFile, String muxFile, int bitrate, int framerate);
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testSimpleEncodeFromSurfaceNative() throws IOException {
+        MediaFormat decoderFormat = setUpSource(mTestFile);
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        String decoder = codecList.findDecoderForFormat(decoderFormat);
+        if (decoder == null) {
+            mExtractor.release();
+            fail("no suitable decoder found for format: " + decoderFormat.toString());
+        }
+        ArrayList<String> listOfEncoders = CodecTestBase.selectCodecs(mMime, null, null, true);
+        assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty());
+        for (String encoder : listOfEncoders) {
+            String tmpPath = null;
+            if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
+                    mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+                tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
+            } else {
+                tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
+            }
+            assertTrue(
+                    nativeTestSimpleEncode(encoder, decoder, mMime, mInpPrefix + mTestFile, tmpPath,
+                            mBitrate, mFrameRate));
+        }
+    }
 }
 
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 48470b8..6a4c7d9 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -42,7 +42,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -366,19 +368,26 @@
 
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() {
-        final ArrayList<String> cddRequiredMimeList =
-                new ArrayList<>(Arrays.asList(
-                        MediaFormat.MIMETYPE_AUDIO_FLAC,
-                        MediaFormat.MIMETYPE_AUDIO_OPUS,
-                        MediaFormat.MIMETYPE_AUDIO_AAC,
-                        MediaFormat.MIMETYPE_AUDIO_AMR_NB,
-                        MediaFormat.MIMETYPE_AUDIO_AMR_WB,
-                        MediaFormat.MIMETYPE_VIDEO_MPEG4,
-                        MediaFormat.MIMETYPE_VIDEO_H263,
-                        MediaFormat.MIMETYPE_VIDEO_AVC,
-                        MediaFormat.MIMETYPE_VIDEO_HEVC,
-                        MediaFormat.MIMETYPE_VIDEO_VP8,
-                        MediaFormat.MIMETYPE_VIDEO_VP9));
+        Set<String> list = new HashSet<>();
+        if (hasMicrophone()) {
+            // sec 5.1.1
+            // TODO(b/154423550)
+            // list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
+            list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
+            list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
+        }
+        if (isHandheld() || isTv() || isAutomotive()) {
+            // sec 2.2.2, 2.3.2, 2.5.2
+            list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+            list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+        }
+        if (isHandheld()) {
+            // sec 2.2.2
+            list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+            list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+        }
+        ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
                 // Audio - CodecMime, arrays of bit-rates, sample rates, channel counts
                 {MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 11025,
diff --git a/tests/media/src/android/mediav2/cts/CodecTestActivity.java b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
new file mode 100644
index 0000000..18099dd
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.mediav2.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class CodecTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.media_decoder_surface_layout);
+    }
+
+    @Override
+    protected void onResume() {
+        setTurnScreenOn(true);
+        setShowWhenLocked(true);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        super.onResume();
+    }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index b9db836..3e09a8d 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -437,6 +437,8 @@
     static final int PER_TEST_TIMEOUT_SMALL_TEST_MS = 60000;
     static final long Q_DEQ_TIMEOUT_US = 5000;
     static final String mInpPrefix = WorkDir.getMediaDirString();
+    static final PackageManager pm =
+            InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
     static String codecSelKeys;
 
     CodecAsyncHandler mAsyncHandle;
@@ -487,8 +489,34 @@
     }
 
     static boolean isTv() {
-        return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
-                .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    static boolean hasMicrophone() {
+        return pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
+    }
+
+    static boolean hasCamera() {
+        return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
+    }
+
+    static boolean isWatch() {
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    static boolean isAutomotive() {
+        return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
+    static boolean hasAudioOutput() {
+        return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+    }
+
+    static boolean isHandheld() {
+        // handheld nature is not exposed to package manager, for now
+        // we check for touchscreen and NOT watch and NOT tv
+        return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) && !isWatch() && !isTv() &&
+                !isAutomotive();
     }
 
     static List<Object[]> prepareParamList(ArrayList<String> cddRequiredMimeList,
@@ -507,6 +535,14 @@
                     }
                 }
             }
+            // TODO(b/154423708): add checks for video o/p port and display length >= 2.5"
+            /* sec 5.2: device implementations include an embedded screen display with the
+            diagonal length of at least 2.5inches or include a video output port or declare the
+            support of a camera */
+            if (isEncoder && hasCamera() && !mimes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) &&
+                    !mimes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
+                fail("device must support at least one of VP8 or AVC video encoders");
+            }
             for (String mime : cddRequiredMimeList) {
                 if (!mimes.contains(mime)) {
                     fail("no codec found for mime " + mime + " as required by cdd");
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index e99ac4a..9438dcb 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -87,10 +87,13 @@
 
     @Parameterized.Parameters(name = "{index}({0})")
     public static Collection<Object[]> input() {
-        final List<String> cddEncodersList =
-                Arrays.asList(MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_VIDEO_AVC,
-                        MediaFormat.MIMETYPE_VIDEO_HEVC, MediaFormat.MIMETYPE_VIDEO_VP8,
-                        MediaFormat.MIMETYPE_VIDEO_VP9);
+        ArrayList<String> cddRequiredMimeList = new ArrayList<>();
+        if (isHandheld() || isTv() || isAutomotive()) {
+            // sec 2.2.2, 2.3.2, 2.5.2
+            cddRequiredMimeList.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+            cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+            cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+        }
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
                 // Audio - CodecMime, bit-rate, sample rate, channel count
                 {MediaFormat.MIMETYPE_AUDIO_AAC, 64000, 8000, 1, -1},
@@ -213,7 +216,7 @@
                 if (!mimes.contains(type)) mimes.add(type);
             }
         }
-        for (String mime : cddEncodersList) {
+        for (String mime : cddRequiredMimeList) {
             if (!mimes.contains(mime)) {
                 fail("media codec encoder list doesn't contain " + mime +
                         " as required by cdd");
@@ -229,7 +232,7 @@
                 }
             }
             if (miss) {
-                if (cddEncodersList.contains(mime)) {
+                if (cddRequiredMimeList.contains(mime)) {
                     fail("no test vectors available for " + mime);
                 }
                 Log.w(LOG_TAG, "no test vectors available for " + mime);
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index 31bba13..a5c0eb9 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -195,11 +195,44 @@
                 refSample.flags >= 0 && refSample.size >= 0 && refSample.presentationTimeUs >= 0;
     }
 
+    static boolean isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat) {
+        String mime = refFormat.getString(MediaFormat.KEY_MIME);
+        /* TODO(b/154177490) */
+        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
+                mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
+            return true;
+        }
+        for (int i = 0; ; i++) {
+            String csdKey = "csd-" + i;
+            boolean refHasCSD = refFormat.containsKey(csdKey);
+            boolean testHasCSD = testFormat.containsKey(csdKey);
+            if (refHasCSD != testHasCSD) {
+                if (ENABLE_LOGS) {
+                    Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + "test fmt has CSD: " +
+                            testHasCSD);
+                }
+                return false;
+            }
+            if (refHasCSD) {
+                ByteBuffer r = refFormat.getByteBuffer(csdKey);
+                ByteBuffer t = testFormat.getByteBuffer(csdKey);
+                if (!r.equals(t)) {
+                    if (ENABLE_LOGS) {
+                        Log.w(LOG_TAG, "ref CSD and test CSD buffers are not identical");
+                    }
+                    return false;
+                }
+            } else break;
+        }
+        return true;
+    }
+
     private static boolean isFormatSimilar(MediaFormat refFormat, MediaFormat testFormat) {
         String refMime = refFormat.getString(MediaFormat.KEY_MIME);
         String testMime = testFormat.getString(MediaFormat.KEY_MIME);
 
         if (!refMime.equals(testMime)) return false;
+        if (!isCSDIdentical(refFormat, testFormat)) return false;
         if (refMime.startsWith("audio/")) {
             return refFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) ==
                     testFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) &&
@@ -292,6 +325,13 @@
                         areTracksIdentical = false;
                         break;
                     }
+                    if (!testBuffer.equals(refBuffer)) {
+                        if (ENABLE_LOGS) {
+                            Log.d(LOG_TAG, "Mime: " + refMime + "sample data is not identical");
+                        }
+                        areTracksIdentical = false;
+                        break;
+                    }
                     boolean haveRefSamples = refExtractor.advance();
                     boolean haveTestSamples = testExtractor.advance();
                     if (haveRefSamples != haveTestSamples) {
diff --git a/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java b/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
index a28f134..cee70e0 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
@@ -23,7 +23,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index 7a5c046..d954f01 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -260,7 +260,9 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         MuxerTestHelper that = (MuxerTestHelper) o;
-
+        int MAX_SAMPLE_SIZE = 4 * 1024 * 1024;
+        byte[] refBuffer = new byte[MAX_SAMPLE_SIZE];
+        byte[] testBuffer = new byte[MAX_SAMPLE_SIZE];
         for (int i = 0; i < mTrackCount; i++) {
             MediaFormat thisFormat = mFormat.get(i);
             String thisMime = thisFormat.getString(MediaFormat.KEY_MIME);
@@ -269,8 +271,9 @@
                 MediaFormat thatFormat = that.mFormat.get(j);
                 String thatMime = thatFormat.getString(MediaFormat.KEY_MIME);
                 if (thisMime != null && thisMime.equals(thatMime)) {
+                    if (!ExtractorTest.isCSDIdentical(thisFormat, thatFormat)) continue;
                     if (mBufferInfo.get(i).size() == that.mBufferInfo.get(j).size()) {
-                        int flagsDiff = 0, sizeDiff = 0, tsDiff = 0;
+                        int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
                         for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
                             MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
                             MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
@@ -279,6 +282,17 @@
                             }
                             if (thisInfo.size != thatInfo.size) {
                                 sizeDiff++;
+                            } else {
+                                mBuff.position(thisInfo.offset);
+                                mBuff.get(refBuffer, 0, thisInfo.size);
+                                that.mBuff.position(thatInfo.offset);
+                                that.mBuff.get(testBuffer, 0, thatInfo.size);
+                                for (int count = 0; count < thisInfo.size; count++) {
+                                    if (refBuffer[count] != testBuffer[count]) {
+                                        buffDiff++;
+                                        break;
+                                    }
+                                }
                             }
                             if (Math.abs(
                                     thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
@@ -286,13 +300,14 @@
                                 tsDiff++;
                             }
                         }
-                        if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0) {
+                        if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0 || buffDiff != 0) {
                             if (ENABLE_LOGS) {
                                 Log.d(LOG_TAG, "For track: " + thisMime +
                                         " Total Samples: " + mBufferInfo.get(i).size() +
                                         " flagsDiff: " + flagsDiff +
                                         " sizeDiff: " + sizeDiff +
-                                        " tsDiff: " + tsDiff);
+                                        " tsDiff: " + tsDiff +
+                                        " buffDiff: " + buffDiff);
                             }
                         } else break;
                     } else {
@@ -304,6 +319,8 @@
                     }
                 }
             }
+            mBuff.position(0);
+            that.mBuff.position(0);
             if (j == that.mTrackCount) {
                 if (ENABLE_LOGS) {
                     Log.d(LOG_TAG, "For track: " + thisMime + " Couldn't find a match ");
diff --git a/tests/signature/api-check/shared-libs-api/AndroidManifest.xml b/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
index 00ddd9a..663ef6b 100644
--- a/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
+++ b/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
@@ -25,6 +25,7 @@
         <uses-library android:name="android.test.base" android:required="false"/>
         <uses-library android:name="android.test.mock" android:required="false"/>
         <uses-library android:name="android.test.runner" android:required="false"/>
+        <uses-library android:name="android.net.ipsec.ike" android:required="false"/>
         <uses-library android:name="com.android.future.usb.accessory" android:required="false"/>
         <uses-library android:name="com.android.location.provider" android:required="false"/>
         <uses-library android:name="com.android.mediadrm.signer" android:required="false"/>
diff --git a/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java b/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
index 7a9d28f..cbb485d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
@@ -322,6 +322,37 @@
                     + " why did call not report that!");
         }
     }
+
+    @Test
+    public void testEGL15CreateDebugContext() {
+        int error;
+
+        if (mEglVersion < 15) {
+            return;
+        }
+
+        mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
+                new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_FALSE, EGL14.EGL_NONE }, 0);
+        if (mEglContext == EGL15.EGL_NO_CONTEXT) {
+            throw new RuntimeException("eglCreateContext failed");
+        }
+        error = EGL14.eglGetError();
+        if (error != EGL14.EGL_SUCCESS) {
+            throw new RuntimeException("eglCreateContext failed");
+        }
+        EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+
+        mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
+                new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_TRUE, EGL14.EGL_NONE }, 0);
+        if (mEglContext == EGL15.EGL_NO_CONTEXT) {
+            throw new RuntimeException("eglCreateContext failed");
+        }
+        error = EGL14.eglGetError();
+        if (error != EGL14.EGL_SUCCESS) {
+            throw new RuntimeException("eglCreateContext failed");
+        }
+        EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+    }
 }
 
 // Note: Need to add tests for eglCreatePlatformWindowSurface
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
index 0374082..a736878 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
@@ -68,7 +68,7 @@
         }
     }
 
-    @CddTest(requirement = "7.1.4.2/C-1-8")
+    @CddTest(requirement = "7.1.4.2/C-1-8,C-1-9")
     @Test
     public void testVulkanDeqpLevel() {
         if (mVulkanHardwareVersion.version >= VULKAN_1_0) {
diff --git a/tests/tests/media/src/android/media/cts/AudioMetadataTest.java b/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
index bafd83d..9a7cd26 100755
--- a/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.*;
 import static org.testng.Assert.assertThrows;
 
+import android.media.AudioFormat;
 import android.media.AudioMetadata;
 import android.media.AudioMetadataMap;
 import android.media.AudioMetadataReadMap;
@@ -117,6 +118,31 @@
         assertEquals(audioMetadata, audioMetadata.dup());
     }
 
+    @Test
+    public void testFormatKeys() throws Exception {
+        final AudioMetadataMap audioMetadata = AudioMetadata.createMap();
+        audioMetadata.set(AudioMetadata.Format.KEY_ATMOS_PRESENT, true);
+        audioMetadata.set(AudioMetadata.Format.KEY_AUDIO_ENCODING, AudioFormat.ENCODING_MP3);
+        audioMetadata.set(AudioMetadata.Format.KEY_BIT_RATE, 64000);
+        audioMetadata.set(AudioMetadata.Format.KEY_BIT_WIDTH, 16);
+        audioMetadata.set(AudioMetadata.Format.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_OUT_STEREO);
+        audioMetadata.set(AudioMetadata.Format.KEY_MIME, "audio/mp3");
+        audioMetadata.set(AudioMetadata.Format.KEY_SAMPLE_RATE, 48000);
+
+        assertEquals(64000, (int)audioMetadata.get(AudioMetadata.Format.KEY_BIT_RATE));
+        assertEquals(AudioFormat.CHANNEL_OUT_STEREO,
+                (int)audioMetadata.get(AudioMetadata.Format.KEY_CHANNEL_MASK));
+        assertEquals("audio/mp3", (String)audioMetadata.get(AudioMetadata.Format.KEY_MIME));
+        assertEquals(48000, (int)audioMetadata.get(AudioMetadata.Format.KEY_SAMPLE_RATE));
+        assertEquals(16, (int)audioMetadata.get(AudioMetadata.Format.KEY_BIT_WIDTH));
+        assertEquals(true, (boolean)audioMetadata.get(AudioMetadata.Format.KEY_ATMOS_PRESENT));
+        assertEquals(AudioFormat.ENCODING_MP3,
+                (int)audioMetadata.get(AudioMetadata.Format.KEY_AUDIO_ENCODING));
+
+        // Additional test to ensure we can survive parceling
+        testPackingAndUnpacking((AudioMetadata.BaseMap)audioMetadata);
+    }
+
     // Vendor keys created by direct override of the AudioMetadata interface.
     private static final AudioMetadata.Key<Integer>
         KEY_VENDOR_INTEGER = new AudioMetadata.Key<Integer>() {
@@ -276,16 +302,7 @@
 
     @Test
     public void testUnpackingByteBuffer() throws Exception {
-        ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
-                AUDIO_METADATA_REFERENCE, ByteOrder.nativeOrder());
-        assertNotNull(bufferPackedAtJava);
-        ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
-        assertNotNull(buffer);
-        buffer.order(ByteOrder.nativeOrder());
-
-        AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
-        assertNotNull(metadataFromByteBuffer);
-        assertEquals(metadataFromByteBuffer, AUDIO_METADATA_REFERENCE);
+        testPackingAndUnpacking(AUDIO_METADATA_REFERENCE);
     }
 
     @Test
@@ -303,6 +320,19 @@
         assertNull(metadataFromByteBuffer);
     }
 
+    private static void testPackingAndUnpacking(AudioMetadata.BaseMap audioMetadata) {
+        ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
+                audioMetadata, ByteOrder.nativeOrder());
+        assertNotNull(bufferPackedAtJava);
+        ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
+        assertNotNull(buffer);
+        buffer.order(ByteOrder.nativeOrder());
+
+        AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
+        assertNotNull(metadataFromByteBuffer);
+        assertEquals(metadataFromByteBuffer, audioMetadata);
+    }
+
     static {
         System.loadLibrary("audio_jni");
     }
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 739bc21..39d8f1b 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -16,7 +16,6 @@
 
 package android.os.cts
 
-import android.Manifest.permission.READ_CALENDAR
 import android.content.Intent
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.content.pm.PackageManager
@@ -175,17 +174,18 @@
         }
     }
 
-    // TODO grantRuntimePermission fails to grant permission
     @AppModeFull(reason = "Uses separate apps for testing")
-    fun _testInstallGrants_notRevokedImmediately() {
+    fun testInstallGrants_notRevokedImmediately() {
         wakeUpScreen()
         withUnusedThresholdMs(TimeUnit.DAYS.toMillis(30)) {
             withDummyApp {
                 // Setup
-                runWithShellPermissionIdentity {
-                    instrumentation.uiAutomation
-                            .grantRuntimePermission(APK_PACKAGE_NAME, READ_CALENDAR)
-                }
+                goToPermissions()
+                click("Calendar")
+                click("Allow")
+                goBack()
+                goBack()
+                goBack()
                 eventually {
                     assertPermission(PERMISSION_GRANTED)
                 }
@@ -283,11 +283,7 @@
 //        }
 
         try {
-            context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
-                    .setData(Uri.fromParts("package", packageName, null))
-                    .addFlags(FLAG_ACTIVITY_NEW_TASK))
-
-            waitFindNode(hasTextThat(containsStringIgnoringCase("Permissions"))).click()
+            goToPermissions(packageName)
 
             waitForIdle()
             val ui = instrumentation.uiAutomation.rootInActiveWindow
@@ -315,6 +311,18 @@
         }
     }
 
+    private fun goToPermissions(packageName: String = APK_PACKAGE_NAME) {
+        context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
+                .setData(Uri.fromParts("package", packageName, null))
+                .addFlags(FLAG_ACTIVITY_NEW_TASK))
+
+        click("Permissions")
+    }
+
+    private fun click(label: String) {
+        waitFindNode(hasTextThat(containsStringIgnoringCase(label))).click()
+    }
+
     private fun assertWhitelistState(state: Boolean) {
         assertThat(
             waitFindObject(By.textStartsWith("Auto-revoke whitelisted: ")).text,
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index 77e2934..be534b7 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -38,6 +38,7 @@
         ":CtsUsePermissionApp28",
         ":CtsUsePermissionApp29",
         ":CtsUsePermissionAppLatest",
+        ":CtsUsePermissionAppLatestWithBackground",
     ],
     test_suites: [
         "cts",
diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml
index 7d80f2c..c4adbfc 100644
--- a/tests/tests/permission3/AndroidTest.xml
+++ b/tests/tests/permission3/AndroidTest.xml
@@ -42,6 +42,7 @@
         <option name="push" value="CtsUsePermissionApp28.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp28.apk" />
         <option name="push" value="CtsUsePermissionApp29.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp29.apk" />
         <option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatest.apk" />
+        <option name="push" value="CtsUsePermissionAppLatestWithBackground.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatestWithBackground.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.bp b/tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
similarity index 62%
rename from hostsidetests/appsecurity/test-apps/tinyapp/Android.bp
rename to tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
index b650bb4..65bc605 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.bp
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
@@ -1,4 +1,5 @@
-// Copyright (C) 2014 The Android Open Source Project
+//
+// Copyright (C) 2020 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.
@@ -11,19 +12,15 @@
 // 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.
+//
 
 android_test_helper_app {
-    name: "CtsPkgInstallTinyApp",
-    defaults: ["cts_support_defaults"],
-    // tag this module as a cts test artifact
-    test_suites: [
-        "cts",
-        "vts10",
-        "general-tests",
+    name: "CtsUsePermissionAppLatestWithBackground",
+    srcs: [
+        "src/**/*.kt",
     ],
-    srcs: ["src/**/*.java"],
-    sdk_version: "current",
-    dex_preopt: {
-        enabled: false,
-    },
+    static_libs: [
+        "kotlin-stdlib",
+    ],
+    certificate: ":cts-testkey2",
 }
diff --git a/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml b/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml
new file mode 100644
index 0000000..adf2eac
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission3.cts.usepermission">
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+    <application>
+        <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+    </application>
+</manifest>
diff --git a/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt b/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
new file mode 100644
index 0000000..1c30c87
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.permission3.cts.usepermission
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+class RequestPermissionsActivity : Activity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+        requestPermissions(permissions, 1)
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<out String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+        setResult(RESULT_OK, Intent().apply {
+            putExtra("$packageName.PERMISSIONS", permissions)
+            putExtra("$packageName.GRANT_RESULTS", grantResults)
+        })
+        finish()
+    }
+}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index e689188..6672f7e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -50,6 +50,8 @@
         const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsUsePermissionApp28.apk"
         const val APP_APK_PATH_29 = "$APK_DIRECTORY/CtsUsePermissionApp29.apk"
         const val APP_APK_PATH_LATEST = "$APK_DIRECTORY/CtsUsePermissionAppLatest.apk"
+        const val APP_APK_PATH_LATEST_WITH_BACKGROUND =
+                "$APK_DIRECTORY/CtsUsePermissionAppLatestWithBackground.apk"
         const val APP_PACKAGE_NAME = "android.permission3.cts.usepermission"
     }
 
@@ -266,10 +268,14 @@
 
     protected fun clickPermissionRequestSettingsLinkAndAllowAlways() {
         clickPermissionRequestSettingsLink()
-        click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+        clickAllowAlwaysInSettings()
         pressBack()
     }
 
+    protected fun clickAllowAlwaysInSettings() {
+        click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+    }
+
     protected fun clickPermissionRequestAllowForegroundButton() =
         click(By.res("com.android.permissioncontroller:id/permission_allow_foreground_only_button"))
 
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
new file mode 100644
index 0000000..0abae86
--- /dev/null
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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.permission3.cts
+
+import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import org.junit.Test
+
+/**
+ * Runtime permission behavior apps targeting API 30
+ */
+class PermissionTest30 : BaseUsePermissionTest() {
+
+    @Test
+    fun testCantRequestFgAndBgAtOnce() {
+        installPackage(APP_APK_PATH_LATEST_WITH_BACKGROUND)
+        assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+        assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+        requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to false,
+                ACCESS_BACKGROUND_LOCATION to false) {
+            // Do nothing, should be automatically denied
+        }
+    }
+
+    @Test
+    fun testRequestBothInSequence() {
+        installPackage(APP_APK_PATH_LATEST_WITH_BACKGROUND)
+        assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+        assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+        requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to true) {
+            clickPermissionRequestAllowForegroundButton()
+        }
+
+        requestAppPermissionsAndAssertResult(ACCESS_BACKGROUND_LOCATION to true) {
+            clickAllowAlwaysInSettings()
+            pressBack()
+        }
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
index 106cd6f..0435a45 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
@@ -112,16 +112,22 @@
         final long unique2 = System.nanoTime();
         final String TEST_TITLE2 = "Title " + unique2;
 
+        File file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
+                "mediaStoreTest1.jpg");
+        String path = file.getAbsolutePath();
+        ProviderTestUtils.stageFile(R.raw.scenery, file);
+
+        file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
+                "mediaStoreTest2.jpg");
+        path = file.getAbsolutePath();
+        ProviderTestUtils.stageFile(R.raw.scenery, file);
+
         Cursor c = Media.query(mContentResolver, mExternalImages, null, null,
                 "_id ASC");
         int previousCount = c.getCount();
         c.close();
 
         // insert an image by path
-        File file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
-                "mediaStoreTest1.jpg");
-        String path = file.getAbsolutePath();
-        ProviderTestUtils.stageFile(R.raw.scenery, file);
         String stringUrl = null;
         try {
             stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE1, null);
@@ -134,10 +140,6 @@
         assertInsertionSuccess(stringUrl);
 
         // insert another image by path
-        file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
-                "mediaStoreTest2.jpg");
-        path = file.getAbsolutePath();
-        ProviderTestUtils.stageFile(R.raw.scenery, file);
         stringUrl = null;
         try {
             stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE2, null);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
index 4e0d109..384213f 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
index aac758e..7c2fd22 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
index 040251e..8e8fef8 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
Binary files differ
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java
new file mode 100644
index 0000000..f16c83d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.telephony.cts;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+
+import android.os.BatteryStatsManager;
+import android.os.connectivity.CellularBatteryStats;
+
+import org.junit.Test;
+
+/**
+ * Test BatteryStatsManager and CellularBatteryStats to ensure that valid data is being reported
+ * and that invalid data is not reported.
+ */
+public class BatteryStatsManagerTest{
+
+    /** Test that {@link CellularBatteryStats} getters return sane values. */
+    @Test
+    public void testGetCellularBatteryStats() {
+        BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class);
+        CellularBatteryStats cellularBatteryStats = bsm.getCellularBatteryStats();
+
+        assertThat(cellularBatteryStats.getEnergyConsumedMaMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getIdleTimeMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getLoggingDurationMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getKernelActiveTimeMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getMonitoredRailChargeConsumedMaMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getNumBytesRx()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getNumBytesTx()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getNumPacketsRx()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getNumPacketsTx()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getRxTimeMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getSleepTimeMillis()).isAtLeast(0L);
+        assertThat(cellularBatteryStats.getTimeInRatMicros(anyInt())).isAtLeast(-1L);
+        assertThat(cellularBatteryStats.getTimeInRxSignalStrengthLevelMicros(
+                anyInt())).isAtLeast(-1L);
+    }
+}
+
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index ea2108b..b7bd6d0 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -552,7 +552,7 @@
         if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
             int[] bands = nr.getBands();
             for (int band: bands) {
-                assertTrue("getBand out of range [1, 95] or [257, 261]",
+                assertTrue("getBand out of range [1, 95] or [257, 261], band = " + band,
                         (band >= BAND_FR1_MIN_NR && band <= BAND_FR1_MAX_NR)
                         || (band >= BAND_FR2_MIN_NR && band <= BAND_FR2_MAX_NR));
             }
@@ -649,7 +649,7 @@
         if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
             int[] bands = lte.getBands();
             for (int band: bands) {
-                assertTrue("getBand out of range [1, 88]",
+                assertTrue("getBand out of range [1, 88], band = " + band,
                         band >= BAND_MIN_LTE && band <= BAND_MAX_LTE);
             }
         }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 0916756..d9ab2f3 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -2815,6 +2815,34 @@
                 tm -> tm.setOpportunisticNetworkState(isEnabled));
     }
 
+    @Test
+    public void testGetSimApplicationState() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        int simApplicationState = mTelephonyManager.getSimApplicationState();
+        assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED,
+                TelephonyManager.SIM_STATE_PUK_REQUIRED,
+                TelephonyManager.SIM_STATE_NETWORK_LOCKED,
+                TelephonyManager.SIM_STATE_NOT_READY,
+                TelephonyManager.SIM_STATE_PERM_DISABLED,
+                TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+    }
+
+    @Test
+    public void testGetSimCardState() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+        int simCardState = mTelephonyManager.getSimCardState();
+        assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+                TelephonyManager.SIM_STATE_ABSENT,
+                TelephonyManager.SIM_STATE_CARD_IO_ERROR,
+                TelephonyManager.SIM_STATE_CARD_RESTRICTED,
+                TelephonyManager.SIM_STATE_PRESENT).contains(simCardState));
+    }
+
 
     private boolean isDataEnabled() {
         return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
diff --git a/tests/tests/tethering/AndroidTest.xml b/tests/tests/tethering/AndroidTest.xml
index 25051ba..d0a2bce 100644
--- a/tests/tests/tethering/AndroidTest.xml
+++ b/tests/tests/tethering/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Config for CTS Tethering test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />