Merge "Migrate Fragments CTS tests to Android bp."
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
index 803ae3f..fd9a0d6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -49,18 +49,42 @@
         super.tearDown();
     }
 
-    public void testQuietMode() throws Exception {
+    public void testQuietMode_defaultForegroundLauncher() throws Exception {
         if (!mHasFeature) {
           return;
         }
         runDeviceTestsAsUser(
                 TEST_PACKAGE,
                 TEST_CLASS,
-                null,
+                "testTryEnableQuietMode_defaultForegroundLauncher",
                 mPrimaryUserId,
                 createParams(mProfileId));
     }
 
+    public void testQuietMode_notForegroundLauncher() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(
+            TEST_PACKAGE,
+            TEST_CLASS,
+            "testTryEnableQuietMode_notForegroundLauncher",
+            mPrimaryUserId,
+            createParams(mProfileId));
+    }
+
+    public void testQuietMode_notDefaultLauncher() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        runDeviceTestsAsUser(
+            TEST_PACKAGE,
+            TEST_CLASS,
+            "testTryEnableQuietMode_notDefaultLauncher",
+            mPrimaryUserId,
+            createParams(mProfileId));
+    }
+
     private void createAndStartManagedProfile() throws Exception {
         mProfileId = createManagedProfile(mPrimaryUserId);
         switchUser(mPrimaryUserId);
diff --git a/hostsidetests/theme/app/src/android/theme/app/ConditionCheck.java b/hostsidetests/theme/app/src/android/theme/app/ConditionCheck.java
new file mode 100644
index 0000000..8954a8d
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ConditionCheck.java
@@ -0,0 +1,82 @@
+/*
+ * 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
+ */
+
+package android.theme.app;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Runnable that re-posts itself on a handler until either all of the conditions are satisfied
+ * or a retry threshold is exceeded.
+ */
+class ConditionCheck implements Runnable {
+    private static final int MAX_RETRIES = 3;
+    private static final int RETRY_DELAY = 500;
+
+    private final Handler mHandler;
+    private final Runnable mOnSuccess;
+    private final Consumer<String> mOnFailure;
+    private final ArrayList<Pair<String, Supplier<Boolean>>> mConditions = new ArrayList<>();
+
+    private ArrayList<Pair<String, Supplier<Boolean>>> mRemainingConditions = new ArrayList<>();
+    private int mRemainingRetries;
+
+    ConditionCheck(Context context, Runnable onSuccess, Consumer<String> onFailure) {
+        mHandler = new Handler(context.getMainLooper());
+        mOnSuccess = onSuccess;
+        mOnFailure = onFailure;
+    }
+
+    public ConditionCheck addCondition(String summary, Supplier<Boolean> condition) {
+        mConditions.add(new Pair<>(summary, condition));
+        return this;
+    }
+
+    public void start() {
+        mRemainingConditions = new ArrayList<>(mConditions);
+        mRemainingRetries = 0;
+
+        mHandler.removeCallbacks(this);
+        mHandler.post(this);
+    }
+
+    public void cancel() {
+        mHandler.removeCallbacks(this);
+    }
+
+    @Override
+    public void run() {
+        mRemainingConditions.removeIf(condition -> condition.second.get());
+        if (mRemainingConditions.isEmpty()) {
+            mOnSuccess.run();
+        } else if (mRemainingRetries < MAX_RETRIES) {
+            mRemainingRetries++;
+            mHandler.removeCallbacks(this);
+            mHandler.postDelayed(this, RETRY_DELAY);
+        } else {
+            final StringBuffer buffer = new StringBuffer("Failed conditions:");
+            mRemainingConditions.forEach(condition ->
+                    buffer.append("\n").append(condition.first));
+            mOnFailure.accept(buffer.toString());
+        }
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
index 3591410..a41e426 100644
--- a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -16,27 +16,20 @@
 
 package android.theme.app;
 
-import android.Manifest.permission;
+import static android.theme.app.TestConfiguration.THEMES;
+
 import android.app.Activity;
-import android.app.KeyguardManager;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
 import android.util.Log;
-import android.util.Pair;
 import android.view.WindowManager.LayoutParams;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
 
 /**
  * Generates images by iterating through all themes and launching instances of
@@ -69,16 +62,9 @@
         mOutputDir = setupOutputDirectory();
         if (mOutputDir == null) {
             finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false);
+        } else {
+            generateNextImage();
         }
-
-        // The activity has been created, but we don't want to start image generation until various
-        // asynchronous conditions are satisfied.
-        new ConditionCheck(this, () -> generateNextImage(), message -> finish(message, false))
-                .addCondition("Device is unlocked",
-                        () -> !getSystemService(KeyguardManager.class).isDeviceLocked())
-                .addCondition("Window is focused",
-                        () -> hasWindowFocus())
-                .start();
     }
 
     private File setupOutputDirectory() {
@@ -93,63 +79,6 @@
     }
 
     /**
-     * Runnable that re-posts itself on a handler until either all of the conditions are satisfied
-     * or a retry threshold is exceeded.
-     */
-    class ConditionCheck implements Runnable {
-        private static final int MAX_RETRIES = 3;
-        private static final int RETRY_DELAY = 500;
-
-        private final Handler mHandler;
-        private final Runnable mOnSuccess;
-        private final Consumer<String> mOnFailure;
-        private final ArrayList<Pair<String, Supplier<Boolean>>> mConditions = new ArrayList<>();
-
-        private ArrayList<Pair<String, Supplier<Boolean>>> mRemainingConditions = new ArrayList<>();
-        private int mRemainingRetries;
-
-        ConditionCheck(Context context, Runnable onSuccess, Consumer<String> onFailure) {
-            mHandler = new Handler(context.getMainLooper());
-            mOnSuccess = onSuccess;
-            mOnFailure = onFailure;
-        }
-
-        public ConditionCheck addCondition(String summary, Supplier<Boolean> condition) {
-            mConditions.add(new Pair<>(summary, condition));
-            return this;
-        }
-
-        public void start() {
-            mRemainingConditions = new ArrayList<>(mConditions);
-            mRemainingRetries = 0;
-
-            mHandler.removeCallbacks(this);
-            mHandler.post(this);
-        }
-
-        public void cancel() {
-            mHandler.removeCallbacks(this);
-        }
-
-        @Override
-        public void run() {
-            mRemainingConditions.removeIf(condition -> condition.second.get());
-            if (mRemainingConditions.isEmpty()) {
-                mOnSuccess.run();
-            } else if (mRemainingRetries < MAX_RETRIES) {
-                mRemainingRetries++;
-                mHandler.removeCallbacks(this);
-                mHandler.postDelayed(this, RETRY_DELAY);
-            } else {
-                final StringBuffer buffer = new StringBuffer("Failed conditions:");
-                mRemainingConditions.forEach(condition ->
-                        buffer.append("\n").append(condition.first));
-                mOnFailure.accept(buffer.toString());
-            }
-        }
-    }
-
-    /**
      * @return whether the test finished successfully
      */
     public boolean isFinishSuccess() {
@@ -167,8 +96,23 @@
     /**
      * Starts the activity to generate the next image.
      */
-    private boolean generateNextImage() {
-        final ThemeDeviceActivity.Theme theme = ThemeDeviceActivity.THEMES[mCurrentTheme];
+    private void generateNextImage() {
+        // Keep trying themes until one works.
+        boolean success = false;
+        while (++mCurrentTheme < THEMES.length && !success) {
+            success = launchThemeDeviceActivity();
+        }
+
+        // If we ran out of themes, we're done.
+        if (!success) {
+            compressOutput();
+
+            finish("Image generation complete!", true);
+        }
+    }
+
+    private boolean launchThemeDeviceActivity() {
+        final ThemeInfo theme = THEMES[mCurrentTheme];
         if (theme.apiLevel > VERSION.SDK_INT) {
             Log.v(TAG, "Skipping theme \"" + theme.name
                     + "\" (requires API " + theme.apiLevel + ")");
@@ -193,18 +137,7 @@
             return;
         }
 
-        // Keep trying themes until one works.
-        boolean success = false;
-        while (++mCurrentTheme < ThemeDeviceActivity.THEMES.length && !success) {
-            success = generateNextImage();
-        }
-
-        // If we ran out of themes, we're done.
-        if (!success) {
-            compressOutput();
-
-            finish("Image generation complete!", true);
-        }
+        generateNextImage();
     }
 
     private void compressOutput() {
diff --git a/hostsidetests/theme/app/src/android/theme/app/LayoutInfo.java b/hostsidetests/theme/app/src/android/theme/app/LayoutInfo.java
new file mode 100644
index 0000000..e7beab7e
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/LayoutInfo.java
@@ -0,0 +1,38 @@
+/*
+ * 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
+ */
+
+package android.theme.app;
+
+import android.theme.app.modifiers.AbstractLayoutModifier;
+
+/**
+ * A class to encapsulate information about a layout.
+ */
+class LayoutInfo {
+    public final int id;
+    public final String name;
+    public final AbstractLayoutModifier modifier;
+
+    LayoutInfo(int id, String name) {
+        this(id, name, null);
+    }
+
+    LayoutInfo(int id, String name, AbstractLayoutModifier modifier) {
+        this.id = id;
+        this.name = name;
+        this.modifier = modifier;
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/LayoutModifier.java b/hostsidetests/theme/app/src/android/theme/app/LayoutModifier.java
deleted file mode 100644
index e007129..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/LayoutModifier.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 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.theme.app;
-
-import android.view.View;
-
-/**
- * Interface used to do further setup on a view after it has been inflated.
- */
-public interface LayoutModifier {
-
-    /**
-     * Modifies the view before it has been added to a parent. Useful for avoiding animations in
-     * response to setter calls.
-     *
-     * @param view the view inflated by the test activity
-     */
-    void modifyViewBeforeAdd(View view);
-
-    /**
-     * Modifies the view after it has been added to a parent. Useful for running animations in
-     * response to setter calls.
-     *
-     * @param view the view inflated by the test activity
-     */
-    void modifyViewAfterAdd(View view);
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java b/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java
new file mode 100644
index 0000000..d6c9f1c
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/TestConfiguration.java
@@ -0,0 +1,229 @@
+/*
+ * 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
+ */
+
+package android.theme.app;
+
+import android.os.Build;
+import android.theme.app.modifiers.DatePickerModifier;
+import android.theme.app.modifiers.ProgressBarModifier;
+import android.theme.app.modifiers.SearchViewModifier;
+import android.theme.app.modifiers.ViewCheckedModifier;
+import android.theme.app.modifiers.ViewPressedModifier;
+import android.theme.app.modifiers.TimePickerModifier;
+
+/**
+ * Constants defining the themes and layouts to be verified.
+ */
+public class TestConfiguration {
+    @SuppressWarnings("deprecation")
+    static final ThemeInfo[] THEMES = {
+            // Holo
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo,
+                    Build.VERSION_CODES.HONEYCOMB, "holo"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Dialog,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Dialog_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_DialogWhenLarge,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_InputMethod,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_NoActionBar_Overscan,
+                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Panel,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Wallpaper,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
+
+            // Holo Light
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_DarkActionBar,
+                    Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_Dialog,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_Dialog_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_NoActionBar,
+                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
+                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
+            new ThemeInfo(ThemeInfo.HOLO, android.R.style.Theme_Holo_Light_Panel,
+                    Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
+
+            // Material
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material,
+                    Build.VERSION_CODES.LOLLIPOP, "material"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog_Alert,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Dialog_Presentation,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_InputMethod,
+                    Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_NoActionBar_Overscan,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Panel,
+                    Build.VERSION_CODES.LOLLIPOP, "material_panel"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Settings,
+                    Build.VERSION_CODES.LOLLIPOP, "material_settings"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Voice,
+                    Build.VERSION_CODES.LOLLIPOP, "material_voice"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Wallpaper,
+                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Wallpaper_NoTitleBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
+
+            // Material Light
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_DarkActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Alert,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Presentation,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_LightStatusBar,
+                    Build.VERSION_CODES.M, "material_light_lightstatusbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Overscan,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Panel,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
+            new ThemeInfo(ThemeInfo.MATERIAL, android.R.style.Theme_Material_Light_Voice,
+                    Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
+    };
+
+    static final LayoutInfo[] LAYOUTS = {
+            new LayoutInfo(R.layout.button, "button"),
+            new LayoutInfo(R.layout.button, "button_pressed",
+                    new ViewPressedModifier()),
+            new LayoutInfo(R.layout.checkbox, "checkbox"),
+            new LayoutInfo(R.layout.checkbox, "checkbox_checked",
+                    new ViewCheckedModifier()),
+            new LayoutInfo(R.layout.chronometer, "chronometer"),
+            new LayoutInfo(R.layout.color_blue_bright, "color_blue_bright"),
+            new LayoutInfo(R.layout.color_blue_dark, "color_blue_dark"),
+            new LayoutInfo(R.layout.color_blue_light, "color_blue_light"),
+            new LayoutInfo(R.layout.color_green_dark, "color_green_dark"),
+            new LayoutInfo(R.layout.color_green_light, "color_green_light"),
+            new LayoutInfo(R.layout.color_orange_dark, "color_orange_dark"),
+            new LayoutInfo(R.layout.color_orange_light, "color_orange_light"),
+            new LayoutInfo(R.layout.color_purple, "color_purple"),
+            new LayoutInfo(R.layout.color_red_dark, "color_red_dark"),
+            new LayoutInfo(R.layout.color_red_light, "color_red_light"),
+            new LayoutInfo(R.layout.datepicker, "datepicker",
+                    new DatePickerModifier()),
+            new LayoutInfo(R.layout.edittext, "edittext"),
+            new LayoutInfo(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
+            new LayoutInfo(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
+            new LayoutInfo(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
+            new LayoutInfo(R.layout.progressbar_large, "progressbar_large",
+                    new ProgressBarModifier()),
+            new LayoutInfo(R.layout.progressbar_small, "progressbar_small",
+                    new ProgressBarModifier()),
+            new LayoutInfo(R.layout.progressbar, "progressbar",
+                    new ProgressBarModifier()),
+            new LayoutInfo(R.layout.radiobutton_checked, "radiobutton_checked"),
+            new LayoutInfo(R.layout.radiobutton, "radiobutton"),
+            new LayoutInfo(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
+            new LayoutInfo(R.layout.radiogroup_vertical, "radiogroup_vertical"),
+            new LayoutInfo(R.layout.ratingbar_0, "ratingbar_0"),
+            new LayoutInfo(R.layout.ratingbar_2point5, "ratingbar_2point5"),
+            new LayoutInfo(R.layout.ratingbar_5, "ratingbar_5"),
+            new LayoutInfo(R.layout.ratingbar_0, "ratingbar_0_pressed",
+                    new ViewPressedModifier()),
+            new LayoutInfo(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
+                    new ViewPressedModifier()),
+            new LayoutInfo(R.layout.ratingbar_5, "ratingbar_5_pressed",
+                    new ViewPressedModifier()),
+            new LayoutInfo(R.layout.searchview, "searchview_query",
+                    new SearchViewModifier(SearchViewModifier.QUERY)),
+            new LayoutInfo(R.layout.searchview, "searchview_query_hint",
+                    new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
+            new LayoutInfo(R.layout.seekbar_0, "seekbar_0"),
+            new LayoutInfo(R.layout.seekbar_100, "seekbar_100"),
+            new LayoutInfo(R.layout.seekbar_50, "seekbar_50"),
+            new LayoutInfo(R.layout.spinner, "spinner"),
+            new LayoutInfo(R.layout.switch_button_checked, "switch_button_checked"),
+            new LayoutInfo(R.layout.switch_button, "switch_button"),
+            new LayoutInfo(R.layout.textview, "textview"),
+            new LayoutInfo(R.layout.timepicker, "timepicker",
+                    new TimePickerModifier()),
+            new LayoutInfo(R.layout.togglebutton_checked, "togglebutton_checked"),
+            new LayoutInfo(R.layout.togglebutton, "togglebutton"),
+    };
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
index b30ffd3..9227edd 100644
--- a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
@@ -16,19 +16,17 @@
 
 package android.theme.app;
 
+import static android.theme.app.TestConfiguration.LAYOUTS;
+import static android.theme.app.TestConfiguration.THEMES;
+
 import android.animation.ValueAnimator;
 import android.app.Activity;
+import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.os.Build;
 import android.os.Bundle;
-import android.theme.app.modifiers.DatePickerModifier;
-import android.theme.app.modifiers.ProgressBarModifier;
-import android.theme.app.modifiers.SearchViewModifier;
-import android.theme.app.modifiers.TimePickerModifier;
-import android.theme.app.modifiers.ViewCheckedModifier;
-import android.theme.app.modifiers.ViewPressedModifier;
+import android.theme.app.modifiers.AbstractLayoutModifier;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,7 +34,6 @@
 import android.widget.DatePicker;
 
 import java.io.File;
-import java.lang.Override;
 
 /**
  * A activity which display various UI elements with non-modifiable themes.
@@ -53,7 +50,7 @@
      */
     private static final long HOLO_CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
 
-    private Theme mTheme;
+    private ThemeInfo mTheme;
     private ReferenceViewGroup mViewGroup;
     private File mOutputDir;
     private int mLayoutIndex;
@@ -94,9 +91,18 @@
 
         mViewGroup = findViewById(R.id.reference_view_group);
 
-        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
-                | LayoutParams.FLAG_TURN_SCREEN_ON
-                | LayoutParams.FLAG_DISMISS_KEYGUARD );
+        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+        // The activity has been created, but we don't want to start image generation until various
+        // asynchronous conditions are satisfied.
+        new ConditionCheck(this, () -> setNextLayout(), message -> finish(message, false))
+                .addCondition("Device is unlocked",
+                        () -> !getSystemService(KeyguardManager.class).isDeviceLocked())
+                .addCondition("Window is focused",
+                        () -> hasWindowFocus())
+                .addCondition("Activity is resumed",
+                        () -> mIsRunning)
+                .start();
     }
 
     @Override
@@ -104,8 +110,6 @@
         super.onResume();
 
         mIsRunning = true;
-
-        setNextLayout();
     }
 
     @Override
@@ -115,8 +119,7 @@
         if (!isFinishing()) {
             // The activity paused for some reason, likely a system crash
             // dialog. Finish it so we can move to the next theme.
-            Log.w(TAG, "onPause() called without a call to finish()", new RuntimeException());
-            finish();
+            Log.e(TAG, "onPause() called without a call to finish(), check system dialogs");
         }
 
         super.onPause();
@@ -125,10 +128,7 @@
     @Override
     protected void onDestroy() {
         if (mLayoutIndex < LAYOUTS.length) {
-            final Intent data = new Intent();
-            data.putExtra(GenerateImagesActivity.EXTRA_REASON, "Only rendered "
-                    + mLayoutIndex + "/" + LAYOUTS.length + " layouts");
-            setResult(RESULT_CANCELED, data);
+            finish("Only rendered " + mLayoutIndex + "/" + LAYOUTS.length + " layouts", false);
         }
 
         super.onDestroy();
@@ -139,15 +139,14 @@
      */
     private void setNextLayout() {
         if (mLayoutIndex >= LAYOUTS.length) {
-            setResult(RESULT_OK);
-            finish();
+            finish("Rendered all layouts", true);
             return;
         }
 
         mViewGroup.removeAllViews();
 
-        final Layout layout = LAYOUTS[mLayoutIndex++];
-        final LayoutModifier modifier = layout.modifier;
+        final LayoutInfo layout = LAYOUTS[mLayoutIndex++];
+        final AbstractLayoutModifier modifier = layout.modifier;
         final String layoutName = String.format("%s_%s", mTheme.name, layout.name);
         final LayoutInflater layoutInflater = LayoutInflater.from(mViewGroup.getContext());
         final View view = layoutInflater.inflate(layout.id, mViewGroup, false);
@@ -166,9 +165,9 @@
                 + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")");
 
         final Runnable generateBitmapRunnable = () ->
-            new BitmapTask(view, layoutName, modifier).execute();
+            new BitmapTask(view, layoutName).execute();
 
-        if (view instanceof DatePicker && mTheme.spec == Theme.HOLO) {
+        if (view instanceof DatePicker && mTheme.spec == ThemeInfo.HOLO) {
             // The Holo-styled DatePicker uses a CalendarView that has a
             // non-configurable adjustment duration of 540ms.
             view.postDelayed(generateBitmapRunnable, HOLO_CALENDAR_VIEW_ADJUSTMENT_DURATION);
@@ -177,13 +176,20 @@
         }
     }
 
+    private void finish(String reason, boolean success) {
+        final Intent data = new Intent();
+        data.putExtra(GenerateImagesActivity.EXTRA_REASON, reason);
+        setResult(success ? RESULT_OK : RESULT_CANCELED, data);
+
+        if (!isFinishing()) {
+            finish();
+        }
+    }
+
     private class BitmapTask extends GenerateBitmapTask {
-        private final LayoutModifier mLayoutModifier;
 
-        public BitmapTask(View view, String name, LayoutModifier modifier) {
+        public BitmapTask(View view, String name) {
             super(view, mOutputDir, name);
-
-            mLayoutModifier = modifier;
         }
 
         @Override
@@ -191,250 +197,9 @@
             if (success && mIsRunning) {
                 setNextLayout();
             } else {
-                Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? "
-                        + mIsRunning + ")");
-                finish();
+                finish("Failed to render view to bitmap: " + mName + " (activity running? "
+                        + mIsRunning + ")", false);
             }
         }
     }
-
-    /**
-     * A class to encapsulate information about a theme.
-     */
-    static class Theme {
-        public static final int HOLO = 0;
-        public static final int MATERIAL = 1;
-
-        public final int spec;
-        public final int id;
-        public final int apiLevel;
-        public final String name;
-
-        private Theme(int spec, int id, int apiLevel, String name) {
-            this.spec = spec;
-            this.id = id;
-            this.apiLevel = apiLevel;
-            this.name = name;
-        }
-    }
-
-    // List of themes to verify.
-    @SuppressWarnings("deprecation")
-    static final Theme[] THEMES = {
-            // Holo
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo,
-                    Build.VERSION_CODES.HONEYCOMB, "holo"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_MinWidth,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_InputMethod,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Fullscreen,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Overscan,
-                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
-                    Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Panel,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
-
-            // Holo Light
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DarkActionBar,
-                    Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_MinWidth,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar,
-                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
-                    Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
-                    Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
-                    Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
-            new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Panel,
-                    Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
-
-            // Material
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material,
-                    Build.VERSION_CODES.LOLLIPOP, "material"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Alert,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_MinWidth,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Presentation,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_InputMethod,
-                    Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Fullscreen,
-                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Overscan,
-                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
-                    Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Panel,
-                    Build.VERSION_CODES.LOLLIPOP, "material_panel"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Settings,
-                    Build.VERSION_CODES.LOLLIPOP, "material_settings"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Voice,
-                    Build.VERSION_CODES.LOLLIPOP, "material_voice"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper,
-                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper_NoTitleBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
-
-            // Material Light
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DarkActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Alert,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_MinWidth,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Presentation,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_LightStatusBar,
-                    Build.VERSION_CODES.M, "material_light_lightstatusbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Overscan,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Panel,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
-            new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Voice,
-                    Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
-    };
-
-    /**
-     * A class to encapsulate information about a layout.
-     */
-    private static class Layout {
-        public final int id;
-        public final String name;
-        public final LayoutModifier modifier;
-
-        private Layout(int id, String name) {
-            this(id, name, null);
-        }
-
-        private Layout(int id, String name, LayoutModifier modifier) {
-            this.id = id;
-            this.name = name;
-            this.modifier = modifier;
-        }
-    }
-
-    // List of layouts to verify for each theme.
-    private static final Layout[] LAYOUTS = {
-            new Layout(R.layout.button, "button"),
-            new Layout(R.layout.button, "button_pressed",
-                    new ViewPressedModifier()),
-            new Layout(R.layout.checkbox, "checkbox"),
-            new Layout(R.layout.checkbox, "checkbox_checked",
-                    new ViewCheckedModifier()),
-            new Layout(R.layout.chronometer, "chronometer"),
-            new Layout(R.layout.color_blue_bright, "color_blue_bright"),
-            new Layout(R.layout.color_blue_dark, "color_blue_dark"),
-            new Layout(R.layout.color_blue_light, "color_blue_light"),
-            new Layout(R.layout.color_green_dark, "color_green_dark"),
-            new Layout(R.layout.color_green_light, "color_green_light"),
-            new Layout(R.layout.color_orange_dark, "color_orange_dark"),
-            new Layout(R.layout.color_orange_light, "color_orange_light"),
-            new Layout(R.layout.color_purple, "color_purple"),
-            new Layout(R.layout.color_red_dark, "color_red_dark"),
-            new Layout(R.layout.color_red_light, "color_red_light"),
-            new Layout(R.layout.datepicker, "datepicker",
-                    new DatePickerModifier()),
-            new Layout(R.layout.edittext, "edittext"),
-            new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
-            new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
-            new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
-            new Layout(R.layout.progressbar_large, "progressbar_large",
-                    new ProgressBarModifier()),
-            new Layout(R.layout.progressbar_small, "progressbar_small",
-                    new ProgressBarModifier()),
-            new Layout(R.layout.progressbar, "progressbar",
-                    new ProgressBarModifier()),
-            new Layout(R.layout.radiobutton_checked, "radiobutton_checked"),
-            new Layout(R.layout.radiobutton, "radiobutton"),
-            new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
-            new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"),
-            new Layout(R.layout.ratingbar_0, "ratingbar_0"),
-            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"),
-            new Layout(R.layout.ratingbar_5, "ratingbar_5"),
-            new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed",
-                    new ViewPressedModifier()),
-            new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
-                    new ViewPressedModifier()),
-            new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed",
-                    new ViewPressedModifier()),
-            new Layout(R.layout.searchview, "searchview_query",
-                    new SearchViewModifier(SearchViewModifier.QUERY)),
-            new Layout(R.layout.searchview, "searchview_query_hint",
-                    new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
-            new Layout(R.layout.seekbar_0, "seekbar_0"),
-            new Layout(R.layout.seekbar_100, "seekbar_100"),
-            new Layout(R.layout.seekbar_50, "seekbar_50"),
-            new Layout(R.layout.spinner, "spinner"),
-            new Layout(R.layout.switch_button_checked, "switch_button_checked"),
-            new Layout(R.layout.switch_button, "switch_button"),
-            new Layout(R.layout.textview, "textview"),
-            new Layout(R.layout.timepicker, "timepicker",
-                    new TimePickerModifier()),
-            new Layout(R.layout.togglebutton_checked, "togglebutton_checked"),
-            new Layout(R.layout.togglebutton, "togglebutton"),
-    };
 }
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeInfo.java b/hostsidetests/theme/app/src/android/theme/app/ThemeInfo.java
new file mode 100644
index 0000000..6a3d4aa
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeInfo.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+
+package android.theme.app;
+
+/**
+ * A class to encapsulate information about a theme.
+ */
+class ThemeInfo {
+    public static final int HOLO = 0;
+    public static final int MATERIAL = 1;
+
+    public final int spec;
+    public final int id;
+    public final int apiLevel;
+    public final String name;
+
+    ThemeInfo(int spec, int id, int apiLevel, String name) {
+        this.spec = spec;
+        this.id = id;
+        this.apiLevel = apiLevel;
+        this.name = name;
+    }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/modifiers/AbstractLayoutModifier.java b/hostsidetests/theme/app/src/android/theme/app/modifiers/AbstractLayoutModifier.java
index e9dca7a..2c71cc6 100644
--- a/hostsidetests/theme/app/src/android/theme/app/modifiers/AbstractLayoutModifier.java
+++ b/hostsidetests/theme/app/src/android/theme/app/modifiers/AbstractLayoutModifier.java
@@ -16,19 +16,30 @@
 
 package android.theme.app.modifiers;
 
-import android.theme.app.LayoutModifier;
 import android.view.View;
 
 /**
- * {@link LayoutModifier} that does nothing.
+ * {@link AbstractLayoutModifier} that does nothing.
  */
-abstract class AbstractLayoutModifier implements LayoutModifier {
+public abstract class AbstractLayoutModifier {
 
-    @Override
+    /**
+     * Modifies the view before it has been added to a parent. Useful for avoiding animations in
+     * response to setter calls.
+     *
+     * @param view the view inflated by the test activity
+     */
     public void modifyViewBeforeAdd(View view) {
+
     }
 
-    @Override
+    /**
+     * Modifies the view after it has been added to a parent. Useful for running animations in
+     * response to setter calls.
+     *
+     * @param view the view inflated by the test activity
+     */
     public void modifyViewAfterAdd(View view) {
+
     }
 }
diff --git a/hostsidetests/theme/app/src/android/theme/app/modifiers/DatePickerModifier.java b/hostsidetests/theme/app/src/android/theme/app/modifiers/DatePickerModifier.java
index f155fcb..58ddb43 100644
--- a/hostsidetests/theme/app/src/android/theme/app/modifiers/DatePickerModifier.java
+++ b/hostsidetests/theme/app/src/android/theme/app/modifiers/DatePickerModifier.java
@@ -16,12 +16,11 @@
 
 package android.theme.app.modifiers;
 
-import android.theme.app.LayoutModifier;
 import android.view.View;
 import android.widget.DatePicker;
 
 /**
- * {@link LayoutModifier} that sets a precise date on a {@link DatePicker}.
+ * {@link AbstractLayoutModifier} that sets a precise date on a {@link DatePicker}.
  */
 public class DatePickerModifier extends AbstractLayoutModifier {
 
diff --git a/hostsidetests/theme/app/src/android/theme/app/modifiers/ProgressBarModifier.java b/hostsidetests/theme/app/src/android/theme/app/modifiers/ProgressBarModifier.java
index dcd8c89..325751f 100644
--- a/hostsidetests/theme/app/src/android/theme/app/modifiers/ProgressBarModifier.java
+++ b/hostsidetests/theme/app/src/android/theme/app/modifiers/ProgressBarModifier.java
@@ -16,7 +16,6 @@
 
 package android.theme.app.modifiers;
 
-import android.animation.ValueAnimator;
 import android.view.View;
 import android.view.animation.Interpolator;
 import android.widget.ProgressBar;
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 199830f..7a931a4 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -159,7 +159,7 @@
             return;
         }
 
-        assertTrue("Aborted image generation", generateDeviceImages());
+        assertTrue("Aborted image generation, see device log for details", generateDeviceImages());
 
         // Pull ZIP file from remote device.
         final File localZip = File.createTempFile("generated", ".zip");
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 9ab8222..7dac96f 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -653,22 +653,46 @@
     }
 
     int64_t getMinFrameDurationFor(int64_t format, int64_t width, int64_t height) {
-        return getDurationFor(ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, format, width, height);
+        int32_t minFrameDurationTag = (format == AIMAGE_FORMAT_HEIC) ?
+                ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS :
+                ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+        return getDurationFor(minFrameDurationTag, format, width, height);
     }
 
     int64_t getStallDurationFor(int64_t format, int64_t width, int64_t height) {
-        return getDurationFor(ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS, format, width, height);
+        int32_t stallDurationTag = (format == AIMAGE_FORMAT_HEIC) ?
+                ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS :
+                ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS;
+        return getDurationFor(stallDurationTag, format, width, height);
     }
 
     bool getMaxSizeForFormat(int32_t format, int32_t *width, int32_t *height) {
         ACameraMetadata_const_entry entry;
-        ACameraMetadata_getConstEntry(mChars,
-                ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
+
+        int32_t streamConfigTag, streamConfigOutputTag;
+        switch (format) {
+            case AIMAGE_FORMAT_HEIC:
+                streamConfigTag = ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS;
+                streamConfigOutputTag = ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT;
+                break;
+            case AIMAGE_FORMAT_JPEG:
+            case AIMAGE_FORMAT_Y8:
+            default:
+                streamConfigTag = ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+                streamConfigOutputTag = ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
+                break;
+        }
+
         bool supported = false;
-        int32_t w = 0, h = 0;
+        camera_status_t status = ACameraMetadata_getConstEntry(mChars, streamConfigTag, &entry);
+        if (status == ACAMERA_ERROR_METADATA_NOT_FOUND) {
+            return supported;
+        }
+
+       int32_t w = 0, h = 0;
         for (uint32_t i = 0; i < entry.count; i += 4) {
             if (entry.data.i32[i] == format &&
-                    entry.data.i32[i+3] == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    entry.data.i32[i+3] == streamConfigOutputTag &&
                     entry.data.i32[i+1] * entry.data.i32[i+2] > w * h) {
                 w = entry.data.i32[i+1];
                 h = entry.data.i32[i+2];
@@ -685,11 +709,25 @@
 
     bool isSizeSupportedForFormat(int32_t format, int32_t width, int32_t height) {
         ACameraMetadata_const_entry entry;
-        ACameraMetadata_getConstEntry(mChars,
-                ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
+
+        int32_t streamConfigTag, streamConfigOutputTag;
+        switch (format) {
+            case AIMAGE_FORMAT_HEIC:
+                streamConfigTag = ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS;
+                streamConfigOutputTag = ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT;
+                break;
+            case AIMAGE_FORMAT_JPEG:
+            case AIMAGE_FORMAT_Y8:
+            default:
+                streamConfigTag = ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+                streamConfigOutputTag = ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT;
+                break;
+        }
+
+        ACameraMetadata_getConstEntry(mChars, streamConfigTag, &entry);
         for (uint32_t i = 0; i < entry.count; i += 4) {
             if (entry.data.i32[i] == format &&
-                    entry.data.i32[i+3] == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                    entry.data.i32[i+3] == streamConfigOutputTag &&
                     entry.data.i32[i+1] == width &&
                     entry.data.i32[i+2] == height) {
                 return true;
@@ -702,7 +740,9 @@
         if (tag != ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS &&
                 tag != ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS &&
                 tag != ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS &&
-                tag != ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS) {
+                tag != ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS &&
+                tag != ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS &&
+                tag != ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS) {
             return -1;
         }
         ACameraMetadata_const_entry entry;
@@ -2860,6 +2900,7 @@
                 testHeight = TEST_HEIGHT;
                 break;
             case AIMAGE_FORMAT_Y8:
+            case AIMAGE_FORMAT_HEIC:
                 if (!staticInfo.getMaxSizeForFormat(format, &testWidth, &testHeight)) {
                     // This isn't an error condition: device does't support this
                     // format.
@@ -2956,13 +2997,13 @@
         }
 
         int64_t minFrameDurationNs = staticInfo.getMinFrameDurationFor(
-                AIMAGE_FORMAT_JPEG, TEST_WIDTH, TEST_HEIGHT);
+                format, testWidth, testHeight);
         if (minFrameDurationNs < 0) {
             LOG_ERROR(errorString, "Get camera %s minFrameDuration failed", cameraId);
             goto cleanup;
         }
-        int64_t stallDurationNs = staticInfo.getStallDurationFor(
-                AIMAGE_FORMAT_JPEG, TEST_WIDTH, TEST_HEIGHT);
+        int64_t stallDurationNs = (format == AIMAGE_FORMAT_Y8) ? 0 :
+                staticInfo.getStallDurationFor(format, testWidth, testHeight);
         if (stallDurationNs < 0) {
             LOG_ERROR(errorString, "Get camera %s stallDuration failed", cameraId);
             goto cleanup;
@@ -3053,6 +3094,15 @@
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeImageReaderTest_\
+testHeicNative(
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+    ALOGV("%s", __FUNCTION__);
+    return nativeImageReaderTestBase(env, jOutPath, AIMAGE_FORMAT_HEIC,
+            ImageReaderListener::validateImageCb);
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeImageReaderTest_\
 testImageReaderCloseAcquiredImagesNative(
         JNIEnv* env, jclass /*clazz*/) {
     ALOGV("%s", __FUNCTION__);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index b095b8a..8eff888 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -187,6 +187,7 @@
 
             boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
                     && arrayContains(outputFormats, ImageFormat.Y8);
+            boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
 
             assertArrayContains(
                     String.format("No valid YUV_420_888 preview formats found for: ID %s",
@@ -202,6 +203,7 @@
             Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
             Size[] y8Sizes = config.getOutputSizes(ImageFormat.Y8);
             Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
+            Size[] heicSizes = config.getOutputSizes(ImageFormat.HEIC);
             Size[] privateSizes = config.getOutputSizes(ImageFormat.PRIVATE);
 
             CameraTestUtils.assertArrayNotEmpty(yuvSizes,
@@ -224,6 +226,12 @@
                         "Required FULLHD size not found for format %x for: ID %s",
                         ImageFormat.JPEG, mIds[counter]), jpegSizes,
                         new Size[] {FULLHD, FULLHD_ALT});
+                if (supportHeic) {
+                    assertArrayContainsAnyOf(String.format(
+                            "Required FULLHD size not found for format %x for: ID %s",
+                            ImageFormat.HEIC, mIds[counter]), heicSizes,
+                            new Size[] {FULLHD, FULLHD_ALT});
+                }
             }
 
             if (activeArraySize.getWidth() >= HD.getWidth() &&
@@ -231,6 +239,11 @@
                 assertArrayContains(String.format(
                         "Required HD size not found for format %x for: ID %s",
                         ImageFormat.JPEG, mIds[counter]), jpegSizes, HD);
+                if (supportHeic) {
+                    assertArrayContains(String.format(
+                            "Required HD size not found for format %x for: ID %s",
+                            ImageFormat.HEIC, mIds[counter]), heicSizes, HD);
+                }
             }
 
             if (activeArraySize.getWidth() >= VGA.getWidth() &&
@@ -238,6 +251,11 @@
                 assertArrayContains(String.format(
                         "Required VGA size not found for format %x for: ID %s",
                         ImageFormat.JPEG, mIds[counter]), jpegSizes, VGA);
+                if (supportHeic) {
+                    assertArrayContains(String.format(
+                            "Required VGA size not found for format %x for: ID %s",
+                            ImageFormat.HEIC, mIds[counter]), heicSizes, VGA);
+                }
             }
 
             if (activeArraySize.getWidth() >= QVGA.getWidth() &&
@@ -245,6 +263,12 @@
                 assertArrayContains(String.format(
                         "Required QVGA size not found for format %x for: ID %s",
                         ImageFormat.JPEG, mIds[counter]), jpegSizes, QVGA);
+                if (supportHeic) {
+                    assertArrayContains(String.format(
+                            "Required QVGA size not found for format %x for: ID %s",
+                            ImageFormat.HEIC, mIds[counter]), heicSizes, QVGA);
+                }
+
             }
 
             ArrayList<Size> jpegSizesList = new ArrayList<>(Arrays.asList(jpegSizes));
@@ -1363,6 +1387,8 @@
                 CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
             Rect activeArray = c.get(
                 CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+            float jpegAspectRatioThreshold = .01f;
+            boolean jpegSizeMatch = false;
 
             // Verify pre-correction array encloses active array
             mCollector.expectTrue("preCorrectionArray [" + precorrectionArray.left + ", " +
@@ -1392,6 +1418,7 @@
                         hasDepth16);
                 if (hasDepth16) {
                     Size[] depthSizes = configs.getOutputSizes(ImageFormat.DEPTH16);
+                    Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
                     mCollector.expectTrue("Supports DEPTH_OUTPUT but no sizes for DEPTH16 supported!",
                             depthSizes != null && depthSizes.length > 0);
                     if (depthSizes != null) {
@@ -1408,6 +1435,24 @@
                             mCollector.expectTrue("Non-negative stall duration for depth size "
                                     + depthSize + " expected, got " + stallDuration,
                                     stallDuration >= 0);
+                            if ((jpegSizes != null) && (!jpegSizeMatch)) {
+                                for (Size jpegSize : jpegSizes) {
+                                    if (jpegSize.equals(depthSize)) {
+                                        jpegSizeMatch = true;
+                                        break;
+                                    } else {
+                                        float depthAR = (float) depthSize.getWidth() /
+                                                (float) depthSize.getHeight();
+                                        float jpegAR = (float) jpegSize.getWidth() /
+                                                (float) jpegSize.getHeight();
+                                        if (Math.abs(depthAR - jpegAR) <=
+                                                jpegAspectRatioThreshold) {
+                                            jpegSizeMatch = true;
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
                         }
                     }
                 }
@@ -1446,6 +1491,8 @@
                     mCollector.expectTrue("Supports DEPTH_JPEG " +
                             "but no sizes for DEPTH_JPEG supported!",
                             depthJpegSizes != null && depthJpegSizes.length > 0);
+                    mCollector.expectTrue("Supports DEPTH_JPEG but there are no JPEG sizes with" +
+                            " matching DEPTH16 aspect ratio", jpegSizeMatch);
                     if (depthJpegSizes != null) {
                         for (Size depthJpegSize : depthJpegSizes) {
                             mCollector.expectTrue("All depth jpeg sizes must be nonzero",
@@ -1462,6 +1509,11 @@
                                     stallDuration >= 0);
                         }
                     }
+                } else {
+                    boolean canSupportDynamicDepth = jpegSizeMatch && !depthIsExclusive;
+                    mCollector.expectTrue("Device must support DEPTH_JPEG, please check whether " +
+                            "library libdepthphoto.so is part of the device PRODUCT_PACKAGES",
+                            !canSupportDynamicDepth);
                 }
 
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index af6e5e0..0716c24 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -91,7 +91,7 @@
         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
 
         prepareStillCaptureAndStartPreview(previewRequest, stillCaptureRequest,
-                previewSize, stillSize, resultListener, imageListener);
+                previewSize, stillSize, resultListener, imageListener, false /*isHeic*/);
 
         CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index e275aa4..b8cf221 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -204,6 +204,17 @@
         }
     }
 
+    public void testHeic() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing heic capture for Camera " + id);
+                openDevice(id);
+                bufferFormatTestByCamera(ImageFormat.HEIC, /*repeating*/false);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
 
     public void testRepeatingJpeg() throws Exception {
         for (String id : mCameraIds) {
@@ -243,6 +254,18 @@
         }
     }
 
+    public void testRepeatingHeic() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing repeating heic capture for Camera " + id);
+                openDevice(id);
+                bufferFormatTestByCamera(ImageFormat.HEIC, /*repeating*/true);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
     public void testLongProcessingRepeatingRaw() throws Exception {
         for (String id : mCameraIds) {
             try {
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index 4eee734..ce5f946 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -43,6 +43,11 @@
                 testY8Native(mDebugFileNameBase));
     }
 
+    public void testHeic() {
+        assertTrue("testHeic fail, see log for details",
+                testHeicNative(mDebugFileNameBase));
+    }
+
     public void testImageReaderCloseAcquiredImages() {
         assertTrue("testImageReaderClose fail, see log for details",
                 testImageReaderCloseAcquiredImagesNative());
@@ -50,5 +55,6 @@
 
     private static native boolean testJpegNative(String filePath);
     private static native boolean testY8Native(String filePath);
+    private static native boolean testHeicNative(String filePath);
     private static native boolean testImageReaderCloseAcquiredImagesNative();
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 0fa1741..fbb8cce 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -363,7 +363,8 @@
 
                     readers = prepareStillCaptureAndStartPreview(previewBuilder, captureBuilder,
                             mOrderedPreviewSizes.get(0), imageSizes, formats,
-                            previewResultListener, NUM_MAX_IMAGES, imageListeners);
+                            previewResultListener, NUM_MAX_IMAGES, imageListeners,
+                            false /*isHeic*/);
 
                     if (addPreviewDelay) {
                         Thread.sleep(500);
@@ -1141,11 +1142,13 @@
      * @param resultListener Capture result listener
      * @param maxNumImages The max number of images set to the image reader
      * @param imageListeners The single capture capture image listeners
+     * @param isHeic Capture HEIC image if true, JPEG image if false
      */
     private ImageReader[] prepareStillCaptureAndStartPreview(
             CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest,
             Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener,
-            int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners)
+            int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners,
+            boolean isHeic)
             throws Exception {
 
         if ((captureSizes == null) || (formats == null) || (imageListeners == null) &&
diff --git a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 8fa1077..1420078 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -116,6 +116,24 @@
     }
 
     /**
+     * Test YUV_420_888 -> HEIC with maximal supported sizes
+     */
+    @Test
+    public void testBasicYuvToHeicReprocessing() throws Exception {
+        for (String id : mCameraIds) {
+            if (!isYuvReprocessSupported(id)) {
+                continue;
+            }
+            if (!mAllStaticInfo.get(id).isHeicSupported()) {
+                continue;
+            }
+
+            // YUV_420_888 -> HEIC must be supported.
+            testBasicReprocessing(id, ImageFormat.YUV_420_888, ImageFormat.HEIC);
+        }
+    }
+
+    /**
      * Test OPAQUE -> YUV_420_888 with maximal supported sizes
      */
     @Test
@@ -146,6 +164,24 @@
     }
 
     /**
+     * Test OPAQUE -> HEIC with maximal supported sizes
+     */
+    @Test
+    public void testBasicOpaqueToHeicReprocessing() throws Exception {
+        for (String id : mCameraIds) {
+            if (!isOpaqueReprocessSupported(id)) {
+                continue;
+            }
+            if (!mAllStaticInfo.get(id).isHeicSupported()) {
+                continue;
+            }
+
+            // OPAQUE -> HEIC must be supported.
+            testBasicReprocessing(id, ImageFormat.PRIVATE, ImageFormat.HEIC);
+        }
+    }
+
+    /**
      * Test all supported size and format combinations.
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
@@ -978,7 +1014,7 @@
                 Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
                 verifyJpegKeys(image, reprocessResults[i], reprocessOutputSize,
                         testThumbnailSizes[i], EXIF_TEST_DATA[i], mStaticInfo, mCollector,
-                        mDebugFileNameBase);
+                        mDebugFileNameBase, ImageFormat.JPEG);
                 image.close();
 
             }
@@ -1391,6 +1427,9 @@
             case ImageFormat.JPEG:
                 filename += ".jpg";
                 break;
+            case ImageFormat.HEIC:
+                filename += ".heic";
+                break;
             case ImageFormat.NV16:
             case ImageFormat.NV21:
             case ImageFormat.YUV_420_888:
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 995a2c7..a519101 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -254,8 +254,10 @@
     private void setupConfigurationTargets(List<MandatoryStreamInformation> streamsInfo,
             List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
             List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
-            List<ImageReader> rawTargets, List<OutputConfiguration> outputConfigs,
-            int numBuffers, boolean substituteY8, MandatoryStreamInformation overrideStreamInfo,
+            List<ImageReader> rawTargets, List<ImageReader> heicTargets,
+            List<OutputConfiguration> outputConfigs,
+            int numBuffers, boolean substituteY8, boolean substituteHeic,
+            MandatoryStreamInformation overrideStreamInfo,
             List<String> overridePhysicalCameraIds, List<Size> overridePhysicalCameraSizes) {
 
         ImageDropperListener imageDropperListener = new ImageDropperListener();
@@ -267,6 +269,8 @@
             int format = streamInfo.getFormat();
             if (substituteY8 && (format == ImageFormat.YUV_420_888)) {
                 format = ImageFormat.Y8;
+            } else if (substituteHeic && (format == ImageFormat.JPEG)) {
+                format = ImageFormat.HEIC;
             }
             Surface newSurface;
             Size[] availableSizes = new Size[streamInfo.getAvailableSizes().size()];
@@ -345,6 +349,18 @@
                         }
                         break;
                     }
+                    case ImageFormat.HEIC: {
+                        ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+                                targetSize.getHeight(), format, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                        if (numConfigs > 1) {
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        heicTargets.add(target);
+                        break;
+                    }
                     default:
                         fail("Unknown output format " + format);
                 }
@@ -366,13 +382,36 @@
             }
         }
 
+        // Check whether substituting JPEG format with HEIC format
+        boolean substituteHeic = false;
+        if (mStaticInfo.isHeicSupported()) {
+            List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
+            for (MandatoryStreamInformation streamInfo : streamsInfo) {
+                if (streamInfo.getFormat() == ImageFormat.JPEG) {
+                    substituteHeic = true;
+                    break;
+                }
+            }
+        }
+
         // Test camera output combination
         Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
                 " on camera: " + cameraId);
-        testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/false);
+        testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/false,
+                /*substituteHeic*/false);
 
         if (substituteY8) {
-            testMandatoryStreamCombination(cameraId, combination, substituteY8);
+            Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
+                    " on camera: " + cameraId + " with Y8");
+            testMandatoryStreamCombination(cameraId, combination, /*substituteY8*/true,
+                    /*substituteHeic*/false);
+        }
+
+        if (substituteHeic) {
+            Log.i(TAG, "Testing mandatory stream combination: " + combination.getDescription() +
+                    " on camera: " + cameraId + " with HEIC");
+            testMandatoryStreamCombination(cameraId, combination,
+                    /*substituteY8*/false, /*substituteHeic*/true);
         }
 
         // Test substituting YUV_888/RAW with physical streams for logical camera
@@ -383,7 +422,7 @@
             testMultiCameraOutputCombination(cameraId, combination, /*substituteY8*/false);
 
             if (substituteY8) {
-                testMultiCameraOutputCombination(cameraId, combination, substituteY8);
+                testMultiCameraOutputCombination(cameraId, combination, /*substituteY8*/true);
             }
         }
     }
@@ -441,10 +480,12 @@
             List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
             List<ImageReader> y8Targets = new ArrayList<ImageReader>();
             List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+            List<ImageReader> heicTargets = new ArrayList<ImageReader>();
 
             setupConfigurationTargets(streamsInfo, privTargets, jpegTargets, yuvTargets,
-                    y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, substituteY8,
-                    streamInfo, physicalCamerasForSize, physicalCameraSizes);
+                    y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT,
+                    substituteY8, /*substituteHeic*/false, streamInfo, physicalCamerasForSize,
+                    physicalCameraSizes);
 
             boolean haveSession = false;
             try {
@@ -516,7 +557,8 @@
     }
 
     private void testMandatoryStreamCombination(String cameraId,
-            MandatoryStreamCombination combination, boolean substituteY8) throws Exception {
+            MandatoryStreamCombination combination,
+            boolean substituteY8, boolean substituteHeic) throws Exception {
 
         // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
         final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
@@ -529,9 +571,11 @@
         List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
         List<ImageReader> y8Targets = new ArrayList<ImageReader>();
         List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+        List<ImageReader> heicTargets = new ArrayList<ImageReader>();
 
         setupConfigurationTargets(combination.getStreamsInformation(), privTargets, jpegTargets,
-                yuvTargets, y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, substituteY8,
+                yuvTargets, y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT,
+                substituteY8, substituteHeic,
                 null /*overrideStreamInfo*/, null /*overridePhysicalCameraIds*/,
                 null /* overridePhysicalCameraSizes) */);
 
@@ -600,6 +644,9 @@
         for (ImageReader target : rawTargets) {
             target.close();
         }
+        for (ImageReader target : heicTargets) {
+            target.close();
+        }
     }
 
     /**
@@ -635,26 +682,42 @@
     private void testMandatoryReprocessableStreamCombination(String cameraId,
             MandatoryStreamCombination combination) {
         // Test reprocess stream combination
-        testMandatoryReprocessableStreamCombination(cameraId, combination, /*substituteY8*/false);
+        testMandatoryReprocessableStreamCombination(cameraId, combination,
+                /*substituteY8*/false, /*substituteHeic*/false);
 
         // Test substituting YUV_888 format with Y8 format in reprocess stream combination.
         if (mStaticInfo.isMonochromeWithY8()) {
             List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
-            boolean hasY8 = false;
+            boolean substituteY8 = false;
             for (MandatoryStreamInformation streamInfo : streamsInfo) {
                 if (streamInfo.getFormat() == ImageFormat.YUV_420_888) {
-                    hasY8 = true;
-                    break;
+                    substituteY8 = true;
                 }
             }
-            if (hasY8) {
-                testMandatoryReprocessableStreamCombination(cameraId, combination, hasY8);
+            if (substituteY8) {
+                testMandatoryReprocessableStreamCombination(cameraId, combination,
+                        /*substituteY8*/true, /*substituteHeic*/false);
+            }
+        }
+
+        if (mStaticInfo.isHeicSupported()) {
+            List<MandatoryStreamInformation> streamsInfo = combination.getStreamsInformation();
+            boolean substituteHeic = false;
+            for (MandatoryStreamInformation streamInfo : streamsInfo) {
+                if (streamInfo.getFormat() == ImageFormat.JPEG) {
+                    substituteHeic = true;
+                }
+            }
+            if (substituteHeic) {
+                testMandatoryReprocessableStreamCombination(cameraId, combination,
+                        /*substituteY8*/false, /*substituteHeic*/true);
             }
         }
     }
 
     private void testMandatoryReprocessableStreamCombination(String cameraId,
-            MandatoryStreamCombination combination, boolean substituteY8) {
+            MandatoryStreamCombination combination, boolean substituteY8,
+            boolean substituteHeic) {
 
         final int TIMEOUT_FOR_RESULT_MS = 3000;
         final int NUM_REPROCESS_CAPTURES_PER_CONFIG = 3;
@@ -664,6 +727,7 @@
         List<ImageReader> yuvTargets = new ArrayList<>();
         List<ImageReader> y8Targets = new ArrayList<>();
         List<ImageReader> rawTargets = new ArrayList<>();
+        List<ImageReader> heicTargets = new ArrayList<>();
         ArrayList<Surface> outputSurfaces = new ArrayList<>();
         List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
         ImageReader inputReader = null;
@@ -685,13 +749,17 @@
             inputFormat = ImageFormat.Y8;
         }
 
+        Log.i(TAG, "testMandatoryReprocessableStreamCombination: " +
+                combination.getDescription() + ", substituteY8 = " + substituteY8 +
+                ", substituteHeic = " + substituteHeic);
         try {
             // The second stream information entry is the ZSL stream, which is configured
             // separately.
             setupConfigurationTargets(streamInfo.subList(2, streamInfo.size()), privTargets,
-                    jpegTargets, yuvTargets, y8Targets, rawTargets, outputConfigs,
-                    NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8,  null /*overrideStreamInfo*/,
-                    null /*overridePhysicalCameraIds*/, null /* overridePhysicalCameraSizes) */);
+                    jpegTargets, yuvTargets, y8Targets, rawTargets, heicTargets, outputConfigs,
+                    NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8,  substituteHeic,
+                    null /*overrideStreamInfo*/, null /*overridePhysicalCameraIds*/,
+                    null /* overridePhysicalCameraSizes) */);
 
             outputSurfaces.ensureCapacity(outputConfigs.size());
             for (OutputConfiguration config : outputConfigs) {
@@ -709,7 +777,8 @@
             final boolean useY8 = inputIsY8 || y8Targets.size() > 0;
             final int totalNumReprocessCaptures =  NUM_REPROCESS_CAPTURES_PER_CONFIG * (
                     ((inputIsYuv || inputIsY8) ? 1 : 0) +
-                    jpegTargets.size() + (useYuv ? yuvTargets.size() : y8Targets.size()));
+                    (substituteHeic ? heicTargets.size() : jpegTargets.size()) +
+                    (useYuv ? yuvTargets.size() : y8Targets.size()));
 
             // It needs 1 input buffer for each reprocess capture + the number of buffers
             // that will be used as outputs.
@@ -750,6 +819,10 @@
                 reprocessOutputs.add(reader.getSurface());
             }
 
+            for (ImageReader reader : heicTargets) {
+                reprocessOutputs.add(reader.getSurface());
+            }
+
             for (ImageReader reader : yuvTargets) {
                 reprocessOutputs.add(reader.getSurface());
             }
@@ -803,6 +876,10 @@
                 target.close();
             }
 
+            for (ImageReader target : heicTargets) {
+                target.close();
+            }
+
             if (inputReader != null) {
                 inputReader.close();
             }
@@ -1916,6 +1993,7 @@
         static final int YUV  = ImageFormat.YUV_420_888;
         static final int RAW  = ImageFormat.RAW_SENSOR;
         static final int Y8   = ImageFormat.Y8;
+        static final int HEIC = ImageFormat.HEIC;
 
         // Max resolution indices
         static final int PREVIEW = 0;
@@ -1933,6 +2011,7 @@
                     StaticMetadata.StreamDirection.Output);
             Size[] jpegSizes = sm.getJpegOutputSizesChecked();
             Size[] rawSizes = sm.getRawOutputSizesChecked();
+            Size[] heicSizes = sm.getHeicOutputSizesChecked();
 
             Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
 
@@ -1975,6 +2054,13 @@
                     maxY8Sizes[MAXIMUM] = CameraTestUtils.getMaxSize(y8Sizes);
                     maxY8Sizes[VGA] = vgaSize;
                 }
+
+                if (sm.isHeicSupported()) {
+                    maxHeicSizes[PREVIEW] = getMaxSize(heicSizes, maxPreviewSize);
+                    maxHeicSizes[RECORD] = getMaxRecordingSize(cameraId);
+                    maxHeicSizes[MAXIMUM] = CameraTestUtils.getMaxSize(heicSizes);
+                    maxHeicSizes[VGA] = vgaSize;
+                }
             }
 
             Size[] privInputSizes = configs.getInputSizes(ImageFormat.PRIVATE);
@@ -1992,6 +2078,7 @@
         public final Size[] maxJpegSizes = new Size[RESOLUTION_COUNT];
         public final Size[] maxYuvSizes = new Size[RESOLUTION_COUNT];
         public final Size[] maxY8Sizes = new Size[RESOLUTION_COUNT];
+        public final Size[] maxHeicSizes = new Size[RESOLUTION_COUNT];
         public final Size maxRawSize;
         // TODO: support non maximum reprocess input.
         public final Size maxInputPrivSize;
@@ -2119,9 +2206,10 @@
             List<ImageReader> yuvTargets = new ArrayList<ImageReader>();
             List<ImageReader> y8Targets = new ArrayList<ImageReader>();
             List<ImageReader> rawTargets = new ArrayList<ImageReader>();
+            List<ImageReader> heicTargets = new ArrayList<ImageReader>();
 
             setupConfigurationTargets(config, maxSizes, privTargets, jpegTargets, yuvTargets,
-                    y8Targets, rawTargets, outputConfigs, MIN_RESULT_COUNT, i,
+                    y8Targets, rawTargets, heicTargets, outputConfigs, MIN_RESULT_COUNT, i,
                     physicalCamerasForSize, physicalCameraSizes);
 
             boolean haveSession = false;
@@ -2196,7 +2284,8 @@
     private void setupConfigurationTargets(int[] configs, MaxStreamSizes maxSizes,
             List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
             List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
-            List<ImageReader> rawTargets, List<OutputConfiguration> outputConfigs, int numBuffers,
+            List<ImageReader> rawTargets, List<ImageReader> heicTargets,
+            List<OutputConfiguration> outputConfigs, int numBuffers,
             int overrideStreamIndex, List<String> overridePhysicalCameraIds,
             List<Size> overridePhysicalCameraSizes) {
 
@@ -2241,6 +2330,20 @@
                         jpegTargets.add(target);
                         break;
                     }
+                    case HEIC: {
+                        Size targetSize = (numConfigs == 1) ? maxSizes.maxHeicSizes[sizeLimit] :
+                                overridePhysicalCameraSizes.get(j);
+                        ImageReader target = ImageReader.newInstance(
+                            targetSize.getWidth(), targetSize.getHeight(), HEIC, numBuffers);
+                        target.setOnImageAvailableListener(imageDropperListener, mHandler);
+                        OutputConfiguration config = new OutputConfiguration(target.getSurface());
+                        if (numConfigs > 1) {
+                            config.setPhysicalCameraId(overridePhysicalCameraIds.get(j));
+                        }
+                        outputConfigs.add(config);
+                        heicTargets.add(target);
+                        break;
+                    }
                     case YUV: {
                         Size targetSize = (numConfigs == 1) ? maxSizes.maxYuvSizes[sizeLimit] :
                                 overridePhysicalCameraSizes.get(j);
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index a169fee..22e2424 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -100,7 +100,46 @@
                     continue;
                 }
                 openDevice(mCameraIds[i]);
-                jpegExifTestByCamera();
+                Size maxJpegSize = mOrderedStillSizes.get(0);
+                stillExifTestByCamera(ImageFormat.JPEG, maxJpegSize);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test HEIC capture exif fields for each camera.
+     */
+    @Test
+    public void testHeicExif() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing HEIC exif for Camera " + mCameraIds[i]);
+                if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+                if (!mAllStaticInfo.get(mCameraIds[i]).isHeicSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support HEIC, skipping");
+                    continue;
+                }
+
+                openDevice(mCameraIds[i]);
+
+                // Test maximum Heic size capture
+                List<Size> orderedHeicSizes = CameraTestUtils.getSupportedHeicSizes(
+                        mCameraIds[i], mCameraManager, null/*bound*/);
+                Size maxHeicSize = orderedHeicSizes.get(0);
+                stillExifTestByCamera(ImageFormat.HEIC, maxHeicSize);
+
+                // Test preview size Heic capture
+                Size previewSize = mOrderedPreviewSizes.get(0);
+                stillExifTestByCamera(ImageFormat.HEIC, previewSize);
+
             } finally {
                 closeDevice();
                 closeImageReader();
@@ -318,7 +357,7 @@
      * Test all combination of available preview sizes and still sizes.
      * <p>
      * For each still capture, Only the jpeg buffer is validated, capture
-     * result validation is covered by {@link #jpegExifTestByCamera} test.
+     * result validation is covered by {@link #stillExifTestByCamera} test.
      * </p>
      */
     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
@@ -585,7 +624,7 @@
 
         // Set the max number of images to number of focal lengths supported
         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
-                maxStillSz, resultListener, focalLengths.length, imageListener);
+                maxStillSz, resultListener, focalLengths.length, imageListener, false /*isHeic*/);
 
         for(float focalLength : focalLengths) {
 
@@ -613,7 +652,7 @@
 
             validateJpegCapture(image, maxStillSz);
             verifyJpegKeys(image, result, maxStillSz, thumbnailSize, exifTestData,
-                    mStaticInfo, mCollector, mDebugFileNameBase);
+                    mStaticInfo, mCollector, mDebugFileNameBase, ImageFormat.JPEG);
         }
     }
 
@@ -633,7 +672,7 @@
         CaptureRequest.Builder stillRequest =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
-                maxStillSz, resultListener, imageListener);
+                maxStillSz, resultListener, imageListener, false /*isHeic*/);
 
         // make sure preview is actually running
         waitForNumResults(resultListener, NUM_FRAMES_WAITED);
@@ -719,7 +758,7 @@
             stillRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         }
         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
-                maxStillSz, resultListener, imageListener);
+                maxStillSz, resultListener, imageListener, false /*isHeic*/);
 
         // Set AE mode to ON_AUTO_FLASH if flash is available.
         if (mStaticInfo.hasFlash()) {
@@ -930,7 +969,7 @@
                 CaptureRequest.Builder stillRequest =
                         mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                 prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
-                        stillSz, resultListener, imageListener);
+                        stillSz, resultListener, imageListener, false /*isHeic*/);
                 mSession.capture(stillRequest.build(), resultListener, mHandler);
                 Image image = imageListener.getImage((mStaticInfo.isHardwareLevelLegacy()) ?
                         RELAXED_CAPTURE_IMAGE_TIMEOUT_MS : CAPTURE_IMAGE_TIMEOUT_MS);
@@ -1049,7 +1088,7 @@
             CaptureResult result = resultListener.getCaptureResultForRequest(multiRequest,
                     NUM_RESULTS_WAIT_TIMEOUT);
             Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
-            basicValidateJpegImage(jpegImage, maxStillSz);
+            basicValidateBlobImage(jpegImage, maxStillSz, ImageFormat.JPEG);
             Image rawImage = rawListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
             validateRaw16Image(rawImage, size);
             verifyRawCaptureResult(multiRequest, result);
@@ -1188,17 +1227,19 @@
     }
 
     /**
-     * Issue a Jpeg capture and validate the exif information.
+     * Issue a still capture and validate the exif information.
      * <p>
      * TODO: Differentiate full and limited device, some of the checks rely on
      * per frame control and synchronization, most of them don't.
      * </p>
      */
-    private void jpegExifTestByCamera() throws Exception {
+    private void stillExifTestByCamera(int format, Size stillSize) throws Exception {
+        assertTrue(format == ImageFormat.JPEG || format == ImageFormat.HEIC);
+        boolean isHeic = (format == ImageFormat.HEIC);
+
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
-        Size maxStillSz = mOrderedStillSizes.get(0);
         if (VERBOSE) {
-            Log.v(TAG, "Testing JPEG exif with jpeg size " + maxStillSz.toString()
+            Log.v(TAG, "Testing exif with size " + stillSize.toString()
                     + ", preview size " + maxPreviewSz);
         }
 
@@ -1209,8 +1250,8 @@
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
-        prepareStillCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, maxStillSz,
-                resultListener, imageListener);
+        prepareStillCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, stillSize,
+                resultListener, imageListener, isHeic);
 
         // Set the jpeg keys, then issue a capture
         Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked();
@@ -1223,15 +1264,15 @@
         for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
             setJpegKeys(stillBuilder, EXIF_TEST_DATA[i], testThumbnailSizes[i], mCollector);
 
-            // Capture a jpeg image.
+            // Capture a jpeg/heic image.
             CaptureRequest request = stillBuilder.build();
             mSession.capture(request, resultListener, mHandler);
             CaptureResult stillResult =
                     resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
             Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
 
-            verifyJpegKeys(image, stillResult, maxStillSz, testThumbnailSizes[i], EXIF_TEST_DATA[i],
-                    mStaticInfo, mCollector, mDebugFileNameBase);
+            verifyJpegKeys(image, stillResult, stillSize, testThumbnailSizes[i], EXIF_TEST_DATA[i],
+                    mStaticInfo, mCollector, mDebugFileNameBase, format);
 
             // Free image resources
             image.close();
@@ -1292,7 +1333,7 @@
         // Set the max number of images to be same as the burst count, as the verification
         // could be much slower than producing rate, and we don't want to starve producer.
         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
-                maxStillSz, resultListener, numSteps, imageListener);
+                maxStillSz, resultListener, numSteps, imageListener, false /*isHeic*/);
 
         for (int i = 0; i <= numSteps; i++) {
             int exposureCompensation = i * stepsPerEv + compensationRange.getLower();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 8c245bd..7a9ae93 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -285,9 +285,10 @@
     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
             CaptureCallback resultListener,
-            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+            ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception {
         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
-                ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener);
+                isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, MAX_READER_IMAGES,
+                imageListener);
     }
 
     /**
@@ -304,9 +305,9 @@
     protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
             CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
             CaptureCallback resultListener, int maxNumImages,
-            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+            ImageReader.OnImageAvailableListener imageListener, boolean isHeic) throws Exception {
         prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
-                ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
+                isHeic ? ImageFormat.HEIC : ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
     }
 
     /**
@@ -573,11 +574,13 @@
      * @param resultListener Capture result listener
      * @param maxNumImages The max number of images set to the image reader
      * @param imageListeners The single capture capture image listeners
+     * @param isHeic HEIC still capture if true, JPEG still capture if false
      */
     protected ImageReader[] prepareStillCaptureAndStartPreview(
             CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest,
             Size previewSz, Size[] captureSizes, int[] formats, CaptureCallback resultListener,
-            int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners)
+            int maxNumImages, ImageReader.OnImageAvailableListener[] imageListeners,
+            boolean isHeic)
             throws Exception {
 
         if ((captureSizes == null) || (formats == null) || (imageListeners == null) &&
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 4fd4b39..883d86b 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -1035,11 +1035,12 @@
 
         ByteBuffer buffer = null;
         // JPEG doesn't have pixelstride and rowstride, treat it as 1D buffer.
-        // Same goes for DEPTH_POINT_CLOUD and DEPTH_JPEG
+        // Same goes for DEPTH_POINT_CLOUD, RAW_PRIVATE, DEPTH_JPEG, and HEIC
         if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD ||
-                format == ImageFormat.RAW_PRIVATE || format == ImageFormat.DEPTH_JPEG) {
+                format == ImageFormat.RAW_PRIVATE || format == ImageFormat.DEPTH_JPEG ||
+                format == ImageFormat.HEIC) {
             buffer = planes[0].getBuffer();
-            assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer);
+            assertNotNull("Fail to get jpeg/depth/heic ByteBuffer", buffer);
             data = new byte[buffer.remaining()];
             buffer.get(data);
             buffer.rewind();
@@ -1122,6 +1123,7 @@
             case ImageFormat.DEPTH_POINT_CLOUD:
             case ImageFormat.DEPTH_JPEG:
             case ImageFormat.Y8:
+            case ImageFormat.HEIC:
                 assertEquals("JPEG/RAW/depth/Y8 Images should have one plane", 1, planes.length);
                 break;
             default:
@@ -1364,6 +1366,11 @@
         return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.JPEG, bound);
     }
 
