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

Commit 70e1718c authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Implement policy for promoted notifications

Test: NotificationTest x2
Test: PreferencesHelperTest
Test: NotificationManagerServiceTest
Flag: android.app.ui_rich_ongoing
Bug: 367741426
Change-Id: I4672fdd5fb56579f742a2753bcc599a04962f33a
parent e116ca15
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6495,6 +6495,7 @@ package android.app {
    field public static final int FLAG_NO_CLEAR = 32; // 0x20
    field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
    field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
    field @FlaggedApi("android.app.api_rich_ongoing") public static final int FLAG_PROMOTED_ONGOING = 262144; // 0x40000
    field @Deprecated public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
    field public static final int FOREGROUND_SERVICE_DEFAULT = 0; // 0x0
    field public static final int FOREGROUND_SERVICE_DEFERRED = 2; // 0x2
+2 −0
Original line number Diff line number Diff line
@@ -258,4 +258,6 @@ interface INotificationManager
    @EnforcePermission(allOf={"INTERACT_ACROSS_USERS", "ACCESS_NOTIFICATIONS"})
    void unregisterCallNotificationEventListener(String packageName, in UserHandle userHandle, in ICallNotificationEventCallback listener);

    void setCanBePromoted(String pkg, int uid, boolean promote);
    boolean canBePromoted(String pkg, int uid);
}
+58 −0
Original line number Diff line number Diff line
@@ -772,6 +772,17 @@ public class Notification implements Parcelable
    @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
    public static final int FLAG_SILENT = 1 << 17;  //0x00020000
    /**
     * Bit to be bitwise-ored into the {@link #flags} field that should be
     * set by the system if this notification is a promoted ongoing notification, either via a
     * user setting or allowlist.
     *
     * Applications cannot set this flag directly, but the posting app and
     * {@link android.service.notification.NotificationListenerService} can read it.
     */
    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
    public static final int FLAG_PROMOTED_ONGOING = 0x00040000;
    private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
            BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
            DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
