Merge "Update test bug reference and SPL" into rvc-dev am: fe4da5703f am: adc85fafa8

Original change: https://googleplex-android-review.googlesource.com/c/platform/cts/+/13193037

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I255052674d34a7244bd5a8276425000181ad1877
diff --git a/apps/CtsVerifier/res/layout/nls_item.xml b/apps/CtsVerifier/res/layout/nls_item.xml
index f1a10bf..e80d333 100644
--- a/apps/CtsVerifier/res/layout/nls_item.xml
+++ b/apps/CtsVerifier/res/layout/nls_item.xml
@@ -14,41 +14,51 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content" >
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
 
-    <ImageView
-        android:id="@+id/nls_status"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginTop="10dip"
-        android:contentDescription="@string/pass_button_text"
-        android:padding="10dip"
-        android:src="@drawable/fs_indeterminate" />
-
-    <TextView
-        android:id="@+id/nls_instructions"
-        style="@style/InstructionsSmallFont"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_alignParentTop="true"
-        android:layout_toRightOf="@id/nls_status"
-        android:text="@string/nls_enable_service" />
+        android:orientation="horizontal">
+        <ImageView
+            android:id="@+id/nls_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginTop="10dip"
+            android:contentDescription="@string/pass_button_text"
+            android:padding="10dip"
+            android:src="@drawable/fs_indeterminate" />
+
+        <TextView
+            android:id="@+id/nls_instructions"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_toRightOf="@id/nls_status"
+            android:text="@string/nls_enable_service" />
+    </LinearLayout>
 
     <Button
         android:id="@+id/nls_action_button"
-        android:layout_width="match_parent"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_below="@id/nls_instructions"
         android:layout_marginLeft="20dip"
         android:layout_marginRight="20dip"
-        android:layout_toRightOf="@id/nls_status"
         android:onClick="actionPressed"
+        android:layout_gravity="center_horizontal"
         android:text="@string/nls_start_settings" />
 
-</RelativeLayout>
\ No newline at end of file
+    <LinearLayout
+        android:id="@+id/feedback"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 33d5340..fe1491c 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2185,6 +2185,9 @@
         and disabled, and that once enabled the service is able to receive notifications and
         dismiss them.
     </string>
+    <string name="nls_anr">This test checks that notifications are not sent with content that is
+        too long. If this test causes the test app to ANR, the test has failed.
+    </string>
     <string name="msg_extras_preserved">Check that Message extras Bundle was preserved.</string>
     <string name="conversation_section_ordering">If this device supports conversation notifications,
         and groups them into a separate section from alerting and silent non-conversation
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index aa5e9c0..121534a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -257,8 +257,8 @@
         return item;
     }
 
-    protected View createAutoItem(ViewGroup parent, int stringId) {
-        View item = mInflater.inflate(R.layout.nls_item, parent, false);
+    protected ViewGroup createAutoItem(ViewGroup parent, int stringId) {
+        ViewGroup item = (ViewGroup) mInflater.inflate(R.layout.nls_item, parent, false);
         TextView instructions = item.findViewById(R.id.nls_instructions);
         instructions.setText(stringId);
         View button = item.findViewById(R.id.nls_action_button);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 12e2b93..31980af 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -51,7 +51,7 @@
     public static final String JSON_STATS = "stats";
     public static final String JSON_LAST_AUDIBLY_ALERTED = "last_audibly_alerted";
 
-    ArrayList<String> mPosted = new ArrayList<String>();
+    ArrayList<StatusBarNotification> mPosted = new ArrayList<>();
     ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
     ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
     ArrayList<String> mRemoved = new ArrayList<String>();
@@ -77,6 +77,15 @@
         return mNotifications.values();
     }
 
+    protected StatusBarNotification getPosted(String tag) {
+        for (StatusBarNotification sbn : mPosted) {
+            if (sbn.getTag().equals(tag)) {
+                return sbn;
+            }
+        }
+        return null;
+    }
+
     protected String getKeyForTag(String tag) {
         return mNotificationKeys.get(tag);
     }
@@ -149,7 +158,7 @@
     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
         if (!mTestPackages.contains(sbn.getPackageName())) { return; }
         Log.d(TAG, "posted: " + sbn.getTag());
-        mPosted.add(sbn.getTag());
+        mPosted.add(sbn);
         mPostedNotifications.add(sbn.getNotification());
         JSONObject notification = new JSONObject();
         try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index 42ee9e1..1d704b0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -56,6 +56,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
 
 import androidx.core.app.NotificationCompat;
 
@@ -117,6 +119,7 @@
         tests.add(new IsEnabledTest());
         tests.add(new ServiceStartedTest());
         tests.add(new NotificationReceivedTest());
+        tests.add(new LongMessageTest());
         tests.add(new DataIntactTest());
         tests.add(new AudiblyAlertedTest());
         tests.add(new DismissOneTest());
@@ -261,8 +264,73 @@
 
         @Override
         protected void test() {
-            List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
-            if (result.size() > 0 && result.contains(mTag1)) {
+            if (MockListener.getInstance().getPosted(mTag1) != null) {
+                status = PASS;
+            } else {
+                logFail();
+                status = FAIL;
+            }
+        }
+    }
+
+    private class LongMessageTest extends InteractiveTestCase {
+        private ViewGroup mParent;
+        @Override
+        protected View inflate(ViewGroup parent) {
+            mParent = createAutoItem(parent, R.string.nls_anr);
+            return mParent;
+        }
+
+        @Override
+        protected void setUp() {
+            createChannels();
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < 20000; i++) {
+                sb.append("\u2009\u200a" + "\u200E\u200F" + "stuff");
+            }
+            Notification.Builder builder = new Notification.Builder(
+                    mContext, NOTIFICATION_CHANNEL_ID)
+                    .setSmallIcon(android.R.id.icon)
+                    .setContentTitle("This is an long notification")
+                    .setContentText("Innocuous content")
+                    .setStyle(new Notification.MessagingStyle("Fake person")
+                            .addMessage("hey how is it goin", 0, "Person 1")
+                            .addMessage("hey", 0, "Person 1")
+                            .addMessage("u there", 0, "Person 1")
+                            .addMessage("how you like tHIS", 0, "Person 1")
+                            .addMessage(sb.toString(), 0, "Person 1")
+                    );
+            mTag1 = UUID.randomUUID().toString();
+            mId1 = NOTIFICATION_ID + 1;
+            mPackageString = "com.android.cts.verifier";
+            mNm.notify(mTag1, mId1, builder.build());
+            status = READY;
+        }
+
+        @Override
+        protected void tearDown() {
+            mNm.cancelAll();
+            MockListener.getInstance().resetData();
+            deleteChannels();
+        }
+
+        @Override
+        protected void test() {
+            StatusBarNotification sbn = MockListener.getInstance().getPosted(mTag1);
+            if (sbn == null) {
+                logFail();
+                status = FAIL;
+            } else {
+                ViewGroup parent = mParent.findViewById(R.id.feedback);
+                parent.setVisibility(View.VISIBLE);
+                final Notification.Builder recoveredBuilder = Notification.Builder.recoverBuilder(
+                        NotificationListenerVerifierActivity.this,
+                        sbn.getNotification());
+                RemoteViews rv = recoveredBuilder.createContentView();
+                View v = rv.apply(NotificationListenerVerifierActivity.this, parent);
+                parent.addView(v);
+            }
+            if (MockListener.getInstance().getPosted(mTag1) != null) {
                 status = PASS;
             } else {
                 logFail();
@@ -985,8 +1053,7 @@
             if (MockListener.getInstance() == null) {
                 status = PASS;
             } else {
-                List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
-                if (result.size() == 0) {
+                if (MockListener.getInstance().mPosted.size() == 0) {
                     status = PASS;
                 } else {
                     logFail();
@@ -1157,8 +1224,7 @@
                     state = READY_TO_CHECK_FOR_UNSNOOZE;
                 }
             } else {
-                List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
-                if (result.size() > 0 && result.contains(mTag1)) {
+                if (MockListener.getInstance().getPosted(mTag1) != null) {
                     status = PASS;
                 } else {
                     logFail();
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
index 14f8285..03d4a50 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/SettingsUtils.java
@@ -197,4 +197,12 @@
     public static String getSecureSetting(String key) {
         return SystemUtil.runShellCommand("settings --user current get secure " + key).trim();
     }
+
+    /**
+     * Get a global setting for the given user. Trims ending new line.
+     */
+    public static String getSecureSettingAsUser(int userId, String key) {
+        return SystemUtil.runShellCommand(
+                String.format("settings --user %d get secure %s", userId, key)).trim();
+    }
 }
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java
new file mode 100644
index 0000000..198e602
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.os.SystemProperties;
+
+/**
+ * Provides utilities to deal with user status.
+ */
+public final class UserUtils {
+
+    private static final String TAG = UserUtils.class.getSimpleName();
+    private static final String SYS_PROP_HEADLESS_SYSTEM_USER = "ro.fw.mu.headless_system_user";
+
+    private UserUtils() {
+        throw new AssertionError("Should not be instantiated");
+    }
+
+    /**
+     * Tells if the device is in headless system user mode.
+     */
+    public static boolean isHeadlessSystemUserMode() {
+        return SystemProperties.getBoolean(SYS_PROP_HEADLESS_SYSTEM_USER, false);
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index fdbb439..20be1a7 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -23,6 +23,11 @@
     created by com.android.cts.appwithdata.
     -->
 
+    <!--
+    We want to test without app data isolation, which comes with targetSdk 30.
+    -->
+    <uses-sdk android:targetSdkVersion="29" />
+
     <uses-permission android:name="android.permission.INTERNET"/>
 
     <application>
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
index b1f804e..74d754d 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/src/com/android/cts/appaccessdata/AccessPrivateDataTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -28,10 +29,14 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -45,6 +50,9 @@
 /**
  * Test that another app's private data cannot be accessed, while its public data can.
  *
+ * These tests are for apps targeting SDK 29 (or lower). Tests for the behavior for apps targeting
+ * SDK 30+ are in {@code AppDataIsolationTests}.
+ *
  * Assumes that {@link #APP_WITH_DATA_PKG} has already created the private and public data.
  */
 @RunWith(AndroidJUnit4.class)
@@ -70,10 +78,15 @@
 
     private static final Uri PRIVATE_TARGET = Uri.parse("content://com.android.cts.appwithdata/");
 
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getContext();
+    }
+
     /**
-     * Tests that another app's private data cannot be accessed. It includes file
-     * and detailed traffic stats.
-     * @throws IOException
+     * Tests that another app's private data cannot be accessed.
      */
     @Test
     public void testAccessPrivateData() throws IOException {
@@ -90,17 +103,8 @@
         }
     }
 
-    private ApplicationInfo getApplicationInfo(String packageName) {
-        try {
-            return InstrumentationRegistry.getContext().getPackageManager().getApplicationInfo(packageName, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new IllegalStateException("Expected package not found: " + e);
-        }
-    }
-
     /**
-     * Tests that another app's public file can be accessed
-     * @throws IOException
+     * Tests that another app's public file cannot be accessed
      */
     @Test
     public void testAccessPublicData() throws IOException {
@@ -117,6 +121,21 @@
         }
     }
 
+    /**
+     * Tests that we can't even access another app's root private data dir.
+     */
+    @Test
+    public void testStatPrivateDataDir() {
+        ApplicationInfo applicationInfo = getApplicationInfo(APP_WITH_DATA_PKG);
+        String path = applicationInfo.dataDir;
+        try {
+            Os.stat(path);
+            fail("Was able to stat() another app's private data dir: " + path);
+        } catch (ErrnoException expected) {
+            assertEquals(path, OsConstants.EACCES, expected.errno);
+        }
+    }
+
     @Test
     public void testAccessProcQtaguidTrafficStatsFailed() {
         // For untrusted app with SDK P or above, proc/net/xt_qtaguid files are no long readable.
@@ -130,7 +149,7 @@
     public void testAccessPrivateTrafficStats() {
         int otherAppUid = -1;
         try {
-            otherAppUid = InstrumentationRegistry.getContext()
+            otherAppUid = mContext
                     .createPackageContext(APP_WITH_DATA_PKG, 0 /*flags*/)
                     .getApplicationInfo().uid;
         } catch (NameNotFoundException e) {
@@ -159,7 +178,7 @@
         final long txp = TrafficStats.getUidTxPackets(uid);
 
         // Start remote server
-        final int port = InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "start", null, null)
+        final int port = mContext.getContentResolver().call(PRIVATE_TARGET, "start", null, null)
                 .getInt("port");
 
         // Try talking to them, but shift blame
@@ -169,7 +188,7 @@
 
             Bundle extras = new Bundle();
             extras.putParcelable("fd", ParcelFileDescriptor.fromSocket(socket));
-            InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
+            mContext.getContentResolver().call(PRIVATE_TARGET, "tag", null, extras);
 
             socket.connect(new InetSocketAddress("localhost", port));
 
@@ -181,7 +200,7 @@
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
-            InstrumentationRegistry.getContext().getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
+            mContext.getContentResolver().call(PRIVATE_TARGET, "stop", null, null);
         }
 
         SystemClock.sleep(1000);
@@ -192,4 +211,12 @@
         assertEquals(txb, TrafficStats.getUidTxBytes(uid));
         assertEquals(txp, TrafficStats.getUidTxPackets(uid));
     }
+
+    private ApplicationInfo getApplicationInfo(String packageName) {
+        try {
+            return mContext.getPackageManager().getApplicationInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Expected package not found: " + e);
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index cf85210..ea8a824 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -23,6 +23,12 @@
     access.
     -->
 
+    <!--
+    We want to test without app data isolation, which comes with targetSdk 30.
+    -->
+    <uses-sdk android:targetSdkVersion="29" />
+
+
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application>
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
index ae3ff05..474df01 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/AndroidManifest.xml
@@ -53,7 +53,14 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".CrossProfileSameTaskLauncherActivity" android:exported="true"/>
+        <activity android:name=".CrossProfileSameTaskLauncherActivity"
+             android:exported="true"/>
+
+        <activity android:name=".CrossProfileResultCheckerActivity"
+             android:exported="true"/>
+
+        <activity android:name=".CrossProfileResultReturnerActivity"
+             android:exported="true"/>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_result_checker.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_result_checker.xml
new file mode 100644
index 0000000..5689f32
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/res/layout/cross_profile_result_checker.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/cross_profile_result_checker_result"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
index 6b41018..7332521 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsStartActivityTest.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertEquals;
@@ -276,6 +278,23 @@
         }
     }
 
+    @Test
+    public void testStartActivityIntent_crossProfile_returnsResult() throws Exception {
+        try {
+            mContext.startActivity(new Intent()
+                    .setComponent(CrossProfileResultCheckerActivity.buildComponentName(mContext))
+                    .putExtra(CrossProfileResultCheckerActivity.TARGET_USER_EXTRA, mTargetUser));
+
+            final UiObject2 textView = mDevice.wait(
+                    Until.findObject(
+                            By.text(CrossProfileResultCheckerActivity.SUCCESS_MESSAGE)),
+                    TIMEOUT_WAIT_UI);
+            assertThat(textView).isNotNull();
+        } catch (Exception e) {
+            fail("unable to start cross-profile activity to obtain a returned result: " + e);
+        }
+    }
+
     /**
      * Calls {@link CrossProfileApps#startActivity(Intent, UserHandle, Activity)}. This can then be
      * used by host-side tests.
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultCheckerActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultCheckerActivity.java
new file mode 100644
index 0000000..49f2d9b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultCheckerActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+/**
+ * An activity that launches {@link CrossProfileResultReturnerActivity} for result, then displays
+ * the string {@link #SUCCESS_MESSAGE} if successful.
+ *
+ * <p>Must be launched with intent extra {@link #TARGET_USER_EXTRA} with the numeric target user ID.
+ */
+public class CrossProfileResultCheckerActivity extends Activity {
+    static final String SUCCESS_MESSAGE = "Successfully received cross-profile result.";
+    static final String TARGET_USER_EXTRA = "TARGET_USER";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Intent intent = getIntent();
+        if (!intent.hasExtra(TARGET_USER_EXTRA)) {
+            throw new IllegalStateException(
+                    "CrossProfileResultCheckerActivity started without " + TARGET_USER_EXTRA);
+        }
+        setContentView(R.layout.cross_profile_result_checker);
+        final Intent resultReturnerIntent =
+                new Intent().setComponent(
+                        CrossProfileResultReturnerActivity.buildComponentName(this));
+        final UserHandle targetUser = intent.getParcelableExtra(TARGET_USER_EXTRA);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                getSystemService(CrossProfileApps.class),
+                crossProfileApps -> crossProfileApps.startActivity(
+                        resultReturnerIntent, targetUser, this));
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (resultCode != CrossProfileResultReturnerActivity.RESULT_CODE) {
+            throw new IllegalStateException("Unknown result code: " + resultCode);
+        }
+        final TextView textView = findViewById(R.id.cross_profile_result_checker_result);
+        textView.setText(SUCCESS_MESSAGE);
+    }
+
+    static ComponentName buildComponentName(Context context) {
+        return new ComponentName(context, CrossProfileResultCheckerActivity.class);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultReturnerActivity.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultReturnerActivity.java
new file mode 100644
index 0000000..8898e91
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileResultReturnerActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+/** An activity that sets the result as {@link #RESULT_CODE} then finishes. */
+public class CrossProfileResultReturnerActivity extends Activity {
+    static final int RESULT_CODE = 998;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        setResult(RESULT_CODE);
+        finish();
+    }
+
+    static ComponentName buildComponentName(Context context) {
+        return new ComponentName(context, CrossProfileResultReturnerActivity.class);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index 345c91e..ddaf864 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -168,6 +168,8 @@
     @LargeTest
     @Test
     public void testStartActivityIntent_sameTaskByDefault() throws Exception {
+        // TODO(b/171957840): replace with device-side test using an inter-process communication
+        //  library.
         if (!mHasManagedUserFeature) {
             return;
         }
@@ -194,6 +196,21 @@
 
     @LargeTest
     @Test
+    public void testStartActivityIntent_crossProfile_returnsResult() throws Exception {
+        // TODO(b/171957840): replace with device-side test using an inter-process communication
+        //  library.
+        if (!mHasManagedUserFeature) {
+            return;
+        }
+        verifyCrossProfileAppsApi(
+                mProfileId,
+                mPrimaryUserId,
+                START_ACTIVITY_TEST_CLASS,
+                "testStartActivityIntent_crossProfile_returnsResult");
+    }
+
+    @LargeTest
+    @Test
     public void testPrimaryUserToSecondaryUser() throws Exception {
         if (!mCanTestMultiUser) {
             return;
diff --git a/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java b/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
index b752b41..6ed0e60 100644
--- a/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
+++ b/hostsidetests/media/app/MediaSessionTest/src/android/media/session/cts/MediaSessionManagerTest.java
@@ -18,6 +18,7 @@
 
 import static android.media.cts.MediaSessionTestHelperConstants.MEDIA_SESSION_TEST_HELPER_PKG;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -25,6 +26,8 @@
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.os.Process;
 import android.service.notification.NotificationListenerService;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,15 +45,16 @@
  */
 @SmallTest
 public class MediaSessionManagerTest extends NotificationListenerService {
-    private ComponentName mComponentName;
+    private Context mContext;
     private MediaSessionManager mMediaSessionManager;
+    private ComponentName mComponentName;
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getTargetContext();
-        mMediaSessionManager = (MediaSessionManager) context.getSystemService(
-                Context.MEDIA_SESSION_SERVICE);
-        mComponentName = new ComponentName(context, MediaSessionManagerTest.class);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mMediaSessionManager =
+                mContext.getSystemService(MediaSessionManager.class);
+        mComponentName = new ComponentName(mContext, MediaSessionManagerTest.class);
     }
 
     /**
@@ -95,4 +99,24 @@
         List<MediaController> controllers = mMediaSessionManager.getActiveSessions(mComponentName);
         assertTrue(controllers.isEmpty());
     }
+
+    /**
+     * Tests if this application is trusted.
+     */
+    @Test
+    public void testIsTrusted_returnsTrue() throws Exception {
+        RemoteUserInfo userInfo = new RemoteUserInfo(
+                mContext.getPackageName(), Process.myPid(), Process.myUid());
+        assertTrue(mMediaSessionManager.isTrustedForMediaControl(userInfo));
+    }
+
+    /**
+     * Tests if this application isn't trusted.
+     */
+    @Test
+    public void testIsTrusted_returnsFalse() throws Exception {
+        RemoteUserInfo userInfo = new RemoteUserInfo(
+                mContext.getPackageName(), Process.myPid(), Process.myUid());
+        assertFalse(mMediaSessionManager.isTrustedForMediaControl(userInfo));
+    }
 }
diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
index 750e98c..e4956b0 100644
--- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
+++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java
@@ -251,6 +251,37 @@
         runTest("testGetActiveSessions_hasMediaSessionFromMediaSessionTestHelper");
     }
 
+    @AppModeFull
+    @RequiresDevice
+    public void testIsTrusted_withEnabledNotificationListener_returnsTrue() throws Exception {
+        if (!canCreateAdditionalUsers(1)) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    "Cannot create a new user. Skipping multi-user test cases.");
+            return;
+        }
+
+        int newUserId = createAndStartUser();
+        setAllowGetActiveSessionForTest(true, newUserId);
+        installAppAsUser(DEVICE_SIDE_TEST_APK, newUserId, false);
+        runTestAsUser("testIsTrusted_returnsTrue", newUserId);
+    }
+
+    @AppModeFull
+    @RequiresDevice
+    public void testIsTrusted_withoutEnabledNotificationListener_returnsFalse()
+            throws Exception {
+        if (!canCreateAdditionalUsers(1)) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    "Cannot create a new user. Skipping multi-user test cases.");
+            return;
+        }
+
+        int newUserId = createAndStartUser();
+        setAllowGetActiveSessionForTest(false, newUserId);
+        installAppAsUser(DEVICE_SIDE_TEST_APK, newUserId, false);
+        runTestAsUser("testIsTrusted_returnsFalse", newUserId);
+    }
+
     private void runTest(String testMethodName) throws DeviceNotAvailableException {
         runTestAsUser(testMethodName, getDevice().getPrimaryUserId());
     }
diff --git a/hostsidetests/mediaparser/Android.bp b/hostsidetests/mediaparser/Android.bp
new file mode 100644
index 0000000..87c9621
--- /dev/null
+++ b/hostsidetests/mediaparser/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_test_host {
+    name: "CtsMediaParserHostTestCases",
+    defaults: ["cts_defaults"],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+        "compatibility-host-util",
+    ],
+    static_libs: [
+        "cts-host-utils",
+    ],
+    data: [
+      ":CtsMediaParserTestCasesApp",
+    ]
+}
diff --git a/hostsidetests/mediaparser/AndroidTest.xml b/hostsidetests/mediaparser/AndroidTest.xml
new file mode 100644
index 0000000..7d53939
--- /dev/null
+++ b/hostsidetests/mediaparser/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS media host test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsMediaParserHostTestCases.jar" />
+        <option name="runtime-hint" value="3m" />
+    </test>
+</configuration>
+
diff --git a/hostsidetests/mediaparser/OWNERS b/hostsidetests/mediaparser/OWNERS
new file mode 100644
index 0000000..51256bf
--- /dev/null
+++ b/hostsidetests/mediaparser/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 817235
+aquilescanta@google.com
+andrewlewis@google.com
+essick@google.com
+marcone@google.com
diff --git a/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java b/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java
new file mode 100644
index 0000000..7f34459
--- /dev/null
+++ b/hostsidetests/mediaparser/src/android/media/mediaparser/cts/MediaParserHostSideTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.mediaparser.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto;
+import com.android.os.AtomsProto.MediametricsMediaParserReported;
+import com.android.os.StatsLog;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.TestRunResult;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Test for checking that the MediaParser CTS tests produce the expected media metrics. */
+// TODO(b/172494357): Re-enable tests once we can check for the mainline train version.
+public class MediaParserHostSideTest extends DeviceTestCase implements IBuildReceiver {
+
+    private static final String MEDIAPARSER_TEST_APK = "CtsMediaParserTestCasesApp.apk";
+    private static final String MEDIAPARSER_TEST_APP_PACKAGE = "android.media.mediaparser.cts";
+    private static final String MEDIAPARSER_TEST_CLASS_NAME =
+            "android.media.mediaparser.cts.MediaParserTest";
+    private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner";
+
+    private static final long CONFIG_ID = "cts_config".hashCode();
+    private static final String MEDIAPARSER_METRICS_SEPARATOR = "\\|";
+    private static final double MEDIAPARSER_METRICS_DITHER_VALUE = .02f;
+
+    private IBuildInfo mCtsBuildInfo;
+
+    // Resource management.
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuildInfo = buildInfo;
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        File apk = new CompatibilityBuildHelper(mCtsBuildInfo).getTestFile(MEDIAPARSER_TEST_APK);
+        assertThat(getDevice().installPackage(apk, /* reinstall= */ true)).isNull();
+        removeConfig();
+        createAndUploadConfig();
+        getAndClearReportList(); // Clear existing reports.
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        removeConfig();
+        getDevice().uninstallPackage(MEDIAPARSER_TEST_APP_PACKAGE);
+    }
+
+    // Tests.
+
+    public void ignored_testCreationByNameMetrics() throws Exception {
+        runDeviceTest("testCreationByName");
+        String[] expectedParserNames = {
+            "android.media.mediaparser.MatroskaParser",
+            "android.media.mediaparser.FragmentedMp4Parser",
+            "android.media.mediaparser.Mp4Parser",
+            "android.media.mediaparser.Mp3Parser",
+            "android.media.mediaparser.AdtsParser",
+            "android.media.mediaparser.Ac3Parser",
+            "android.media.mediaparser.TsParser",
+            "android.media.mediaparser.FlvParser",
+            "android.media.mediaparser.OggParser",
+            "android.media.mediaparser.PsParser",
+            "android.media.mediaparser.WavParser",
+            "android.media.mediaparser.AmrParser",
+            "android.media.mediaparser.Ac4Parser",
+            "android.media.mediaparser.FlacParser",
+        };
+        // All of the above are created by name.
+        int[] expectedCreatedByName =
+                Arrays.stream(expectedParserNames).mapToInt(unusedArgument -> 1).toArray();
+        runDeviceTest("testCreationByName");
+        List<MediametricsMediaParserReported> mediaParserReportedEvents =
+                getMediaParserReportedEvents();
+        String[] observedParserNames =
+                mediaParserReportedEvents.stream()
+                        .map(MediametricsMediaParserReported::getParserName)
+                        .toArray(String[]::new);
+        int[] observedCreatedByName =
+                mediaParserReportedEvents.stream()
+                        .mapToInt(MediametricsMediaParserReported::getCreatedByName)
+                        .toArray();
+        assertThat(observedParserNames).isEqualTo(expectedParserNames);
+        assertThat(observedCreatedByName).isEqualTo(expectedCreatedByName);
+    }
+
+    public void ignored_testParserPool() throws Exception {
+        runDeviceTest("testMp4");
+        String[] expectedParserNamesInPool = {
+            "android.media.mediaparser.MatroskaParser",
+            "android.media.mediaparser.FragmentedMp4Parser",
+            "android.media.mediaparser.Mp4Parser",
+            "android.media.mediaparser.Mp3Parser",
+            "android.media.mediaparser.AdtsParser",
+            "android.media.mediaparser.Ac3Parser",
+            "android.media.mediaparser.TsParser",
+            "android.media.mediaparser.FlvParser",
+            "android.media.mediaparser.OggParser",
+            "android.media.mediaparser.PsParser",
+            "android.media.mediaparser.WavParser",
+            "android.media.mediaparser.AmrParser",
+            "android.media.mediaparser.Ac4Parser",
+            "android.media.mediaparser.FlacParser",
+        };
+        String parserPool = getSingleMediaParserReportedEvent().getParserPool();
+        List<String> parserNamesInParserPool =
+                Arrays.asList(parserPool.split(MEDIAPARSER_METRICS_SEPARATOR));
+        // We do not assert the order in the pool in order to allow test robustness against future
+        // mainline changes.
+        assertThat(parserNamesInParserPool).containsExactlyElementsIn(expectedParserNamesInPool);
+    }
+
+    public void ignored_testLastException() throws Exception {
+        runDeviceTest("testOggInvalidHeaderSniff");
+        List<MediametricsMediaParserReported> mediaParserReportedEvents =
+                getMediaParserReportedEvents();
+        assertThat(mediaParserReportedEvents).hasSize(2);
+        for (MediametricsMediaParserReported event : mediaParserReportedEvents) {
+            assertThat(event.getLastException())
+                .isEqualTo("android.media.MediaParser$UnrecognizedInputFormatException");
+        }
+    }
+
+    public void ignored_testResourceByteCount() throws Exception {
+        long actualInputSize = 101597;
+        long minimumExpectedResourceByteCount =
+                (long) (actualInputSize * (1 - MEDIAPARSER_METRICS_DITHER_VALUE));
+        long maximumExpectedResourceByteCount =
+                (long) (actualInputSize * (1 + MEDIAPARSER_METRICS_DITHER_VALUE));
+        runDeviceTest("testMp4");
+        long reportedByteCount = getSingleMediaParserReportedEvent().getResourceByteCount();
+        assertThat(reportedByteCount).isAtLeast(minimumExpectedResourceByteCount);
+        assertThat(reportedByteCount).isAtMost(maximumExpectedResourceByteCount);
+    }
+
+    public void ignored_testDurationMillis() throws Exception {
+        long actualDurationMillis = 1024;
+        long minimumExpectedResourceByteCount =
+                (long) (actualDurationMillis * (1 - MEDIAPARSER_METRICS_DITHER_VALUE));
+        long maximumExpectedResourceByteCount =
+                (long) (actualDurationMillis * (1 + MEDIAPARSER_METRICS_DITHER_VALUE));
+        runDeviceTest("testMp4");
+        long reportedDurationMillis = getSingleMediaParserReportedEvent().getDurationMillis();
+        assertThat(reportedDurationMillis).isAtLeast(minimumExpectedResourceByteCount);
+        assertThat(reportedDurationMillis).isAtMost(maximumExpectedResourceByteCount);
+    }
+
+    public void ignored_testTrackMimeTypes() throws Exception {
+        String[] expectedTrackMimeTypes = new String[] {"video/avc", "audio/mp4a-latm"};
+        runDeviceTest("testMp4");
+        String trackMimeTypesField = getSingleMediaParserReportedEvent().getTrackMimeTypes();
+        List<String> actualTrackMimeTypes =
+                Arrays.asList(trackMimeTypesField.split(MEDIAPARSER_METRICS_SEPARATOR));
+        assertThat(actualTrackMimeTypes).containsExactlyElementsIn(expectedTrackMimeTypes);
+    }
+
+    public void ignored_testTrackCodecs() throws Exception {
+        String[] expectedCodecs = new String[] {"", "mp4a.40.2"};
+        runDeviceTest("testMp4");
+        String trackMimeTypesField = getSingleMediaParserReportedEvent().getTrackCodecs();
+        List<String> actualTrackMimeTypes =
+                Arrays.asList(trackMimeTypesField.split(MEDIAPARSER_METRICS_SEPARATOR));
+        assertThat(actualTrackMimeTypes).containsExactlyElementsIn(expectedCodecs);
+    }
+
+    public void ignored_testAlteredParameters() throws Exception {
+        runDeviceTest("testTsWithH264DtsAudio");
+        assertThat(getSingleMediaParserReportedEvent().getAlteredParameters())
+                .isEqualTo("android.media.mediaparser.ts.enableHdmvDtsAudioStreams");
+    }
+
+    public void ignored_testVideoSize() throws Exception {
+        runDeviceTest("testMp4");
+        MediametricsMediaParserReported reportedEvent = getSingleMediaParserReportedEvent();
+        assertThat(reportedEvent.getVideoWidth()).isEqualTo(1080);
+        assertThat(reportedEvent.getVideoHeight()).isEqualTo(720);
+    }
+
+    // Internal methods.
+
+    /** Creates the statsd config and passes it to statsd. */
+    private void createAndUploadConfig() throws Exception {
+        StatsdConfig.Builder configBuilder =
+                StatsdConfigProto.StatsdConfig.newBuilder()
+                        .setId(CONFIG_ID)
+                        .addAllowedLogSource(MEDIAPARSER_TEST_APP_PACKAGE)
+                        .addWhitelistedAtomIds(
+                                AtomsProto.Atom.MEDIAMETRICS_MEDIAPARSER_REPORTED_FIELD_NUMBER);
+        addAtomEvent(configBuilder);
+        uploadConfig(configBuilder.build());
+    }
+
+    /** Removes any existing config with id {@link #CONFIG_ID}. */
+    private void removeConfig() throws Exception {
+        getDevice().executeShellCommand("cmd stats config remove " + CONFIG_ID);
+    }
+
+    /** Writes the given config into a file and passes is to statsd via standard input. */
+    private void uploadConfig(StatsdConfig config) throws Exception {
+        File configFile = File.createTempFile("statsdconfig", ".config");
+        configFile.deleteOnExit();
+        Files.write(config.toByteArray(), configFile);
+        String remotePath = "/data/local/tmp/" + configFile.getName();
+        // Make sure a config file with the same name doesn't exist already.
+        getDevice().deleteFile(remotePath);
+        assertThat(getDevice().pushFile(configFile, remotePath)).isTrue();
+        getDevice()
+                .executeShellCommand(
+                        "cat " + remotePath + " | cmd stats config update " + CONFIG_ID);
+        getDevice().deleteFile(remotePath);
+    }
+
+    /**
+     * Asserts that there is only one MediaParser reported metric event, and returns it.
+     *
+     * <p>Note: Calls {@link #getAndClearReportList()} to obtain the statsd report.
+     */
+    private MediametricsMediaParserReported getSingleMediaParserReportedEvent() throws Exception {
+        List<MediametricsMediaParserReported> mediaParserReportedEvents =
+                getMediaParserReportedEvents();
+        assertThat(mediaParserReportedEvents).hasSize(1);
+        return mediaParserReportedEvents.get(0);
+    }
+
+    /**
+     * Returns all MediaParser reported metric events sorted by timestamp.
+     *
+     * <p>Note: Calls {@link #getAndClearReportList()} to obtain the statsd report.
+     */
+    private List<MediametricsMediaParserReported> getMediaParserReportedEvents() throws Exception {
+        ConfigMetricsReportList reportList = getAndClearReportList();
+        assertThat(reportList.getReportsCount()).isEqualTo(1);
+        StatsLog.ConfigMetricsReport report = reportList.getReports(0);
+        ArrayList<EventMetricData> data = new ArrayList<>();
+        report.getMetricsList()
+                .forEach(
+                        statsLogReport ->
+                                data.addAll(statsLogReport.getEventMetrics().getDataList()));
+        // We sort the reported events by the elapsed timestamp so as to ensure they are returned
+        // in the same order as they were generated by the CTS tests.
+        return data.stream()
+                .sorted(Comparator.comparing(EventMetricData::getElapsedTimestampNanos))
+                .map(event -> event.getAtom().getMediametricsMediaparserReported())
+                .collect(Collectors.toList());
+    }
+
+    /** Gets a statsd report and removes it from the device. */
+    private ConfigMetricsReportList getAndClearReportList() throws Exception {
+        CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+        getDevice()
+                .executeShellCommand(
+                        "cmd stats dump-report " + CONFIG_ID + " --include_current_bucket --proto",
+                        receiver);
+        return ConfigMetricsReportList.parser().parseFrom(receiver.getOutput());
+    }
+
+    /** Runs the test with the given name from the MediaParser CTS apk. */
+    private void runDeviceTest(String testMethodName) throws DeviceNotAvailableException {
+        RemoteAndroidTestRunner testRunner =
+                new RemoteAndroidTestRunner(
+                        MEDIAPARSER_TEST_APP_PACKAGE, TEST_RUNNER, getDevice().getIDevice());
+        testRunner.setMethodName(MEDIAPARSER_TEST_CLASS_NAME, testMethodName);
+        CollectingTestListener listener = new CollectingTestListener();
+        assertThat(getDevice().runInstrumentationTests(testRunner, listener)).isTrue();
+        TestRunResult result = listener.getCurrentRunResults();
+        assertThat(result.isRunFailure()).isFalse();
+        assertThat(result.getNumTests()).isEqualTo(1);
+        assertThat(result.hasFailedTests()).isFalse();
+    }
+
+    /** Adds an event to the config in order to match MediaParser reported atoms. */
+    private static void addAtomEvent(StatsdConfig.Builder config) {
+        String atomName = "Atom" + System.nanoTime();
+        String eventName = "Event" + System.nanoTime();
+        SimpleAtomMatcher.Builder sam =
+                SimpleAtomMatcher.newBuilder()
+                        .setAtomId(AtomsProto.Atom.MEDIAMETRICS_MEDIAPARSER_REPORTED_FIELD_NUMBER);
+        config.addAtomMatcher(
+                AtomMatcher.newBuilder().setId(atomName.hashCode()).setSimpleAtomMatcher(sam));
+        config.addEventMetric(
+                StatsdConfigProto.EventMetric.newBuilder()
+                        .setId(eventName.hashCode())
+                        .setWhat(atomName.hashCode()));
+    }
+}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 72f8415..566dc08 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -115,6 +115,7 @@
             "com.android.cts.device.statsd.emptyapp";
     private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsd";
     private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+    private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
 
     private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
     private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping.
@@ -2112,18 +2113,48 @@
                 AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
 
         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);  // False: does not use attribution.
+        createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
         Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
 
         getDevice().executeShellCommand(String.format(
-            "am start -n '%s' -e %s %s",
-            "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
-            "action", ACTION_SHOW_APPLICATION_OVERLAY));
+                "am start -n '%s' -e %s %s",
+                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
+                "action", ACTION_SHOW_APPLICATION_OVERLAY));
         final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
         Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
 
         List<EventMetricData> data = getEventMetricDataList();
