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

Commit 0d4546d1 authored by Felipe Leme's avatar Felipe Leme
Browse files

Fixes Toast.show() for visible background users.

When its caller is a visible background user using a non-ui context
(like the application context), the Toast must be displayed in the
display the user was started visible on.

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

Test: atest NotificationManagerServiceTest

Fixes: 273314058
Bug: 272376728

Change-Id: I0a28d20462900eb0437781ab26d520be824014d1
parent f6669089
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -47,8 +47,8 @@ interface INotificationManager
    void cancelAllNotifications(String pkg, int userId);

    void clearData(String pkg, int uid, boolean fromApp);
    void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
    void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
    void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, boolean isUiContext, int displayId, @nullable ITransientNotificationCallback callback);
    void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, boolean isUiContext, int displayId);
    void cancelToast(String pkg, IBinder token);
    void finishToken(String pkg, IBinder token);

+5 −3
Original line number Diff line number Diff line
@@ -206,21 +206,23 @@ public class Toast {
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;
        final boolean isUiContext = mContext.isUiContext();
        final int displayId = mContext.getDisplayId();

        try {
            if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
                if (mNextView != null) {
                    // It's a custom toast
                    service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
                    service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
                } else {
                    // It's a text toast
                    ITransientNotificationCallback callback =
                            new CallbackBinder(mCallbacks, mHandler);
                    service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
                    service.enqueueTextToast(pkg, mToken, mText, mDuration, isUiContext, displayId,
                            callback);
                }
            } else {
                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
                service.enqueueToast(pkg, mToken, tn, mDuration, isUiContext, displayId);
            }
        } catch (RemoteException e) {
            // Empty
+27 −6
Original line number Diff line number Diff line
@@ -265,6 +265,7 @@ import android.util.SparseBooleanArray;
import android.util.StatsEvent;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.RemoteViews;
@@ -316,6 +317,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.Slogf;
import com.android.server.utils.quota.MultiRateLimiter;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.BackgroundActivityStartCallback;
@@ -3288,19 +3290,22 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
                int displayId, @Nullable ITransientNotificationCallback callback) {
            enqueueToast(pkg, token, text, null, duration, displayId, callback);
                boolean isUiContext, int displayId,
                @Nullable ITransientNotificationCallback textCallback) {
            enqueueToast(pkg, token, text, /* callback= */ null, duration, isUiContext, displayId,
                    textCallback);
        }
        @Override
        public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
                int duration, int displayId) {
            enqueueToast(pkg, token, null, callback, duration, displayId, null);
                int duration, boolean isUiContext, int displayId) {
            enqueueToast(pkg, token, /* text= */ null, callback, duration, isUiContext, displayId,
                    /* textCallback= */ null);
        }
        private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
                @Nullable ITransientNotification callback, int duration, int displayId,
                @Nullable ITransientNotificationCallback textCallback) {
                @Nullable ITransientNotification callback, int duration, boolean isUiContext,
                int displayId, @Nullable ITransientNotificationCallback textCallback) {
            if (DBG) {
                Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
                        + " duration=" + duration + " displayId=" + displayId);
@@ -3322,6 +3327,22 @@ public class NotificationManagerService extends SystemService {
                return;
            }
            if (!isUiContext && displayId == Display.DEFAULT_DISPLAY
                    && UserManager.isVisibleBackgroundUsersEnabled()) {
                // When the caller is a visible background user using a non-ui context (like the
                // application context), the Toast must be displayed in the display the user was
                // started visible on
                int userId = UserHandle.getUserId(callingUid);
                int userDisplayId = mUmInternal.getMainDisplayAssignedToUser(userId);
                if (displayId != userDisplayId) {
                    if (DBG) {
                        Slogf.d(TAG, "Changing display id from %d to %d on user %d", displayId,
                                userDisplayId, userId);
                    }
                    displayId = userDisplayId;
                }
            }
            synchronized (mToastQueue) {
                int callingPid = Binder.getCallingPid();
                final long callingId = Binder.clearCallingIdentity();
+42 −41
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import static android.service.notification.NotificationListenerService.FLAG_FILT
import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;

import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
@@ -278,7 +279,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
    private static final String PKG_NO_CHANNELS = "com.example.no.channels";
    private static final int TEST_TASK_ID = 1;
    private static final int UID_HEADLESS = 1000000;
    private static final int UID_HEADLESS = 1_000_000;
    private static final int TOAST_DURATION = 2_000;

    private final int mUid = Binder.getCallingUid();
    private TestableNotificationManagerService mService;
@@ -6539,8 +6541,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, true);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(1, mService.mToastQueue.size());
    }

@@ -6559,8 +6560,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> no toasts enqueued
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(0, mService.mToastQueue.size());
    }