@@ -3109,6 +3120,53 @@ public class Notification implements Parcelable
        }
    }
    /**
     * @hide
     */
    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
    public boolean containsCustomViews() {
        return contentView != null
                || bigContentView != null
                || headsUpContentView != null
                || (publicVersion != null
                && (publicVersion.contentView != null
                || publicVersion.bigContentView != null
                || publicVersion.headsUpContentView != null));
    }
    /**
     * @hide
     */
    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
    public boolean hasTitle() {
        return extras != null
                && (!TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE))
                || !TextUtils.isEmpty(extras.getCharSequence(EXTRA_TITLE_BIG)));
    }
    /**
     * @hide
     */
    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
    public boolean hasPromotableStyle() {
        //TODO(b/367739672): Add progress style
        return extras == null || !extras.containsKey(Notification.EXTRA_TEMPLATE)
                || isStyle(Notification.BigPictureStyle.class)
                || isStyle(Notification.BigTextStyle.class)
                || isStyle(Notification.CallStyle.class);
    }
    /**
     * @hide
     */
    @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING)
    public boolean hasPromotableCharacteristics() {
        return isColorized()
                && hasTitle()
                && !containsCustomViews()
                && hasPromotableStyle();
    }
    /**
     * Whether this notification was posted by a headless system app.
     *
+278 −3
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import static android.app.Notification.EXTRA_PICTURE;
import static android.app.Notification.EXTRA_PICTURE_ICON;
import static android.app.Notification.EXTRA_SUMMARY_TEXT;
import static android.app.Notification.EXTRA_TITLE;
import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.Notification.GROUP_KEY_SILENT;
@@ -96,6 +97,7 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Pair;
import android.util.Slog;
import android.widget.RemoteViews;

import androidx.test.InstrumentationRegistry;
@@ -126,6 +128,8 @@ public class NotificationTest {

    private Context mContext;

    private RemoteViews mRemoteViews;

    @Rule
    public TestRule compatChangeRule = new PlatformCompatChangeRule();
    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -133,23 +137,25 @@ public class NotificationTest {
    @Before
    public void setUp() {
        mContext = InstrumentationRegistry.getContext();
        mRemoteViews = new RemoteViews(
                mContext.getPackageName(), R.layout.notification_template_header);
    }

    @Test
    public void testColorizedByPermission() {
        Notification n = new Notification.Builder(mContext, "test")
                .setFlag(Notification.FLAG_CAN_COLORIZE, true)
                .setFlag(FLAG_CAN_COLORIZE, true)
                .setColorized(true).setColor(Color.WHITE)
                .build();
        assertTrue(n.isColorized());

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

        n = new Notification.Builder(mContext, "test")
                .setFlag(Notification.FLAG_CAN_COLORIZE, false)
                .setFlag(FLAG_CAN_COLORIZE, false)
                .setColorized(true).setColor(Color.WHITE)
                .build();
        assertFalse(n.isColorized());
@@ -214,6 +220,275 @@ public class NotificationTest {
        assertFalse(n.hasCompletedProgress());
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasTitle_noStyle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setContentTitle("TITLE")
                .build();
        assertThat(n.hasTitle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasTitle_bigText() {
        Notification n = new Notification.Builder(mContext, "test")
                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
                .build();
        assertThat(n.hasTitle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasTitle_noTitle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setContentText("text not title")
                .build();
        assertThat(n.hasTitle()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_none() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_content() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setCustomContentView(mRemoteViews)
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_big() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setCustomBigContentView(mRemoteViews)
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_headsUp() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setCustomHeadsUpContentView(mRemoteViews)
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_content_public() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("public")
                .setCustomContentView(mRemoteViews)
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_big_public() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setCustomBigContentView(mRemoteViews)
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testContainsCustomViews_headsUp_public() {
        Notification np = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setCustomHeadsUpContentView(mRemoteViews)
                .build();
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .setPublicVersion(np)
                .build();
        assertThat(n.containsCustomViews()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_noStyle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setContentText("test")
                .build();
        assertThat(n.hasPromotableStyle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_bigPicture() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.BigPictureStyle())
                .build();
        assertThat(n.hasPromotableStyle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_bigText() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.BigTextStyle())
                .build();
        assertThat(n.hasPromotableStyle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_no_messagingStyle() {
        Notification.MessagingStyle style = new Notification.MessagingStyle("self name")
                .setGroupConversation(true)
                .setConversationTitle("test conversation title");
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(style)
                .build();
        assertThat(n.hasPromotableStyle()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_no_mediaStyle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.MediaStyle())
                .build();
        assertThat(n.hasPromotableStyle()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_no_inboxStyle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.InboxStyle())
                .build();
        assertThat(n.hasPromotableStyle()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableStyle_callText() {
        PendingIntent answerIntent = createPendingIntent("answer");
        PendingIntent declineIntent = createPendingIntent("decline");
        Notification.CallStyle style = Notification.CallStyle.forIncomingCall(
                new Person.Builder().setName("A Caller").build(),
                declineIntent,
                answerIntent
        );
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(style)
                .build();
        assertThat(n.hasPromotableStyle()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableCharacteristics() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
                .setColor(Color.WHITE)
                .setColorized(true)
                .setFlag(FLAG_CAN_COLORIZE, true)
                .build();
        assertThat(n.hasPromotableCharacteristics()).isTrue();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableCharacteristics_wrongStyle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.InboxStyle())
                .setContentTitle("TITLE")
                .setColor(Color.WHITE)
                .setColorized(true)
                .setFlag(FLAG_CAN_COLORIZE, true)
                .build();
        assertThat(n.hasPromotableCharacteristics()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableCharacteristics_notColorized() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG"))
                .setColor(Color.WHITE)
                .build();
        assertThat(n.hasPromotableCharacteristics()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_UI_RICH_ONGOING)
    public void testHasPromotableCharacteristics_noTitle() {
        Notification n = new Notification.Builder(mContext, "test")
                .setSmallIcon(android.R.drawable.sym_def_app_icon)
                .setStyle(new Notification.BigTextStyle())
                .setColor(Color.WHITE)
                .setColorized(true)
                .setFlag(FLAG_CAN_COLORIZE, true)
                .build();
        assertThat(n.hasPromotableCharacteristics()).isFalse();
    }

    @Test
    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
    public void testGetShortCriticalText_noneSet() {
+104 −9
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import static android.app.Notification.EXTRA_LARGE_ICON_BIG;
import static android.app.Notification.EXTRA_SUB_TEXT;
import static android.app.Notification.EXTRA_TEXT;
import static android.app.Notification.EXTRA_TEXT_LINES;
import static android.app.Notification.EXTRA_TITLE;
import static android.app.Notification.EXTRA_TITLE_BIG;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_AUTO_CANCEL;
@@ -45,6 +46,7 @@ import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_NO_DISMISS;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
import static android.app.NotificationChannel.NEWS_ID;
@@ -3516,7 +3518,7 @@ public class NotificationManagerService extends SystemService {
    private String getHistoryTitle(Notification n) {
        CharSequence title = null;
        if (n.extras != null) {
            title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
            title = n.extras.getCharSequence(EXTRA_TITLE);
            if (title == null) {
                title = n.extras.getCharSequence(EXTRA_TITLE_BIG);
            }
@@ -4113,6 +4115,75 @@ public class NotificationManagerService extends SystemService {
            handleSavePolicyFile();
        }
        @Override
        @FlaggedApi(android.app.Flags.FLAG_UI_RICH_ONGOING)
        public boolean canBePromoted(String pkg, int uid) {
            checkCallerIsSystemOrSystemUiOrShell();
            if (!android.app.Flags.uiRichOngoing()) {
                return false;
            }
            return mPreferencesHelper.canBePromoted(pkg, uid);
        }
        @Override
        @FlaggedApi(android.app.Flags.FLAG_UI_RICH_ONGOING)
        public void setCanBePromoted(String pkg, int uid, boolean promote) {
            checkCallerIsSystemOrSystemUiOrShell();
            if (!android.app.Flags.uiRichOngoing()) {
                return;
            }
            boolean changed = mPreferencesHelper.setCanBePromoted(pkg, uid, promote);
            if (changed) {
                // check for pending/posted notifs from this app and update the flag
                synchronized (mNotificationLock) {
                    // for enqueued we just need to update the flag
                    List<NotificationRecord> enqueued = findAppNotificationByListLocked(
                            mEnqueuedNotifications, pkg, UserHandle.getUserId(uid));
                    for (NotificationRecord r : enqueued) {
                        if (promote
                                && r.getNotification().hasPromotableCharacteristics()
                                && r.getImportance() > IMPORTANCE_MIN) {
                            r.getNotification().flags |= FLAG_PROMOTED_ONGOING;
                        } else if (!promote) {
                            r.getNotification().flags &= ~FLAG_PROMOTED_ONGOING;
                        }
                    }
                    // if the notification is posted we need to update the flag and tell listeners
                    List<NotificationRecord> posted = findAppNotificationByListLocked(
                            mNotificationList, pkg, UserHandle.getUserId(uid));
                    for (NotificationRecord r : posted) {
                        if (promote
                                && !hasFlag(r.getNotification().flags, FLAG_PROMOTED_ONGOING)
                                && r.getNotification().hasPromotableCharacteristics()
                                && r.getImportance() > IMPORTANCE_MIN) {
                            r.getNotification().flags |= FLAG_PROMOTED_ONGOING;
                            // we could set a wake lock here but this value should only change
                            // in response to user action, so the device should be awake long enough
                            // to post
                            PostNotificationTracker tracker =
                                    mPostNotificationTrackerFactory.newTracker(null);
                            // Set false for isAppForeground because that field is only used
                            // for bubbles and messagingstyle can not be promoted
                            mHandler.post(new EnqueueNotificationRunnable(
                                    r.getUser().getIdentifier(),
                                    r, /* isAppForeground */ false, /* isAppProvided= */ false,
                                    tracker));
                        } else if (!promote
                                && hasFlag(r.getNotification().flags, FLAG_PROMOTED_ONGOING)){
                            r.getNotification().flags &= ~FLAG_PROMOTED_ONGOING;
                            PostNotificationTracker tracker =
                                    mPostNotificationTrackerFactory.newTracker(null);
                            mHandler.post(new EnqueueNotificationRunnable(
                                    r.getUser().getIdentifier(),
                                    r, /* isAppForeground */ false, /* isAppProvided= */ false,
                                    tracker));
                        }
                    }
                }
                handleSavePolicyFile();
            }
        }
        @Override
        public boolean hasSentValidMsg(String pkg, int uid) {
            checkCallerIsSystem();
@@ -7698,6 +7769,16 @@ public class NotificationManagerService extends SystemService {
            return false;
        }
        if (android.app.Flags.uiRichOngoing()) {
            // This would normally be done in fixNotification(), but we need the channel info so
            // it's done a little late
            if (mPreferencesHelper.canBePromoted(pkg, notificationUid)
                    && notification.hasPromotableCharacteristics()
                    && channel.getImportance() > IMPORTANCE_MIN) {
                notification.flags |= FLAG_PROMOTED_ONGOING;
            }
        }
        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
        r.setIsAppImportanceLocked(mPermissionHelper.isPermissionUserSet(pkg, userId));
        r.setPostSilently(postSilently);
@@ -7938,6 +8019,9 @@ public class NotificationManagerService extends SystemService {
            }
        }
        // Apps cannot set this flag
         notification.flags &= ~FLAG_PROMOTED_ONGOING;
        // Ensure CallStyle has all the correct actions
        if (notification.isStyle(Notification.CallStyle.class)) {
            Notification.Builder builder =
@@ -8061,12 +8145,7 @@ public class NotificationManagerService extends SystemService {
    private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
        if (android.app.Flags.removeRemoteViews()) {
            if (notification.contentView != null || notification.bigContentView != null
                    ||  notification.headsUpContentView != null
                    || (notification.publicVersion != null
                    && (notification.publicVersion.contentView != null
                    || notification.publicVersion.bigContentView != null
                    || notification.publicVersion.headsUpContentView != null))) {
            if (notification.containsCustomViews()) {
                Slog.i(TAG, "Removed customViews for " + pkg);
                mUsageStats.registerImageRemoved(pkg);
            }
@@ -9236,8 +9315,8 @@ public class NotificationManagerService extends SystemService {
            }
        }
        final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
        final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
        final String oldTitle = String.valueOf(oldN.extras.get(EXTRA_TITLE));
        final String newTitle = String.valueOf(newN.extras.get(EXTRA_TITLE));
        if (!Objects.equals(oldTitle, newTitle)) {
            if (DEBUG_INTERRUPTIVENESS) {
                Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -10653,6 +10732,22 @@ public class NotificationManagerService extends SystemService {
    }
    @GuardedBy("mNotificationLock")
    @FlaggedApi(android.app.Flags.FLAG_UI_RICH_ONGOING)
    private @NonNull List<NotificationRecord> findAppNotificationByListLocked(
            ArrayList<NotificationRecord> list, String pkg, int userId) {
        List<NotificationRecord> records = new ArrayList<>();
        final int len = list.size();
        for (int i = 0; i < len; i++) {
            NotificationRecord r = list.get(i);
            if (notificationMatchesUserId(r, userId, false)
                    && r.getSbn().getPackageName().equals(pkg)) {
                records.add(r);
            }
        }
        return records;
    }
    @GuardedBy("mNotificationLock")
    private @NonNull List<NotificationRecord> findGroupNotificationByListLocked(
            ArrayList<NotificationRecord> list, String pkg, String groupKey, int userId) {
Loading