-        Function<Atom, Integer> appUsageStateFunction = atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
+        Function<Atom, Integer> appUsageStateFunction =
+                atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
+        popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.s
+        assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
+    }
+
+    public void testAppForceStopUsageEvent() throws Exception {
+        Set<Integer> onStates = new HashSet<>(Arrays.asList(
+                AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
+        Set<Integer> offStates = new HashSet<>(Arrays.asList(
+                AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
+
+        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
+        createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
+        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
+
+        getDevice().executeShellCommand(String.format(
+                "am start -n '%s' -e %s %s",
+                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
+                "action", ACTION_LONG_SLEEP_WHILE_TOP));
+        final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
+        Thread.sleep(waitTime);
+
+        getDevice().executeShellCommand(String.format(
+                "am force-stop %s",
+                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
+        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
+
+        List<EventMetricData> data = getEventMetricDataList();
+        Function<Atom, Integer> appUsageStateFunction =
+                atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
         popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
         assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
     }
diff --git a/libs/install/Android.bp b/libs/install/Android.bp
index 0e1ebb0..e7bf788 100644
--- a/libs/install/Android.bp
+++ b/libs/install/Android.bp
@@ -91,7 +91,7 @@
 }
 
 java_library {
-    name: "cts-install-lib",
+    name: "cts-install-lib-java",
     srcs: ["src/**/*.java"],
     static_libs: ["androidx.test.rules", "compatibility-device-util-axt", "truth-prebuilt"],
     sdk_version: "test_current",
@@ -110,3 +110,11 @@
         ":StagedInstallTestApexV3",
     ],
 }
+
+android_library {
+    name: "cts-install-lib",
+    manifest: "AndroidManifest.xml",
+    static_libs: [
+        "cts-install-lib-java",
+    ],
+}
diff --git a/libs/install/AndroidManifest.xml b/libs/install/AndroidManifest.xml
new file mode 100644
index 0000000..e9f5b7d
--- /dev/null
+++ b/libs/install/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.cts.install.lib"
+          android:versionCode="1"
+          android:versionName="1.0">
+
+    <queries>
+        <package android:name="com.android.cts.install.lib.testapp.A"/>
+        <package android:name="com.android.cts.install.lib.testapp.B"/>
+        <package android:name="com.android.cts.install.lib.testapp.C"/>
+    </queries>
+
+    <uses-sdk android:minSdkVersion="8"/>
+</manifest>
diff --git a/libs/install/src/com/android/cts/install/lib/TestApp.java b/libs/install/src/com/android/cts/install/lib/TestApp.java
index cb77517..9bf34cf 100644
--- a/libs/install/src/com/android/cts/install/lib/TestApp.java
+++ b/libs/install/src/com/android/cts/install/lib/TestApp.java
@@ -47,6 +47,8 @@
             "TestAppASplitV1.apk", "TestAppASplitV1_anydpi.apk");
     public static final TestApp ASplit2 = new TestApp("ASplitV2", A, 2, /*isApex*/false,
             "TestAppASplitV2.apk", "TestAppASplitV2_anydpi.apk");
+    public static final TestApp AIncompleteSplit = new TestApp("AIncompleteSplit", A, 1,
+            /*isApex*/false, "TestAppASplitV1_anydpi.apk");
 
     public static final TestApp B1 = new TestApp("Bv1", B, 1, /*isApex*/false,
             "TestAppBv1.apk");
diff --git a/libs/install/testapp/ACrashingV2.xml b/libs/install/testapp/ACrashingV2.xml
index 0ec90cf..338a5b9 100644
--- a/libs/install/testapp/ACrashingV2.xml
+++ b/libs/install/testapp/ACrashingV2.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App A v2" android:forceQueryable="true">
+    <application android:label="Test App A v2">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.CrashingMainActivity">
diff --git a/libs/install/testapp/Av1.xml b/libs/install/testapp/Av1.xml
index 5b47699..e9714fc 100644
--- a/libs/install/testapp/Av1.xml
+++ b/libs/install/testapp/Av1.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App A1" android:forceQueryable="true">
+    <application android:label="Test App A1">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
                   android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/libs/install/testapp/Av2.xml b/libs/install/testapp/Av2.xml
index 9f2c21a..fd8afa0 100644
--- a/libs/install/testapp/Av2.xml
+++ b/libs/install/testapp/Av2.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App A2" android:forceQueryable="true">
+    <application android:label="Test App A2">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
             android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/libs/install/testapp/Av3.xml b/libs/install/testapp/Av3.xml
index d86aebd..a7839e3 100644
--- a/libs/install/testapp/Av3.xml
+++ b/libs/install/testapp/Av3.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App A3" android:forceQueryable="true">
+    <application android:label="Test App A3">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
             android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/libs/install/testapp/Bv1.xml b/libs/install/testapp/Bv1.xml
index f990713..403e7e2 100644
--- a/libs/install/testapp/Bv1.xml
+++ b/libs/install/testapp/Bv1.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App B1" android:forceQueryable="true">
+    <application android:label="Test App B1">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
             android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/libs/install/testapp/Bv2.xml b/libs/install/testapp/Bv2.xml
index 3bd7292..f030c3f 100644
--- a/libs/install/testapp/Bv2.xml
+++ b/libs/install/testapp/Bv2.xml
@@ -22,7 +22,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App B2" android:forceQueryable="true">
+    <application android:label="Test App B2">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
             android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/libs/install/testapp/Cv1.xml b/libs/install/testapp/Cv1.xml
index 32f6989..edb69f9 100644
--- a/libs/install/testapp/Cv1.xml
+++ b/libs/install/testapp/Cv1.xml
@@ -23,7 +23,7 @@
 
     <uses-sdk android:minSdkVersion="19" />
 
-    <application android:label="Test App C1" android:forceQueryable="true">
+    <application android:label="Test App C1">
         <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
             android:exported="true" />
         <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index 827375b..bc5b110 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -33,7 +33,10 @@
         "platformprotosnano",
         "permission-test-util-lib"
     ],
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+        "NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl",
+    ],
     // Tag this module as a cts test artifact
     test_suites: [
         "cts",
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index 645d233..465b633 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -33,6 +33,8 @@
         <option name="test-file-name" value="CtsCantSaveState1.apk" />
         <option name="test-file-name" value="CtsCantSaveState2.apk" />
         <option name="test-file-name" value="NotificationDelegator.apk" />
+        <option name="test-file-name" value="NotificationProvider.apk" />
+        <option name="test-file-name" value="NotificationListener.apk" />
         <option name="test-file-name" value="StorageDelegator.apk" />
         <option name="test-file-name" value="CtsActivityManagerApi29.apk" />
     </target_preparer>
diff --git a/tests/app/NotificationListener/Android.bp b/tests/app/NotificationListener/Android.bp
new file mode 100644
index 0000000..96a0c3c
--- /dev/null
+++ b/tests/app/NotificationListener/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "NotificationListener",
+    defaults: ["cts_support_defaults"],
+    srcs: [
+        "**/*.java",
+        "**/*.kt",
+        "src/com/android/test/notificationlistener/INotificationUriAccessService.aidl",
+    ],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "platform-test-annotations",
+    ],
+    platform_apis: true,
+    sdk_version: "test_current",
+}
diff --git a/tests/app/NotificationListener/AndroidManifest.xml b/tests/app/NotificationListener/AndroidManifest.xml
new file mode 100644
index 0000000..f510637
--- /dev/null
+++ b/tests/app/NotificationListener/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.test.notificationlistener">
+    <application android:label="Notification Listener">
+
+        <service android:name=".NotificationUriAccessService"
+                 android:exported="true"/>
+
+        <service android:name=".TestNotificationListener"
+                 android:exported="true"
+                 android:label="TestNotificationListener"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService"/>
+            </intent-filter>
+        </service>
+
+    </application>
+</manifest>
diff --git a/tests/app/NotificationListener/res/layout/activity.xml b/tests/app/NotificationListener/res/layout/activity.xml
new file mode 100644
index 0000000..f001f29
--- /dev/null
+++ b/tests/app/NotificationListener/res/layout/activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="@string/activity_description"
+    />
+
+</LinearLayout>
diff --git a/tests/app/NotificationListener/res/values/strings.xml b/tests/app/NotificationListener/res/values/strings.xml
new file mode 100644
index 0000000..e19d5bf
--- /dev/null
+++ b/tests/app/NotificationListener/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <string name="activity_description">This app has a listener and an service used for tests</string>
+</resources>
\ No newline at end of file
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl b/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl
new file mode 100644
index 0000000..eb93179
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/INotificationUriAccessService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.notificationlistener;
+
+interface INotificationUriAccessService {
+    void ensureNotificationListenerServiceConnected(boolean connected);
+    boolean isFileUriAccessible(in android.net.Uri uri);
+}
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt b/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt
new file mode 100644
index 0000000..5e6e469
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/NotificationUriAccessService.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.notificationlistener
+
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.net.Uri
+import android.os.IBinder
+import java.io.IOException
+
+class NotificationUriAccessService : Service() {
+    private inner class MyNotificationUriAccessService : INotificationUriAccessService.Stub() {
+        @Throws(IllegalStateException::class)
+        override fun ensureNotificationListenerServiceConnected(ensureConnected: Boolean) {
+            val nm = getSystemService(NotificationManager::class.java)!!
+            val testListener = TestNotificationListener.componentName
+            check(nm.isNotificationListenerAccessGranted(testListener) == ensureConnected) {
+                "$testListener has incorrect listener access; expected=$ensureConnected"
+            }
+            val listener = TestNotificationListener.instance
+            if (ensureConnected) {
+                check(listener != null) {
+                    "$testListener has not been created, but should be connected"
+                }
+            }
+            val isConnected = listener?.isConnected ?: false
+            check(isConnected == ensureConnected) {
+                "$testListener has incorrect listener connection state; expected=$ensureConnected"
+            }
+        }
+
+        override fun isFileUriAccessible(uri: Uri?): Boolean {
+            try {
+                contentResolver.openAssetFile(uri!!, "r", null).use { return true }
+            } catch (e: SecurityException) {
+                return false
+            } catch (e: IOException) {
+                throw IllegalStateException("Exception without security error", e)
+            }
+        }
+    }
+
+    private val mBinder = MyNotificationUriAccessService()
+    override fun onBind(intent: Intent): IBinder? {
+        return mBinder
+    }
+}
\ No newline at end of file
diff --git a/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt b/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt
new file mode 100644
index 0000000..19c7d82
--- /dev/null
+++ b/tests/app/NotificationListener/src/com/android/test/notificationlistener/TestNotificationListener.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.notificationlistener
+
+import android.content.ComponentName
+import android.service.notification.NotificationListenerService
+
+class TestNotificationListener : NotificationListenerService() {
+    var isConnected = false
+
+    override fun onListenerConnected() {
+        super.onListenerConnected()
+        instance = this
+        isConnected = true
+    }
+
+    override fun onListenerDisconnected() {
+        super.onListenerDisconnected()
+        isConnected = false
+    }
+
+    companion object {
+        var instance: TestNotificationListener? = null
+            private set
+        val componentName: ComponentName by lazy {
+            val javaClass = TestNotificationListener::class.java
+            ComponentName(javaClass.getPackage().name, javaClass.name)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/app/NotificationProvider/Android.bp b/tests/app/NotificationProvider/Android.bp
new file mode 100644
index 0000000..26e69d7
--- /dev/null
+++ b/tests/app/NotificationProvider/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "NotificationProvider",
+    defaults: ["cts_support_defaults"],
+    srcs: ["**/*.java", "**/*.kt"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    platform_apis: true,
+    sdk_version: "current",
+}
diff --git a/tests/app/NotificationProvider/AndroidManifest.xml b/tests/app/NotificationProvider/AndroidManifest.xml
new file mode 100644
index 0000000..09ae4b0
--- /dev/null
+++ b/tests/app/NotificationProvider/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.notificationprovider">
+    <application android:label="Notification Provider">
+        <activity android:name=".RichNotificationActivity" android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <provider
+            android:name=".AssetFileProvider"
+            android:authorities="com.android.test.notificationprovider.provider"
+            android:exported="false"
+            android:grantUriPermissions="true"
+            />
+
+    </application>
+</manifest>
diff --git a/tests/app/NotificationProvider/assets/background7.png b/tests/app/NotificationProvider/assets/background7.png
new file mode 100644
index 0000000..20c22f7
--- /dev/null
+++ b/tests/app/NotificationProvider/assets/background7.png
Binary files differ
diff --git a/tests/app/NotificationProvider/assets/background8.png b/tests/app/NotificationProvider/assets/background8.png
new file mode 100644
index 0000000..a7a593d
--- /dev/null
+++ b/tests/app/NotificationProvider/assets/background8.png
Binary files differ
diff --git a/tests/app/NotificationProvider/res/layout/activity.xml b/tests/app/NotificationProvider/res/layout/activity.xml
new file mode 100644
index 0000000..f001f29
--- /dev/null
+++ b/tests/app/NotificationProvider/res/layout/activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="25dp"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="@string/activity_description"
+    />
+
+</LinearLayout>
diff --git a/tests/app/NotificationProvider/res/values/strings.xml b/tests/app/NotificationProvider/res/values/strings.xml
new file mode 100644
index 0000000..266ea53
--- /dev/null
+++ b/tests/app/NotificationProvider/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources>
+    <string name="activity_description">This app has a provider and posts notifications</string>
+</resources>
\ No newline at end of file
diff --git a/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt
new file mode 100644
index 0000000..af10f1b
--- /dev/null
+++ b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/AssetFileProvider.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.notificationprovider
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.res.AssetFileDescriptor
+import android.database.Cursor
+import android.net.Uri
+
+class AssetFileProvider : ContentProvider() {
+    override fun onCreate(): Boolean {
+        return true
+    }
+
+    override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? {
+        val assets = context?.assets
+        val filename = uri.lastPathSegment
+        if (mode == "r" && assets != null && filename != null) {
+            return assets.openFd(filename)
+        }
+        return super.openAssetFile(uri, mode)
+    }
+
+    override fun query(
+        uri: Uri,
+        projection: Array<String>?,
+        selection: String?,
+        selectionArgs: Array<String>?,
+        sortOrder: String?
+    ): Cursor? {
+        throw UnsupportedOperationException()
+    }
+
+    override fun getType(uri: Uri): String? {
+        return null
+    }
+
+    override fun insert(uri: Uri, values: ContentValues?): Uri? {
+        throw UnsupportedOperationException()
+    }
+
+    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
+        throw UnsupportedOperationException()
+    }
+
+    override fun update(
+        uri: Uri,
+        values: ContentValues?,
+        selection: String?,
+        selectionArgs: Array<String>?
+    ): Int {
+        throw UnsupportedOperationException()
+    }
+}
\ No newline at end of file
diff --git a/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt
new file mode 100644
index 0000000..4197760
--- /dev/null
+++ b/tests/app/NotificationProvider/src/com/android/test/notificationprovider/RichNotificationActivity.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.notificationprovider
+
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.os.Bundle
+
+/**
+ * Used by NotificationManagerTest for testing policy around content uris.
+ */
+class RichNotificationActivity : Activity() {
+    companion object {
+        const val NOTIFICATION_CHANNEL_ID = "NotificationManagerTest"
+        const val EXTRA_ACTION = "action"
+        const val ACTION_SEND_7 = "send-7"
+        const val ACTION_SEND_8 = "send-8"
+        const val ACTION_CANCEL_7 = "cancel-7"
+        const val ACTION_CANCEL_8 = "cancel-8"
+    }
+
+    enum class NotificationPreset(val id: Int) {
+        Preset7(7),
+        Preset8(8);
+
+        fun build(context: Context): Notification {
+            val extras = Bundle()
+            extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
+                    "content://com.android.test.notificationprovider.provider/background$id.png")
+            return Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
+                    .setContentTitle("Rich Notification #$id")
+                    .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                    .addExtras(extras)
+                    .build()
+        }
+    }
+
+    public override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity)
+        when (intent?.extras?.getString(EXTRA_ACTION)) {
+            ACTION_SEND_7 -> sendNotification(NotificationPreset.Preset7)
+            ACTION_SEND_8 -> sendNotification(NotificationPreset.Preset8)
+            ACTION_CANCEL_7 -> cancelNotification(NotificationPreset.Preset7)
+            ACTION_CANCEL_8 -> cancelNotification(NotificationPreset.Preset8)
+            else -> {
+                // reset both
+                cancelNotification(NotificationPreset.Preset7)
+                cancelNotification(NotificationPreset.Preset8)
+            }
+        }
+        finish()
+    }
+
+    private val notificationManager by lazy { getSystemService(NotificationManager::class.java)!! }
+
+    private fun sendNotification(preset: NotificationPreset) {
+        notificationManager.createNotificationChannel(NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                "Notifications", NotificationManager.IMPORTANCE_DEFAULT))
+        notificationManager.notify(preset.id, preset.build(this))
+    }
+
+    private fun cancelNotification(preset: NotificationPreset) {
+        notificationManager.cancel(preset.id)
+    }
+}
\ No newline at end of file
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
index 12a24c8..5a13eab 100644
--- a/tests/app/app/src/android/app/stubs/CommandReceiver.java
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -48,6 +48,7 @@
     public static final int COMMAND_STOP_ACTIVITY = 11;
     public static final int COMMAND_CREATE_FGSL_PENDING_INTENT = 12;
     public static final int COMMAND_SEND_FGSL_PENDING_INTENT = 13;
