Merge "cts: unregister softap callback after finish the test" into rvc-dev
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index aa28a97..af9d4d2 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -17,21 +17,22 @@
 package android.appsecurity.cts;
 
 import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.LargeTest;
+
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.util.AbiFormatter;
-import com.android.tradefed.util.AbiUtils;
 
 /**
  * Tests that verify intent filters.
  */
+@LargeTest
 @AppModeFull(reason="Instant applications can never be system or privileged")
 public class PrivilegedUpdateTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
     private static final String TAG = "PrivilegedUpdateTests";
@@ -151,6 +152,28 @@
         }
     }
 
+    public void testUpdatedSystemAppPreservedOnReboot() throws Exception {
+        if (!isDefaultAbi()) {
+            Log.w(TAG, "Skipping test for non-default abi.");
+            return;
+        }
+
+        getDevice().executeShellCommand("pm enable " + SHIM_PKG);
+        runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testPrivAppAndEnabled");
+        try {
+            assertNull(getDevice().installPackage(
+                    mBuildHelper.getTestFile(SHIM_UPDATE_APK), true));
+            getDevice().executeShellCommand("pm enable " + SHIM_PKG);
+            runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testUpdatedPrivAppAndEnabled");
+
+            getDevice().reboot();
+
+            runDeviceTests(TEST_PKG, ".PrivilegedAppDisableTest", "testUpdatedPrivAppAndEnabled");
+        } finally {
+            getDevice().uninstallPackage(SHIM_PKG);
+        }
+    }
+
     private void runDeviceTests(String packageName, String testClassName, String testMethodName)
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
index 95169b6..a1487af 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/AndroidManifest.xml
@@ -17,6 +17,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.privilegedupdate">
 
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
     <application android:label="PrivilegedUpdateApp">
         <uses-library android:name="android.test.runner" />
         <activity android:name=".MainActivity" />
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
index 606ada1..18962a9 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/src/com/android/cts/privilegedupdate/PrivilegedAppDisableTest.java
@@ -35,29 +35,27 @@
     private static final String PRIVILEGED_SHIM_PKG = "com.android.cts.priv.ctsshim";
 
     public void testPrivAppAndEnabled() throws Exception {
-        assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
-                0);
+        assertEquals(0, (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
         assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
     }
 
     public void testPrivAppAndDisabled() throws Exception {
-        assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
-                0);
+        assertEquals(0, (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
         assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
     }
 
     public void testUpdatedPrivAppAndEnabled() throws Exception {
-        assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
-                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+        assertEquals(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+                (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
         assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
     }
 
     public void testUpdatedPrivAppAndDisabled() throws Exception {
-        assertEquals((getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP),
-                ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+        assertEquals(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+                (getApplicationFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
         assertPackageEnabledState(PRIVILEGED_SHIM_PKG,
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
     }
diff --git a/tests/JobScheduler/Android.bp b/tests/JobScheduler/Android.bp
index 9869c74..e9be178 100644
--- a/tests/JobScheduler/Android.bp
+++ b/tests/JobScheduler/Android.bp
@@ -19,6 +19,7 @@
         "compatibility-device-util-axt",
         "ub-uiautomator",
         "androidx.test.rules",
+        "cts-wm-util",
     ],
     libs: ["android.test.base.stubs"],
     srcs: [
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index effabb7..895c563 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -232,7 +232,7 @@
         sendScheduleJobBroadcast(false);
         assertFalse("Job started for restricted app",
                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
-        mTestAppInterface.startAndKeepTestActivity();
+        mTestAppInterface.startAndKeepTestActivity(true);
         assertTrue("Job did not start when app had an activity",
                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
     }
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
index 36d1825..d871a2b 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
@@ -18,6 +18,7 @@
 import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED;
 import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
 import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
 
 import android.app.job.JobParameters;
 import android.content.BroadcastReceiver;
@@ -28,6 +29,7 @@
 import android.jobscheduler.cts.jobtestapp.TestActivity;
 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
 import android.os.SystemClock;
+import android.server.wm.WindowManagerStateHelper;
 import android.util.Log;
 
 /**
@@ -77,10 +79,18 @@
     }
 
     void startAndKeepTestActivity() {
+        startAndKeepTestActivity(false);
+    }
+
+    void startAndKeepTestActivity(boolean waitForResume) {
         final Intent testActivity = new Intent();
         testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        testActivity.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+        ComponentName testComponentName = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+        testActivity.setComponent(testComponentName);
         mContext.startActivity(testActivity);
+        if (waitForResume) {
+            new WindowManagerStateHelper().waitForActivityState(testComponentName, STATE_RESUMED);
+        }
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 4de36b9..10934dd 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -474,8 +474,7 @@
         final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
 
         if (data == null) {
-            data = new Notification.BubbleMetadata.Builder()
-                    .createIntentBubble(pendingIntent,
+            data = new Notification.BubbleMetadata.Builder(pendingIntent,
                             Icon.createWithResource(mContext, R.drawable.black))
                     .build();
         }
@@ -3077,9 +3076,8 @@
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
         // BubbleMetadata with manifest shortcut
-        Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
-                .createShortcutBubble(BUBBLE_SHORTCUT_ID_MANIFEST)
-                .build();
+        Notification.BubbleMetadata data =
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_MANIFEST).build();
 
         sendAndVerifyBubble(1, nb, data, false /* shouldBeBubble */);
     }
@@ -3134,9 +3132,9 @@
                     .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
             // BubbleMetadata with our dynamic shortcut ic
-            Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
-                    .createShortcutBubble(BUBBLE_SHORTCUT_ID_DYNAMIC)
-                    .build();
+            Notification.BubbleMetadata data =
+                    new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_DYNAMIC)
+                            .build();
 
             boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
             sendAndVerifyBubble(1, nb, data, shouldBeBubble);
@@ -3181,9 +3179,9 @@
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
         // BubbleMetadata with shortcut that doesn't exist
-        Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
-                .createShortcutBubble("shortcutDoesntExist")
-                .build();
+        Notification.BubbleMetadata data =
+                new Notification.BubbleMetadata.Builder("shortcutDoesntExist")
+                        .build();
 
         sendAndVerifyBubble(1, nb, data, false);
     }
@@ -3198,9 +3196,9 @@
         toggleBubbleSetting(true);
 
         // BubbleMetadata with manifest shortcut
-        Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
-                .createShortcutBubble(BUBBLE_SHORTCUT_ID_MANIFEST)
-                .build();
+        Notification.BubbleMetadata data =
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_MANIFEST)
+                        .build();
 
         sendAndVerifyBubble(1, null /* use default notif builder */, data,
                 false /* shouldBeBubble */);
@@ -3257,10 +3255,10 @@
                     .setActions(replyAction)
                     .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-            // BubbleMetadata with our dynamic shortcut ic
-            Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder()
-                    .createShortcutBubble(BUBBLE_SHORTCUT_ID_DYNAMIC)
-                    .build();
+            // BubbleMetadata with our dynamic shortcut
+            Notification.BubbleMetadata data =
+                    new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID_DYNAMIC)
+                            .build();
 
             boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
             sendAndVerifyBubble(1, nb, data, shouldBeBubble);
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index 439f3a9..b9f8784 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -625,15 +625,14 @@
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
                 .setDesiredHeight(BUBBLE_HEIGHT)
-                .createIntentBubble(bubbleIntent, icon)
                 .setDeleteIntent(deleteIntent);
 
         Notification.BubbleMetadata data = metadataBuilder.build();
         assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight());
-        assertEquals(icon, data.getBubbleIcon());
-        assertEquals(bubbleIntent, data.getBubbleIntent());
+        assertEquals(icon, data.getIcon());
+        assertEquals(bubbleIntent, data.getIntent());
         assertEquals(deleteIntent, data.getDeleteIntent());
         assertFalse(data.isNotificationSuppressed());
         assertFalse(data.getAutoExpandBubble());
@@ -644,18 +643,17 @@
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata metadata =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
                         .setDesiredHeight(BUBBLE_HEIGHT)
                         .setAutoExpandBubble(true)
                         .setSuppressNotification(true)
-                        .createIntentBubble(bubbleIntent, icon)
                         .setDeleteIntent(deleteIntent)
                         .build();
 
         writeAndReadParcelable(metadata);
         assertEquals(BUBBLE_HEIGHT, metadata.getDesiredHeight());
-        assertEquals(icon, metadata.getBubbleIcon());
-        assertEquals(bubbleIntent, metadata.getBubbleIntent());
+        assertEquals(icon, metadata.getIcon());
+        assertEquals(bubbleIntent, metadata.getIntent());
         assertEquals(deleteIntent, metadata.getDeleteIntent());
         assertTrue(metadata.getAutoExpandBubble());
         assertTrue(metadata.isNotificationSuppressed());
@@ -664,9 +662,8 @@
     public void testBubbleMetadataBuilder_shortcutId() {
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
                         .setDesiredHeight(BUBBLE_HEIGHT)
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID)
                         .setDeleteIntent(deleteIntent);
 
         Notification.BubbleMetadata data = metadataBuilder.build();
@@ -681,11 +678,10 @@
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
 
         Notification.BubbleMetadata metadata =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
                         .setDesiredHeight(BUBBLE_HEIGHT)
                         .setAutoExpandBubble(true)
                         .setSuppressNotification(true)
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID)
                         .setDeleteIntent(deleteIntent)
                         .build();
 
@@ -701,73 +697,67 @@
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata metadata =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
-                        .createIntentBubble(bubbleIntent, icon)
                 .build();
         writeAndReadParcelable(metadata);
         assertEquals(BUBBLE_HEIGHT_RESID, metadata.getDesiredHeightResId());
-        assertEquals(icon, metadata.getBubbleIcon());
-        assertEquals(bubbleIntent, metadata.getBubbleIntent());
+        assertEquals(icon, metadata.getIcon());
+        assertEquals(bubbleIntent, metadata.getIntent());
         assertFalse(metadata.getAutoExpandBubble());
         assertFalse(metadata.isNotificationSuppressed());
     }
 
     public void testBubbleMetadataBuilder_throwForNoIntentNoShortcut() {
-        Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
-                        .setDesiredHeight(BUBBLE_HEIGHT);
+                new Notification.BubbleMetadata.Builder();
         try {
             metadataBuilder.build();
-            fail("Should have thrown IllegalArgumentException, no pending intent or shortcutId");
-        } catch (IllegalStateException e) {
+            fail("Should have thrown exception, no pending intent or shortcutId");
+        } catch (NullPointerException e) {
             // expected
         }
     }
 
     public void testBubbleMetadataBuilder_noThrowWithShortcut() {
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
-                        .setDesiredHeight(BUBBLE_HEIGHT)
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID);
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+                        .setDesiredHeight(BUBBLE_HEIGHT);
         Notification.BubbleMetadata metadata = metadataBuilder.build();
         assertNotNull(metadata.getShortcutId());
