Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b7ac0ad3 authored by Jan Tomljanovic's avatar Jan Tomljanovic
Browse files

Add permission for avoiding toast rate limiting.

I've decided to use a new permission since it needs to be added to
the settings app, which doesn't have INTERNAL_SYSTEM_WINDOW permission,
so I didn't want to add it as it may enable it to do some other things
it's not supposed to do.

Bug: 182448615
Test: atest android.widget.cts.ToastTest as regression test
Test: atest NotificationManagerServiceTest
Change-Id: I6adc84856f1fe9c33e0ad8470e7248a20587a1e9
parent 04addcb1
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -3447,6 +3447,14 @@
    <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
        android:protectionLevel="signature" />

    <!-- Allows an application to avoid all toast rate limiting restrictions.
         <p>Not for use by third-party applications.
         @hide
    -->
    <permission android:name="android.permission.UNLIMITED_TOASTS"
                android:protectionLevel="signature" />
    <uses-permission android:name="android.permission.UNLIMITED_TOASTS" />

    <!-- @SystemApi Allows an application to use
         {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
         to hide non-system-overlay windows.
+3 −0
Original line number Diff line number Diff line
@@ -438,6 +438,9 @@
    <!-- Permission required for CTS test - ResourceObserverNativeTest -->
    <uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />

    <!-- Permission required for CTS test - android.widget.cts.ToastTest -->
    <uses-permission android:name="android.permission.UNLIMITED_TOASTS" />

    <application android:label="@string/app_label"
                android:theme="@android:style/Theme.DeviceDefault.DayNight"
                android:defaultToDeviceProtectedStorage="true"
+16 −2
Original line number Diff line number Diff line
@@ -7490,9 +7490,11 @@ public class NotificationManagerService extends SystemService {
            boolean rateLimitingEnabled =
                    !mToastRateLimitingDisabledUids.contains(record.uid);
            boolean isWithinQuota =
                    mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);
                    mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG)
                            || isExemptFromRateLimiting(record.pkg, userId);

            if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) {
            if (tryShowToast(
                    record, rateLimitingEnabled, isWithinQuota)) {
                scheduleDurationReachedLocked(record, lastToastWasTextRecord);
                mIsCurrentToastShown = true;
                if (rateLimitingEnabled) {
@@ -7526,6 +7528,18 @@ public class NotificationManagerService extends SystemService {
        return record.show();
    }

    private boolean isExemptFromRateLimiting(String pkg, int userId) {
        boolean isExemptFromRateLimiting = false;
        try {
            isExemptFromRateLimiting = mPackageManager.checkPermission(
                    android.Manifest.permission.UNLIMITED_TOASTS, pkg, userId)
                    == PackageManager.PERMISSION_GRANTED;
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to connect with package manager");
        }
        return isExemptFromRateLimiting;
    }

    /** Reports rate limiting toasts compat change (used when the toast was blocked). */
    private void reportCompatRateLimitingToastsChange(int uid) {
        final long id = Binder.clearCallingIdentity();
+68 −2
Original line number Diff line number Diff line
@@ -4910,6 +4910,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4933,6 +4934,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4952,6 +4954,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4974,11 +4977,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    }

    @Test
    public void testToastRateLimiterCanPreventsShowCallForCustomToast() throws Exception {
    public void testToastRateLimiterCanPreventShowCallForCustomToast() throws Exception {
        final String testPackage = "testPackageName";
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(false); // rate limit reached
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4994,6 +4998,29 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        verify(callback, times(0)).show(any());
    }

    @Test
    public void testCustomToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
        final String testPackage = "testPackageName";
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(false); // rate limit reached
        // Avoids rate limiting.
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
                .thenReturn(false);

        setAppInForegroundForToasts(mUid, true);

        Binder token = new Binder();
        ITransientNotification callback = mock(ITransientNotification.class);
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueToast(testPackage, token, callback, 2000, 0);
        verify(callback).show(any());
    }

    @Test
    public void testCustomToastPostedWhileInForeground_blockedIfAppGoesToBackground()
            throws Exception {
@@ -5001,6 +5028,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5034,6 +5062,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5053,6 +5082,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5072,6 +5102,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5095,11 +5126,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    }

    @Test
    public void testToastRateLimiterCanPreventsShowCallForTextToast() throws Exception {
    public void testToastRateLimiterCanPreventShowCallForTextToast() throws Exception {
        final String testPackage = "testPackageName";
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(false); // rate limit reached
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5113,6 +5145,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
    }

    @Test
    public void testTextToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
        final String testPackage = "testPackageName";
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(false); // rate limit reached
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
                .thenReturn(false);

        Binder token = new Binder();
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
    }

    @Test
    public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
            Exception {
@@ -5120,6 +5171,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = true;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5145,6 +5197,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5166,6 +5219,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5186,6 +5240,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5203,6 +5258,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5224,6 +5280,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5247,6 +5304,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = true;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5270,6 +5328,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);

        // package is not suspended
        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5305,6 +5364,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                .thenReturn(isWithinQuota);
    }

    private void setIfPackageHasPermissionToAvoidToastRateLimiting(
            String pkg, boolean hasPermission) throws Exception {
        when(mPackageManager.checkPermission(android.Manifest.permission.UNLIMITED_TOASTS,
                pkg, UserHandle.getUserId(mUid)))
                .thenReturn(hasPermission ? PERMISSION_GRANTED : PERMISSION_DENIED);
    }

    @Test
    public void testOnPanelRevealedAndHidden() {
        int items = 5;