+    public static final int COMMAND_BIND_FOREGROUND_SERVICE = 14;
 
     public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
     public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
@@ -82,7 +83,7 @@
                 + intent);
         switch (command) {
             case COMMAND_BIND_SERVICE:
-                doBindService(context, intent);
+                doBindService(context, intent, SERVICE_NAME);
                 break;
             case COMMAND_UNBIND_SERVICE:
                 doUnbindService(context, intent);
@@ -120,15 +121,18 @@
             case COMMAND_SEND_FGSL_PENDING_INTENT:
                 doSendFgslPendingIntent(context, intent);
                 break;
+            case COMMAND_BIND_FOREGROUND_SERVICE:
+                doBindService(context, intent, FG_LOCATION_SERVICE_NAME);
+                break;
         }
     }
 
-    private void doBindService(Context context, Intent commandIntent) {
+    private void doBindService(Context context, Intent commandIntent, String serviceName) {
         String targetPackage = getTargetPackage(commandIntent);
         int flags = getFlags(commandIntent);
 
         Intent bindIntent = new Intent();
-        bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
+        bindIntent.setComponent(new ComponentName(targetPackage, serviceName));
 
         ServiceConnection connection = addServiceConnection(targetPackage);
 
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 9302e67..c982cd6 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -327,4 +327,43 @@
             uid2Watcher.finish();
         }
     }
+
+
+    public void testFgsLocationStartFromBGWithBind() throws Exception {
+        ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+                PACKAGE_NAME_APP1, 0);
+        WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+                WAITFOR_MSEC);
+
+        try {
+            // Package1 is in BG state, bind FGSL in package1 first.
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_FOREGROUND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+            Bundle bundle = new Bundle();
+            bundle.putInt(LocalForegroundServiceLocation.EXTRA_FOREGROUND_SERVICE_TYPE,
+                    ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
+            // Then start FGSL in package1, it won't get location capability.
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_START_FOREGROUND_SERVICE_LOCATION,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, bundle);
+
+            // Package1 is in FGS state, but won't get location capability.
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+                    WatchUidRunner.STATE_FG_SERVICE,
+                    new Integer(PROCESS_CAPABILITY_NONE));
+
+            // unbind service.
+            CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+            // stop FGSL
+            CommandReceiver.sendCommand(mContext,
+                    CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
+                    PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+            uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+                    WatchUidRunner.STATE_CACHED_EMPTY,
+                    new Integer(PROCESS_CAPABILITY_NONE));
+        } finally {
+            uid1Watcher.finish();
+        }
+    }
 }
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 029ed92..22c604b 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -75,13 +75,16 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.OperationApplicationException;
+import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
+import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
@@ -91,6 +94,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -111,12 +115,12 @@
 import android.util.Log;
 import android.widget.RemoteViews;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.FeatureUtil;
 import com.android.compatibility.common.util.SystemUtil;
-
-import junit.framework.Assert;
+import com.android.test.notificationlistener.INotificationUriAccessService;
 
 import java.io.BufferedReader;
 import java.io.FileInputStream;
@@ -133,11 +137,15 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 /* This tests NotificationListenerService together with NotificationManager, as you need to have
  * notifications to manipulate in order to test the listener service. */
 public class NotificationManagerTest extends AndroidTestCase {
+    public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider";
+    public static final String RICH_NOTIFICATION_ACTIVITY =
+            "com.android.test.notificationprovider.RichNotificationActivity";
     final String TAG = NotificationManagerTest.class.getSimpleName();
     final boolean DEBUG = false;
     static final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
@@ -160,6 +168,7 @@
     private List<String> mRuleIds;
     private BroadcastReceiver mBubbleBroadcastReceiver;
     private boolean mBubblesEnabledSettingToRestore;
+    private INotificationUriAccessService mNotificationUriAccessService;
 
     @Override
     protected void setUp() throws Exception {
@@ -195,7 +204,8 @@
         // delay between tests so notifications aren't dropped by the rate limiter
         try {
             Thread.sleep(500);
-        } catch(InterruptedException e) {}
+        } catch (InterruptedException e) {
+        }
     }
 
     @Override
@@ -222,8 +232,7 @@
         suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
                 false);
 
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), false);
+        toggleListenerAccess(false);
         toggleNotificationPolicyAccess(mContext.getPackageName(),
                 InstrumentationRegistry.getInstrumentation(), false);
 
@@ -237,17 +246,17 @@
         setBubblesGlobal(mBubblesEnabledSettingToRestore);
     }
 