-        assertNull(metadata.getBubbleIcon());
-        assertNull(metadata.getBubbleIntent());
+        assertNull(metadata.getIcon());
+        assertNull(metadata.getIntent());
     }
 
-    public void testBubbleMetadataBuilder_shortcutOverwritesIconIntent() {
+    public void testBubbleMetadataBuilder_shortcutBuilder_throwsForSetIntent() {
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        Icon icon = Icon.createWithResource(mContext, 1);
-        Notification.BubbleMetadata metadata =
-                new Notification.BubbleMetadata.Builder()
-                        .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
-                        .createIntentBubble(bubbleIntent, icon)
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID)
-                        .build();
-        assertNotNull(metadata.getShortcutId());
-        assertNull(metadata.getBubbleIcon());
-        assertNull(metadata.getBubbleIntent());
+        try {
+            Notification.BubbleMetadata.Builder metadataBuilder =
+                    new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+                            .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
+                            .setIntent(bubbleIntent);
+            fail("Should have thrown exception, can't set intent on shortcut builder");
+        } catch (Exception e) {
+            // expected
+        }
     }
 
-    public void testBubbleMetadataBuilder_intentIconOverwritesShortcut() {
-        PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        Icon icon = Icon.createWithResource(mContext, 1);
-        Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
-                        .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID)
-                        .createIntentBubble(bubbleIntent, icon);
-        Notification.BubbleMetadata metadata = metadataBuilder.build();
-        assertNull(metadata.getShortcutId());
-        assertNotNull(metadata.getBubbleIcon());
-        assertNotNull(metadata.getBubbleIntent());
+    public void testBubbleMetadataBuilder_shortcutBuilder_throwsForSetIcon() {
+        try {
+            Icon icon = Icon.createWithResource(mContext, 1);
+            Notification.BubbleMetadata.Builder metadataBuilder =
+                    new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID)
+                            .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
+                            .setIcon(icon);
+            fail("Should have thrown exception, can't set icon on shortcut builder");
+        } catch (Exception e) {
+            // expected
+        }
     }
 
     public void testBubbleMetadataBuilder_notifBubbleShortcutIds_match_noThrow() {
-        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
-                        .createShortcutBubble(BUBBLE_SHORTCUT_ID).build();
+        Notification.BubbleMetadata metadata =
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID).build();
 
         mNotification = new Notification.Builder(mContext, CHANNEL.getId())
                 .setSmallIcon(1)
