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

Commit 4de5d6dc authored by Julia Reynolds's avatar Julia Reynolds
Browse files

DO NOT MERGE FLAG_FOREGROUND_SERVICE requires a running FGS

Bug: 269738057
Test: NotificationManagerServiceTest
Test: NotificationManagerTest
Test: ServiceTest
Change-Id: I11f5d3f19cb38cfb7e37489cbf029f7b2692f67c
Merged-In: d88e3522
(cherry picked from commit d88e3522)
parent 52199b39
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -6969,8 +6969,10 @@ public class Notification implements Parcelable
    /**
     * Returns whether an app can colorize due to the android.permission.USE_COLORIZED_NOTIFICATIONS
     * permission. The permission is checked when a notification is enqueued.
     *
     * @hide
     */
    private boolean hasColorizedPermission() {
    public boolean hasColorizedPermission() {
        return (flags & Notification.FLAG_CAN_COLORIZE) != 0;
    }
+20 −11
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
@@ -6502,9 +6503,17 @@ public class NotificationManagerService extends SystemService {
        checkRestrictedCategories(notification);
        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
        // but it's also possible that the app has called notify() with an update to an
        // FGS notification that hasn't yet been displayed.  Make sure we check for any
        // FGS-related situation up front, outside of any locks so it's safe to call into
        // the Activity Manager.
        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
                notification, tag, id, pkg, userId);
        // Fix the notification as best we can.
        try {
            fixNotification(notification, pkg, tag, id, userId);
            fixNotification(notification, pkg, tag, id, userId, notificationUid, policy);
        } catch (Exception e) {
            if (notification.isForegroundService()) {
                throw new SecurityException("Invalid FGS notification", e);
@@ -6513,13 +6522,7 @@ public class NotificationManagerService extends SystemService {
            return;
        }
        // Notifications passed to setForegroundService() have FLAG_FOREGROUND_SERVICE,
        // but it's also possible that the app has called notify() with an update to an
        // FGS notification that hasn't yet been displayed.  Make sure we check for any
        // FGS-related situation up front, outside of any locks so it's safe to call into
        // the Activity Manager.
        final ServiceNotificationPolicy policy = mAmi.applyForegroundServiceNotification(
                notification, tag, id, pkg, userId);
        if (policy == ServiceNotificationPolicy.UPDATE_ONLY) {
            // Proceed if the notification is already showing/known, otherwise ignore
            // because the service lifecycle logic has retained responsibility for its
@@ -6682,14 +6685,20 @@ public class NotificationManagerService extends SystemService {
    @VisibleForTesting
    protected void fixNotification(Notification notification, String pkg, String tag, int id,
            int userId) throws NameNotFoundException, RemoteException {
            @UserIdInt int userId, int notificationUid, ServiceNotificationPolicy fgsPolicy)
            throws NameNotFoundException, RemoteException {
        final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
        Notification.addFieldsFromContext(ai, notification);
        int canColorize = mPackageManagerClient.checkPermission(
                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
        if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) {
            notification.flags &= ~FLAG_FOREGROUND_SERVICE;
        }
        int canColorize = getContext().checkPermission(
                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, -1, notificationUid);
        if (canColorize == PERMISSION_GRANTED) {
            notification.flags |= Notification.FLAG_CAN_COLORIZE;
        } else {
+121 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.notification;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
@@ -1160,6 +1162,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testEnqueuedBlockedNotifications_appBlockedChannelForegroundService()
            throws Exception {
        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);

        NotificationChannel channel = new NotificationChannel("blocked", "name",
                NotificationManager.IMPORTANCE_NONE);
@@ -1182,6 +1186,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testEnqueuedBlockedNotifications_userBlockedChannelForegroundService()
            throws Exception {
        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);

        NotificationChannel channel =
                new NotificationChannel("blockedbyuser", "name", IMPORTANCE_HIGH);
@@ -1261,6 +1267,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);

        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -1590,6 +1598,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
        mContext.getTestablePermissions().setPermission(
                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
        DeviceConfig.setProperty(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -1618,6 +1630,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
        mContext.getTestablePermissions().setPermission(
                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
        DeviceConfig.setProperty(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -1904,6 +1920,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
        mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1917,8 +1935,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertEquals(1, mService.getNotificationRecordCount());
    }

    @Test
    public void testCancelAllNotifications_FgsFlag_NoFgs_Allowed() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(NOT_FOREGROUND_SERVICE);
        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
        mBinderService.enqueueNotificationWithTag(PKG, PKG,
                "testCancelAllNotifications_IgnoreForegroundService",
                sbn.getId(), sbn.getNotification(), sbn.getUserId());
        mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
        waitForIdle();
        StatusBarNotification[] notifs =
                mBinderService.getActiveNotifications(sbn.getPackageName());
        assertEquals(0, notifs.length);
    }

    @Test
    public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
        sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
        mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -2006,6 +2044,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        Notification n =
                new Notification.Builder(mContext, mTestNotificationChannel.getId())
                        .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -2043,6 +2084,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationWithTag_fromApp_cannotCancelFgsChild()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        mService.isSystemUid = false;
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
@@ -2066,6 +2110,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationWithTag_fromApp_cannotCancelFgsParent()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        mService.isSystemUid = false;
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
@@ -2135,6 +2182,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelAllNotificationsFromApp_cannotCancelFgsChild()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        mService.isSystemUid = false;
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
@@ -2160,6 +2210,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelAllNotifications_fromApp_cannotCancelFgsParent()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        mService.isSystemUid = false;
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
@@ -2281,6 +2334,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_clearAll_GroupWithFgsParent()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2304,6 +2360,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_clearAll_GroupWithFgsChild()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
        final NotificationRecord child = generateNotificationRecord(
@@ -2402,6 +2461,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_clearAll_Fgs()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord child2 = generateNotificationRecord(
                mTestNotificationChannel, 3, null, false);
        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2466,6 +2528,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_byKey_GroupWithFgsParent()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
        parent.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2491,6 +2556,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_byKey_GroupWithFgsChild()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
        final NotificationRecord child = generateNotificationRecord(
@@ -2596,6 +2664,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Test
    public void testCancelNotificationsFromListener_byKey_Fgs()
            throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord child2 = generateNotificationRecord(
                mTestNotificationChannel, 3, null, false);
        child2.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -2762,6 +2833,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testUserInitiatedCancelAllWithGroup_ForegroundServiceFlag() throws Exception {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        final NotificationRecord parent = generateNotificationRecord(
                mTestNotificationChannel, 1, "group", true);
        final NotificationRecord child = generateNotificationRecord(
@@ -6199,6 +6273,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testRemoveForegroundServiceFlagFromNotification_enqueued() {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        Notification n = new Notification.Builder(mContext, "").build();
        n.flags |= FLAG_FOREGROUND_SERVICE;

@@ -6218,6 +6295,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testRemoveForegroundServiceFlagFromNotification_posted() {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        Notification n = new Notification.Builder(mContext, "").build();
        n.flags |= FLAG_FOREGROUND_SERVICE;

@@ -6241,6 +6321,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testCannotRemoveForegroundFlagWhenOverLimit_enqueued() {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
            Notification n = new Notification.Builder(mContext, "").build();
            StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
@@ -6269,6 +6352,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testCannotRemoveForegroundFlagWhenOverLimit_posted() {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
            Notification n = new Notification.Builder(mContext, "").build();
            StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, i, null, mUid, 0,
@@ -8286,7 +8372,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertNotNull(n.publicVersion.bigContentView);
        assertNotNull(n.publicVersion.headsUpContentView);

        mService.fixNotification(n, PKG, "tag", 9, 0);
        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);

        assertNull(n.contentView);
        assertNull(n.bigContentView);
@@ -8977,6 +9063,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testCanPostFgsWhenOverLimit() throws RemoteException {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
            StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                    i, null, false).getSbn();
@@ -9002,6 +9091,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    @Test
    public void testCannotPostNonFgsWhenOverLimit() throws RemoteException {
        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(SHOW_IMMEDIATELY);
        for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) {
            StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel,
                    i, null, false).getSbn();
@@ -9024,6 +9116,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                "testCanPostFgsWhenOverLimit - non fgs over limit!",
                sbn2.getId(), sbn2.getNotification(), sbn2.getUserId());


        when(mAmi.applyForegroundServiceNotification(
                any(), anyString(), anyInt(), anyString(), anyInt()))
                .thenReturn(NOT_FOREGROUND_SERVICE);
        final StatusBarNotification sbn3 = generateNotificationRecord(mTestNotificationChannel,
                101, null, false).getSbn();
        sbn3.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
        mBinderService.enqueueNotificationWithTag(PKG, PKG,
                "testCanPostFgsWhenOverLimit - fake fgs over limit!",
                sbn3.getId(), sbn3.getNotification(), sbn3.getUserId());

        waitForIdle();

        StatusBarNotification[] notifs =
@@ -10081,4 +10184,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mInternalService.sendReviewPermissionsNotification();
        verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class));
    }

    @Test
    public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception {
        final ApplicationInfo applicationInfo = new ApplicationInfo();
        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
                .thenReturn(applicationInfo);

        Notification n = new Notification.Builder(mContext, "test")
                .setFlag(FLAG_FOREGROUND_SERVICE, true)
                .setFlag(FLAG_CAN_COLORIZE, true)
                .build();

        mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE);

        assertFalse(n.isForegroundService());
        assertFalse(n.hasColorizedPermission());
    }
}