-    private boolean isNotificationCancelled(int id, boolean all) {
+    private void assertNotificationCancelled(int id, boolean all) {
         for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
-            StatusBarNotification sbn = findPostedNotification(id, all);
-            if (sbn == null) return true;
+            StatusBarNotification sbn = findNotificationNoWait(id, all);
+            if (sbn == null) return;
             try {
                 Thread.sleep(SHORT_WAIT_TIME);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
-        return false;
+        assertNull(findNotificationNoWait(id, all));
     }
 
     private void insertSingleContact(String name, String phone, String email, boolean starred) {
@@ -296,8 +305,8 @@
         try {
             Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                     Uri.encode(phone));
-            String[] projection = new String[] { ContactsContract.Contacts._ID,
-                    ContactsContract.Contacts.LOOKUP_KEY };
+            String[] projection = new String[]{ContactsContract.Contacts._ID,
+                    ContactsContract.Contacts.LOOKUP_KEY};
             c = mContext.getContentResolver().query(phoneUri, projection, null, null, null);
             if (c != null && c.getCount() > 0) {
                 c.moveToFirst();
@@ -320,25 +329,28 @@
     private StatusBarNotification findPostedNotification(int id, boolean all) {
         // notification is a bit asynchronous so it may take a few ms to appear in
         // getActiveNotifications()
-        // we will check for it for up to 300ms before giving up
-        StatusBarNotification n = null;
-        for (int tries = 3; tries-- > 0; ) {
-            final StatusBarNotification[] sbns = getActiveNotifications(all);
-            for (StatusBarNotification sbn : sbns) {
-                Log.d(TAG, "Found " + sbn.getKey());
-                if (sbn.getId() == id) {
-                    n = sbn;
-                    break;
-                }
+        // we will check for it for up to 1000ms before giving up
+        for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) {
+            StatusBarNotification n = findNotificationNoWait(id, all);
+            if (n != null) {
+                return n;
             }
-            if (n != null) break;
             try {
-                Thread.sleep(100);
+                Thread.sleep(SHORT_WAIT_TIME);
             } catch (InterruptedException ex) {
                 // pass
             }
         }
-        return n;
+        return findNotificationNoWait(id, all);
+    }
+
+    private StatusBarNotification findNotificationNoWait(int id, boolean all) {
+        for (StatusBarNotification sbn : getActiveNotifications(all)) {
+            if (sbn.getId() == id) {
+                return sbn;
+            }
+        }
+        return null;
     }
 
     private StatusBarNotification[] getActiveNotifications(boolean all) {
@@ -449,8 +461,7 @@
 
     private void setUpNotifListener() {
         try {
-            toggleListenerAccess(TestNotificationListener.getId(),
-                    InstrumentationRegistry.getInstrumentation(), true);
+            toggleListenerAccess(true);
             mListener = TestNotificationListener.getInstance();
             mListener.resetData();
             assertNotNull(mListener);
@@ -471,16 +482,16 @@
 
         if (data == null) {
             data = new Notification.BubbleMetadata.Builder(pendingIntent,
-                            Icon.createWithResource(mContext, R.drawable.black))
+                    Icon.createWithResource(mContext, R.drawable.black))
                     .build();
         }
         if (builder == null) {
             builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
-                            .setSmallIcon(R.drawable.black)
-                            .setWhen(System.currentTimeMillis())
-                            .setContentTitle("notify#" + id)
-                            .setContentText("This is #" + id + "notification  ")
-                            .setContentIntent(pendingIntent);
+                    .setSmallIcon(R.drawable.black)
+                    .setWhen(System.currentTimeMillis())
+                    .setContentTitle("notify#" + id)
+                    .setContentText("This is #" + id + "notification  ")
+                    .setContentIntent(pendingIntent);
         }
         builder.setBubbleMetadata(data);
 
@@ -523,7 +534,7 @@
         // getActiveNotifications()
         // we will check for it for up to 300ms before giving up
         boolean found = false;
-        for (int tries = 3; tries--> 0;) {
+        for (int tries = 3; tries-- > 0; ) {
             // Need reset flag.
             found = false;
             final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
@@ -549,7 +560,7 @@
         // getActiveNotifications()
         // we will check for it for up to 400ms before giving up
         int lastCount = 0;
-        for (int tries = 4; tries-- > 0;) {
+        for (int tries = 4; tries-- > 0; ) {
             final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
             lastCount = sbns.length;
             if (expectedCount == lastCount) return;
@@ -559,7 +570,7 @@
                 // pass
             }
         }
-        fail("Expected " + expectedCount + " posted notifications, were " +  lastCount);
+        fail("Expected " + expectedCount + " posted notifications, were " + lastCount);
     }
 
     private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
@@ -599,8 +610,8 @@
         runCommand(command, instrumentation);
 
         NotificationManager nm = mContext.getSystemService(NotificationManager.class);
-        Assert.assertEquals("Notification Policy Access Grant is " +
-                        nm.isNotificationPolicyAccessGranted() + " not " + on, on,
+        assertEquals("Notification Policy Access Grant is "
+                        + nm.isNotificationPolicyAccessGranted() + " not " + on, on,
                 nm.isNotificationPolicyAccessGranted());
     }
 
@@ -613,18 +624,23 @@
         runCommand(command, instrumentation);
     }
 
-    private void toggleListenerAccess(String componentName, Instrumentation instrumentation,
-            boolean on) throws IOException {
-
+    private void toggleListenerAccess(boolean on) throws IOException {
         String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
-                + componentName;
+                + TestNotificationListener.getId();
 
-        runCommand(command, instrumentation);
+        runCommand(command, InstrumentationRegistry.getInstrumentation());
 
         final NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         final ComponentName listenerComponent = TestNotificationListener.getComponentName();
-        assertTrue(listenerComponent + " has not been granted access",
-                nm.isNotificationListenerAccessGranted(listenerComponent) == on);
+        assertEquals(listenerComponent + " has incorrect listener access",
+                on, nm.isNotificationListenerAccessGranted(listenerComponent));
+    }
+
+    private void toggleExternalListenerAccess(ComponentName listenerComponent, boolean on)
+            throws IOException {
+        String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ")
+                + listenerComponent.flattenToString();
+        runCommand(command, InstrumentationRegistry.getInstrumentation());
     }
 
     private void setBubblesGlobal(boolean enabled)
@@ -656,15 +672,18 @@
         Thread.sleep(500); // wait for ranking update
     }
 
+    @SuppressWarnings("StatementWithEmptyBody")
     private void runCommand(String command, Instrumentation instrumentation) throws IOException {
         UiAutomation uiAutomation = instrumentation.getUiAutomation();
         // Execute command
         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
-            Assert.assertNotNull("Failed to execute shell command: " + command, fd);
+            assertNotNull("Failed to execute shell command: " + command, fd);
             // Wait for the command to finish by reading until EOF
             try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
                 byte[] buffer = new byte[4096];
-                while (in.read(buffer) > 0) {}
+                while (in.read(buffer) > 0) {
+                    // discard output
+                }
             } catch (IOException e) {
                 throw new IOException("Could not read stdout of command: " + command, e);
             }
@@ -698,7 +717,7 @@
 
     private void assertExpectedDndState(int expectedState) {
         int tries = 3;
-        for (int i = tries; i >=0; i--) {
+        for (int i = tries; i >= 0; i--) {
             if (expectedState ==
                     mNotificationManager.getCurrentInterruptionFilter()) {
                 break;
@@ -808,11 +827,11 @@
         KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
         keyguardManager.requestDismissKeyguard(sendBubbleActivity,
                 new KeyguardManager.KeyguardDismissCallback() {
-            @Override
-            public void onDismissSucceeded() {
-                latch.countDown();
-            }
-        });
+                    @Override
+                    public void onDismissSucceeded() {
+                        latch.countDown();
+                    }
+                });
         try {
             latch.await(500, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
@@ -1098,7 +1117,7 @@
                 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT);
         channel.setDescription("bananas");
         channel.enableVibration(true);
-        channel.setVibrationPattern(new long[] {5, 8, 2, 1});
+        channel.setVibrationPattern(new long[]{5, 8, 2, 1});
         channel.setSound(new Uri.Builder().scheme("test").build(),
                 new AudioAttributes.Builder().setUsage(
                         AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build());
@@ -1213,7 +1232,8 @@
         try {
             mNotificationManager.createNotificationChannel(channel);
             fail("Created notification with bad group");
-        } catch (IllegalArgumentException e) {}
+        } catch (IllegalArgumentException e) {
+        }
     }
 
     public void testCreateChannelInvalidImportance() throws Exception {
@@ -1380,8 +1400,7 @@
     }
 
     public void testSuspendPackage() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1422,8 +1441,7 @@
     }
 
     public void testSuspendedPackageSendsNotification() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1475,8 +1493,7 @@
         assertEquals(1, Settings.Global.getInt(
                 mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES));
 
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1520,8 +1537,7 @@
         assertEquals(1, Settings.Secure.getInt(
                 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING));
 
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1563,8 +1579,7 @@
     }
 
     public void testGetSuppressedVisualEffectsOff_ranking() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1592,8 +1607,7 @@
         final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
         NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy();
         try {
-            toggleListenerAccess(TestNotificationListener.getId(),
-                    InstrumentationRegistry.getInstrumentation(), true);
+            toggleListenerAccess(true);
             Thread.sleep(500); // wait for listener to be allowed
 
             mListener = TestNotificationListener.getInstance();
@@ -1641,8 +1655,7 @@
     }
 
     public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -1934,7 +1947,8 @@
                         .setStyle(new Notification.BigPictureStyle()
                                 .setBigContentTitle("title")
                                 .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
-                                .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
+                                .bigLargeIcon(
+                                        Icon.createWithResource(getContext(), R.drawable.icon_blue))
                                 .setSummaryText("summary"))
                         .build();
         mNotificationManager.notify(id, notification);
@@ -2365,31 +2379,31 @@
     }
 
     public void testNotificationPolicyVisualEffectsEqual() {
-        NotificationManager.Policy policy = new NotificationManager.Policy(0,0 ,0 ,
+        NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON);
-        NotificationManager.Policy policy2 = new NotificationManager.Policy(0,0 ,0 ,
+        NotificationManager.Policy policy2 = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_PEEK);
         assertTrue(policy.equals(policy2));
         assertTrue(policy2.equals(policy));
 
-        policy = new NotificationManager.Policy(0,0 ,0 ,
+        policy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_ON);
-        policy2 = new NotificationManager.Policy(0,0 ,0 ,
+        policy2 = new NotificationManager.Policy(0, 0, 0,
                 0);
         assertFalse(policy.equals(policy2));
         assertFalse(policy2.equals(policy));
 
-        policy = new NotificationManager.Policy(0,0 ,0 ,
+        policy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_OFF);
-        policy2 = new NotificationManager.Policy(0,0 ,0 ,
+        policy2 = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT
                         | SUPPRESSED_EFFECT_LIGHTS);
         assertTrue(policy.equals(policy2));
         assertTrue(policy2.equals(policy));
 
-        policy = new NotificationManager.Policy(0,0 ,0 ,
+        policy = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_SCREEN_OFF);
-        policy2 = new NotificationManager.Policy(0,0 ,0 ,
+        policy2 = new NotificationManager.Policy(0, 0, 0,
                 SUPPRESSED_EFFECT_LIGHTS);
         assertFalse(policy.equals(policy2));
         assertFalse(policy2.equals(policy));
@@ -2440,7 +2454,7 @@
         mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n);
         assertNotNull(findPostedNotification(10000, false));
         mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000);
-        assertTrue(isNotificationCancelled(10000, false));
+        assertNotificationCancelled(10000, false);
         final Intent revokeIntent = new Intent();
         revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
         revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -2450,8 +2464,7 @@
 
     public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
             throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2470,7 +2483,7 @@
 
         try {
             mNotificationManager.cancelAsPackage(DELEGATOR, null, 9);
-            fail ("Delegate should not be able to cancel notification they did not post");
+            fail("Delegate should not be able to cancel notification they did not post");
         } catch (SecurityException e) {
             // yay
         }
@@ -2618,8 +2631,7 @@
             // pass
         }
 
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         // no exception this time
         mNotificationManager.shouldHideSilentStatusBarIcons();
     }
@@ -2681,9 +2693,250 @@
         listener.onListenerDisconnected();
     }
 
+    private void performNotificationProviderAction(@NonNull String action) {
+        // Create an intent to launch an activity which just posts or cancels notifications
+        Intent activityIntent = new Intent();
+        activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY);
+        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        activityIntent.putExtra("action", action);
+        mContext.startActivity(activityIntent);
+    }
+
+    public void testNotificationUriPermissionsGranted() throws Exception {
+        Uri background7Uri = Uri.parse(
+                "content://com.android.test.notificationprovider.provider/background7.png");
+        Uri background8Uri = Uri.parse(
+                "content://com.android.test.notificationprovider.provider/background8.png");
+
+        toggleListenerAccess(true);
+        Thread.sleep(500); // wait for listener to be allowed
+
+        mListener = TestNotificationListener.getInstance();
+        assertNotNull(mListener);
+
+        try {
+            // Post #7
+            performNotificationProviderAction("send-7");
+
+            assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+            assertNotificationCancelled(8, true);
+            assertAccessible(background7Uri);
+            assertInaccessible(background8Uri);
+
+            // Post #8
+            performNotificationProviderAction("send-8");
+
+            assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+            assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
+            assertAccessible(background7Uri);
+            assertAccessible(background8Uri);
+
+            // Cancel #7
+            performNotificationProviderAction("cancel-7");
+
+            assertNotificationCancelled(7, true);
+            assertEquals(background8Uri, getNotificationBackgroundImageUri(8));
+            assertInaccessible(background7Uri);
+            assertAccessible(background8Uri);
+
+            // Cancel #8
+            performNotificationProviderAction("cancel-8");
+
+            assertNotificationCancelled(7, true);
+            assertNotificationCancelled(8, true);
+            assertInaccessible(background7Uri);
+            assertInaccessible(background8Uri);
+
+        } finally {
+            // Clean up -- reset any remaining notifications
+            performNotificationProviderAction("reset");
+            Thread.sleep(500);
+        }
+    }
+
+    public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception {
+        Uri background7Uri = Uri.parse(
+                "content://com.android.test.notificationprovider.provider/background7.png");
+
+        try {
+            // Post #7
+            performNotificationProviderAction("send-7");
+
+            Thread.sleep(500);
+            // Don't have access the notification yet, but we can test the URI
+            assertInaccessible(background7Uri);
+
+            toggleListenerAccess(true);
+            Thread.sleep(500); // wait for listener to be allowed
+
+            mListener = TestNotificationListener.getInstance();
+            assertNotNull(mListener);
+
+            assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+            assertAccessible(background7Uri);
+
+        } finally {
+            // Clean Up -- Cancel #7
+            performNotificationProviderAction("cancel-7");
+            Thread.sleep(500);
+        }
+    }
+
+    public void testNotificationUriPermissionsRevokedFromRemovedListeners() throws Exception {
+        Uri background7Uri = Uri.parse(
+                "content://com.android.test.notificationprovider.provider/background7.png");
+
+        toggleListenerAccess(true);
+        Thread.sleep(500); // wait for listener to be allowed
+
+        try {
+            // Post #7
+            performNotificationProviderAction("send-7");
+
+            mListener = TestNotificationListener.getInstance();
+            assertNotNull(mListener);
+
+            assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+            assertAccessible(background7Uri);
+
+            // Remove the listener to ensure permissions get revoked
+            toggleListenerAccess(false);
+            Thread.sleep(500); // wait for listener to be disabled
+
+            assertInaccessible(background7Uri);
+
+        } finally {
+            // Clean Up -- Cancel #7
+            performNotificationProviderAction("cancel-7");
+            Thread.sleep(500);
+        }
+    }
+
+    private class NotificationListenerConnection implements ServiceConnection {
+        private final Semaphore mSemaphore = new Semaphore(0);
+
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mNotificationUriAccessService = INotificationUriAccessService.Stub.asInterface(service);
+            mSemaphore.release();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            mNotificationUriAccessService = null;
+        }
+
+        public void waitForService() {
+            try {
+                if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
+                    return;
+                }
+            } catch (InterruptedException e) {
+            }
+            fail("failed to connec to service");
+        }
+    }
+
+    ;
+
+    public void testNotificationUriPermissionsRevokedOnlyFromRemovedListeners() throws Exception {
+        Uri background7Uri = Uri.parse(
+                "content://com.android.test.notificationprovider.provider/background7.png");
+
+        // Connect to a service in the NotificationListener app which allows us to validate URI
+        // permissions granted to a second app, so that we show that permissions aren't being
+        // revoked too broadly.
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName("com.android.test.notificationlistener",
+                "com.android.test.notificationlistener.NotificationUriAccessService"));
+        NotificationListenerConnection connection = new NotificationListenerConnection();
+        mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+        connection.waitForService();
+
+        // Before starting the test, make sure the service works, that there is no listener, and
+        // that the URI starts inaccessible to that process.
+        mNotificationUriAccessService.ensureNotificationListenerServiceConnected(false);
+        assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+        // Give the NotificationListener app access to notifications, and validate that.
+        toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener",
+                "com.android.test.notificationlistener.TestNotificationListener"), true);
+        Thread.sleep(500);
+        mNotificationUriAccessService.ensureNotificationListenerServiceConnected(true);
+        assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+        // Give the test app access to notifications, and get that listener
+        toggleListenerAccess(true);
+        Thread.sleep(500); // wait for listener to be allowed
+        mListener = TestNotificationListener.getInstance();
+        assertNotNull(mListener);
+
+        try {
+            try {
+                // Post #7
+                performNotificationProviderAction("send-7");
+
+                // Check that both the test app (this code) and the external app have URI access.
+                assertEquals(background7Uri, getNotificationBackgroundImageUri(7));
+                assertAccessible(background7Uri);
+                assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+                // Remove the listener to ensure permissions get revoked
+                toggleListenerAccess(false);
+                Thread.sleep(500); // wait for listener to be disabled
+
+                // Ensure that revoking listener access to this one app does not effect the other.
+                assertInaccessible(background7Uri);
+                assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+            } finally {
+                // Clean Up -- Cancel #7
+                performNotificationProviderAction("cancel-7");
+                Thread.sleep(500);
+            }
+
+            // Finally, cancelling the permission must still revoke those other permissions.
+            assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri));
+
+        } finally {
+            // Clean Up -- Make sure the external listener is has access revoked
+            toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener",
+                    "com.android.test.notificationlistener.TestNotificationListener"), false);
+        }
+    }
+
+    private void assertAccessible(Uri uri)
+            throws IOException {
+        ContentResolver contentResolver = mContext.getContentResolver();
+        try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
+            assertNotNull(fd);
+        } catch (SecurityException e) {
+            throw new AssertionError("URI should be accessible: " + uri, e);
+        }
+    }
+
+    private void assertInaccessible(Uri uri)
+            throws IOException {
+        ContentResolver contentResolver = mContext.getContentResolver();
+        try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) {
+            fail("URI should be inaccessible: " + uri);
+        } catch (SecurityException e) {
+            // pass
+        }
+    }
+
+    @NonNull
+    private Uri getNotificationBackgroundImageUri(int notificationId) {
+        StatusBarNotification sbn = findPostedNotification(notificationId, true);
+        assertNotNull(sbn);
+        String imageUriString = sbn.getNotification().extras
+                .getString(Notification.EXTRA_BACKGROUND_IMAGE_URI);
+        assertNotNull(imageUriString);
+        return Uri.parse(imageUriString);
+    }
+
     public void testNotificationListener_setNotificationsShown() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2699,8 +2952,7 @@
         StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
         mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
 
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), false);
+        toggleListenerAccess(false);
         Thread.sleep(500); // wait for listener to be disallowed
         try {
             mListener.setNotificationsShown(new String[]{ sbn2.getKey() });
@@ -2711,8 +2963,7 @@
     }
 
     public void testNotificationListener_getNotificationChannels() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2727,8 +2978,7 @@
     }
 
     public void testNotificationListener_getNotificationChannelGroups() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2742,8 +2992,7 @@
     }
 
     public void testNotificationListener_updateNotificationChannel() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2762,8 +3011,7 @@
     }
 
     public void testNotificationListener_getActiveNotifications() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2790,8 +3038,7 @@
 
 
     public void testNotificationListener_getCurrentRanking() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
@@ -2804,8 +3051,7 @@
     }
 
     public void testNotificationListener_cancelNotifications() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
+        toggleListenerAccess(true);
         Thread.sleep(500); // wait for listener to be allowed
 
         mListener = TestNotificationListener.getInstance();
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index e877350..b474fb8 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -23,11 +23,13 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 import com.android.compatibility.common.util.BatteryUtils;
 import com.android.compatibility.common.util.SettingsUtils;
+import com.android.compatibility.common.util.UserUtils;
 
 import junit.framework.Assert;
 
@@ -108,6 +110,11 @@
     }
 
     public void testNightModeYesPersisted() throws InterruptedException {
+        if (mUiModeManager.isNightModeLocked()) {
+            Log.i(TAG, "testNightModeYesPersisted skipped: night mode is locked");
+            return;
+        }
+
         // Reset the mode to no if it is set to another value
         setNightMode(UiModeManager.MODE_NIGHT_NO);
 
@@ -116,6 +123,11 @@
     }
 
     public void testNightModeAutoPersisted() throws InterruptedException {
+        if (mUiModeManager.isNightModeLocked()) {
+            Log.i(TAG, "testNightModeAutoPersisted skipped: night mode is locked");
+            return;
+        }
+
         // Reset the mode to no if it is set to another value
         setNightMode(UiModeManager.MODE_NIGHT_NO);
 
@@ -125,6 +137,7 @@
 
     public void testNightModeAutoNotPersistedCarMode() {
         if (mUiModeManager.isNightModeLocked()) {
+            Log.i(TAG, "testNightModeAutoNotPersistedCarMode skipped: night mode is locked");
             return;
         }
 
@@ -139,6 +152,7 @@
 
     public void testNightModeInCarModeIsTransient() {
         if (mUiModeManager.isNightModeLocked()) {
+            Log.i(TAG, "testNightModeInCarModeIsTransient skipped: night mode is locked");
             return;
         }
 
@@ -156,6 +170,8 @@
 
     public void testNightModeToggleInCarModeDoesNotChangeSetting() {
         if (mUiModeManager.isNightModeLocked()) {
+            Log.i(TAG, "testNightModeToggleInCarModeDoesNotChangeSetting skipped: "
+                    + "night mode is locked");
             return;
         }
 
@@ -174,6 +190,8 @@
 
     public void testNightModeInCarModeOnPowerSaveIsTransient() throws Throwable {
         if (mUiModeManager.isNightModeLocked() || !BatteryUtils.isBatterySaverSupported()) {
+            Log.i(TAG, "testNightModeInCarModeOnPowerSaveIsTransient skipped: "
+                    + "night mode is locked or battery saver is not supported");
             return;
         }
 
@@ -347,7 +365,7 @@
         int storedModeInt = -1;
         // Settings.Secure.UI_NIGHT_MODE
         for (int i = 0; i < MAX_WAIT_TIME; i += WAIT_TIME_INCR) {
-            String storedMode = SettingsUtils.getSecureSetting("ui_night_mode");
+            String storedMode = getUiNightModeFromSetting();
             storedModeInt = Integer.parseInt(storedMode);
             if (mode == storedModeInt) break;
             try {
@@ -404,4 +422,10 @@
         }
     }
 
+    private String getUiNightModeFromSetting() {
+        String key = "ui_night_mode";
+        return UserUtils.isHeadlessSystemUserMode()
+                ? SettingsUtils.getSecureSettingAsUser(UserHandle.USER_SYSTEM, key)
+                : SettingsUtils.getSecureSetting(key);
+    }
 }
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index d3b5621..108e034 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -302,6 +302,7 @@
         public static final String EXTRA_APP_CONFIG_INFO = "app_config_info";
         public static final String EXTRA_CONFIG_INFO_IN_ON_CREATE = "config_info_in_on_create";
         public static final String EXTRA_DISPLAY_REAL_SIZE = "display_real_size";
+        public static final String EXTRA_SYSTEM_RESOURCES_CONFIG_INFO = "sys_config_info";
     }
 
     /** Extra key constants for {@link android.server.wm.app.FontScaleActivity}. */
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
index dd1ad87..8ceeab2 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
@@ -19,9 +19,11 @@
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO;
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO;
 
 import android.app.Application;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
@@ -50,6 +52,8 @@
                     // own display adjustments.
                     app.getSystemService(DisplayManager.class)
                             .getDisplay(Display.DEFAULT_DISPLAY)));
+            extras.putParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO,
+                    new ConfigInfo(Resources.getSystem()));
             client.putExtras(extras);
         });
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 859eb29..302c915 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -40,6 +40,7 @@
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_APP_CONFIG_INFO;
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
 import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_SYSTEM_RESOURCES_CONFIG_INFO;
 import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
@@ -59,6 +60,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
@@ -68,6 +70,7 @@
 import android.server.wm.CommandSession.ConfigInfo;
 import android.server.wm.CommandSession.SizeInfo;
 import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.util.DisplayMetrics;
 import android.view.Display;
 
 import org.junit.Test;
@@ -469,6 +472,9 @@
         final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE);
         final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE);
         final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo;
+        final ConfigInfo globalConfigInfo =
+                extras.getParcelable(EXTRA_SYSTEM_RESOURCES_CONFIG_INFO);
+        final SizeInfo globalSizeInfo = globalConfigInfo.sizeInfo;
 
         assertEquals("The last reported size should be the same as the one from onCreate",
                 reportedSizes, onCreateConfigInfo.sizeInfo);
@@ -482,6 +488,10 @@
                 expectedRotation, onCreateConfigInfo.rotation);
         assertEquals("The application should get the final display rotation in onCreate",
                 expectedRotation, appConfigInfo.rotation);
+        assertEquals("The orientation of application must be landscape",
+                ORIENTATION_LANDSCAPE, appConfigInfo.sizeInfo.orientation);
+        assertEquals("The orientation of system resources must be landscape",
+                ORIENTATION_LANDSCAPE, globalSizeInfo.orientation);
         assertEquals("The activity should get the final display size in onCreate",
                 expectedRealDisplaySize, onCreateRealDisplaySize);
 
@@ -490,6 +500,13 @@
                 onCreateSize.displayWidth > onCreateSize.displayHeight);
         assertEquals("The application should get the same orientation", isLandscape,
                 appConfigInfo.sizeInfo.displayWidth > appConfigInfo.sizeInfo.displayHeight);
+        assertEquals("The app display metrics must be landscape", isLandscape,
+                appConfigInfo.sizeInfo.metricsWidth > appConfigInfo.sizeInfo.metricsHeight);
+
+        final DisplayMetrics globalMetrics = Resources.getSystem().getDisplayMetrics();
+        assertEquals("The display metrics of system resources must be landscape",
+                new Point(globalMetrics.widthPixels, globalMetrics.heightPixels),
+                new Point(globalSizeInfo.metricsWidth, globalSizeInfo.metricsHeight));
     }
 
     @Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