@@ -781,8 +771,8 @@
     }
 
     public void testBubbleMetadataBuilder_notifBubbleShortcutIds_different_throw() {
-        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
-                .createShortcutBubble(BUBBLE_SHORTCUT_ID).build();
+        Notification.BubbleMetadata metadata =
+                new Notification.BubbleMetadata.Builder(BUBBLE_SHORTCUT_ID).build();
 
         Notification.Builder nb = new Notification.Builder(mContext, CHANNEL.getId())
                 .setSmallIcon(1)
@@ -806,11 +796,10 @@
 
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
-                        .createIntentBubble(bubbleIntent, icon);
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
         Notification.BubbleMetadata metadata = metadataBuilder.build();
-        assertNotNull(metadata.getBubbleIcon());
-        assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getBubbleIcon().getType());
+        assertNotNull(metadata.getIcon());
+        assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getIcon().getType());
     }
 
     public void testBubbleMetadataBuilder_noThrowForNonBitmapIcon() {
@@ -818,11 +807,10 @@
 
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
-                        .createIntentBubble(bubbleIntent, icon);
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon);
         Notification.BubbleMetadata metadata = metadataBuilder.build();
-        assertNotNull(metadata.getBubbleIcon());
-        assertEquals(TYPE_RESOURCE, metadata.getBubbleIcon().getType());
+        assertNotNull(metadata.getIcon());
+        assertEquals(TYPE_RESOURCE, metadata.getIcon().getType());
     }
 
     public void testBubbleMetadataBuilder_replaceHeightRes() {
@@ -830,10 +818,9 @@
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
                         .setDesiredHeight(BUBBLE_HEIGHT)
                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
-                        .createIntentBubble(bubbleIntent, icon)
                         .setDeleteIntent(deleteIntent);
 
         Notification.BubbleMetadata data = metadataBuilder.build();
@@ -848,10 +835,9 @@
         PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         Icon icon = Icon.createWithResource(mContext, 1);
         Notification.BubbleMetadata.Builder metadataBuilder =
-                new Notification.BubbleMetadata.Builder()
+                new Notification.BubbleMetadata.Builder(bubbleIntent, icon)
                         .setDesiredHeightResId(BUBBLE_HEIGHT_RESID)
                         .setDesiredHeight(BUBBLE_HEIGHT)
-                        .createIntentBubble(bubbleIntent, icon)
                         .setDeleteIntent(deleteIntent);
 
         Notification.BubbleMetadata data = metadataBuilder.build();
@@ -923,9 +909,9 @@
     private Notification.BubbleMetadata makeBubbleMetadata() {
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
 
-        return new Notification.BubbleMetadata.Builder()
-                        .createIntentBubble(bubbleIntent, Icon.createWithResource(mContext, 1))
-                        .setDesiredHeight(BUBBLE_HEIGHT)
-                        .build();
+        return new Notification.BubbleMetadata.Builder(bubbleIntent,
+                Icon.createWithResource(mContext, 1))
+                .setDesiredHeight(BUBBLE_HEIGHT)
+                .build();
     }
 }
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
index 85221d3..efe5700 100644
--- a/tests/camera/AndroidManifest.xml
+++ b/tests/camera/AndroidManifest.xml
@@ -70,6 +70,13 @@
             android:process=":camera2ActivityProcess">
         </activity>
 
+        <activity android:name="android.hardware.camera2.cts.Camera2OfflineTestActivity"
+            android:label="RemoteCamera2OfflineTestActivity"
+            android:screenOrientation="landscape"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:process=":camera2ActivityProcess">
+        </activity>
+
         <activity android:name="android.hardware.multiprocess.camera.cts.MediaRecorderCameraActivity"
             android:label="RemoteMediaRecorderCameraActivity"
             android:screenOrientation="landscape"
diff --git a/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java
new file mode 100644
index 0000000..2483eef
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2OfflineTestActivity.java
@@ -0,0 +1,158 @@
+/*
+ * 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.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.OFFLINE_CAMERA_ID;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.multiprocess.camera.cts.ErrorLoggingService;
+import android.hardware.multiprocess.camera.cts.TestConstants;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * Activity implementing basic access of the Camera2 API.
+ *
+ * <p />
+ * This will log all errors to {@link android.hardware.multiprocess.camera.cts.ErrorLoggingService}.
+ */
+public class Camera2OfflineTestActivity extends Activity {
+    private static final String TAG = "Camera2OfflineTestActivity";
+
+    ErrorLoggingService.ErrorServiceConnection mErrorServiceConnection;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate called.");
+        super.onCreate(savedInstanceState);
+        mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(this);
+        mErrorServiceConnection.start();
+    }
+
+    @Override
+    protected void onPause() {
+        Log.i(TAG, "onPause called.");
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        Log.i(TAG, "onResume called.");
+        super.onResume();
+
+        try {
+            CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
+
+            if (manager == null) {
+                mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+                        " could not connect camera service");
+                return;
+            }
+            Intent intent = getIntent();
+            Bundle bundledExtras = intent.getExtras();
+            if (null == bundledExtras) {
+                mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+                        " not bundled intent extras");
+                return;
+            }
+
+            String cameraId = bundledExtras.getString(OFFLINE_CAMERA_ID);
+            if (null == cameraId) {
+                mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+                        " no camera id present in bundled extra");
+                return;
+            }
+
+            manager.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
+                @Override
+                public void onCameraAvailable(String cameraId) {
+                    super.onCameraAvailable(cameraId);
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_AVAILABLE,
+                            cameraId);
+                    Log.i(TAG, "Camera " + cameraId + " is available");
+                }
+
+                @Override
+                public void onCameraUnavailable(String cameraId) {
+                    super.onCameraUnavailable(cameraId);
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_UNAVAILABLE,
+                            cameraId);
+                    Log.i(TAG, "Camera " + cameraId + " is unavailable");
+                }
+
+                @Override
+                public void onPhysicalCameraAvailable(String cameraId, String physicalCameraId) {
+                    super.onPhysicalCameraAvailable(cameraId, physicalCameraId);
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_AVAILABLE,
+                            cameraId + " : " + physicalCameraId);
+                    Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is available");
+                }
+
+                @Override
+                public void onPhysicalCameraUnavailable(String cameraId, String physicalCameraId) {
+                    super.onPhysicalCameraUnavailable(cameraId, physicalCameraId);
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_UNAVAILABLE,
+                            cameraId + " : " + physicalCameraId);
+                    Log.i(TAG, "Camera " + cameraId + " : " + physicalCameraId + " is unavailable");
+                }
+            }, null);
+
+            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
+                @Override
+                public void onOpened(CameraDevice cameraDevice) {
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_CONNECT,
+                            cameraId);
+                    Log.i(TAG, "Camera " + cameraId + " is opened");
+                }
+
+                @Override
+                public void onDisconnected(CameraDevice cameraDevice) {
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_EVICTED,
+                            cameraId);
+                    Log.i(TAG, "Camera " + cameraId + " is disconnected");
+                }
+
+                @Override
+                public void onError(CameraDevice cameraDevice, int i) {
+                    mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+                            " Camera " + cameraId + " experienced error " + i);
+                    Log.e(TAG, "Camera " + cameraId + " onError called with error " + i);
+                }
+            }, null);
+        } catch (CameraAccessException e) {
+            mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
+                    " camera exception during connection: " + e);
+            Log.e(TAG, "Access exception: " + e);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(TAG, "onDestroy called.");
+        super.onDestroy();
+        if (mErrorServiceConnection != null) {
+            mErrorServiceConnection.stop();
+            mErrorServiceConnection = null;
+        }
+    }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
index a897944..e504803 100644
--- a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
@@ -24,8 +24,11 @@
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 import com.android.ex.camera2.blocking.BlockingOfflineSessionCallback;
 
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
 import android.graphics.ImageFormat;