@@ -6583,12 +6583,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        INotificationManager nmService = (INotificationManager) mService.mService;

        // first time trying to show the toast, showToast gets called
        nmService.enqueueToast(testPackage, token, callback, 2000, 0);
        enqueueToast(nmService, testPackage, token, callback);
        verify(callback, times(1)).show(any());

        // second time trying to show the same toast, showToast isn't called again (total number of
        // invocations stays at one)
        nmService.enqueueToast(testPackage, token, callback, 2000, 0);
        enqueueToast(nmService, testPackage, token, callback);
        verify(callback, times(1)).show(any());
    }

@@ -6611,7 +6611,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        ITransientNotification callback = mock(ITransientNotification.class);
        INotificationManager nmService = (INotificationManager) mService.mService;

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

@@ -6636,8 +6636,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        ITransientNotification callback2 = mock(ITransientNotification.class);
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueToast(testPackage, token1, callback1, 2000, 0);
        nmService.enqueueToast(testPackage, token2, callback2, 2000, 0);
        enqueueToast(nmService, testPackage, token1, callback1);
        enqueueToast(nmService, testPackage, token2, callback2);

        assertEquals(2, mService.mToastQueue.size()); // Both toasts enqueued.
        verify(callback1, times(1)).show(any()); // First toast shown.
@@ -6665,8 +6665,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, true);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        assertEquals(1, mService.mToastQueue.size());
    }

@@ -6685,8 +6684,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        assertEquals(1, mService.mToastQueue.size());
    }

@@ -6708,13 +6706,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        INotificationManager nmService = (INotificationManager) mService.mService;

        // first time trying to show the toast, showToast gets called
        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar, times(1))
                .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(), anyInt());

        // second time trying to show the same toast, showToast isn't called again (total number of
        // invocations stays at one)
        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar, times(1))
                .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(), anyInt());
    }
@@ -6736,7 +6734,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        Binder token = new Binder();
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar, times(0))
                .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(), anyInt());
    }
@@ -6758,7 +6756,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        Binder token = new Binder();
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar, times(1))
                .showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(), anyInt());
    }
@@ -6779,7 +6777,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        Binder token = new Binder();
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
                anyInt());
    }
@@ -6800,7 +6798,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        Binder token = new Binder();
        INotificationManager nmService = (INotificationManager) mService.mService;

        nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");

        // window token was added when enqueued
        ArgumentCaptor<Binder> binderCaptor =
@@ -6836,8 +6834,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(1, mService.mToastQueue.size());
        verify(mAm).setProcessImportant(any(), anyInt(), eq(true), any());
    }
@@ -6858,8 +6855,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, true);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        assertEquals(1, mService.mToastQueue.size());
        verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
    }
@@ -6880,8 +6876,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> toast should still enqueue
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        assertEquals(1, mService.mToastQueue.size());
        verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any());
    }
@@ -6899,8 +6894,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                .thenReturn(false);

        // enqueue toast -> no toasts enqueued
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                "Text", 2000, 0, null);
        enqueueTextToast(testPackage, "Text");
        verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any(),
                anyInt());
    }
@@ -6921,8 +6915,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);

        // enqueue toast -> no toasts enqueued
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(0, mService.mToastQueue.size());
    }

@@ -6944,8 +6937,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> no toasts enqueued
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(0, mService.mToastQueue.size());
    }

@@ -6967,8 +6959,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        setAppInForegroundForToasts(mUid, false);

        // enqueue toast -> system toast can still be enqueued
        ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
                new TestableToastCallback(), 2000, 0);
        enqueueToast(testPackage, new TestableToastCallback());
        assertEquals(1, mService.mToastQueue.size());
    }

@@ -6988,13 +6979,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

        // Trying to quickly enqueue more toast than allowed.
        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_TOASTS + 1; i++) {
            nmService.enqueueTextToast(
                    testPackage,
                    new Binder(),
                    "Text",
                    /* duration */ 2000,
                    /* displayId */ 0,
                    /* callback */ null);
            enqueueTextToast(testPackage, "Text");
        }
        // Only allowed number enqueued, rest ignored.
        assertEquals(NotificationManagerService.MAX_PACKAGE_TOASTS, mService.mToastQueue.size());
@@ -10718,4 +10703,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                String.valueOf(isOn),
                /* makeDefault= */ false);
    }

    private void enqueueToast(String testPackage, ITransientNotification callback)
            throws RemoteException {
        enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(), callback);
    }

    private void enqueueToast(INotificationManager service, String testPackage,
            IBinder token, ITransientNotification callback) throws RemoteException {
        service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */ true,
                DEFAULT_DISPLAY);
    }

    private void enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
        ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), text,
                TOAST_DURATION, /* isUiContext= */ true, DEFAULT_DISPLAY, /* textCallback= */ null);
    }
}