index 6ad0dc1..bd415bf 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -23,6 +23,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
@@ -60,6 +61,18 @@
     }
 
     /**
+     * Tests that config_remoteInsetsControllerControlsSystemBars is not set to true for
+     * non-automotive devices.
+     */
+    @Test
+    public void testRemoteInsetsControllerNotControlSystemBarsForNonAutoDevies() {
+        assumeFalse(isCar());
+
+        assertFalse("Non auto devices should not set config_remoteInsetsControllerControlsSystemBars",
+                remoteInsetsControllerControlsSystemBars());
+    }
+
+    /**
      * Tests that secondary display has override configuration set.
      */
     @Test
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 6ca5fc9..f4e7d89 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -1169,6 +1169,11 @@
                 .getBoolean(android.R.bool.config_perDisplayFocusEnabled);
     }
 
+    protected static boolean remoteInsetsControllerControlsSystemBars() {
+        return getInstrumentation().getTargetContext().getResources()
+                .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars);
+    }
+
     /** @see ObjectTracker#manage(AutoCloseable) */
     protected HomeActivitySession createManagedHomeActivitySession(ComponentName homeActivity) {
         return mObjectTracker.manage(new HomeActivitySession(homeActivity));
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index 4039a13..418d298 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -1023,6 +1023,12 @@
             sizeInfo = new SizeInfo(display, metrics, config);
         }
 
+        public ConfigInfo(Resources res) {
+            final DisplayMetrics metrics = res.getDisplayMetrics();
+            final Configuration config = res.getConfiguration();
+            sizeInfo = new SizeInfo(null /* display */, metrics, config);
+        }
+
         @Override
         public String toString() {
             return "ConfigInfo: {displayId=" + displayId + " rotation=" + rotation
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
index 728d136..0d169e3 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
@@ -16,8 +16,14 @@
 
 package android.view.inputmethod.cts;
 
+import static android.view.View.VISIBLE;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
 import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeInvisible;
 import static android.view.inputmethod.cts.util.InputMethodVisibilityVerifier.expectImeVisible;
 import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
@@ -75,6 +81,7 @@
 @RunWith(AndroidJUnit4.class)
 public class KeyboardVisibilityControlTest extends EndToEndImeTestBase {
     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(1);
 
     @Rule
     public final UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
@@ -407,6 +414,96 @@
         }
     }
 
+    @Test
+    public void testImeState_EditorDialogLostFocusAfterUnlocked_Unspecified() throws Exception {
+        runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_UNSPECIFIED);
+    }
+
+    @Test
+    public void testImeState_EditorDialogLostFocusAfterUnlocked_Visible() throws Exception {
+        runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_VISIBLE);
+    }
+
+    @Test
+    public void testImeState_EditorDialogLostFocusAfterUnlocked_AlwaysVisible() throws Exception {
+        runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+    }
+
+    @Test
+    public void testImeState_EditorDialogLostFocusAfterUnlocked_Hidden() throws Exception {
+        runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_HIDDEN);
+    }
+
+    @Test
+    public void testImeState_EditorDialogLostFocusAfterUnlocked_AlwaysHidden() throws Exception {
+        runImeDoesntReshowAfterKeyguardTest(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+    }
+
+    private void runImeDoesntReshowAfterKeyguardTest(int softInputState) throws Exception {
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder())) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            // Launch a simple test activity
+            final TestActivity testActivity =
+                    TestActivity.startSync(activity -> new LinearLayout(activity));
+
+            // Launch a dialog and show keyboard
+            final String marker = getTestMarker();
+            final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+            final AtomicReference<AlertDialog> dialogRef = new AtomicReference<>();
+            TestUtils.runOnMainSync(() -> {
+                final EditText editText = new EditText(testActivity);
+                editText.setHint("focused editText");
+                editText.setPrivateImeOptions(marker);
+                editText.requestFocus();
+                final AlertDialog dialog = new AlertDialog.Builder(testActivity)
+                        .setView(editText)
+                        .create();
+                dialog.getWindow().setSoftInputMode(softInputState);
+                dialog.show();
+                editText.getWindowInsetsController().show(ime());
+                editTextRef.set(editText);
+                dialogRef.set(dialog);
+            });
+
+            TestUtils.waitOnMainUntil(() -> dialogRef.get().isShowing()
+                    && editTextRef.get().hasFocus(), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
+                    View.VISIBLE, TIMEOUT);
+            expectImeVisible(TIMEOUT);
+
+            // Clear editor focus after screen-off
+            TestUtils.turnScreenOff();
+            TestUtils.waitOnMainUntil(() -> editTextRef.get().getWindowVisibility() != VISIBLE,
+                    TIMEOUT);
+            expectEvent(stream, onFinishInputViewMatcher(true), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+            expectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT);
+            // Expect showSoftInput comes when system notify InsetsController to apply show IME
+            // insets after IME input target updated.
+            expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+            notExpectEvent(stream, hideSoftInputMatcher(), NOT_EXPECT_TIMEOUT);
+            TestUtils.runOnMainSync(editTextRef.get()::clearFocus);
+
+            // Verify IME will invisible after device unlocked
+            TestUtils.turnScreenOn();
+            TestUtils.unlockScreen();
+            // Expect hideSoftInput and onFinishInputView will called by IMMS when the same window
+            // focused since the editText view focus has been cleared.
+            TestUtils.waitOnMainUntil(() -> editTextRef.get().hasWindowFocus()
+                    && !editTextRef.get().hasFocus(), TIMEOUT);
+            expectEvent(stream, hideSoftInputMatcher(), TIMEOUT);
+            expectEvent(stream, onFinishInputViewMatcher(false), TIMEOUT);
+            expectImeInvisible(TIMEOUT);
+        }
+    }
+
     private static ImeSettings.Builder getFloatingImeSettings(@ColorInt int navigationBarColor) {
         final ImeSettings.Builder builder = new ImeSettings.Builder();
         builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index 0ca67f2..4bad587 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -1045,6 +1045,15 @@
             }
         }
 
+        @Test
+        public void testSimpleMuxNative() {
+            Assume.assumeTrue("TODO(b/146421018)",
+                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+            Assume.assumeTrue("TODO(b/146923287)",
+                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+            assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
+        }
+
         /* Does MediaMuxer throw IllegalStateException on missing codec specific data when required.
          * Check if relevant exception is thrown for AAC, AVC, HEVC, and MPEG4
          * codecs that require CSD in MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4.
@@ -1077,14 +1086,161 @@
                 }
             }
         }
+    }
+
+    @LargeTest
+    @RunWith(Parameterized.class)
+    public static class TestAddEmptyTracks {
+        private final List<String> mimeListforTypeMp4 =
+                Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+                        MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_VIDEO_HEVC,
+                        MediaFormat.MIMETYPE_AUDIO_AAC, MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC,
+                        MediaFormat.MIMETYPE_TEXT_SUBRIP);
+        private final List<String> mimeListforTypeWebm =
+                Arrays.asList(MediaFormat.MIMETYPE_VIDEO_VP8, MediaFormat.MIMETYPE_VIDEO_VP9,
+                        MediaFormat.MIMETYPE_AUDIO_VORBIS, MediaFormat.MIMETYPE_AUDIO_OPUS);
+        private final List<String> mimeListforType3gp =
+                Arrays.asList(MediaFormat.MIMETYPE_VIDEO_MPEG4, MediaFormat.MIMETYPE_VIDEO_H263,
+                        MediaFormat.MIMETYPE_VIDEO_AVC, MediaFormat.MIMETYPE_AUDIO_AAC,
+                        MediaFormat.MIMETYPE_AUDIO_AMR_NB, MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+        private final List<String> mimeListforTypeOgg =
+                Arrays.asList(MediaFormat.MIMETYPE_AUDIO_OPUS);
+        private String mMime;
+        private String mOutPath;
+
+        public TestAddEmptyTracks(String mime) {
+            mMime = mime;
+        }
+
+        @Before
+        public void prologue() throws IOException {
+            mOutPath = File.createTempFile("tmp", ".out").getAbsolutePath();
+        }
+
+        @After
+        public void epilogue() {
+            new File(mOutPath).delete();
+        }
+
+        private boolean isMimeContainerPairValid(int format) {
+            boolean result = false;
+            if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
+                result = mimeListforTypeMp4.contains(mMime);
+            else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM) {
+                return mimeListforTypeWebm.contains(mMime);
+            } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP) {
+                result = mimeListforType3gp.contains(mMime);
+            } else if (format == MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG) {
+                result = mimeListforTypeOgg.contains(mMime);
+            }
+            return result;
+        }
+
+        @Parameterized.Parameters(name = "{index}({0})")
+        public static Collection<Object[]> input() {
+            return Arrays.asList(new Object[][]{
+                    // Video
+                    {MediaFormat.MIMETYPE_VIDEO_H263},
+                    {MediaFormat.MIMETYPE_VIDEO_AVC},
+                    {MediaFormat.MIMETYPE_VIDEO_HEVC},
+                    {MediaFormat.MIMETYPE_VIDEO_MPEG4},
+                    {MediaFormat.MIMETYPE_VIDEO_VP8},
+                    {MediaFormat.MIMETYPE_VIDEO_VP9},
+                    // Audio
+                    {MediaFormat.MIMETYPE_AUDIO_AAC},
+                    {MediaFormat.MIMETYPE_AUDIO_AMR_NB},
+                    {MediaFormat.MIMETYPE_AUDIO_AMR_WB},
+                    {MediaFormat.MIMETYPE_AUDIO_OPUS},
+                    {MediaFormat.MIMETYPE_AUDIO_VORBIS},
+                    // Metadata
+                    {MediaFormat.MIMETYPE_TEXT_SUBRIP},
+                    // Image
+                    {MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC}
+            });
+        }
 
         @Test
-        public void testSimpleMuxNative() {
-            Assume.assumeTrue("TODO(b/146421018)",
-                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
-            Assume.assumeTrue("TODO(b/146923287)",
-                    !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
-            assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
+        public void testEmptyVideoTrack() {
+            for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+                if (!mMime.startsWith("video/")) continue;
+                if (!isMimeContainerPairValid(format)) continue;
+                if (format != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) continue;
+                try {
+                    MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+                    MediaFormat mediaFormat = new MediaFormat();
+                    mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+                    mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, 480);
+                    mediaFormat.setInteger(MediaFormat.KEY_WIDTH, 640);
+                    mediaMuxer.addTrack(mediaFormat);
+                    mediaMuxer.start();
+                    mediaMuxer.stop();
+                    mediaMuxer.release();
+                } catch (Exception e) {
+                    fail("testEmptyVideoTrack : unexpected exception : " + e.getMessage());
+                }
+            }
+        }
+
+        @Test
+        public void testEmptyAudioTrack() {
+            for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+                if (!mMime.startsWith("audio/")) continue;
+                if (!isMimeContainerPairValid(format)) continue;
+                if (format != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) continue;
+                try {
+                    MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+                    MediaFormat mediaFormat = new MediaFormat();
+                    mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+                    mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 12000);
+                    mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
+                    mediaMuxer.addTrack(mediaFormat);
+                    mediaMuxer.start();
+                    mediaMuxer.stop();
+                    mediaMuxer.release();
+                } catch (Exception e) {
+                    fail("testEmptyAudioTrack : unexpected exception : " + e.getMessage());
+                }
+            }
+        }
+
+        @Test
+        public void testEmptyMetaDataTrack() {
+            for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+                if (!mMime.startsWith("application/")) continue;
+                if (!isMimeContainerPairValid(format)) continue;
+                try {
+                    MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+                    MediaFormat mediaFormat = new MediaFormat();
+                    mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+                    mediaMuxer.addTrack(mediaFormat);
+                    mediaMuxer.start();
+                    mediaMuxer.stop();
+                    mediaMuxer.release();
+                } catch (Exception e) {
+                    fail("testEmptyMetaDataTrack : unexpected exception : " + e.getMessage());
+                }
+            }
+        }
+
+        @Test
+        public void testEmptyImageTrack() {
+            for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; ++format) {
+                if (!mMime.startsWith("image/")) continue;
+                if (!isMimeContainerPairValid(format)) continue;
+                try {
+                    MediaMuxer mediaMuxer = new MediaMuxer(mOutPath, format);
+                    MediaFormat mediaFormat = new MediaFormat();
+                    mediaFormat.setString(MediaFormat.KEY_MIME, mMime);
+                    mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, 480);
+                    mediaFormat.setInteger(MediaFormat.KEY_WIDTH, 640);
+                    mediaMuxer.addTrack(mediaFormat);
+                    mediaMuxer.start();
+                    mediaMuxer.stop();
+                    mediaMuxer.release();
+                } catch (Exception e) {
+                    fail("testEmptyImageTrack : unexpected exception : " + e.getMessage());
+                }
+            }
         }
     }
 }
diff --git a/tests/sensor/src/android/hardware/cts/SensorAdditionalInfoTest.java b/tests/sensor/src/android/hardware/cts/SensorAdditionalInfoTest.java
index df86fa6..6915db3 100644
--- a/tests/sensor/src/android/hardware/cts/SensorAdditionalInfoTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorAdditionalInfoTest.java
@@ -55,13 +55,12 @@
         List<Sensor> list = mSensorManager.getSensorList(Sensor.TYPE_ALL);
         List<String> errors = new ArrayList<String>();
         for (Sensor s : list) {
-            // skip vendor sensor types, one-shot and on-change sensors.
-            if (s.getType() >= Sensor.TYPE_DEVICE_PRIVATE_BASE ||
-                    s.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT ||
-                    s.getReportingMode() == Sensor.REPORTING_MODE_ON_CHANGE) {
+            // Only test sensor additional info for Accelerometer, Gyroscope and Magnetometer.
+            if (s.getType() != Sensor.TYPE_ACCELEROMETER &&
+                    s.getType() != Sensor.TYPE_GYROSCOPE &&
+                    s.getType() != Sensor.TYPE_MAGNETIC_FIELD) {
                 continue;
             }
-
             if (!s.isAdditionalInfoSupported()) {
                 // check SensorAdditionalInfo is supported for Automotive sensors.
                 if (getContext().getPackageManager().hasSystemFeature(
@@ -266,7 +265,7 @@
 
         // Checks sensor placement data length and determinant of rotation matrix is 1.
         private void verifySensorPlacementData(float[] m) {
-            if(m.length != 12) {
+            if(m.length < 12) {
                 mIsSensorPlacementSizeValid = false;
                 return;
             }
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
index 6503bdb..26f6b90 100644
--- a/tests/tests/appenumeration/AndroidTest.xml
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -26,7 +26,7 @@
         <option name="force-queryable" value="false" />
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAppEnumerationTestCases.apk" />
-        <option name="test-file-name" value="CtsAppEnumerationForceQueryable.apk" />
+        <option name="test-file-name" value="CtsAppEnumerationForceQueryableNormalInstall.apk" />
         <option name="test-file-name" value="CtsAppEnumerationFilters.apk" />
         <option name="test-file-name" value="CtsAppEnumerationNoApi.apk" />
         <option name="test-file-name" value="CtsAppEnumerationContactsActivityTarget.apk" />
@@ -61,6 +61,12 @@
         <option name="test-file-name" value="CtsAppEnumerationWildcardBrowserActivitySource.apk" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="force-queryable" value="true" />
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsAppEnumerationForceQueryable.apk" />
+    </target_preparer>
+
     <!-- Create place to store apks -->
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="mkdir -p /data/local/tmp/cts/appenumeration" />
diff --git a/tests/tests/appenumeration/app/target/Android.bp b/tests/tests/appenumeration/app/target/Android.bp
index 04ebc78..b3afe4b 100644
--- a/tests/tests/appenumeration/app/target/Android.bp
+++ b/tests/tests/appenumeration/app/target/Android.bp
@@ -27,6 +27,20 @@
 }
 
 android_test_helper_app {
+    name: "CtsAppEnumerationForceQueryableNormalInstall",
+    manifest: "AndroidManifest-forceQueryable-normalInstall.xml",
+    defaults: ["cts_support_defaults"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+    sdk_version: "test_current",
+}
+
+android_test_helper_app {
     name: "CtsAppEnumerationFilters",
     manifest: "AndroidManifest-filters.xml",
     defaults: ["cts_support_defaults"],
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable-normalInstall.xml b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable-normalInstall.xml
new file mode 100644
index 0000000..2918e37
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable-normalInstall.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.appenumeration.forcequeryable.normalinstall">
+    <application android:forceQueryable="true">
+        <!-- This app will not be a system app and should be installed as a normal app (not
+             forceQueryable) to ensure it's not visible by default -->
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
index e6535b3..041d350 100644
--- a/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
@@ -18,6 +18,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.appenumeration.forcequeryable">
     <application android:forceQueryable="true">
+        <!-- This app will not be a system app and so must be installed as forceQueryable by the
+             test framework -->
         <uses-library android:name="android.test.runner" />
     </application>
 </manifest>
diff --git a/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java b/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
index 6fcba54..d7c8dae 100644
--- a/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
+++ b/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
@@ -78,8 +78,13 @@
     public static final String TARGET_SHARED_USER = PKG_BASE + "noapi.shareduid";
     /** A package that exposes itself via various intent filters (activities, services, etc.) */
     public static final String TARGET_FILTERS = PKG_BASE + "filters";
-    /** A package that declares itself force queryable, making it visible to all other packages */
+    /** A package that declares itself force queryable, making it visible to all other packages.
+     *  This is installed as forceQueryable as non-system apps cannot declare themselves as such. */
     public static final String TARGET_FORCEQUERYABLE = PKG_BASE + "forcequeryable";
+    /** A package that declares itself force queryable, but is installed normally making it not
+     *  visible to other packages */
+    public static final String TARGET_FORCEQUERYABLE_NORMAL =
+            PKG_BASE + "forcequeryable.normalinstall";
     /** A package with no published API and so isn't queryable by anything but package name */
     public static final String TARGET_NO_API = PKG_BASE + "noapi";
     /** A package that offers an activity used for opening / editing file types */
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
index d95970b..cfd12a2 100644
--- a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -66,6 +66,7 @@
 import static android.appenumeration.cts.Constants.TARGET_FILTERS;
 import static android.appenumeration.cts.Constants.TARGET_FILTERS_APK;
 import static android.appenumeration.cts.Constants.TARGET_FORCEQUERYABLE;
+import static android.appenumeration.cts.Constants.TARGET_FORCEQUERYABLE_NORMAL;
 import static android.appenumeration.cts.Constants.TARGET_NO_API;
 import static android.appenumeration.cts.Constants.TARGET_SHARE;
 import static android.appenumeration.cts.Constants.TARGET_SHARED_USER;
@@ -177,6 +178,15 @@
     }
 
     @Test
+    public void all_cannotSeeForceQueryableInstalledNormally() throws Exception {
+        assertNotVisible(QUERIES_NOTHING, TARGET_FORCEQUERYABLE_NORMAL);
+        assertNotVisible(QUERIES_ACTIVITY_ACTION, TARGET_FORCEQUERYABLE_NORMAL);
+        assertNotVisible(QUERIES_SERVICE_ACTION, TARGET_FORCEQUERYABLE_NORMAL);
+        assertNotVisible(QUERIES_PROVIDER_AUTH, TARGET_FORCEQUERYABLE_NORMAL);
+        assertNotVisible(QUERIES_PACKAGE, TARGET_FORCEQUERYABLE_NORMAL);
+    }
+
+    @Test
     public void startExplicitly_canStartNonVisible() throws Exception {
         assertNotVisible(QUERIES_NOTHING, TARGET_FILTERS);
         startExplicitIntentViaComponent(QUERIES_NOTHING, TARGET_FILTERS);
diff --git a/tests/tests/contactsprovider/Android.bp b/tests/tests/contactsprovider/Android.bp
index ac20675..67f5687 100644
--- a/tests/tests/contactsprovider/Android.bp
+++ b/tests/tests/contactsprovider/Android.bp
@@ -23,8 +23,13 @@
         "truth-prebuilt",
     ],
 
-    srcs: ["src/**/*.java"],
+    srcs: [
+        "src/**/*.java",
+
+        // Include the GAL provider source code to pull in the constants.
+        "gal/src/**/*.java",
+    ],
 
     sdk_version: "test_current",
-    min_sdk_version: "21",
+    min_sdk_version: "30",
 }
diff --git a/tests/tests/contactsprovider/AndroidManifest.xml b/tests/tests/contactsprovider/AndroidManifest.xml
index f81bb5e..23f01fe 100644
--- a/tests/tests/contactsprovider/AndroidManifest.xml
+++ b/tests/tests/contactsprovider/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.provider.cts.contacts">
 
-    <uses-sdk android:targetSdkVersion="29" />
+    <uses-sdk android:targetSdkVersion="30" />
 
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
@@ -38,14 +38,6 @@
             <meta-data android:name="android.accounts.AccountAuthenticator"
                        android:resource="@xml/contactprovider_authenticator"/>
         </service>