-import android.hardware.Camera;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureFailure;
@@ -34,7 +37,13 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.multiprocess.camera.cts.ErrorLoggingService;
+import android.hardware.multiprocess.camera.cts.TestConstants;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
@@ -46,6 +55,7 @@
 import org.junit.Test;
 
 import java.lang.IllegalArgumentException;
+import java.util.concurrent.TimeoutException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -54,9 +64,30 @@
     private static final String TAG = "OfflineSessionTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private final String REMOTE_PROCESS_NAME = "camera2ActivityProcess";
+    private final java.lang.Class<?> REMOTE_PROCESS_CLASS = Camera2OfflineTestActivity.class;
 
+    private static final Size MANDATORY_STREAM_BOUND = new Size(1920, 1080);
     private static final int WAIT_FOR_FRAMES_TIMEOUT_MS = 3000;
     private static final int WAIT_FOR_STATE_TIMEOUT_MS = 5000;
+    private static final int WAIT_FOR_REMOTE_ACTIVITY_LAUNCH_MS = 2000;
+    private static final int WAIT_FOR_REMOTE_ACTIVITY_DESTROY_MS = 2000;
+
+    private enum OfflineTestSequence {
+        /** Regular offline switch without extra calls */
+        NoExtraSteps,
+
+        /** Offline switch followed by immediate offline session close */
+        CloseOfflineSession,
+
+        /** Offline switch followed in parallel with immediate device close and same device open
+         *  in a separate activity
+         */
+        CloseDeviceAndOpenRemote,
+
+        /** Offline session running in parallel with reinitialized regular capture session */
+        InitializeRegularSession,
+    }
 
     /**
      * Test offline switch behavior in case of invalid/bad input.
@@ -171,8 +202,8 @@
                 }
 
                 openDevice(mCameraIdsUnderTest[i]);
-                camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
-                        false /*closeDevice*/, false /*closeSession*/);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+                        ImageFormat.JPEG, OfflineTestSequence.NoExtraSteps);
             } finally {
                 closeDevice();
             }
@@ -213,8 +244,8 @@
                         mCameraIdsUnderTest[i], mCameraManager, ImageFormat.DEPTH_JPEG,
                         null /*bound*/);
                 openDevice(mCameraIdsUnderTest[i]);
-                camera2OfflineSessionTest(depthJpegSizes.get(0), ImageFormat.DEPTH_JPEG,
-                        false /*closeDevice*/, false /*closeSession*/);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], depthJpegSizes.get(0),
+                        ImageFormat.DEPTH_JPEG, OfflineTestSequence.NoExtraSteps);
             } finally {
                 closeDevice();
             }
@@ -254,8 +285,8 @@
                 List<Size> heicSizes = CameraTestUtils.getSupportedHeicSizes(
                         mCameraIdsUnderTest[i], mCameraManager, null /*bound*/);
                 openDevice(mCameraIdsUnderTest[i]);
-                camera2OfflineSessionTest(heicSizes.get(0), ImageFormat.HEIC,
-                        false /*closeDevice*/, false /*closeSession*/);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], heicSizes.get(0),
+                        ImageFormat.HEIC, OfflineTestSequence.NoExtraSteps);
             } finally {
                 closeDevice();
             }
@@ -263,13 +294,17 @@
     }
 
     /**
-     * Test camera offline session behavior during inactive camera device.
+     * Test camera offline session behavior after close and reopen.
      *
-     * <p> Verify that closing the initial camera device does not impact the
-     * offline camera session.</p>
+     * <p> Verify that closing the initial camera device and opening the same
+     * sensor during offline processing does not have any unexpected side effects.</p>
      */
     @Test
-    public void testDeviceClose() throws Exception {
+    public void testDeviceCloseAndOpen() throws Exception {
+        ErrorLoggingService.ErrorServiceConnection errorConnection =
+                new ErrorLoggingService.ErrorServiceConnection(mContext);
+
+        errorConnection.start();
         for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
                 Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
@@ -287,12 +322,26 @@
                 }
 
                 openDevice(mCameraIdsUnderTest[i]);
-                camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
-                        true /*closeDevice*/, false /*closeSession*/);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+                        ImageFormat.JPEG, OfflineTestSequence.CloseDeviceAndOpenRemote);
+
+                // Verify that the remote camera was opened correctly
+                List<ErrorLoggingService.LogEvent> allEvents = null;
+                try {
+                    allEvents = errorConnection.getLog(WAIT_FOR_STATE_TIMEOUT_MS,
+                            TestConstants.EVENT_CAMERA_CONNECT);
+                } catch (TimeoutException e) {
+                    fail("Timed out waiting on remote offline process error log!");
+                }
+                assertNotNull("Failed to connect to camera device in remote offline process!",
+                        allEvents);
             } finally {
                 closeDevice();
+
             }
         }
