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

Commit 270b766d authored by Felipe Leme's avatar Felipe Leme
Browse files

Fix NMS.enqueueTextToast() for user visibility.

When a visible background user toasts, the toast should be displayed
on the display associated to the user, but currently its shown in the
default display.

This change partially fixes that by blocking the toast - the full fix
(i.e., showing the toast in the right display) will come next...

Test: atest NotificationManagerServiceTest SnoozeHelperTest
Test: atest --user-type secondary_user_on_secondary_display android.widget.cts ToastTest#testShow_whenTextToast,testShow_appContext_whenTextToast
Test: atest android.widget.cts ToastTest#testShow_whenTextToast,testShow_appContext_whenTextToast # to make sure the "normal" case still works

Bug: 273314058

Change-Id: If7c789500460eff822946d24ba093e3b9c7a435c
parent fc10ca01
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -3323,7 +3323,8 @@ public class NotificationManagerService extends SystemService {
            final boolean isSystemToast = isCallerSystemOrPhone()
                    || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
            boolean isAppRenderedToast = (callback != null);
            if (!checkCanEnqueueToast(pkg, callingUid, isAppRenderedToast, isSystemToast)) {
            if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
                    isSystemToast)) {
                return;
            }
@@ -3393,7 +3394,7 @@ public class NotificationManagerService extends SystemService {
            }
        }
        private boolean checkCanEnqueueToast(String pkg, int callingUid,
        private boolean checkCanEnqueueToast(String pkg, int callingUid, int displayId,
                boolean isAppRenderedToast, boolean isSystemToast) {
            final boolean isPackageSuspended = isPackagePaused(pkg);
            final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
@@ -3423,6 +3424,13 @@ public class NotificationManagerService extends SystemService {
                return false;
            }
            int userId = UserHandle.getUserId(callingUid);
            if (!isSystemToast && !mUmInternal.isUserVisible(userId, displayId)) {
                Slog.e(TAG, "Suppressing toast from package " + pkg + "/" + callingUid + " as user "
                        + userId + " is not visible on display " + displayId);
                return false;
            }
            return true;
        }
+30 −0
Original line number Diff line number Diff line
@@ -611,6 +611,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
                anyString(), anyInt(), any())).thenReturn(true);
        when(mUserManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true);
        mockIsUserVisible(DEFAULT_DISPLAY, true);
        mockIsVisibleBackgroundUsersSupported(false);

        // Set the testable bubble extractor
@@ -6913,6 +6914,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testTextToastsCallStatusBar_nonUiContext_secondaryDisplay()
            throws Exception {
        allowTestPackageToToast();
        mockIsUserVisible(SECONDARY_DISPLAY_ID, true);

        enqueueTextToast(TEST_PACKAGE, "Text", /* isUiContext= */ false, SECONDARY_DISPLAY_ID);

@@ -6936,6 +6938,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testTextToastsCallStatusBar_visibleBgUsers_uiContext_secondaryDisplay()
            throws Exception {
        mockIsVisibleBackgroundUsersSupported(true);
        mockIsUserVisible(SECONDARY_DISPLAY_ID, true);
        mockDisplayAssignedToUser(INVALID_DISPLAY); // make sure it's not used
        allowTestPackageToToast();

@@ -6960,6 +6963,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testTextToastsCallStatusBar_visibleBgUsers_nonUiContext_secondaryDisplay()
            throws Exception {
        mockIsVisibleBackgroundUsersSupported(true);
        mockIsUserVisible(SECONDARY_DISPLAY_ID, true);
        mockDisplayAssignedToUser(INVALID_DISPLAY); // make sure it's not used
        allowTestPackageToToast();

@@ -6968,6 +6972,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        verifyToastShownForTestPackage("Text", SECONDARY_DISPLAY_ID);
    }

    @Test
    public void testTextToastsCallStatusBar_userNotVisibleOnDisplay() throws Exception {
        final String testPackage = "testPackageName";
        assertEquals(0, mService.mToastQueue.size());
        mService.isSystemUid = false;
        setToastRateIsWithinQuota(true);
        setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
        mockIsUserVisible(DEFAULT_DISPLAY, false);

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

        // enqueue toast -> no toasts enqueued
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
                anyInt());
        assertEquals(0, mService.mToastQueue.size());
    }

    @Test
    public void testDisallowToastsFromSuspendedPackages() throws Exception {
        final String testPackage = "testPackageName";
@@ -6985,6 +7009,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

        // enqueue toast -> no toasts enqueued
        enqueueToast(testPackage, new TestableToastCallback());
        verify(mStatusBar, never()).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
                anyInt());
        assertEquals(0, mService.mToastQueue.size());
    }

@@ -10808,6 +10834,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mUm.isVisibleBackgroundUsersSupported()).thenReturn(supported);
    }

    private void mockIsUserVisible(int displayId, boolean visible) {
        when(mUmInternal.isUserVisible(mUserId, displayId)).thenReturn(visible);
    }

    private void mockDisplayAssignedToUser(int displayId) {
        when(mUmInternal.getMainDisplayAssignedToUser(mUserId)).thenReturn(displayId);
    }