-
-        <provider
-            android:name=".DummyGalProvider"
-            android:authorities="android.provider.cts.contacts.dgp"
-            android:exported="true"
-            android:readPermission="android.permission.BIND_DIRECTORY_SEARCH" >
-            <meta-data android:name="android.content.ContactDirectory" android:value="true" />
-        </provider>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/contactsprovider/AndroidTest.xml b/tests/tests/contactsprovider/AndroidTest.xml
index 0d6562f..35c6b21 100644
--- a/tests/tests/contactsprovider/AndroidTest.xml
+++ b/tests/tests/contactsprovider/AndroidTest.xml
@@ -25,6 +25,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsContactsProviderTestCases.apk" />
+        <option name="test-file-name" value="CtsContactsProviderGalProvider.apk" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/contactsprovider/gal/Android.bp b/tests/tests/contactsprovider/gal/Android.bp
new file mode 100644
index 0000000..4804a85
--- /dev/null
+++ b/tests/tests/contactsprovider/gal/Android.bp
@@ -0,0 +1,14 @@
+android_test_helper_app {
+    name: "CtsContactsProviderGalProvider",
+    defaults: ["cts_defaults"],
+
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    min_sdk_version: "30",
+}
diff --git a/tests/tests/contactsprovider/gal/AndroidManifest.xml b/tests/tests/contactsprovider/gal/AndroidManifest.xml
new file mode 100644
index 0000000..e738ae3
--- /dev/null
+++ b/tests/tests/contactsprovider/gal/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.provider.cts.contacts.galprovider">
+
+    <uses-sdk android:targetSdkVersion="30" />
+
+    <application>
+        <provider
+            android:name=".CtsGalProvider"
+            android:authorities="android.provider.cts.contacts.gal"
+            android:exported="true"
+            android:readPermission="android.permission.BIND_DIRECTORY_SEARCH" >
+            <meta-data android:name="android.content.ContactDirectory" android:value="true" />
+        </provider>
+    </application>
+</manifest>
+
diff --git a/tests/tests/contactsprovider/src/android/provider/cts/contacts/DummyGalProvider.java b/tests/tests/contactsprovider/gal/src/android/provider/cts/contacts/galprovider/CtsGalProvider.java
similarity index 84%
rename from tests/tests/contactsprovider/src/android/provider/cts/contacts/DummyGalProvider.java
rename to tests/tests/contactsprovider/gal/src/android/provider/cts/contacts/galprovider/CtsGalProvider.java
index 7609143..6806204 100644
--- a/tests/tests/contactsprovider/src/android/provider/cts/contacts/DummyGalProvider.java
+++ b/tests/tests/contactsprovider/gal/src/android/provider/cts/contacts/galprovider/CtsGalProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package android.provider.cts.contacts;
+package android.provider.cts.contacts.galprovider;
 
 import android.content.ContentProvider;
 import android.content.ContentValues;
@@ -26,7 +26,6 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.RawContacts;
-import android.provider.cts.contacts.account.StaticAccountAuthenticator;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -36,15 +35,17 @@
 /**
  * GAL provider for CTS.
  */
-public class DummyGalProvider extends ContentProvider {
-    private static final String TAG = "DummyGalProvider";
+public class CtsGalProvider extends ContentProvider {
+    private static final String TAG = "GalProvider";
 
-    public static final String AUTHORITY = "android.provider.cts.contacts.dgp";
+    // The main CTS refers to it.
+    public static final String GAL_PACKAGE_NAME = "android.provider.cts.contacts.galprovider";
+    public static final String AUTHORITY = "android.provider.cts.contacts.gal";
 
-    public static final String ACCOUNT_NAME = "dummygal";
-    public static final String ACCOUNT_TYPE = StaticAccountAuthenticator.TYPE;
+    public static final String ACCOUNT_NAME = "ctsgal";
+    public static final String ACCOUNT_TYPE = "com.android.cts.contactsprovider";
 
-    public static final String DISPLAY_NAME = "dummy-gal";
+    public static final String DISPLAY_NAME = "cts-gal";
 
     public static final String ERROR_MESSAGE_KEY = "error";
     public static final String QUERY_KEY = "query";
@@ -54,23 +55,14 @@
 
     private static final int GAL_DIRECTORIES = 0;
     private static final int GAL_FILTER = 1;
-    private static final int GAL_CONTACT = 2;
-    private static final int GAL_CONTACT_WITH_ID = 3;
-    private static final int GAL_EMAIL_FILTER = 4;
-    private static final int GAL_PHONE_FILTER = 5;
-    private static final int GAL_PHONE_LOOKUP = 6;
+    private static final int SUB_PATH = 1;
 
     private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
     static {
         sURIMatcher.addURI(AUTHORITY, "directories", GAL_DIRECTORIES);
         sURIMatcher.addURI(AUTHORITY, "contacts/filter/*", GAL_FILTER);
-        // The following URIs are not supported by this class.
-//        sURIMatcher.addURI(AUTHORITY, "contacts/lookup/*/entities", GAL_CONTACT);
-//        sURIMatcher.addURI(AUTHORITY, "contacts/lookup/*/#/entities", GAL_CONTACT_WITH_ID);
-//        sURIMatcher.addURI(AUTHORITY, "data/emails/filter/*", GAL_EMAIL_FILTER);
-//        sURIMatcher.addURI(AUTHORITY, "data/phones/filter/*", GAL_PHONE_FILTER);
-//        sURIMatcher.addURI(AUTHORITY, "phone_lookup/*", GAL_PHONE_LOOKUP);
+        sURIMatcher.addURI(AUTHORITY, "sub/*", SUB_PATH);
     }
 
     @Override
diff --git a/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java b/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java
new file mode 100644
index 0000000..f91015a
--- /dev/null
+++ b/tests/tests/contactsprovider/src/android/provider/cts/contacts/CallLogProviderTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts.contacts;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.test.InstrumentationTestCase;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class CallLogProviderTest extends InstrumentationTestCase {
+    private ContentResolver mContentResolver;
+    private ContentProviderClient mProvider;
+
+    private static final String TEST_NUMBER = "5551234";
+    private static final int TIME_OUT_MILLIS = 5000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContentResolver = getInstrumentation().getTargetContext().getContentResolver();
+        mProvider = mContentResolver.acquireContentProviderClient(CallLog.AUTHORITY);
+    }
+
+    public void testNoSubqueries() throws Exception {
+        // Add a single call just to make sure the call log has something inside
+        ContentValues values = new ContentValues();
+        values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
+        values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
+        values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
+        values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
+
+        mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
+
+        // Attempt to do a query that contains a subquery -- this should fail since this test does
+        // not have READ_VOICEMAIL.
+        try {
+            Cursor c = mProvider.query(Calls.CONTENT_URI, null, CallLog.Calls.NUMBER + " = ?",
+                    new String[]{TEST_NUMBER},
+                    "date DESC LIMIT (SELECT count(*) + 1 FROM calls WHERE type = 4");
+            assertEquals(0, c.getCount());
+        } catch (IllegalArgumentException e) {
+            // expected/tolerated
+        }
+    }
+
+    public void testUpdate() throws Exception {
+        // Add a single call just to make sure the call log has something inside
+        ContentValues values = new ContentValues();
+        values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
+        values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
+        values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
+        values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
+        Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
+
+        CountDownLatch changeLatch = new CountDownLatch(1);
+        mContentResolver.registerContentObserver(
+                CallLog.Calls.CONTENT_URI, true,
+                new ContentObserver(null /* handler */) {
+                    @Override
+                    public void onChange(boolean selfChange, Uri uri) {
+                        mContentResolver.unregisterContentObserver(this);
+                        changeLatch.countDown();
+                        super.onChange(selfChange);
+                    }
+                });
+
+        // Update it!
+        values.put(CallLog.Calls.DURATION, Long.valueOf(6 /*call duration*/));
+        int numUpdated = mContentResolver.update(uri, values, null, null);
+        assertEquals(1, numUpdated);
+        try {
+            assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+            fail("Expected update notification.");
+        }
+    }
+
+    public void testDelete() throws Exception {
+        // Add a single call just to make sure the call log has something inside
+        ContentValues values = new ContentValues();
+        values.put(CallLog.Calls.NUMBER, TEST_NUMBER);
+        values.put(CallLog.Calls.TYPE, Calls.OUTGOING_TYPE);
+        values.put(CallLog.Calls.DATE, Long.valueOf(0 /*start time*/));
+        values.put(CallLog.Calls.DURATION, Long.valueOf(5 /*call duration*/));
+        Uri uri = mContentResolver.insert(CallLog.Calls.CONTENT_URI, values);
+
+        CountDownLatch changeLatch = new CountDownLatch(1);
+        mContentResolver.registerContentObserver(
+                CallLog.Calls.CONTENT_URI, true,
+                new ContentObserver(null /* handler */) {
+                    @Override
+                    public void onChange(boolean selfChange, Uri uri) {
+                        mContentResolver.unregisterContentObserver(this);
+                        changeLatch.countDown();
+                        super.onChange(selfChange);
+                    }
+                });
+
+        // Delete it.
+        // Yuck, you can't just delete using the uri passed in; you need to build a where clause.
+        int count = mContentResolver.delete(Calls.CONTENT_URI, Calls._ID + "="
+                + ContentUris.parseId(uri), null);
+        assertEquals(1, count);
+        try {
+            assertTrue(changeLatch.await(TIME_OUT_MILLIS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+            fail("Expected update notification.");
+        }
+    }
+}
diff --git a/tests/tests/contactsprovider/src/android/provider/cts/contacts/ContactsContract_DirectoryTest.java b/tests/tests/contactsprovider/src/android/provider/cts/contacts/ContactsContract_DirectoryTest.java
index c3706d3..d800206 100644
--- a/tests/tests/contactsprovider/src/android/provider/cts/contacts/ContactsContract_DirectoryTest.java
+++ b/tests/tests/contactsprovider/src/android/provider/cts/contacts/ContactsContract_DirectoryTest.java
@@ -18,6 +18,7 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.content.ContentResolver;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.net.Uri;
@@ -25,8 +26,9 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Directory;
-import android.provider.cts.contacts.DummyGalProvider;
+import android.provider.cts.contacts.galprovider.CtsGalProvider;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 import org.json.JSONObject;
 
@@ -36,6 +38,8 @@
  * the check in there, so it won't create the account multiple times.
  */
 public class ContactsContract_DirectoryTest extends AndroidTestCase {
+    private static final String TAG = "ContactsContract_DirectoryTest";
+
     private ContentResolver mResolver;
     private AccountManager mAccountManager;
     private Account mAccount;
@@ -49,7 +53,7 @@
         mResolver = getContext().getContentResolver();
 
         mAccountManager = getContext().getSystemService(AccountManager.class);
-        mAccount = new Account(DummyGalProvider.ACCOUNT_NAME, DummyGalProvider.ACCOUNT_TYPE);
+        mAccount = new Account(CtsGalProvider.ACCOUNT_NAME, CtsGalProvider.ACCOUNT_TYPE);
 
         // The directory table is populated asynchronously.  Wait for it...
         waitForDirectorySetup();
@@ -75,19 +79,19 @@
         while (SystemClock.elapsedRealtime() < timeout) {
             try (Cursor c = getContext().getContentResolver().query(Directory.CONTENT_URI,
                     null, Directory.ACCOUNT_NAME + "=? and " + Directory.ACCOUNT_TYPE + "=?",
-                    new String[]{DummyGalProvider.ACCOUNT_NAME, DummyGalProvider.ACCOUNT_TYPE},
+                    new String[]{CtsGalProvider.ACCOUNT_NAME, CtsGalProvider.ACCOUNT_TYPE},
                     null)) {
                 if (c.getCount() == 0) {
                     Thread.sleep(1000);
                     continue;
                 }
                 assertTrue(c.moveToPosition(0));
-                assertEquals(getContext().getPackageName(), getString(c, Directory.PACKAGE_NAME));
-                assertEquals(DummyGalProvider.AUTHORITY,
+                assertEquals(CtsGalProvider.GAL_PACKAGE_NAME, getString(c, Directory.PACKAGE_NAME));
+                assertEquals(CtsGalProvider.AUTHORITY,
                         getString(c, Directory.DIRECTORY_AUTHORITY));
-                assertEquals(DummyGalProvider.DISPLAY_NAME, getString(c, Directory.DISPLAY_NAME));
-                assertEquals(DummyGalProvider.ACCOUNT_NAME, getString(c, Directory.ACCOUNT_NAME));
-                assertEquals(DummyGalProvider.ACCOUNT_TYPE, getString(c, Directory.ACCOUNT_TYPE));
+                assertEquals(CtsGalProvider.DISPLAY_NAME, getString(c, Directory.DISPLAY_NAME));
+                assertEquals(CtsGalProvider.ACCOUNT_NAME, getString(c, Directory.ACCOUNT_NAME));
+                assertEquals(CtsGalProvider.ACCOUNT_TYPE, getString(c, Directory.ACCOUNT_TYPE));
                 return c.getLong(c.getColumnIndex(Directory._ID));
             }
         }
@@ -127,14 +131,28 @@
             // The result is stored in the display_name column.
             final JSONObject result = new JSONObject(getString(c, Contacts.DISPLAY_NAME));
 
-            if (result.has(DummyGalProvider.ERROR_MESSAGE_KEY)) {
-                fail(result.getString(DummyGalProvider.ERROR_MESSAGE_KEY));
+            if (result.has(CtsGalProvider.ERROR_MESSAGE_KEY)) {
+                fail(result.getString(CtsGalProvider.ERROR_MESSAGE_KEY));
             }
 
-            assertEquals("12", result.getString(DummyGalProvider.LIMIT_KEY));
-            assertEquals("[QUERY]", result.getString(DummyGalProvider.QUERY_KEY));
+            assertEquals("12", result.getString(CtsGalProvider.LIMIT_KEY));
+            assertEquals("[QUERY]", result.getString(CtsGalProvider.QUERY_KEY));
             assertEquals(getContext().getPackageName(),
-                    result.getString(DummyGalProvider.CALLER_PACKAGE_NAME_KEY));
+                    result.getString(CtsGalProvider.CALLER_PACKAGE_NAME_KEY));
+        }
+
+        // After getting any result from the gal provider, the package will become visible.
+        assertTrue("GAL provider should be visible here", isGalProviderVisible());
+    }
+
+    private boolean isGalProviderVisible() {
+        try {
+            String pkg = CtsGalProvider.GAL_PACKAGE_NAME;
+            int uid = getContext().getPackageManager().getPackageUid(pkg, 0);
+            Log.w(TAG, "UID of " + pkg + " = " + uid);
+            return true;
+        } catch (NameNotFoundException e) {
+            return false;
         }
     }
 }
diff --git a/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
index 06ae8ab..c63b793 100644
--- a/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/AttestationTest.java
@@ -102,16 +102,6 @@
         // Since we cannot get the implementation name or author at this layer, we can't test for
         // it. This can be tested for in the VTS test, however.
 
-        // As per the IC HAL, the keymasterVersion field should be the version of the Identity
-        // Credential HAL - 1.0 - and this is encoded as major*10 + minor. This field is used by
-        // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
-        // and integers greater or equal than 41 (for KM starting with 4.1).
-        //
-        // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
-        // version isn't errornously returned.
-        assertTrue(record.getKeymasterVersion() >= 10);
-        assertTrue(record.getKeymasterVersion() < 40);
-
         // Check that the challenge we passed in, is in fact in the attestation record.
         assertArrayEquals(challenge, record.getAttestationChallenge());
 
diff --git a/tests/tests/identity/src/android/security/identity/cts/Util.java b/tests/tests/identity/src/android/security/identity/cts/Util.java
index df9a300..bbd6ee7 100644
--- a/tests/tests/identity/src/android/security/identity/cts/Util.java
+++ b/tests/tests/identity/src/android/security/identity/cts/Util.java
@@ -37,6 +37,7 @@
 import java.security.KeyStore;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.PrivateKey;
@@ -1002,15 +1003,10 @@
 
             byte[] sessionTranscriptBytes =
                     Util.prependSemanticTagForEncodedCbor(encodedSessionTranscript);
-            byte[] sharedSecretWithSessionTranscriptBytes =
-                    Util.concatArrays(sharedSecret, sessionTranscriptBytes);
 
-            byte[] salt = new byte[1];
-            byte[] info = new byte[0];
-
-            salt[0] = 0x00;
-            byte[] derivedKey = Util.computeHkdf("HmacSha256",
-                    sharedSecretWithSessionTranscriptBytes, salt, info, 32);
+            byte[] salt = MessageDigest.getInstance("SHA-256").digest(sessionTranscriptBytes);
+            byte[] info = new byte[] {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+            byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
             SecretKey secretKey = new SecretKeySpec(derivedKey, "");
             return secretKey;
         } catch (InvalidKeyException
diff --git a/tests/tests/mediaparser/Android.bp b/tests/tests/mediaparser/Android.bp
index 6a13d24..13859a77 100644
--- a/tests/tests/mediaparser/Android.bp
+++ b/tests/tests/mediaparser/Android.bp
@@ -14,25 +14,33 @@
 
 android_test {
     name: "CtsMediaParserTestCases",
-    defaults: ["cts_defaults"],
+    defaults: ["CtsMediaParserTestCasesDefaults", "cts_defaults"],
+    min_sdk_version: "29",
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts",
+    ],
+}
+
+// App for host-side testing of the MediaParser integration with MediaMetrics.
+android_test_helper_app {
+    name: "CtsMediaParserTestCasesApp",
+    defaults: ["CtsMediaParserTestCasesDefaults"],
+}
+
+java_defaults {
+    name: "CtsMediaParserTestCasesDefaults",
+    srcs: ["src/**/*.java"],
     static_libs: [
         "ctstestrunner-axt",
         "androidx.test.ext.junit",
         "exoplayer2-extractor-test-utils",
         "exoplayer2-extractor-tests-assets",
     ],
-    srcs: ["src/**/*.java"],
-    sdk_version: "test_current",
-    min_sdk_version: "29",
     libs: [
         "android.test.base.stubs",
         "android.test.runner.stubs",
     ],
-
-    test_suites: [
-        "cts",
-        "vts10",
-        "general-tests",
-        "mts",
-    ],
+    sdk_version: "test_current",
 }
diff --git a/tests/tests/mediaparser/TEST_MAPPING b/tests/tests/mediaparser/TEST_MAPPING
index ec2d2e2..3d21914 100644
--- a/tests/tests/mediaparser/TEST_MAPPING
+++ b/tests/tests/mediaparser/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "CtsMediaParserTestCases"
+    },
+    {
+      "name": "CtsMediaParserHostTestCases"
     }
   ]
 }
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index 40ddad9..145ac99 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -673,42 +673,44 @@
             mediaParser.setParameter(entry.getKey(), entry.getValue());
         }
 
-        mediaParser.advance(inputReader);
-        if (expectedParserName != null) {
-            assertThat(expectedParserName).isEqualTo(mediaParser.getParserName());
-            // We are only checking that the extractor is the right one.
-            mediaParser.release();
-            return;
-        }
+        try {
+            mediaParser.advance(inputReader);
+            if (expectedParserName != null) {
+                assertThat(expectedParserName).isEqualTo(mediaParser.getParserName());
+                // We are only checking that the extractor is the right one.
+                return;
+            }
 
-        while (mediaParser.advance(inputReader)) {
-            // Do nothing.
-        }
+            while (mediaParser.advance(inputReader)) {
+                // Do nothing.
+            }
 
-        // If the SeekMap is seekable, test seeking in the stream.
-        MediaParser.SeekMap seekMap = outputConsumer.getSeekMap();
-        assertThat(seekMap).isNotNull();
-        if (seekMap.isSeekable()) {
-            long durationUs = seekMap.getDurationMicros();
-            for (int j = 0; j < 4; j++) {
-                outputConsumer.clearTrackOutputs();
-                long timeUs =
-                        durationUs == MediaParser.SeekMap.UNKNOWN_DURATION
-                                ? 0
-                                : (durationUs * j) / 3;
-                MediaParser.SeekPoint seekPoint = seekMap.getSeekPoints(timeUs).first;
-                inputReader.reset();
-                inputReader.setPosition((int) seekPoint.position);
-                mediaParser.seek(seekPoint);
-                while (mediaParser.advance(inputReader)) {
-                    // Do nothing.
-                }
-                if (durationUs == MediaParser.SeekMap.UNKNOWN_DURATION) {
-                    break;
+            // If the SeekMap is seekable, test seeking in the stream.
+            MediaParser.SeekMap seekMap = outputConsumer.getSeekMap();
+            assertThat(seekMap).isNotNull();
+            if (seekMap.isSeekable()) {
+                long durationUs = seekMap.getDurationMicros();
+                for (int j = 0; j < 4; j++) {
+                    outputConsumer.clearTrackOutputs();
+                    long timeUs =
+                            durationUs == MediaParser.SeekMap.UNKNOWN_DURATION
+                                    ? 0
+                                    : (durationUs * j) / 3;
+                    MediaParser.SeekPoint seekPoint = seekMap.getSeekPoints(timeUs).first;
+                    inputReader.reset();
+                    inputReader.setPosition((int) seekPoint.position);
+                    mediaParser.seek(seekPoint);
+                    while (mediaParser.advance(inputReader)) {
+                        // Do nothing.
+                    }
+                    if (durationUs == MediaParser.SeekMap.UNKNOWN_DURATION) {
+                        break;
+                    }
                 }
             }
+        } finally {
+            mediaParser.release();
         }
-        mediaParser.release();
     }
 
     private static MockMediaParserInputReader getInputReader(String assetPath) throws IOException {
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
index 88e6b68..9f414b6 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
@@ -48,6 +48,53 @@
     const StreamBuilderHelper::Parameters& actual() const { return mHelper->actual(); }
     int32_t framesPerBurst() const { return mHelper->framesPerBurst(); }
 
+    // This checks for expected behavior after a stream has been released.
+    void checkCallsAfterRelease() {
+        // We expect these not to crash.
+        AAudioStream_setBufferSizeInFrames(stream(), 0);
+        AAudioStream_setBufferSizeInFrames(stream(), 99999999);
+
+        // We should NOT be able to start or change a stream after it has been released.
+        EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+                  AAudioStream_requestStart(stream()));
+        EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+        // Pause is only implemented for OUTPUT.
+        if (AAudioStream_getDirection(stream()) == AAUDIO_DIRECTION_OUTPUT) {
+            EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+                      AAudioStream_requestPause(stream()));
+        }
+        EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+        EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE,
+                  AAudioStream_requestStop(stream()));
+        EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, AAudioStream_getState(stream()));
+
+        // Do these return positive integers?
+        // Frames read or written may be zero if the stream has not had time to advance.
+        EXPECT_GE(AAudioStream_getFramesRead(stream()), 0);
+        EXPECT_GE(AAudioStream_getFramesWritten(stream()), 0);
+        EXPECT_GT(AAudioStream_getFramesPerBurst(stream()), 0);
+        EXPECT_GE(AAudioStream_getXRunCount(stream()), 0);
+        EXPECT_GT(AAudioStream_getBufferCapacityInFrames(stream()), 0);
+        EXPECT_GT(AAudioStream_getBufferSizeInFrames(stream()), 0);
+
+        int64_t timestampFrames = 0;
+        int64_t timestampNanos = 0;
+        aaudio_result_t result = AAudioStream_getTimestamp(stream(), CLOCK_MONOTONIC,
+                                           &timestampFrames, &timestampNanos);
+        EXPECT_TRUE(result == AAUDIO_ERROR_INVALID_STATE
+                        || result == AAUDIO_ERROR_UNIMPLEMENTED
+                        || result == AAUDIO_OK
+                        );
+
+        // Verify Closing State. Does this crash?
+        aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+        EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(stream(),
+                                                             AAUDIO_STREAM_STATE_UNKNOWN,
+                                                             &state,
+                                                             500 * NANOS_PER_MILLISECOND));
+        EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
+    }
+
     std::unique_ptr<T> mHelper;
     bool mSetupSuccessful = false;
     std::unique_ptr<int16_t[]> mData;