+    static public List<Size> getSupportedHeicSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.HEIC, bound);
+    }
+
     static public Size getMinPreviewSize(String cameraId, CameraManager cameraManager)
             throws CameraAccessException {
         List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, null);
@@ -1551,6 +1558,9 @@
             case ImageFormat.Y8:
                 validateY8Data(data, width, height, format, image.getTimestamp(), filePath);
                 break;
+            case ImageFormat.HEIC:
+                validateHeicData(data, width, height, filePath);
+                break;
             default:
                 throw new UnsupportedOperationException("Unsupported format for validation: "
                         + format);
@@ -1733,6 +1743,26 @@
 
     }
 
+    private static void validateHeicData(byte[] heicData, int width, int height, String filePath) {
+        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
+        // DecodeBound mode: only parse the frame header to get width/height.
+        // it doesn't decode the pixel.
+        bmpOptions.inJustDecodeBounds = true;
+        BitmapFactory.decodeByteArray(heicData, 0, heicData.length, bmpOptions);
+        assertEquals(width, bmpOptions.outWidth);
+        assertEquals(height, bmpOptions.outHeight);
+
+        // Pixel decoding mode: decode whole image. check if the image data
+        // is decodable here.
+        assertNotNull("Decoding heic failed",
+                BitmapFactory.decodeByteArray(heicData, 0, heicData.length));
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + ".heic";
+            dumpFile(fileName, heicData);
+        }
+    }
+
     public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
         if (result == null) {
             throw new IllegalArgumentException("Result must not be null");
@@ -2047,49 +2077,54 @@
      * continue the test if the jpeg image captured has some serious failures.
      * </p>
      *
-     * @param image The captured jpeg image
-     * @param expectedSize Expected capture jpeg size
+     * @param image The captured JPEG/HEIC image
+     * @param expectedSize Expected capture JEPG/HEIC size
+     * @param format JPEG/HEIC image format
      */
-    public static void basicValidateJpegImage(Image image, Size expectedSize) {
+    public static void basicValidateBlobImage(Image image, Size expectedSize, int format) {
         Size imageSz = new Size(image.getWidth(), image.getHeight());
         assertTrue(
                 String.format("Image size doesn't match (expected %s, actual %s) ",
                         expectedSize.toString(), imageSz.toString()), expectedSize.equals(imageSz));
-        assertEquals("Image format should be JPEG", ImageFormat.JPEG, image.getFormat());
+        assertEquals("Image format should be " + ((format == ImageFormat.HEIC) ? "HEIC" : "JPEG"),
+                format, image.getFormat());
         assertNotNull("Image plane shouldn't be null", image.getPlanes());
         assertEquals("Image plane number should be 1", 1, image.getPlanes().length);
 
-        // Jpeg decoding validate was done in ImageReaderTest, no need to duplicate the test here.
+        // Jpeg/Heic decoding validate was done in ImageReaderTest,
+        // no need to duplicate the test here.
     }
 
     /**
-     * Verify the JPEG EXIF and JPEG related keys in a capture result are expected.
+     * Verify the EXIF and JPEG related keys in a capture result are expected.
      * - Capture request get values are same as were set.
      * - capture result's exif data is the same as was set by
      *   the capture request.
      * - new tags in the result set by the camera service are
      *   present and semantically correct.
      *
-     * @param image The output JPEG image to verify.
+     * @param image The output JPEG/HEIC image to verify.
      * @param captureResult The capture result to verify.
-     * @param expectedSize The expected JPEG size.
+     * @param expectedSize The expected JPEG/HEIC size.
      * @param expectedThumbnailSize The expected thumbnail size.
      * @param expectedExifData The expected EXIF data
      * @param staticInfo The static metadata for the camera device.
-     * @param jpegFilename The filename to dump the jpeg to.
+     * @param blobFilename The filename to dump the jpeg/heic to.
      * @param collector The camera error collector to collect errors.
+     * @param format JPEG/HEIC format
      */
     public static void verifyJpegKeys(Image image, CaptureResult captureResult, Size expectedSize,
             Size expectedThumbnailSize, ExifTestData expectedExifData, StaticMetadata staticInfo,
-            CameraErrorCollector collector, String debugFileNameBase) throws Exception {
+            CameraErrorCollector collector, String debugFileNameBase, int format) throws Exception {
 
-        basicValidateJpegImage(image, expectedSize);
+        basicValidateBlobImage(image, expectedSize, format);
 
-        byte[] jpegBuffer = getDataFromImage(image);
+        byte[] blobBuffer = getDataFromImage(image);
         // Have to dump into a file to be able to use ExifInterface
-        String jpegFilename = debugFileNameBase + "/verifyJpegKeys.jpeg";
-        dumpFile(jpegFilename, jpegBuffer);
-        ExifInterface exif = new ExifInterface(jpegFilename);
+        String filePostfix = (format == ImageFormat.HEIC ? ".heic" : ".jpeg");
+        String blobFilename = debugFileNameBase + "/verifyJpegKeys" + filePostfix;
+        dumpFile(blobFilename, blobBuffer);
+        ExifInterface exif = new ExifInterface(blobFilename);
 
         if (expectedThumbnailSize.equals(new Size(0,0))) {
             collector.expectTrue("Jpeg shouldn't have thumbnail when thumbnail size is (0, 0)",
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 15701f1..6af6be5 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -1347,6 +1347,16 @@
     }
 
     /**
+     * Get supported heic output sizes and do the check.
+     *
+     * @return Empty size array if heic output is not supported
+     */
+    public Size[] getHeicOutputSizesChecked() {
+        return getAvailableSizesForFormatChecked(ImageFormat.HEIC,
+                StreamDirection.Output);
+    }
+
+    /**
      * Used to determine the stream direction for various helpers that look up
      * format or size information.
      */
@@ -2361,6 +2371,14 @@
     }
 
     /**
+     * Check if HEIC format is supported
+     */
+    public boolean isHeicSupported() {
+        int[] formats = getAvailableFormats(StaticMetadata.StreamDirection.Output);
+        return CameraTestUtils.contains(formats, ImageFormat.HEIC);
+    }
+
+    /**
      * Check if the dynamic black level is supported.
      *
      * <p>
diff --git a/tests/providerui/AndroidTest.xml b/tests/providerui/AndroidTest.xml
index 6a123f6..9ef1d6c 100644
--- a/tests/providerui/AndroidTest.xml
+++ b/tests/providerui/AndroidTest.xml
@@ -16,7 +16,9 @@
 <configuration description="Config for CTS Provider UI test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="framework" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <!-- Instant apps cannot access external storage -->
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsProviderUiTestCases.apk" />
diff --git a/tests/tests/permission/AndroidManifest.xml b/tests/tests/permission/AndroidManifest.xml
index 928da95..5400c7d 100644
--- a/tests/tests/permission/AndroidManifest.xml
+++ b/tests/tests/permission/AndroidManifest.xml
@@ -32,33 +32,8 @@
                 android:permissionGroup="android.permission.cts.groupC"
                 android:description="@string/perm_c" />
 
-    <!-- for android.permission.cts.PermissionUsage -->
-    <permission android:name="android.permission.cts.D"
-                android:usageInfoRequired="true" />
-
-    <!-- for android.permission.cts.PermissionUsage and LocationAccessCheckTest -->
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
-      android:dataSentOffDevice="no"
-      android:dataSharedWithThirdParty="no"
-      android:dataUsedForMonetization="no"
-      android:dataRetentionTime="notRetained" />
-
-    <!-- for android.permission.cts.PermissionUsage -->
-    <uses-permission android:name="android.permission.READ_PHONE_STATE"
-      android:dataSentOffDevice="yes"
-      android:dataSharedWithThirdParty="yes"
-      android:dataUsedForMonetization="yes"
-      android:dataRetentionTime="32" />
-
-    <!-- for android.permission.cts.PermissionUsage -->
-    <uses-permission android:name="android.permission.RECORD_AUDIO"
-      android:dataSentOffDevice="userTriggered"
-      android:dataSharedWithThirdParty="userTriggered"
-      android:dataUsedForMonetization="userTriggered"
-      android:dataRetentionTime="unlimited" />
-
-    <!-- for android.permission.cts.PermissionUsage -->
-    <uses-permission android:name="android.permission.READ_CALENDAR" />
+    <!-- for android.permission.cts.LocationAccessCheckTest -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
     <!-- for android.permission.cts.PermissionGroupChange -->
     <permission-group android:description="@string/perm_group_b"
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java b/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java
deleted file mode 100644
index 39c66a6..0000000
--- a/tests/tests/permission/src/android/permission/cts/PermissionUsageTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission.cts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.pm.UsesPermissionInfo;
-import android.test.InstrumentationTestCase;
-
-import org.junit.Before;
-
-public final class PermissionUsageTest extends InstrumentationTestCase {
-    private PackageManager mPm;
-    private Context mContext;
-
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        mContext = getInstrumentation().getTargetContext();
-        mPm = mContext.getPackageManager();
-        assertNotNull(mPm);
-    }
-
-    private UsesPermissionInfo getUsesPermissionInfo(String permission) throws Exception {
-        PackageInfo info = mPm.getPackageInfo(mContext.getPackageName(),
-                PackageManager.GET_PERMISSIONS);
-        assertNotNull(info);
-        assertNotNull(info.usesPermissions);
-        for (UsesPermissionInfo upi : info.usesPermissions) {
-            if (permission.equals(upi.getPermission())) {
-                return upi;
-            }
-        }
-        return null;
-    }
-
-    public void testBasic() throws Exception {
-        UsesPermissionInfo upi = getUsesPermissionInfo(Manifest.permission.ACCESS_FINE_LOCATION);
-        assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataSentOffDevice());
-        assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataSharedWithThirdParty());
-        assertEquals(UsesPermissionInfo.USAGE_NO, upi.getDataUsedForMonetization());
-        assertEquals(UsesPermissionInfo.RETENTION_NOT_RETAINED, upi.getDataRetention());
-    }
-
-    public void testRetentionWeeks() throws Exception {
-        UsesPermissionInfo upi
-                = getUsesPermissionInfo(Manifest.permission.READ_PHONE_STATE);
-        assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataSentOffDevice());
-        assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataSharedWithThirdParty());
-        assertEquals(UsesPermissionInfo.USAGE_YES, upi.getDataUsedForMonetization());
-        assertEquals(UsesPermissionInfo.RETENTION_SPECIFIED, upi.getDataRetention());
-        assertEquals(32, upi.getDataRetentionWeeks());
-    }
-
-    public void testUserTriggered() throws Exception {
-        UsesPermissionInfo upi
-                = getUsesPermissionInfo(Manifest.permission.RECORD_AUDIO);
-        assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED, upi.getDataSentOffDevice());
-        assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED,
-                upi.getDataSharedWithThirdParty());
-        assertEquals(UsesPermissionInfo.USAGE_USER_TRIGGERED,
-                upi.getDataUsedForMonetization());
-        assertEquals(UsesPermissionInfo.RETENTION_UNLIMITED, upi.getDataRetention());
-    }
-
-    public void testUndefined() throws Exception {
-        UsesPermissionInfo upi
-                = getUsesPermissionInfo(Manifest.permission.READ_CALENDAR);
-        assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataSentOffDevice());
-        assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataSharedWithThirdParty());
-        assertEquals(UsesPermissionInfo.USAGE_UNDEFINED, upi.getDataUsedForMonetization());
-        assertEquals(UsesPermissionInfo.RETENTION_UNDEFINED, upi.getDataRetention());
-    }
-
-    public void testUsageInfoRequired() throws Exception {
-        PermissionInfo pi = mPm.getPermissionInfo("android.permission.cts.D", 0);
-        assertTrue(pi.usageInfoRequired);
-    }
-}
diff --git a/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java
index cf49f50..96eef77 100644
--- a/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.UsesPermissionInfo;
 import android.os.Process;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -65,10 +64,9 @@
         String pkg = pkgs[0];
 
         final PackageInfo packageInfo = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
-        assertNotNull("No permissions found for " + pkg, packageInfo.usesPermissions);
+        assertNotNull("No permissions found for " + pkg, packageInfo.requestedPermissions);
 
-        for (UsesPermissionInfo permInfo : packageInfo.usesPermissions) {
-            final String permission = permInfo.getPermission();
+        for (String permission : packageInfo.requestedPermissions) {
             Log.d(LOG_TAG, "SHELL as " + pkg + " uses permission " + permission);
             assertFalse("SHELL as " + pkg + " contains the illegal permission " + permission,
                     blacklist.contains(permission));
diff --git a/tests/tests/preference/AndroidTest.xml b/tests/tests/preference/AndroidTest.xml
index 4287b76..1980c38 100644
--- a/tests/tests/preference/AndroidTest.xml
+++ b/tests/tests/preference/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/preference/OWNERS b/tests/tests/preference/OWNERS
index d20511f..827134e 100644
--- a/tests/tests/preference/OWNERS
+++ b/tests/tests/preference/OWNERS
@@ -1,2 +1,3 @@
+lpf@google.com
 pavlis@google.com
 clarabayarri@google.com
diff --git a/tests/tests/preference2/AndroidTest.xml b/tests/tests/preference2/AndroidTest.xml
index a3fb234..452702d 100644
--- a/tests/tests/preference2/AndroidTest.xml
+++ b/tests/tests/preference2/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="uitoolkit" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsPreference2TestCases.apk" />
diff --git a/tests/tests/preference2/OWNERS b/tests/tests/preference2/OWNERS
new file mode 100644
index 0000000..827134e
--- /dev/null
+++ b/tests/tests/preference2/OWNERS
@@ -0,0 +1,3 @@
+lpf@google.com
+pavlis@google.com
+clarabayarri@google.com
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index bb5857e..213b506 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -167,7 +167,7 @@
     @Test
     public void testIsUsableSubscriptionId() throws Exception {
         if (!isSupported()) return;
-        assertTrue(SubscriptionManager.isUsableSubIdValue(mSubId));
+        assertTrue(SubscriptionManager.isUsableSubscriptionId(mSubId));
     }
 
     @Test
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index dabbc1a..925f3e2 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -31,6 +31,15 @@
                 android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
 
+        <activity android:name="android.app.Activity"
+                  android:label="Empty Activity"
+                  android:theme="@style/ViewStyleTestTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.cts.ViewStubCtsActivity"
                   android:screenOrientation="locked"
                   android:label="ViewStubCtsActivity">
diff --git a/tests/tests/view/res/layout/view_style_layout.xml b/tests/tests/view/res/layout/view_style_layout.xml
new file mode 100644
index 0000000..dd488ef
--- /dev/null
+++ b/tests/tests/view/res/layout/view_style_layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+    <View android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:id="@+id/view1"
+          android:paddingTop="7dp"
+          style="@style/ExplicitStyle1" />
+
+    <View android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:id="@+id/view2"
+          style="@style/ExplicitStyle2" />
+
+    <View android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:id="@+id/view3" />
+
+    <View android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:id="@+id/view4"
+          style="?android:attr/textAppearanceLarge"/>
+
+    <Button android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/button1" />
+
+    <Button android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/button2"
+            style="@style/ExplicitStyle1" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/view/res/values/styles.xml b/tests/tests/view/res/values/styles.xml
index 5e47363..ab984e8 100644
--- a/tests/tests/view/res/values/styles.xml
+++ b/tests/tests/view/res/values/styles.xml
@@ -187,4 +187,26 @@
         <item name="android:windowContentTransitions">false</item>
         <item name="android:windowAnimationStyle">@null</item>
     </style>
+
+    <style name="ViewStyleTestTheme" parent="@android:Theme.Material">
+        <item name="android:buttonStyle">@style/MyButtonStyle</item>
+    </style>
+
+    <style name="MyButtonStyle" parent="@style/MyButtonStyleParent">
+        <item name="android:paddingTop">3dp</item>
+    </style>
+
+    <style name="MyButtonStyleParent">
+        <item name="android:textColor">#ff00ff</item>
+    </style>
+
+    <style name="ExplicitStyle1" parent="@style/ParentOfExplicitStyle1">
+        <item name="android:padding">1dp</item>
+    </style>
+
+    <style name="ParentOfExplicitStyle1">
+        <item name="android:paddingLeft">2dp</item>
+    </style>
+
+    <style name="ExplicitStyle2" />
 </resources>
diff --git a/tests/tests/view/src/android/view/cts/ViewStyleTest.java b/tests/tests/view/src/android/view/cts/ViewStyleTest.java
new file mode 100644
index 0000000..866e8b6
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ViewStyleTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+package android.view.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Map;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewStyleTest {
+
+    @Rule
+    public ActivityTestRule<Activity> mActivityRule =
+            new ActivityTestRule<>(Activity.class, true, false);
+
+    private static final String DISABLE_SHELL_COMMAND =
+            "settings delete global debug_view_attributes_application_package";
+
+    private static final String ENABLE_SHELL_COMMAND =
+            "settings put global debug_view_attributes_application_package android.view.cts";
+
+    private UiDevice mUiDevice;
+
+    @Before
+    public void setUp() throws Exception {
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mUiDevice.executeShellCommand(ENABLE_SHELL_COMMAND);
+        mActivityRule.launchActivity(null);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mUiDevice.executeShellCommand(DISABLE_SHELL_COMMAND);
+    }
+
+    @Test
+    public void testGetExplicitStyle() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        LayoutInflater inflater = LayoutInflater.from(context);
+        LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.view_style_layout, null);
+        View view1 = rootView.findViewById(R.id.view1);
+        assertEquals(R.style.ExplicitStyle1, view1.getExplicitStyle());
+
+        View view2 = rootView.findViewById(R.id.view2);
+        assertEquals(R.style.ExplicitStyle2, view2.getExplicitStyle());
+
+        View view3 = rootView.findViewById(R.id.view3);
+        assertEquals(Resources.ID_NULL, view3.getExplicitStyle());
+
+        View view4 = rootView.findViewById(R.id.view4);
+        assertEquals(android.R.style.TextAppearance_Material_Large, view4.getExplicitStyle());
+    }
+
+    @Test
+    public void testGetAttributeResolutionStack() {
+        LayoutInflater inflater = LayoutInflater.from(mActivityRule.getActivity());
+        LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.view_style_layout, null);
+        // View that has an explicit style ExplicitStyle1 set via style = ...
+        View view1 = rootView.findViewById(R.id.view1);
+        List<Integer> stackView1 = view1.getAttributeResolutionStack();
+        assertEquals(3, stackView1.size());
+        assertEquals(R.layout.view_style_layout, stackView1.get(0).intValue());
+        assertEquals(R.style.ExplicitStyle1, stackView1.get(1).intValue());
+        assertEquals(R.style.ParentOfExplicitStyle1, stackView1.get(2).intValue());
+
+        // Button that has the default style MyButtonStyle set in ViewStyleTestTheme Activity theme
+        // via android:buttonStyle
+        Button button1 = rootView.findViewById(R.id.button1);
+        List<Integer> stackButton1 = button1.getAttributeResolutionStack();
+        assertEquals(3, stackButton1.size());
+        assertEquals(R.layout.view_style_layout, stackButton1.get(0).intValue());
+        assertEquals(R.style.MyButtonStyle, stackButton1.get(1).intValue());
+        assertEquals(R.style.MyButtonStyleParent, stackButton1.get(2).intValue());
+
+        // Button that has the default style MyButtonStyle set in ViewStyleTestTheme Activity theme
+        // via android:buttonStyle and has an explicit style ExplicitStyle1 set via style = ...
+        Button button2 = rootView.findViewById(R.id.button2);
+        List<Integer> stackButton2 = button2.getAttributeResolutionStack();
+        assertEquals(5, stackButton2.size());
+        assertEquals(R.layout.view_style_layout, stackButton2.get(0).intValue());
+        assertEquals(R.style.ExplicitStyle1, stackButton2.get(1).intValue());
+        assertEquals(R.style.ParentOfExplicitStyle1, stackButton2.get(2).intValue());
+        assertEquals(R.style.MyButtonStyle, stackButton2.get(3).intValue());
+        assertEquals(R.style.MyButtonStyleParent, stackButton2.get(4).intValue());
+    }
+
+    @Test
+    public void testGetAttributeSourceResourceMap() {
+        LayoutInflater inflater = LayoutInflater.from(mActivityRule.getActivity());
+        LinearLayout rootView = (LinearLayout) inflater.inflate(R.layout.view_style_layout, null);
+        // View that has an explicit style ExplicitStyle1 set via style = ...
+        View view1 = rootView.findViewById(R.id.view1);
+        Map<Integer, Integer> attributeMapView1 = view1.getAttributeSourceResourceMap();
+        assertEquals(9, attributeMapView1.size());
+        assertEquals(R.style.ExplicitStyle1,
+                (attributeMapView1.get(android.R.attr.padding)).intValue());
+        assertEquals(R.style.ParentOfExplicitStyle1,
+                (attributeMapView1.get(android.R.attr.paddingLeft)).intValue());
+        assertEquals(R.layout.view_style_layout,
+                (attributeMapView1.get(android.R.attr.paddingTop)).intValue());
+    }
+}