+
+        errorConnection.stop();
     }
 
     /**
@@ -320,8 +369,41 @@
                 }
 
                 openDevice(mCameraIdsUnderTest[i]);
-                camera2OfflineSessionTest(mOrderedStillSizes.get(0), ImageFormat.JPEG,
-                        false /*closeDevice*/, true /*closeSession*/);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+                        ImageFormat.JPEG, OfflineTestSequence.CloseOfflineSession);
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test camera offline session in case of new capture session
+     *
+     * <p>Verify that clients are able to initialize a new regular capture session
+     * in parallel with the offline session.</p>
+     */
+    @Test
+    public void testOfflineSessionWithRegularSession() throws Exception {
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            try {
+                Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
+
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isOfflineProcessingSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support offline processing, skipping");
+                    continue;
+                }
+
+                openDevice(mCameraIdsUnderTest[i]);
+                camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+                        ImageFormat.JPEG, OfflineTestSequence.InitializeRegularSession);
             } finally {
                 closeDevice();
             }
@@ -363,35 +445,38 @@
         }
     }
 
-    // Find the last frame number received in results and failures.
-    private long findLastFrameNumber(SimpleCaptureCallback captureListener) {
-        long lastFrameNumber = -1;
-        while (captureListener.hasMoreResults()) {
-            TotalCaptureResult result = captureListener.getTotalCaptureResult(0 /*timeout*/);
-            if (lastFrameNumber < result.getFrameNumber()) {
-                lastFrameNumber = result.getFrameNumber();
+    private void verifyCaptureResults(SimpleCaptureCallback resultListener,
+            SimpleImageReaderListener imageListener, int sequenceId, boolean offlineResults)
+            throws Exception {
+        long sequenceLastFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+                sequenceId, 0 /*timeoutMs*/);
+
+        long lastFrameNumberReceived = -1;
+        while (resultListener.hasMoreResults()) {
+            TotalCaptureResult result = resultListener.getTotalCaptureResult(0 /*timeout*/);
+            if (lastFrameNumberReceived < result.getFrameNumber()) {
+                lastFrameNumberReceived = result.getFrameNumber();
+            }
+
+            if (imageListener != null) {
+                long resultTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+                Image offlineImage = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                assertEquals("Offline image timestamp: " + offlineImage.getTimestamp() +
+                        " doesn't match with the result timestamp: " + resultTimestamp,
+                        offlineImage.getTimestamp(), resultTimestamp);
             }
         }
 
-        while (captureListener.hasMoreFailures()) {
-            ArrayList<CaptureFailure> failures = captureListener.getCaptureFailures(
+        while (resultListener.hasMoreFailures()) {
+            ArrayList<CaptureFailure> failures = resultListener.getCaptureFailures(
                     /*maxNumFailures*/ 1);
             for (CaptureFailure failure : failures) {
-                if (lastFrameNumber < failure.getFrameNumber()) {
-                    lastFrameNumber = failure.getFrameNumber();
+                if (lastFrameNumberReceived < failure.getFrameNumber()) {
+                    lastFrameNumberReceived = failure.getFrameNumber();
                 }
             }
         }
 
-        return lastFrameNumber;
-    }
-
-    private void verifyCaptureResults(SimpleCaptureCallback resultListener, int sequenceId,
-            boolean offlineResults) {
-        long sequenceLastFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
-                sequenceId, 0 /*timeoutMs*/);
-
-        long lastFrameNumberReceived = findLastFrameNumber(resultListener);
         String assertString = offlineResults ?
                 "Last offline frame number from " +
                 "onCaptureSequenceCompleted (%d) doesn't match the last frame number " +
@@ -403,16 +488,34 @@
                 sequenceLastFrameNumber, lastFrameNumberReceived);
     }
 
-    private void camera2OfflineSessionTest(Size offlineSize, int offlineFormat, boolean closeDevice,
-            boolean closeSession) throws Exception {
+    private void camera2OfflineSessionTest(String cameraId, Size offlineSize, int offlineFormat,
+            OfflineTestSequence testSequence) throws Exception {
+        int remoteOfflinePID = -1;
+        Size previewSize = mOrderedPreviewSizes.get(0);
+        for (Size sz : mOrderedPreviewSizes) {
+            if (sz.getWidth() <= MANDATORY_STREAM_BOUND.getWidth() && sz.getHeight() <=
+                    MANDATORY_STREAM_BOUND.getHeight()) {
+                previewSize = sz;
+                break;
+            }
+        }
+        Size privateSize = previewSize;
+        if (mAllStaticInfo.get(cameraId).isPrivateReprocessingSupported()) {
+            privateSize = mAllStaticInfo.get(cameraId).getSortedSizesForInputFormat(
+                    ImageFormat.PRIVATE, MANDATORY_STREAM_BOUND).get(0);
+        }
+
         CaptureRequest.Builder previewRequest =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
         CaptureRequest.Builder stillCaptureRequest =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
-        Size previewSize = mOrderedPreviewSizes.get(0);
         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+        SimpleCaptureCallback regularResultListener = new SimpleCaptureCallback();
         SimpleCaptureCallback offlineResultListener = new SimpleCaptureCallback();
         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        ImageReader privateReader = null;
+        ImageReader yuvCallbackReader = null;
+        ImageReader jpegReader = null;
 
         // Update preview size.
         updatePreviewSurface(previewSize);
@@ -441,18 +544,7 @@
         // Start preview.
         int repeatingSeqId = mSession.setRepeatingRequest(previewRequest.build(), resultListener,
                 mHandler);
-
-        CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
-
-        Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
-        assertNotNull("Can't read a capture result timestamp", timestamp);
-
-        CaptureResult result2 = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
-
-        Long timestamp2 = result2.get(CaptureResult.SENSOR_TIMESTAMP);
-        assertNotNull("Can't read a capture result 2 timestamp", timestamp2);
-
-        assertTrue("Bad timestamps", timestamp2 > timestamp);
+        checkInitialResults(resultListener);
 
         ArrayList<Integer> allowedOfflineStates = new ArrayList<Integer>();
         allowedOfflineStates.add(BlockingOfflineSessionCallback.STATE_READY);
@@ -487,45 +579,128 @@
             verify(mockOfflineCb, times(0)).onError(offlineSession,
                     CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
 
-            verifyCaptureResults(resultListener, repeatingSeqId, false /*offlineResults*/);
-            verifyCaptureResults(offlineResultListener, offlineSeqId, true /*offlineResults*/);
+            verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
+                    false /*offlineResults*/);
+            verifyCaptureResults(offlineResultListener, null /*imageListener*/, offlineSeqId,
+                    true /*offlineResults*/);
         } else {
             verify(mockOfflineCb, times(1)).onReady(offlineSession);
             verify(mockOfflineCb, times(0)).onSwitchFailed(offlineSession);
 
-            if (closeDevice) {
-                // According to specification, closing the initial camera device after
-                // successful offline session switch must not have any noticeable impact
-                // on the offline client.
-                closeDevice();
+            switch (testSequence) {
+                case CloseDeviceAndOpenRemote:
+                    // According to the documentation, closing the initial camera device and
+                    // re-opening the same device from a different client after successful
+                    // offline session switch must not have any noticeable impact on the
+                    // offline processing.
+                    closeDevice();
+                    remoteOfflinePID = startRemoteOfflineTestProcess(cameraId);
+
+                    break;
+                case CloseOfflineSession:
+                    offlineSession.close();
+
+                    break;
+                case InitializeRegularSession:
+                    // According to the documentation, initializing a regular capture session
+                    // along with the offline session should not have any side effects.
+                    // We also don't want to re-use the same offline output surface as part
+                    // of the new  regular capture session.
+                    outputSurfaces.remove(mReaderSurface);
+
+                    // According to the specification, an active offline session must allow
+                    // camera devices to support at least one preview stream, one yuv stream
+                    // of size up-to 1080p, one jpeg stream with any supported size and
+                    // an extra input/output private pair in case reprocessing is also available.
+                    yuvCallbackReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
+                            1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+                    outputSurfaces.add(yuvCallbackReader.getSurface());
+
+                    jpegReader = makeImageReader(offlineSize, ImageFormat.JPEG,
+                            1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+                    outputSurfaces.add(jpegReader.getSurface());
+
+                    if (mAllStaticInfo.get(cameraId).isPrivateReprocessingSupported()) {
+                        privateReader = makeImageReader(privateSize, ImageFormat.PRIVATE,
+                                1 /*maxNumImages*/, new SimpleImageReaderListener(), mHandler);
+                        outputSurfaces.add(privateReader.getSurface());
+
+                        InputConfiguration inputConfig = new InputConfiguration(
+                                privateSize.getWidth(), privateSize.getHeight(),
+                                ImageFormat.PRIVATE);
+                        mSession = CameraTestUtils.configureReprocessableCameraSession(mCamera,
+                                inputConfig, outputSurfaces, mSessionListener, mHandler);
+
+                    } else {
+                        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener,
+                                mHandler);
+                    }
+
+                    mSession.setRepeatingRequest(previewRequest.build(), regularResultListener,
+                            mHandler);
+
+                    break;
+                case NoExtraSteps:
+                default:
             }
 
-            if (closeSession) {
-                offlineSession.close();
-            }
+            // The repeating non-offline request should be done after the switch returns.
+            verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
+                    false /*offlineResults*/);
 
-            // The repeating non-offline request should be completed after the switch returns.
-            verifyCaptureResults(resultListener, repeatingSeqId, false /*offlineResults*/);
-
-            if (!closeSession) {
+            if (testSequence != OfflineTestSequence.CloseOfflineSession) {
                 offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_IDLE,
                         WAIT_FOR_STATE_TIMEOUT_MS);
                 verify(mockOfflineCb, times(1)).onIdle(offlineSession);
                 verify(mockOfflineCb, times(0)).onError(offlineSession,
                         CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
 
-                // The offline requests should be completed after we reach idle state.
-                verifyCaptureResults(offlineResultListener, offlineSeqId, true /*offlineResults*/);
+                // The offline requests should be done after we reach idle state.
+                verifyCaptureResults(offlineResultListener, imageListener, offlineSeqId,
+                        true /*offlineResults*/);
 
                 offlineSession.close();
-                offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_CLOSED,
-                        WAIT_FOR_STATE_TIMEOUT_MS);
             }
 
+            if (testSequence == OfflineTestSequence.InitializeRegularSession) {
+                checkInitialResults(regularResultListener);
+                stopPreview();
+
+                if (privateReader != null) {
+                    privateReader.close();
+                }
+
+                if (yuvCallbackReader != null) {
+                    yuvCallbackReader.close();
+                }
+
+                if (jpegReader != null) {
+                    jpegReader.close();
+                }
+            }
+
+            offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_CLOSED,
+                  WAIT_FOR_STATE_TIMEOUT_MS);
             verify(mockOfflineCb, times(1)).onClosed(offlineSession);
         }
 
         closeImageReader();
+
+        stopRemoteOfflineTestProcess(remoteOfflinePID);
+    }
+
+    private void checkInitialResults(SimpleCaptureCallback resultListener) {
+        CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
+
+        Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+        assertNotNull("Can't read a capture result timestamp", timestamp);
+
+        CaptureResult result2 = resultListener.getCaptureResult(WAIT_FOR_FRAMES_TIMEOUT_MS);
+
+        Long timestamp2 = result2.get(CaptureResult.SENSOR_TIMESTAMP);
+        assertNotNull("Can't read a capture result 2 timestamp", timestamp2);
+
+        assertTrue("Bad timestamps", timestamp2 > timestamp);
     }
 
     private void camera2UnsupportedOfflineOutputTest(boolean useSurfaceGroup) throws Exception {
@@ -574,4 +749,56 @@
 
         session.close();
     }
+
+    private int startRemoteOfflineTestProcess(String cameraId) throws InterruptedException {
+        // Ensure no running activity process with same name
+        String cameraActivityName = mContext.getPackageName() + ":" + REMOTE_PROCESS_NAME;
+        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(
+                Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningAppProcessInfo> list = activityManager.getRunningAppProcesses();
+        for (ActivityManager.RunningAppProcessInfo rai : list) {
+            if (cameraActivityName.equals(rai.processName)) {
+                fail("Remote offline session test activity already running");
+                return -1;
+            }
+        }
+
+        Activity activity = mActivityRule.getActivity();
+        Intent activityIntent = new Intent(activity, REMOTE_PROCESS_CLASS);
+        Bundle b = new Bundle();
+        b.putString(CameraTestUtils.OFFLINE_CAMERA_ID, cameraId);
+        activityIntent.putExtras(b);
+        activity.startActivity(activityIntent);
+        Thread.sleep(WAIT_FOR_REMOTE_ACTIVITY_LAUNCH_MS);
+
+        // Fail if activity isn't running
+        list = activityManager.getRunningAppProcesses();
+        for (ActivityManager.RunningAppProcessInfo rai : list) {
+            if (cameraActivityName.equals(rai.processName))
+                return rai.pid;
+        }
+
+        fail("Remote offline session test activity failed to start");
+
+        return -1;
+    }
+
+    private void stopRemoteOfflineTestProcess(int remotePID) throws InterruptedException {
+        if (remotePID < 0) {
+            return;
+        }
+
+        android.os.Process.killProcess(remotePID);
+        Thread.sleep(WAIT_FOR_REMOTE_ACTIVITY_DESTROY_MS);
+
+        ActivityManager activityManager = (ActivityManager) mContext.getSystemService(
+                Context.ACTIVITY_SERVICE);
+        String cameraActivityName = mContext.getPackageName() + ":" + REMOTE_PROCESS_NAME;
+        List<ActivityManager.RunningAppProcessInfo> list = activityManager.getRunningAppProcesses();
+        for (ActivityManager.RunningAppProcessInfo rai : list) {
+            if (cameraActivityName.equals(rai.processName))
+                fail("Remote offline session test activity is still running");
+        }
+
+    }
 }
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index f64ff08..da6593f 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -120,6 +120,8 @@
 
     public static final int MAX_READER_IMAGES = 5;
 
+    public static final String OFFLINE_CAMERA_ID = "offline_camera_id";
+
     private static final int EXIF_DATETIME_LENGTH = 19;
     private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
     private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f;
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index bcad582..a5bedea 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -2082,6 +2082,45 @@
     }
 
     /**
+     * Determine whether the current device supports a private reprocessing capability or not.
+     *
+     * @return {@code true} if the capability is supported, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if {@code capability} was negative
+     */
+    public boolean isPrivateReprocessingSupported() {
+        return isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
+    }
+
+    /**
+     * Get sorted (descending order) size list for given input format. Remove the sizes larger than
+     * the bound. If the bound is null, don't do the size bound filtering.
+     *
+     * @param format input format
+     * @param bound maximum allowed size bound
+     *
+     * @return Sorted input size list (descending order)
+     */
+    public List<Size> getSortedSizesForInputFormat(int format, Size bound) {
+        Size[] availableSizes = getAvailableSizesForFormatChecked(format, StreamDirection.Input);
+        if (bound == null) {
+            return CameraTestUtils.getAscendingOrderSizes(Arrays.asList(availableSizes),
+                    /*ascending*/false);
+        }
+
+        List<Size> sizes = new ArrayList<Size>();
+        for (Size sz: availableSizes) {
+            if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+                sizes.add(sz);
+            }
+        }
+
+        return CameraTestUtils.getAscendingOrderSizes(sizes, /*ascending*/false);
+    }
+
+
+    /**
      * Determine whether or not all the {@code keys} are available characteristics keys
      * (as in {@link CameraCharacteristics#getKeys}.
      *
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
index 2ff0d87..957062b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -73,7 +73,6 @@
  *     atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationTests
  */
 @Presubmit