@@ -171,6 +218,9 @@
       aaudio_stream_state_t state = AAudioStream_getState(stream());
       EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
     }
+
+    checkCallsAfterRelease();
+
 }
 
 INSTANTIATE_TEST_CASE_P(SPM, AAudioInputStreamTest,
@@ -376,11 +426,16 @@
     if (!mSetupSuccessful) return;
 
     mHelper->startStream();
-    aaudio_result_t result = AAudioStream_write(
-            stream(), &mData[0], framesPerBurst(),
-            DEFAULT_READ_TIMEOUT);
-    ASSERT_GT(result, 0);
+    // Write a few times so the device has time to read some of the data
+    // and maybe advance the framesRead.
+    for (int i = 0; i < 3; i++) {
+        aaudio_result_t result = AAudioStream_write(
+                stream(), &mData[0], framesPerBurst(),
+                DEFAULT_READ_TIMEOUT);
+        ASSERT_GT(result, 0);
+    }
     mHelper->stopStream();
+    EXPECT_GE(AAudioStream_getFramesRead(stream()), 0);
 
     // It should be safe to release multiple times.
     for (int i = 0; i < 3; i++) {
@@ -388,6 +443,9 @@
       aaudio_stream_state_t state = AAudioStream_getState(stream());
       EXPECT_EQ(AAUDIO_STREAM_STATE_CLOSING, state);
     }
+
+    checkCallsAfterRelease();
+
 }
 
 // Note that the test for EXCLUSIVE sharing mode may fail gracefully if
diff --git a/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/Android.bp b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/Android.bp
new file mode 100644
index 0000000..a5e2fd3
--- /dev/null
+++ b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+    name: "CtsSelfUninstallingTestApp",
+    defaults: ["cts_defaults"],
+
+    sdk_version: "current",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.core_core",
+    ],
+
+    // tag this module as a cts test artifact
+    test_suites: [
+        "arcts",
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+}
diff --git a/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/AndroidManifest.xml b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..87dc715
--- /dev/null
+++ b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.packageinstaller.selfuninstalling.cts" >
+
+    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
+
+    <application android:label="Self Uninstalling Test App">
+        <activity android:name=".SelfUninstallActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
diff --git a/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/res/layout/self_uninstalling_activity.xml b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/res/layout/self_uninstalling_activity.xml
new file mode 100644
index 0000000..ac0fb40
--- /dev/null
+++ b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/res/layout/self_uninstalling_activity.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" >
+    <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Pin me!" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/src/android/packageinstaller/selfuninstalling/cts/SelfUninstallActivity.java b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/src/android/packageinstaller/selfuninstalling/cts/SelfUninstallActivity.java
new file mode 100644
index 0000000..df5b1d4
--- /dev/null
+++ b/tests/tests/packageinstaller/test-apps/SelfUninstallingTestApp/src/android/packageinstaller/selfuninstalling/cts/SelfUninstallActivity.java
@@ -0,0 +1,33 @@
+package android.packageinstaller.selfuninstalling.cts;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.Nullable;
+
+public class SelfUninstallActivity extends Activity {
+
+    private static final String ACTION_SELF_UNINSTALL =
+            "android.packageinstaller.selfuninstalling.cts.action.SELF_UNINSTALL";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.self_uninstalling_activity);
+        registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Intent i = new Intent(Intent.ACTION_UNINSTALL_PACKAGE).setData(
+                        Uri.fromParts("package", getPackageName(), null));
+                startActivity(i);
+            }
+        }, new IntentFilter(ACTION_SELF_UNINSTALL));
+    }
+}
diff --git a/tests/tests/packageinstaller/uninstall/Android.bp b/tests/tests/packageinstaller/uninstall/Android.bp
index 9f61f87..50d1a52 100644
--- a/tests/tests/packageinstaller/uninstall/Android.bp
+++ b/tests/tests/packageinstaller/uninstall/Android.bp
@@ -20,6 +20,7 @@
         "androidx.test.rules",
         "compatibility-device-util-axt",
         "platform-test-annotations",
+        "cts-wm-util",
     ],
     resource_dirs: ["res"],
     srcs: ["src/**/*.java"],
diff --git a/tests/tests/packageinstaller/uninstall/AndroidTest.xml b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
index 9fc0a88..dd98214 100644
--- a/tests/tests/packageinstaller/uninstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
@@ -31,4 +31,14 @@
         <option name="package" value="android.packageinstaller.uninstall.cts" />
         <option name="runtime-hint" value="1m" />
     </test>
+    <!-- Create place to store apks -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="mkdir -p /data/local/tmp/cts/uninstall" />
+        <option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
+    </target_preparer>
+
+    <!-- Load additional APKs onto device -->
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="push" value="CtsSelfUninstallingTestApp.apk->/data/local/tmp/cts/uninstall/CtsSelfUninstallingTestApp.apk" />
+    </target_preparer>
 </configuration>
diff --git a/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallPinnedTest.java b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallPinnedTest.java
new file mode 100644
index 0000000..84c2696
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/src/android/packageinstaller/uninstall/cts/UninstallPinnedTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.packageinstaller.uninstall.cts;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.server.wm.WindowManagerStateHelper;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AppOpsUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class UninstallPinnedTest {
+
+    private static final String APK =
+            "/data/local/tmp/cts/uninstall/CtsSelfUninstallingTestApp.apk";
+    private static final String TEST_PKG_NAME = "android.packageinstaller.selfuninstalling.cts";
+    private static final String TEST_ACTIVITY_NAME = TEST_PKG_NAME + ".SelfUninstallActivity";
+    private static final String ACTION_SELF_UNINSTALL =
+            "android.packageinstaller.selfuninstalling.cts.action.SELF_UNINSTALL";
+    private static final ComponentName COMPONENT = new ComponentName(TEST_PKG_NAME, TEST_ACTIVITY_NAME);
+    public static final String CALLBACK_ACTION =
+            "android.packageinstaller.uninstall.cts.action.UNINSTALL_PINNED_CALLBACK";
+
+    private WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
+    private Context mContext;
+    private UiDevice mUiDevice;
+    private ActivityTaskManager mActivityTaskManager;
+
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+
+        // Unblock UI
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        if (!mUiDevice.isScreenOn()) {
+            mUiDevice.wakeUp();
+        }
+        mUiDevice.executeShellCommand("wm dismiss-keyguard");
+        AppOpsUtils.reset(mContext.getPackageName());
+
+        runShellCommand("pm install -r --force-queryable " + APK);
+
+        Intent i = new Intent()
+                .setComponent(COMPONENT)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(i);
+
+        pinActivity(COMPONENT);
+    }
+
+    @Test
+    public void testAppCantUninstallItself() throws Exception {
+        mUiDevice.waitForIdle();
+        eventually(() -> {
+            mContext.sendBroadcast(new Intent(ACTION_SELF_UNINSTALL));
+            waitFindObject(By.text("OK")).click();
+        }, 60000);
+
+        mUiDevice.waitForIdle();
+
+        Thread.sleep(5000);
+
+        assertTrue("Package was uninstalled.", isInstalled());
+    }
+
+    @Test
+    public void testCantUninstallAppDirectly() {
+        CompletableFuture<Integer> statusFuture = new CompletableFuture<>();
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                statusFuture.complete(
+                        intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Integer.MAX_VALUE));
+            }
+        }, new IntentFilter(CALLBACK_ACTION));
+
+        runWithShellPermissionIdentity(() -> {
+            mContext.getPackageManager().getPackageInstaller().uninstall(TEST_PKG_NAME,
+                    PendingIntent.getBroadcast(mContext, 1,
+                            new Intent(CALLBACK_ACTION),
+                            0).getIntentSender());
+        });
+
+        int status = statusFuture.join();
+        assertEquals("Wrong code received", PackageInstaller.STATUS_FAILURE_BLOCKED, status);
+        assertTrue("Package was uninstalled.", isInstalled());
+    }
+
+    @Test
+    public void testCantUninstallWithShell() throws Exception {
+        mUiDevice.executeShellCommand("pm uninstall " + TEST_PKG_NAME);
+        assertTrue("Package was uninstalled.", isInstalled());
+    }
+
+    @After
+    public void unpinAndUninstall() throws IOException {
+        runWithShellPermissionIdentity(() -> mActivityTaskManager.stopSystemLockTaskMode());
+        mUiDevice.executeShellCommand("pm uninstall " + TEST_PKG_NAME);
+    }
+
+    private void pinActivity(ComponentName component) {
+        mWmState.computeState();
+
+        int stackId = mWmState.getStackIdByActivity(component);
+
+        runWithShellPermissionIdentity(() -> {
+            mActivityTaskManager.startSystemLockTaskMode(
+                    stackId);
+        });
+    }
+
+    private boolean isInstalled() {
+        try {
+            mContext.getPackageManager().getPackageInfo(TEST_PKG_NAME, 0);
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+}
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 451360d..4acb83f 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -4263,6 +4263,10 @@
     <permission android:name="android.permission.WRITE_DREAM_STATE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @hide Allows applications to read whether ambient display is suppressed. -->
+    <permission android:name="android.permission.READ_DREAM_SUPPRESSION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allow an application to read and write the cache partition.
          @hide -->
     <permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
diff --git a/tests/tests/permission4/Android.bp b/tests/tests/permission4/Android.bp
new file mode 100644
index 0000000..ad1658d
--- /dev/null
+++ b/tests/tests/permission4/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "CtsPermission4TestCases",
+    sdk_version: "system_current",
+    defaults: ["cts_defaults"],
+    platform_apis: true,
+    srcs: [
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "kotlin-stdlib",
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+        "mts",
+    ],
+}
diff --git a/tests/tests/permission4/AndroidManifest.xml b/tests/tests/permission4/AndroidManifest.xml
new file mode 100644
index 0000000..d4cc71a
--- /dev/null
+++ b/tests/tests/permission4/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.permission4.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+    <application>
+
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name=".StartForFutureActivity" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.permission4.cts"
+        android:label="CTS UI tests for permissions">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/permission4/AndroidTest.xml b/tests/tests/permission4/AndroidTest.xml
new file mode 100644
index 0000000..71353aa
--- /dev/null
+++ b/tests/tests/permission4/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration description="Config for CTS Permission4 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="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsPermission4TestCases.apk" />
+        <option name="test-file-name" value="CtsAppThatAccessesMicAndCameraPermission.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.permission4.cts" />
+        <option name="runtime-hint" value="5m" />
+    </test>
+</configuration>
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp b/tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp
new file mode 100644
index 0000000..508e44c
--- /dev/null
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/Android.bp
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+    name: "CtsAppThatAccessesMicAndCameraPermission",
+    defaults: ["cts_defaults"],
+    sdk_version: "system_current",
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts10",
+        "general-tests",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "kotlin-stdlib",
+        "kotlinx-coroutines-android",
+    ],
+
+    srcs: [
+        "src/**/*.kt"
+    ],
+}
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.xml b/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.xml
new file mode 100644
index 0000000..938b5b5
--- /dev/null
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.permission4.cts.appthataccessescameraandmic"
+          android:versionCode="1">
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+
+    <application android:label="CtsCameraMicAccess">
+        <activity android:name=".AccessCameraOrMicActivity"
+        android:exported="true">
+            <intent-filter>
+                <action android:name="test.action.USE_CAMERA_OR_MIC" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
new file mode 100644
index 0000000..2be6926
--- /dev/null
+++ b/tests/tests/permission4/AppThatAccessesCameraAndMic/src/android/permission4/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission4.cts.appthataccessescameraandmic
+
+import android.app.Activity
+import android.hardware.camera2.CameraAccessException
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.media.AudioFormat.CHANNEL_IN_MONO
+import android.media.AudioFormat.ENCODING_PCM_16BIT
+import android.media.AudioRecord
+import android.media.MediaRecorder.AudioSource.MIC
+import androidx.annotation.NonNull
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val USE_DURATION_MS = 10000L
+private const val SAMPLE_RATE_HZ = 44100
+
+/**
+ * Activity which will, depending on the extra passed in the intent, use the camera, the microphone,
+ * or both.
+ */
+class AccessCameraOrMicActivity : Activity() {
+    private lateinit var cameraId: String
+    private var cameraDevice: CameraDevice? = null
+    private var recorder: AudioRecord? = null
+    private var cameraFinished = false
+    private var runCamera = false
+    private var micFinished = false
+    private var runMic = false
+
+    override fun onStart() {
+        super.onStart()
+        runCamera = intent.getBooleanExtra(USE_CAMERA, false)
+        runMic = intent.getBooleanExtra(USE_MICROPHONE, false)
+
+        if (runMic) {
+            useMic()
+        }
+
+        if (runCamera) {
+            useCamera()
+        }
+    }
+
+    override fun onStop() {
+        super.onStop()
+        cameraDevice?.close()
+        recorder?.stop()
+        finish()
+    }
+
+    private val stateCallback = object : CameraDevice.StateCallback() {
+        override fun onOpened(@NonNull camDevice: CameraDevice) {
+            cameraDevice = camDevice
+            GlobalScope.launch {
+                delay(USE_DURATION_MS)
+                cameraFinished = true
+                if (!runMic || micFinished) {
+                    finish()
+                }
+            }
+        }
+
+        override fun onDisconnected(@NonNull camDevice: CameraDevice) {
+            camDevice.close()
+            throw RuntimeException("Camera was disconnected")
+        }
+
+        override fun onError(@NonNull camDevice: CameraDevice, error: Int) {
+            camDevice.close()
+            throw RuntimeException("Camera error")
+        }
+    }
+
+    @Throws(CameraAccessException::class)
+    private fun useCamera() {
+        val manager = getSystemService(CameraManager::class.java)!!
+        cameraId = manager.cameraIdList[0]
+        manager.openCamera(cameraId, mainExecutor, stateCallback)
+    }
+
+    private fun useMic() {
+        val minSize =
+            AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT)
+        recorder = AudioRecord(MIC, SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, minSize)
+        recorder?.startRecording()
+        GlobalScope.launch {
+            delay(USE_DURATION_MS)
+            micFinished = true
+            if (!runCamera || cameraFinished) {
+                finish()
+            }
+        }
+    }
+}
diff --git a/tests/tests/permission4/OWNERS b/tests/tests/permission4/OWNERS
new file mode 100644
index 0000000..d4d6a95
--- /dev/null
+++ b/tests/tests/permission4/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 137825
+svetoslavganov@google.com
+moltmann@google.com
+zhanghai@google.com
+eugenesusla@google.com
+evanseverson@google.com
+ntmyren@google.com
diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
new file mode 100644
index 0000000..0f36f5a
--- /dev/null
+++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission4.cts
+
+import android.Manifest
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.app.compat.CompatChanges
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.hardware.camera2.CameraManager
+import android.os.Process
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiSelector
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+private const val APP_LABEL = "CtsCameraMicAccess"
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val INTENT_ACTION = "test.action.USE_CAMERA_OR_MIC"
+private const val PRIVACY_CHIP_ID = "com.android.systemui:id/privacy_chip"
+private const val INDICATORS_FLAG = "camera_mic_icons_enabled"
+private const val PERMISSION_INDICATORS_NOT_PRESENT = 162547999L
+private const val IDLE_TIMEOUT_MILLIS: Long = 1000
+private const val UNEXPECTED_TIMEOUT_MILLIS = 1000
+private const val TIMEOUT_MILLIS: Long = 20000
+
+class CameraMicIndicatorsPermissionTest {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val context: Context = instrumentation.context
+    private val uiAutomation: UiAutomation = instrumentation.uiAutomation
+    private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+    private val packageManager: PackageManager = context.packageManager
+
+    private var wasEnabled = false
+    private val micLabel = packageManager.getPermissionGroupInfo(
+        Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString()
+    private val cameraLabel = packageManager.getPermissionGroupInfo(
+        Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString()
+
+    private var screenTimeoutBeforeTest: Long = 0L
+
+    @Before
+    fun setUp() {
+        runWithShellPermissionIdentity {
+            screenTimeoutBeforeTest = Settings.System.getLong(
+                context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT
+            )
+            Settings.System.putLong(
+                context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT, 1800000L
+            )
+        }
+
+        uiDevice.wakeUp()
+        runShellCommand(instrumentation, "wm dismiss-keyguard")
+
+        uiDevice.findObject(By.text("Close"))?.click()
+        wasEnabled = setIndicatorsEnabledStateIfNeeded(true)
+        // If the change Id is not present, then isChangeEnabled will return true. To bypass this,
+        // the change is set to "false" if present.
+        assumeFalse("feature not present on this device", callWithShellPermissionIdentity {
+            CompatChanges.isChangeEnabled(PERMISSION_INDICATORS_NOT_PRESENT, Process.SYSTEM_UID)
+        })
+    }
+
+    private fun setIndicatorsEnabledStateIfNeeded(shouldBeEnabled: Boolean): Boolean {
+        var currentlyEnabled = false
+        runWithShellPermissionIdentity {
+            currentlyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                INDICATORS_FLAG, false)
+            if (currentlyEnabled != shouldBeEnabled) {
+                DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG,
+                    shouldBeEnabled.toString(), false)
+            }
+        }
+        return currentlyEnabled
+    }
+
+    @After
+    fun tearDown() {
+        if (!wasEnabled) {
+            setIndicatorsEnabledStateIfNeeded(false)
+        }
+        runWithShellPermissionIdentity {
+            Settings.System.putLong(
+                context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT,
+                screenTimeoutBeforeTest
+            )
+        }
+
+        pressHome()
+    }
+
+    private fun openApp(useMic: Boolean, useCamera: Boolean) {
+        context.startActivity(Intent(INTENT_ACTION).apply {
+            putExtra(USE_CAMERA, useCamera)
+            putExtra(USE_MICROPHONE, useMic)
+            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        })
+    }
+
+    @Test
+    fun testCameraIndicator() {
+        val manager = context.getSystemService(CameraManager::class.java)!!
+        assumeTrue(manager.cameraIdList.isNotEmpty())
+        testCameraAndMicIndicator(useMic = false, useCamera = true)
+    }
+
+    @Test
+    fun testMicIndicator() {
+        testCameraAndMicIndicator(useMic = true, useCamera = false)
+    }
+
+    private fun testCameraAndMicIndicator(useMic: Boolean, useCamera: Boolean) {
+        openApp(useMic, useCamera)
+        eventually {
+            val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+            assertTrue("View with text $APP_LABEL not found", appView.exists())
+        }
+        uiDevice.openNotification()
+        // Ensure the privacy chip is present
+        eventually {
+            val privacyChip = uiDevice.findObject(UiSelector().resourceId(PRIVACY_CHIP_ID))
+            assertTrue("view with id $PRIVACY_CHIP_ID not found", privacyChip.exists())
+            privacyChip.click()
+        }
+        eventually {
+            if (useMic) {
+                val appView = uiDevice.findObject(UiSelector().textContains(micLabel))
+                assertTrue("View with text $APP_LABEL not found", appView.exists())
+            }
+            if (useCamera) {
+                val appView = uiDevice.findObject(UiSelector().textContains(cameraLabel))
+                assertTrue("View with text $APP_LABEL not found", appView.exists())
+            }
+            val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+            assertTrue("View with text $APP_LABEL not found", appView.exists())
+        }
+        pressBack()
+    }
+
+    private fun pressBack() {
+        uiDevice.pressBack()
+        waitForIdle()
+    }
+
+    private fun pressHome() {
+        uiDevice.pressHome()
+        waitForIdle()
+    }
+
+    private fun waitForIdle() =
+        uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+}
\ No newline at end of file
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
index 479c3ab..11a0e66 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
@@ -17,8 +17,6 @@
 package android.provider.cts.media;
 
 import static android.provider.cts.ProviderTestUtils.assertColorMostlyEquals;
-import static android.provider.cts.ProviderTestUtils.assertExists;
-import static android.provider.cts.ProviderTestUtils.assertNotExists;
 import static android.provider.cts.ProviderTestUtils.extractAverageColor;
 import static android.provider.cts.media.MediaStoreTest.TAG;
 
@@ -51,6 +49,9 @@
 import android.provider.cts.R;
 import android.provider.cts.media.MediaStoreUtils.PendingParams;
 import android.provider.cts.media.MediaStoreUtils.PendingSession;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Size;
@@ -69,6 +70,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
 
@@ -211,7 +213,7 @@
         c.close();
 
         ProviderTestUtils.waitForIdle();
-        assertExists("image file does not exist", imagePath);
+        assertFileExists(imagePath);
         assertNotNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MINI_KIND, null));
         assertNotNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MICRO_KIND, null));
 
@@ -222,7 +224,7 @@
         mRowsAdded.remove(stringUri);
 
         ProviderTestUtils.waitForIdle();
