Apply delivery group policies to CLOSE_SYSTEM_DIALOG broadcast.

- The "set-defer-until-active" policy is applied so that the
broadcast targeted to apps in the Cached state is deferred until
they come out of that state.
- The "deliver-most-recent" policy is applied so that if there are
already pending broadcasts waiting to be delivered when a new
broadcast is sent, the old ones are discarded.

Bug: 271950524
Test: TH
Change-Id: Iab6064cf1436e02dd3d46badeeb6ab8dd6198c69
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0cd42a3..82f4315 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3619,6 +3619,17 @@
         scheduleFinalCleanup(getClass().getName(), getOuterContext().getClass().getSimpleName());
     }
 
+    @Override
+    public void closeSystemDialogs() {
+        final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        final Bundle options = BroadcastOptions.makeBasic()
+                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .toBundle();
+        sendBroadcast(intent, null /* receiverPermission */, options);
+    }
+
     // ----------------------------------------------------------------------
     // ----------------------------------------------------------------------
     // ----------------------------------------------------------------------
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fc75323..3b2ea78 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7873,4 +7873,15 @@
     public boolean isConfigurationContext() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
+
+    /**
+     * Closes temporary system dialogs. Some examples of temporary system dialogs are the
+     * notification window-shade and the recent tasks dialog.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS)
+    public void closeSystemDialogs() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
 }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 21de5cf..4327c7a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1484,4 +1484,15 @@
         // Do nothing if the callback hasn't been registered to Application Context by
         // super.unregisterComponentCallbacks() for Application that is targeting prior to T.
     }
+
+    /**
+     * Closes temporary system dialogs. Some examples of temporary system dialogs are the
+     * notification window-shade and the recent tasks dialog.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS)
+    public void closeSystemDialogs() {
+        mBase.closeSystemDialogs();
+    }
 }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 5562684..2c4b478 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -422,7 +422,7 @@
             Log.e(TAG, "Failed to disable DynamicSystem.");
 
             // Dismiss status bar and show a toast.
-            sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+            closeSystemDialogs();
             Toast.makeText(this,
                     getString(R.string.toast_failed_to_disable_dynsystem),
                     Toast.LENGTH_LONG).show();
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 6f7d20a..067efe9 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1697,7 +1697,7 @@
     }
 
     private void collapseNotificationBar() {
-        sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+        closeSystemDialogs();
     }
 
     private static Looper newLooper(String name) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt
index 6615f6b..f9613d50 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt
@@ -109,7 +109,7 @@
     @AnyThread
     fun closeSystemDialogs() {
         sendInBackground {
-            context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+            context.closeSystemDialogs()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 7a42642..c2c1306 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -772,9 +772,7 @@
                         mSaverConfirmation.dismiss();
                     }
                     // Also close the notification shade, if it's open.
-                    mBroadcastSender.sendBroadcast(
-                            new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
-                                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND));
+                    mBroadcastSender.closeSystemDialogs();
 
                     final Uri uri = Uri.parse(getURL());
                     Context context = widget.getContext();
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 0477626..4349bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -205,7 +205,7 @@
                 }, false, false);
 
                 // Close quick shade
-                sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+                closeSystemDialogs();
                 break;
         }
         return Service.START_STICKY;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 7ad594e..25399ec 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -959,7 +959,7 @@
                                 transitionDestination, onTransitionEnd,
                                 longScreenshot);
                         // TODO: Do this via ActionIntentExecutor instead.
-                        mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+                        mContext.closeSystemDialogs();
                     }
             );
 
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
index 7a31fa5..f9f14e0 100644
--- a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
@@ -97,7 +97,7 @@
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         registerReceiver(mWifiChangeReceiver, filter);
         // Close quick shade
-        sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+        closeSystemDialogs();
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
index fbd2c91..8e81727 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
@@ -30,7 +30,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -126,13 +125,10 @@
 
     @Test
     fun sendCloseSystemDialogs_dispatchesWithWakelock() {
-        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
-
         broadcastSender.closeSystemDialogs()
 
         runExecutorAssertingWakelock {
-            verify(mockContext).sendBroadcast(intentCaptor.capture())
-            assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+            verify(mockContext).closeSystemDialogs()
         }
     }
 
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 20ff51c..8c0e19d 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -63,6 +63,10 @@
     // Time to allow the dream to perform an exit transition when waking up.
     private static final int DREAM_FINISH_TIMEOUT = 5 * 1000;
 
+    // Extras used with ACTION_CLOSE_SYSTEM_DIALOGS broadcast
+    private static final String EXTRA_REASON_KEY = "reason";
+    private static final String EXTRA_REASON_VALUE = "dream";
+
     private final Context mContext;
     private final Handler mHandler;
     private final Listener mListener;
@@ -77,6 +81,7 @@
     private final Bundle mDreamingStartedStoppedOptions = createDreamingStartedStoppedOptions();
 
     private final Intent mCloseNotificationShadeIntent;
+    private final Bundle mCloseNotificationShadeOptions;
 
     private DreamRecord mCurrentDream;
 
@@ -96,7 +101,14 @@
         mListener = listener;
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
         mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        mCloseNotificationShadeIntent.putExtra("reason", "dream");
+        mCloseNotificationShadeIntent.putExtra(EXTRA_REASON_KEY, EXTRA_REASON_VALUE);
+        mCloseNotificationShadeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mCloseNotificationShadeOptions = BroadcastOptions.makeBasic()
+                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                .setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
+                        EXTRA_REASON_VALUE)
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .toBundle();
     }
 
     /**
@@ -149,7 +161,8 @@
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
         try {
             // Close the notification shade. No need to send to all, but better to be explicit.
-            mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL);
+            mContext.sendBroadcastAsUser(mCloseNotificationShadeIntent, UserHandle.ALL,
+                    null /* receiverPermission */, mCloseNotificationShadeOptions);
 
             Slog.i(TAG, "Starting dream: name=" + name
                     + ", isPreviewMode=" + isPreviewMode + ", canDoze=" + canDoze