-@FlakyTest(detail = "Promote once confirmed non-flaky")
 public class WindowInsetsAnimationTests extends WindowManagerTestBase {
 
     TestActivity mActivity;
@@ -117,6 +116,7 @@
     }
 
     @Test
+    @FlakyTest(detail = "Promote once confirmed non-flaky")
     public void testAnimationCallbacks_overlapping() {
         WindowInsets before = mActivity.mLastWindowInsets;
 
diff --git a/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index 5eb3e36..29f091b 100644
--- a/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -64,6 +64,9 @@
 
     // wait for Wi-Fi Aware state changes & network requests callbacks
     static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10; // 10 seconds
+    private static final int MIN_DISTANCE_MM = 1 * 1000;
+    private static final int MAX_DISTANCE_MM = 3 * 1000;
+    private static final byte[] PMK_VALID = "01234567890123456789012345678901".getBytes();
 
     private final Object mLock = new Object();
     private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
@@ -615,7 +618,8 @@
 
         // 2. update-subscribe
         subscribeConfig = new SubscribeConfig.Builder().setServiceName(
-                serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+                serviceName).setServiceSpecificInfo("extras".getBytes())
+                .setMinDistanceMm(MIN_DISTANCE_MM).build();
         discoverySession.updateSubscribe(subscribeConfig);
         assertTrue("Subscribe update", discoveryCb.waitForCallback(
                 DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
@@ -741,7 +745,8 @@
     }
 
     /**
-     * Request an Aware data-path (encrypted) as a Responder with an arbitrary peer MAC address.
+     * Request an Aware data-path (encrypted with Passphrase) as a Responder with an arbitrary peer
+     * MAC address.
      * Validate that receive an onUnavailable() callback.
      */
     public void testDataPathPassphraseOutOfBandFail() {
@@ -773,6 +778,40 @@
         session.close();
     }
 
+    /**
+     * Request an Aware data-path (encrypted with PMK) as a Responder with an arbitrary peer MAC
+     * address.
+     * Validate that receive an onUnavailable() callback.
+     */
+    public void testDataPathPmkOutOfBandFail() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+        MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
+
+        // 1. initialize Aware: only purpose is to make sure it is available for OOB data-path
+        WifiAwareSession session = attachAndGetSession();
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                "ValidName").build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+
+        // 2. request an AWARE network
+        NetworkCallbackTest networkCb = new NetworkCallbackTest();
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                session.createNetworkSpecifierPmk(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, mac.toByteArray(),
+                        PMK_VALID)).build();
+        mConnectivityManager.requestNetwork(nr, networkCb);
+        assertTrue("OnUnavailable not received", networkCb.waitForOnUnavailable());
+
+        session.close();
+    }
+
     // local utilities
 
     private WifiAwareSession attachAndGetSession() {
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 7dcf97b..da3cc11 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -727,7 +727,7 @@
     <!-- ====================================================================== -->
     <eat-comment />
 
-    <!-- @SystemApi Allows accessing the messages on ICC
+    <!-- Allows accessing the messages on ICC
          @hide Used internally. -->
     <permission android:name="android.permission.ACCESS_MESSAGES_ON_ICC"
         android:protectionLevel="signature" />
diff --git a/tests/tests/view/surfacevalidator/Android.mk b/tests/tests/view/surfacevalidator/Android.mk
index fbb6894..973d374 100644
--- a/tests/tests/view/surfacevalidator/Android.mk
+++ b/tests/tests/view/surfacevalidator/Android.mk
@@ -25,6 +25,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     androidx.test.rules \
+    cts-wm-util \
     ub-uiautomator
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 4d6bab5..51ef4a0 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -36,6 +36,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.provider.Settings;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
@@ -49,6 +50,8 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import android.server.wm.settings.SettingsSession;
+
 import androidx.test.InstrumentationRegistry;
 
 import org.junit.rules.TestName;
@@ -66,6 +69,15 @@
         public final SparseArray<Bitmap> failures = new SparseArray<>();
     }
 