-        assertNotExists("image file should no longer exist", imagePath);
+        assertFileNotExists(imagePath);
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MINI_KIND, null));
         assertNull(Thumbnails.getThumbnail(resolver, imageId, Thumbnails.MICRO_KIND, null));
 
@@ -234,10 +236,10 @@
         imageId = c.getLong(c.getColumnIndex(Media._ID));
         imagePath = c.getString(c.getColumnIndex(Media.DATA));
         c.close();
-        assertExists("image file does not exist", imagePath);
+        assertFileExists(imagePath);
         Uri fileuri = MediaStore.Files.getContentUri("external", imageId);
         mContentResolver.delete(fileuri, null, null);
-        assertNotExists("image file should no longer exist", imagePath);
+        assertFileNotExists(imagePath);
     }
 
     @Test
@@ -503,4 +505,26 @@
         bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
     }
 
+    private static void assertFileExists(String path) throws Exception {
+        try {
+            Os.access(path, OsConstants.F_OK);
+        } catch (ErrnoException e) {
+            if (e.errno == OsConstants.ENOENT) {
+                fail("File " + path + " doesn't exist.");
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private static void assertFileNotExists(String path) throws Exception {
+        try {
+            Os.access(path, OsConstants.F_OK);
+            fail("File " + path + " exists.");
+        } catch (ErrnoException e) {
+            if (e.errno != OsConstants.ENOENT) {
+                throw e;
+            }
+        }
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java b/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
index ee9359c..5043e15 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MissedCallTest.java
@@ -17,6 +17,7 @@
 package android.telecom.cts;
 
 import android.content.Intent;
+import android.os.Process;
 import android.telecom.Call;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
@@ -29,6 +30,8 @@
     TestUtils.InvokeCounter mShowMissedCallNotificationIntentCounter =
             new TestUtils.InvokeCounter("ShowMissedCallNotificationIntent");
 
+    private static final String CMD_DEVICE_IDLE_TEMP_EXEMPTIONS = "cmd deviceidle tempwhitelist";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -71,6 +74,15 @@
         connection.setDisconnected(new DisconnectCause(DisconnectCause.MISSED));
         connection.destroy();
         mShowMissedCallNotificationIntentCounter.waitForCount(1);
+        assertTrue("After missing a call, if the default dialer is handling the missed call "
+                + "notification, then it must be in the temporary power exemption list.",
+                isOnTemporaryPowerExemption());
     }
 
+    private boolean isOnTemporaryPowerExemption() throws Exception {
+        String exemptions = TestUtils.executeShellCommand(
+                getInstrumentation(), CMD_DEVICE_IDLE_TEMP_EXEMPTIONS);
+        // Just check that this process's UID is in the result.
+        return exemptions.contains(String.valueOf(Process.myUid()));
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
index b87d594..1112b28 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellIdentityTest.java
@@ -22,6 +22,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
 import android.telephony.CellIdentityNr;
 import android.telephony.CellIdentityTdscdma;
 import android.telephony.CellIdentityWcdma;
@@ -97,14 +98,48 @@
     }
 
     @Test
-    public void testCellIdentityNr_asCellLocation() {
-        CellIdentity cellIdentity =
-                new CellIdentityNr(123, 456, 789, null, null, null, 0, null, null, EMPTY_SET);
+    public void testCellIdentityLte_asCellLocation() {
+        int tac = 1;
+        int ci = 2;
+        CellIdentity cellIdentity = new CellIdentityLte(123, 456, ci, 7, tac);
 
         CellLocation cellLocation = cellIdentity.asCellLocation();
 
         GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
-        assertEquals(new GsmCellLocation(), gsmCellLocation);
+        assertEquals(tac, gsmCellLocation.getLac());
+        assertEquals(ci, gsmCellLocation.getCid());
+        // psc is not supported in LTE so always 0
+        assertEquals(0, gsmCellLocation.getPsc());
+    }
+
+    @Test
+    public void testCellIdentityLte_unavailable_asCellLocation() {
+        CellIdentity cellIdentity = new CellIdentityLte();
+
+        CellLocation cellLocation = cellIdentity.asCellLocation();
+
+        GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
+        // -1 for unintialized lac and cid
+        assertEquals(-1, gsmCellLocation.getLac());
+        assertEquals(-1, gsmCellLocation.getCid());
+        // psc is not supported in LTE so always 0
+        assertEquals(0, gsmCellLocation.getPsc());
+    }
+
+    @Test
+    public void testCellIdentityNr_asCellLocation() {
+        int tac = 1;
+        CellIdentity cellIdentity =
+                new CellIdentityNr(123, tac, 789, null, null, null, 321, null, null, EMPTY_SET);
+
+        CellLocation cellLocation = cellIdentity.asCellLocation();
+
+        GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
+        assertEquals(tac, gsmCellLocation.getLac());
+        // NR cid is 36 bits and can't fit into the 32-bit cid in GsmCellLocation, so always -1.
+        assertEquals(-1, gsmCellLocation.getCid());
+        // psc is not supported in NR so always 0, same as in LTE
+        assertEquals(0, gsmCellLocation.getPsc());
     }
 
     @Test
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index 0a5a374..c6f1aa6 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -331,6 +331,9 @@
         assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
                 TextUtils.isEmpty(mDestAddr));
 
+        // disable suppressing blocking.
+        TelephonyUtils.endBlockSuppression(getInstrumentation());
+
         String mccmnc = mTelephonyManager.getSimOperator();
         // Setting default SMS App is needed to be able to block numbers.
         setDefaultSmsApp(true);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
index 2bd8bb8..f389098 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyUtils.java
@@ -33,6 +33,8 @@
     private static final String COMMAND_REMOVE_TEST_EMERGENCY_NUMBER =
             "cmd phone emergency-number-test-mode -r ";
 
+    private static final String COMMAND_END_BLOCK_SUPPRESSION = "cmd phone end-block-suppression";
+
     public static void addTestEmergencyNumber(Instrumentation instr, String testNumber)
             throws Exception {
         executeShellCommand(instr, COMMAND_ADD_TEST_EMERGENCY_NUMBER + testNumber);
@@ -43,6 +45,10 @@
         executeShellCommand(instr, COMMAND_REMOVE_TEST_EMERGENCY_NUMBER + testNumber);
     }
 
+    public static void endBlockSuppression(Instrumentation instr) throws Exception {
+        executeShellCommand(instr, COMMAND_END_BLOCK_SUPPRESSION);
+    }
+
     public static boolean isSkt(TelephonyManager telephonyManager) {
         return isOperator(telephonyManager, "45005");
     }
diff --git a/tests/tests/voiceRecognition/Android.bp b/tests/tests/voiceRecognition/Android.bp
new file mode 100644
index 0000000..ca53417
--- /dev/null
+++ b/tests/tests/voiceRecognition/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+    name: "CtsVoiceRecognitionTestCases",
+    defaults: ["cts_defaults"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    libs: ["android.test.base"],
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+        "androidx.test.ext.junit",
+        "truth-prebuilt",
+    ],
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    sdk_version: "system_current",
+}
diff --git a/tests/tests/voiceRecognition/AndroidManifest.xml b/tests/tests/voiceRecognition/AndroidManifest.xml
new file mode 100644
index 0000000..41e29cb
--- /dev/null
+++ b/tests/tests/voiceRecognition/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.voicerecognition.cts">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application android:label="CtsVoiceRecognitionTestCases">
+        <uses-library android:name="android.test.runner"/>
+        <!--The Activity that uses SpeechRecognizer APIs to access RecognitionService -->
+        <activity android:name="SpeechRecognitionActivity"
+                  android:label="SpeechRecognitionActivity"
+                  android:exported="true">
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.voicerecognition.cts"
+         android:label="CTS tests of android voicerecognition">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/voiceRecognition/AndroidTest.xml b/tests/tests/voiceRecognition/AndroidTest.xml
new file mode 100644
index 0000000..6934357
--- /dev/null
+++ b/tests/tests/voiceRecognition/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Voice Recognition 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="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="CtsVoiceRecognitionTestCases.apk"/>
+        <option name="test-file-name" value="CtsVoiceRecognitionService.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.voicerecognition.cts" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/voiceRecognition/RecognitionService/Android.bp b/tests/tests/voiceRecognition/RecognitionService/Android.bp
new file mode 100644
index 0000000..2f9fde8
--- /dev/null
+++ b/tests/tests/voiceRecognition/RecognitionService/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+    name: "CtsVoiceRecognitionService",
+    defaults: ["cts_defaults"],
+    sdk_version: "current",
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests"
+    ],
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+}
diff --git a/tests/tests/voiceRecognition/RecognitionService/AndroidManifest.xml b/tests/tests/voiceRecognition/RecognitionService/AndroidManifest.xml
new file mode 100644
index 0000000..072df55
--- /dev/null
+++ b/tests/tests/voiceRecognition/RecognitionService/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.recognitionservice.service"
+          android:targetSandboxVersion="2">
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application android:label="CtsVoiceRecognitionService">
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="CtsVoiceRecognitionService"
+                 android:label="@string/service_name"
+                 android:exported="true">
+            <intent-filter>
+                <action android:name="android.speech.RecognitionService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </service>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="The VoiceRecognitionService for CTS test."
+        android:targetPackage="android.recognitionservice.service" >
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/voiceRecognition/RecognitionService/res/values/strings.xml b/tests/tests/voiceRecognition/RecognitionService/res/values/strings.xml
new file mode 100644
index 0000000..f96159c
--- /dev/null
+++ b/tests/tests/voiceRecognition/RecognitionService/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="service_name">CtsVoiceRecognitionService</string>
+</resources>
diff --git a/tests/tests/voiceRecognition/RecognitionService/src/com/android/recognitionservice/service/CtsVoiceRecognitionService.java b/tests/tests/voiceRecognition/RecognitionService/src/com/android/recognitionservice/service/CtsVoiceRecognitionService.java
new file mode 100644
index 0000000..25cfadd
--- /dev/null
+++ b/tests/tests/voiceRecognition/RecognitionService/src/com/android/recognitionservice/service/CtsVoiceRecognitionService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.recognitionservice.service;
+
+import android.app.AppOpsManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.MediaRecorder;
+import android.os.Binder;
+import android.speech.RecognitionService;
+import android.speech.RecognizerIntent;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Implementation of {@link RecognitionService} used in the tests.
+ */
+public class CtsVoiceRecognitionService extends RecognitionService {
+
+    private final String TAG = "CtsVoiceRecognitionService";
+
+    private MediaRecorder mMediaRecorder;
+    private File mOutputFile;
+
+    @Override
+    protected void onCancel(Callback listener) {
+        // No-op.
+    }
+
+    @Override
+    protected void onStopListening(Callback listener) {
+        // No-op.
+    }
+
+    @Override
+    protected void onStartListening(Intent recognizerIntent, Callback listener) {
+        Log.d(TAG, "onStartListening");
+        mediaRecorderReady();
+        blameCameraPermission(recognizerIntent, listener.getCallingUid());
+        try {
+            mMediaRecorder.prepare();
+            mMediaRecorder.start();
+        } catch (IOException e) {
+            // We focus on the open mic behavior, wedon't need to real record and save to the file.
+            // Because we don't set the output the output file. The IOException occurred when start.
+            // We catch this and reset the media record.
+            e.printStackTrace();
+            mMediaRecorder.release();
+            mMediaRecorder = null;
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(TAG, "onDestroy");
+        stopRecord();
+        super.onDestroy();
+    }
+
+    // RecognitionService try to blame non-mic permission
+    private void blameCameraPermission(Intent recognizerIntent, int callingPackageUid) {
+        final String callingPackage =
+                recognizerIntent.getStringExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE);
+        final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
+        appOpsManager.noteProxyOpNoThrow(AppOpsManager.OPSTR_CAMERA, callingPackage,
+                callingPackageUid, /*attributionTag*/ null, /*message*/ null);
+    }
+
+    private void mediaRecorderReady() {
+        mMediaRecorder = new MediaRecorder();
+        mOutputFile = new File(getExternalCacheDir(), "test.3gp");
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mMediaRecorder.setAudioEncoder(MediaRecorder.OutputFormat.AMR_NB);
+        mMediaRecorder.setOutputFile(mOutputFile);
+    }
+
+    private void stopRecord() {
+        if (mMediaRecorder != null) {
+            mMediaRecorder.stop();
+            mMediaRecorder.release();
+            mMediaRecorder = null;
+        }
+        if (mOutputFile != null && mOutputFile.exists()) {
+            mOutputFile.delete();
+        }
+    }
+}
diff --git a/tests/tests/voiceRecognition/res/layout/main.xml b/tests/tests/voiceRecognition/res/layout/main.xml
new file mode 100644
index 0000000..9cab939
--- /dev/null
+++ b/tests/tests/voiceRecognition/res/layout/main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
new file mode 100644
index 0000000..086c1c7
--- /dev/null
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.voicerecognition.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.app.compat.CompatChanges;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.util.Log;
+import android.text.TextUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class RecognitionServiceMicIndicatorTest {
+
+    private final String TAG = "RecognitionServiceMicIndicatorTest";
+    // same as Settings.Secure.VOICE_RECOGNITION_SERVICE
+    private final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
+    // same as Settings.Secure.VOICE_INTERACTION_SERVICE
+    private final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
+    // Th notification privacy indicator
+    private final String PRIVACY_CHIP_PACLAGE_NAME = "com.android.systemui";
+    private final String PRIVACY_CHIP_ID = "privacy_chip";
+    // The cts app label
+    private final String APP_LABEL = "CtsVoiceRecognitionTestCases";
+    // A simple test voice recognition service implementation
+    private final String CTS_VOICE_RECOGNITION_SERVICE =
+            "android.recognitionservice.service/android.recognitionservice.service"
+                    + ".CtsVoiceRecognitionService";
+    private final String INDICATORS_FLAG = "camera_mic_icons_enabled";
+    private final long INDICATOR_DISMISS_TIMEOUT = 5000L;
+    private final long UI_WAIT_TIMEOUT = 1000L;
+    private final long PERMISSION_INDICATORS_NOT_PRESENT = 162547999L;
+
+    private UiDevice mUiDevice;
+    private SpeechRecognitionActivity mActivity;
+    private Context mContext;
+    private String mOriginalVoiceRecognizer;
+    private String mCameraLabel;
+    private boolean mOriginalIndicatorsEnabledState;
+    private boolean mTestRunnung;
+
+    @Rule
+    public ActivityTestRule<SpeechRecognitionActivity> mActivityTestRule =
+            new ActivityTestRule<>(SpeechRecognitionActivity.class);
+
+    @Before
+    public void setup() {
+        // If the change Id is not present, then isChangeEnabled will return true. To bypass this,
+        // the change is set to "false" if present.
+        assumeFalse("feature not present on this device", runWithShellPermissionIdentity(
+                () -> CompatChanges.isChangeEnabled(PERMISSION_INDICATORS_NOT_PRESENT,
+                        Process.SYSTEM_UID)));
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        boolean hasTvFeature = pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        assumeFalse("Not run in the tv device", hasTvFeature);
+        mTestRunnung = true;
+        prepareDevice();
+        mContext = InstrumentationRegistry.getTargetContext();
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        mActivity = mActivityTestRule.getActivity();
+
+        try {
+            mCameraLabel = pm.getPermissionGroupInfo(Manifest.permission_group.CAMERA, 0).loadLabel(
+                    pm).toString();
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        // get original indicator enable state
+        runWithShellPermissionIdentity(() -> {
+            mOriginalIndicatorsEnabledState =
+                    DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG, false);
+        });
+        // get original voice services
+        mOriginalVoiceRecognizer = Settings.Secure.getString(
+                mContext.getContentResolver(), VOICE_RECOGNITION_SERVICE);
+        // QPR is default disabled, we need to enable it
+        setIndicatorsEnabledStateIfNeeded(/* shouldBeEnabled */ true);
+    }
+
+    @After
+    public void teardown() {
+        if (!mTestRunnung) {
+            return;
+        }
+        // press back to close the dialog
+        mUiDevice.pressBack();
+        // restore to original voice services
+        setCurrentRecognizer(mOriginalVoiceRecognizer);
+        // restore to original indicator enable state
+        setIndicatorsEnabledStateIfNeeded(mOriginalIndicatorsEnabledState);
+    }
+
+    private void prepareDevice() {
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+    }
+
+    private void setIndicatorsEnabledStateIfNeeded(Boolean shouldBeEnabled) {
+        runWithShellPermissionIdentity(() -> {
+            final boolean currentlyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                    INDICATORS_FLAG, false);
+            if (currentlyEnabled != shouldBeEnabled) {
+                DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG,
+                        shouldBeEnabled.toString(), false);
+            }
+        });
+    }
+
+    private void setCurrentRecognizer(String recognizer) {
+        runWithShellPermissionIdentity(
+                () -> Settings.Secure.putString(mContext.getContentResolver(),
+                        VOICE_RECOGNITION_SERVICE, recognizer));
+        mUiDevice.waitForIdle();
+    }
+
+    private boolean hasPreInstalledRecognizer(String packageName) {
+        Log.v(TAG, "hasPreInstalledRecognizer package=" + packageName);
+        try {
+            final PackageManager pm = mContext.getPackageManager();
+            final ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private static String getComponentPackageNameFromString(String from) {
+        ComponentName componentName = from != null ? ComponentName.unflattenFromString(from) : null;
+        return componentName != null ? componentName.getPackageName() : "";
+    }
+
+    @Test
+    public void testNonTrustedRecognitionServiceCannotBlameCallingApp() throws Throwable {
+        // This is a workaound solution for R QPR. We treat trusted if the current voice recognizer
+        // is also a preinstalled app. This is a untrusted case.
+        setCurrentRecognizer(CTS_VOICE_RECOGNITION_SERVICE);
+
+        // verify that the untrusted app cannot blame the calling app mic access
+        testVoiceRecognitionServiceBlameCallingApp(/* trustVoiceService */ false);
+    }
+
+    @Test
+    public void testTrustedRecognitionServiceCanBlameCallingApp() throws Throwable {
+        // This is a workaound solution for R QPR. We treat trusted if the current voice recognizer
+        // is also a preinstalled app. This is a trusted case.
+        boolean hasPreInstalledRecognizer = hasPreInstalledRecognizer(
+                getComponentPackageNameFromString(mOriginalVoiceRecognizer));
+        assumeTrue("No preinstalled recognizer.", hasPreInstalledRecognizer);
+
+        // verify that the trusted app can blame the calling app mic access
+        testVoiceRecognitionServiceBlameCallingApp(/* trustVoiceService */ true);
+    }
+
+    private void testVoiceRecognitionServiceBlameCallingApp(boolean trustVoiceService)
+            throws Throwable {
+        // Start SpeechRecognition
+        mActivity.startListening();
+
+        assertPrivacyChipAndIndicatorsPresent(trustVoiceService);
+    }
+
+    private void assertPrivacyChipAndIndicatorsPresent(boolean trustVoiceService) {
+        // Open notification and verify the privacy indicator is shown
+        mUiDevice.openNotification();
+        SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+        final UiObject2 privacyChip =
+                mUiDevice.findObject(By.res(PRIVACY_CHIP_PACLAGE_NAME, PRIVACY_CHIP_ID));
+        assertWithMessage("Can not find mic indicator").that(privacyChip).isNotNull();
+
+        // Click the privacy indicator and verify the calling app name display status in the dialog.
+        privacyChip.click();
+        SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+        final UiObject2 recognitionCallingAppLabel = mUiDevice.findObject(By.text(APP_LABEL));
+        if (trustVoiceService) {
+            // Check trust recognizer can blame calling app mic permission
+            assertWithMessage(
+                    "Trusted voice recognition service can blame the calling app name " + APP_LABEL
+                            + ", but does not find it.").that(
+                    recognitionCallingAppLabel).isNotNull();
+            assertThat(recognitionCallingAppLabel.getText()).isEqualTo(APP_LABEL);
+
+            // Check trust recognizer cannot blame non-mic permission
+            final UiObject2 cemaraLabel = mUiDevice.findObject(By.text(mCameraLabel));
+            assertWithMessage("Trusted voice recognition service cannot blame non-mic permission")
+                    .that(cemaraLabel).isNull();
+        } else {
+            assertWithMessage(
+                    "Untrusted voice recognition service cannot blame the calling app name "
+                            + APP_LABEL).that(recognitionCallingAppLabel).isNull();
+        }
+        // Wait for the privacy indicator to disappear to avoid the test becoming flaky.
+        SystemClock.sleep(INDICATOR_DISMISS_TIMEOUT);
+    }
+}
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognitionActivity.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognitionActivity.java
new file mode 100644
index 0000000..66c8c9c
--- /dev/null
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognitionActivity.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.voicerecognition.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.speech.RecognizerIntent;
+import android.speech.SpeechRecognizer;
+
+/**
+ * An activity that uses SpeechRecognition APIs. SpeechRecognition will bind the RecognitionService
+ * to provide the voice recognition functions.
+ */
+public class SpeechRecognitionActivity extends Activity {
+
+    private final String TAG = "SpeechRecognitionActivity";
+
+    private SpeechRecognizer mRecognizer;
+    private Intent mRecognizerIntent;
+    private Handler mHandler;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+        init();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mRecognizer != null) {
+            mRecognizer.destroy();
+            mRecognizer = null;
+        }
+        super.onDestroy();
+    }
+
+    public void startListening() {
+        mHandler.post(() -> {
+            if (mRecognizer != null) {
+                final Intent recognizerIntent =
+                        new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+                recognizerIntent.putExtra(
+                        RecognizerIntent.EXTRA_CALLING_PACKAGE, this.getPackageName());
+                mRecognizer.startListening(recognizerIntent);
+            }
+        });
+    }
+
+    private void init() {
+        mHandler = new Handler(getMainLooper());
+        mRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
+    }
+}