Switch to controling if toast rate limiting is enabled using TestApi.

We tried to use compat changeid to turn rate limiting on/off in CTS
tests but it doesn't work on user builds (see bug). We're therefore
migrating to using a new TestApi guarded by a new permission that we
give to the shell.

Test: atest android.widget.cts.ToastTest
Bug: 175720818
Change-Id: I2ef147ed6449333fc6b4273eca82bce533cb13f3
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4ef24ff..a4d3aa7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -18,6 +18,7 @@
     field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
     field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+    field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
     field public static final String MODIFY_REFRESH_RATE_SWITCHING_TYPE = "android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE";
     field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -275,6 +276,7 @@
     method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
     method public boolean matchesCallFilter(android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING) public void setToastRateLimitingEnabled(boolean);
     method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel);
   }
 
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 697a377..bda2fa9 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -228,4 +228,6 @@
 
     NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
     void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
+
+    void setToastRateLimitingEnabled(boolean enable);
 }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 58f382d..6cce270 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1730,6 +1730,23 @@
     }
 
     /**
+     * Controls whether toast rate limiting is enabled for the calling uid.
+     *
+     * @param enable true to enable toast rate limiting, false to disable it
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING)
+    public void setToastRateLimitingEnabled(boolean enable) {
+        INotificationManager service = getService();
+        try {
+            service.setToastRateLimitingEnabled(enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Notification policy configuration.  Represents user-preferences for notification
      * filtering.
      */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8682fea..3053518 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5317,6 +5317,12 @@
     <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
+                android:protectionLevel="signature" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index f83f670..e572aa4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -378,6 +378,9 @@
     <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
     <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
 
+    <!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
+    <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c106558..3751c9b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -445,16 +445,6 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
     private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
 
-    /**
-     * Rate limit showing toasts, on a per package basis.
-     *
-     * It limits the effects of {@link android.widget.Toast#show()} calls to prevent overburdening
-     * the user with too many toasts in a limited time. Any attempt to show more toasts than allowed
-     * in a certain time frame will result in the toast being discarded.
-     */
-    @ChangeId
-    private static final long RATE_LIMIT_TOASTS = 154198299L;
-
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
@@ -526,6 +516,9 @@
     @GuardedBy("mNotificationLock")
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
+    // set of uids for which toast rate limiting is disabled
+    @GuardedBy("mToastQueue")
+    private final Set<Integer> mToastRateLimitingDisabledUids = new ArraySet<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
 
     // True if the toast that's on top of the queue is being shown at the moment.
@@ -3067,6 +3060,22 @@
         }
 
         @Override
+        public void setToastRateLimitingEnabled(boolean enable) {
+            getContext().enforceCallingPermission(
+                    android.Manifest.permission.MANAGE_TOAST_RATE_LIMITING,
+                    "App doesn't have the permission to enable/disable toast rate limiting");
+
+            synchronized (mToastQueue) {
+                int uid = Binder.getCallingUid();
+                if (enable) {
+                    mToastRateLimitingDisabledUids.remove(uid);
+                } else {
+                    mToastRateLimitingDisabledUids.add(uid);
+                }
+            }
+        }
+
+        @Override
         public void finishToken(String pkg, IBinder token) {
             synchronized (mToastQueue) {
                 final long callingId = Binder.clearCallingIdentity();
@@ -7377,7 +7386,7 @@
         while (record != null) {
             int userId = UserHandle.getUserId(record.uid);
             boolean rateLimitingEnabled =
-                    CompatChanges.isChangeEnabled(RATE_LIMIT_TOASTS, record.uid);
+                    !mToastRateLimitingDisabledUids.contains(record.uid);
             boolean isWithinQuota =
                     mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);