+    private static class ImmersiveConfirmationSetting extends SettingsSession<String> {
+        ImmersiveConfirmationSetting() {
+            super(Settings.Secure.getUriFor(
+                Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS),
+                Settings.Secure::getString, Settings.Secure::putString);
+        }
+    }
+  private ImmersiveConfirmationSetting mSettingsSession;
+
     private static final String TAG = "CapturedActivity";
     private static final int PERMISSION_CODE = 1;
     private MediaProjectionManager mProjectionManager;
@@ -100,6 +112,9 @@
         // longer duration to capture the expected number of frames
         mOnEmbedded = packageManager.hasSystemFeature(PackageManager.FEATURE_EMBEDDED);
 
+        mSettingsSession = new ImmersiveConfirmationSetting();
+        mSettingsSession.set("confirmed");
+
         getWindow().getDecorView().setSystemUiVisibility(
                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
         // Set the NULL pointer icon so that it won't obstruct the captured image.
@@ -115,6 +130,11 @@
         bindMediaProjectionService();
     }
 
+    @Override
+    public void onStop() {
+        mSettingsSession.close();
+    }
+
     public void dismissPermissionDialog() {
         // The permission dialog will be auto-opened by the activity - find it and accept
         UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 47b120b..9bdb58f 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -22,6 +22,7 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWidgetTestCases.apk" />
+        <option name="test-file-name" value="CtsWidgetApp.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.widget.cts" />
diff --git a/tests/tests/widget/app/Android.bp b/tests/tests/widget/app/Android.bp
new file mode 100644
index 0000000..819e4a0
--- /dev/null
+++ b/tests/tests/widget/app/Android.bp
@@ -0,0 +1,30 @@
+// 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: "CtsWidgetApp",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "test_current",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
diff --git a/tests/tests/widget/app/AndroidManifest.xml b/tests/tests/widget/app/AndroidManifest.xml
new file mode 100755
index 0000000..9c19ce1
--- /dev/null
+++ b/tests/tests/widget/app/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.widget.cts.app">
+
+    <application>
+        <activity
+            android:name=".TranslucentActivity"
+            android:exported="true"
+            android:theme="@style/TranslucentTheme" />
+    </application>
+</manifest>
diff --git a/tests/tests/widget/app/res/values/styles.xml b/tests/tests/widget/app/res/values/styles.xml
new file mode 100644
index 0000000..a38f6b2
--- /dev/null
+++ b/tests/tests/widget/app/res/values/styles.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.
+  -->
+
+<resources>
+    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+    </style>
+</resources>
diff --git a/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java
new file mode 100644
index 0000000..c9b79b3
--- /dev/null
+++ b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java
@@ -0,0 +1,87 @@
+/*
+ * 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.widget.cts.app;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentManager;
+
+public class TranslucentActivity extends AppCompatActivity {
+    private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED =
+            "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED";
+    private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH =
+            "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        if (fragmentManager.findFragmentByTag("dialog") == null) {
+            DialogFragment fragment = new SampleFragment();
+            fragment.show(fragmentManager, "dialog");
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_TRANSLUCENT_ACTIVITY_FINISH);
+        registerReceiver(mFinishActivityReceiver, filter);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_RESUMED));
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        unregisterReceiver(mFinishActivityReceiver);
+    }
+
+    private final BroadcastReceiver mFinishActivityReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            finish();
+        }
+    };
+
+    public static class SampleFragment extends DialogFragment {
+        @NonNull
+        @Override
+        public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getActivity())
+                    .setTitle("Title")
+                    .setMessage("Message")
+                    .setOnDismissListener(dialog -> getActivity().finish())
+                    .create();
+        }
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index dc9f732..896eade 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -16,6 +16,8 @@
 
 package android.widget.cts;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -24,7 +26,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.ConditionVariable;
@@ -71,6 +77,13 @@
     private static final int ACCESSIBILITY_STATE_WAIT_TIMEOUT_MS = 3000;
     private static final long TIME_FOR_UI_OPERATION  = 1000L;
     private static final long TIME_OUT = 5000L;
+    private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED =
+            "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED";
+    private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH =
+            "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH";
+    private static final ComponentName COMPONENT_TRANSLUCENT_ACTIVITY =
+            ComponentName.unflattenFromString("android.widget.cts.app/.TranslucentActivity");
+
     private Toast mToast;
     private Context mContext;
     private boolean mLayoutDone;
@@ -650,6 +663,24 @@
         assertNotShowCustomToast(view);
     }
 
+    @Test
+    public void testCustomToastBlocked_whenBehindTranslucentActivity() throws Throwable {
+        ConditionVariable activityStarted = registerBlockingReceiver(
+                ACTION_TRANSLUCENT_ACTIVITY_RESUMED);
+        Intent intent = new Intent();
+        intent.setComponent(COMPONENT_TRANSLUCENT_ACTIVITY);
+        intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(intent);
+        activityStarted.block();
+        makeCustomToast();
+        View view = mToast.getView();
+
+        mActivityRule.runOnUiThread(mToast::show);
+
+        assertNotShowCustomToast(view);
+        mContext.sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_FINISH));
+    }
+
     @UiThreadTest
     @Test
     public void testGetWindowParams_whenTextToast_returnsNull() {
@@ -665,6 +696,18 @@
         assertNotNull(toast.getWindowParams());
     }
 
+    private ConditionVariable registerBlockingReceiver(String action) {
+        ConditionVariable broadcastReceived = new ConditionVariable(false);
+        IntentFilter filter = new IntentFilter(action);
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                broadcastReceived.open();
+            }
+        }, filter);
+        return broadcastReceived;
+    }
+
     private void runOnMainAndDrawSync(@NonNull final View toastView,
             @Nullable final Runnable runner) {
         final CountDownLatch latch = new CountDownLatch(1);