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

Commit 49b1bf1d authored by Mady Mellor's avatar Mady Mellor
Browse files

Actually fix notification listeners dismissing bubbles

Similar to foreground service notifications, bubbles should also not be
dismissable via a cancel on a notification listener.

This CL makes it such that any notif with FLAG_BUBBLE will not be dismissed
via a cancel from a notification listener.

Test: atest NotificationManagerServiceTest
Bug: 128459529
Change-Id: I4602931209dc6d65b0d79ba5aba684cba705d25e
parent 11ed72c5
Loading
Loading
Loading
Loading
+17 −10
Original line number Diff line number Diff line
@@ -17,7 +17,11 @@
package com.android.server.notification;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -844,7 +848,7 @@ public class NotificationManagerService extends SystemService {
                }
            }
            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                    Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
                    FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
                    true, userId, REASON_CANCEL, nv.rank, nv.count,null);
            nv.recycle();
        }
@@ -2339,7 +2343,7 @@ public class NotificationManagerService extends SystemService {
            // Don't allow client applications to cancel foreground service notis or autobundled
            // summaries.
            final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
                    (FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTOGROUP_SUMMARY);
                    (FLAG_FOREGROUND_SERVICE | FLAG_AUTOGROUP_SUMMARY);
            cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
                    mustNotHaveFlags, false, userId, REASON_APP_CANCEL, null);
        }
@@ -3143,7 +3147,7 @@ public class NotificationManagerService extends SystemService {
        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
                int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
            cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                    Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE,
                    FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE | FLAG_BUBBLE,
                    true,
                    userId, REASON_LISTENER_CANCEL, info);
        }
@@ -4234,7 +4238,7 @@ public class NotificationManagerService extends SystemService {
                                .setGroupSummary(true)
                                .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN)
                                .setGroup(GroupHelper.AUTOGROUP_KEY)
                                .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
                                .setFlag(FLAG_AUTOGROUP_SUMMARY, true)
                                .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
                                .setColor(adjustedSbn.getNotification().color)
                                .setLocalOnly(true)
@@ -4763,9 +4767,9 @@ public class NotificationManagerService extends SystemService {
        boolean canBubble = mPreferencesHelper.areBubblesAllowed(pkg, userId)
                && r.getChannel().canBubble();
        if (notification.getBubbleMetadata() != null && canBubble) {
            notification.flags |= Notification.FLAG_BUBBLE;
            notification.flags |= FLAG_BUBBLE;
        } else {
            notification.flags &= ~Notification.FLAG_BUBBLE;
            notification.flags &= ~FLAG_BUBBLE;
        }
    }

@@ -5228,8 +5232,8 @@ public class NotificationManagerService extends SystemService {
                    // Ensure if this is a foreground service that the proper additional
                    // flags are set.
                    if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                        notification.flags |= Notification.FLAG_ONGOING_EVENT
                                | Notification.FLAG_NO_CLEAR;
                        notification.flags |= FLAG_ONGOING_EVENT
                                | FLAG_NO_CLEAR;
                    }

                    mRankingHelper.extractSignals(r);
@@ -6686,8 +6690,11 @@ public class NotificationManagerService extends SystemService {
                            null, userId, 0, 0, reason, listenerName);

                    FlagChecker flagChecker = (int flags) -> {
                        if ((flags & (Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR))
                                != 0) {
                        int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR;
                        if (REASON_LISTENER_CANCEL_ALL == reason) {
                            flagsToCheck |= FLAG_BUBBLE;
                        }
                        if ((flags & flagsToCheck) != 0) {
                            return false;
                        }
                        return true;
+75 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.notification;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
@@ -123,6 +124,9 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;

import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;

import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.statusbar.NotificationVisibility;
@@ -133,7 +137,6 @@ import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.pm.UserManagerService;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;

@@ -159,9 +162,6 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -4424,6 +4424,77 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                sbn.getKey()).getNotification().isBubbleNotification());
    }

    @Test
    public void testCancelAllNotifications_cancelsBubble() throws Exception {
        final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
        nr.sbn.getNotification().flags |= FLAG_BUBBLE;
        mService.addNotification(nr);

        mBinderService.cancelAllNotifications(PKG, nr.sbn.getUserId());
        waitForIdle();

        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
        assertEquals(0, notifs.length);
        assertEquals(0, mService.getNotificationRecordCount());
    }

    @Test
    public void testAppCancelNotifications_cancelsBubbles() throws Exception {
        final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;

        // Post the notification
        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
        waitForIdle();

        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
        assertEquals(1, notifs.length);
        assertEquals(1, mService.getNotificationRecordCount());

        mBinderService.cancelNotificationWithTag(PKG, null, nrBubble.sbn.getId(),
                nrBubble.sbn.getUserId());
        waitForIdle();

        StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
        assertEquals(0, notifs2.length);
        assertEquals(0, mService.getNotificationRecordCount());
    }

    @Test
    public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception {
        final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
        final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;

        mService.addNotification(nrNormal);
        mService.addNotification(nrBubble);

        mService.getBinderService().cancelNotificationsFromListener(null, null);
        waitForIdle();

        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
        assertEquals(1, notifs.length);
        assertEquals(1, mService.getNotificationRecordCount());
    }

    @Test
    public void testCancelNotificationsFromListener_ignoresBubbles() throws Exception {
        final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
        final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;

        mService.addNotification(nrNormal);
        mService.addNotification(nrBubble);

        String[] keys = {nrNormal.sbn.getKey(), nrBubble.sbn.getKey()};
        mService.getBinderService().cancelNotificationsFromListener(null, keys);
        waitForIdle();

        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
        assertEquals(1, notifs.length);
        assertEquals(1, mService.getNotificationRecordCount());
    }

    public void testGetAllowedAssistantCapabilities() throws Exception {
        List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null);