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

Commit ada95761 authored by Valentin Iftime's avatar Valentin Iftime
Browse files

Cancel notification summary if all children were bundled before posting

 If a group summary is posted after all children were already bundled: then it should be canceled to avoid having empty summary notifications.

 This can happen if group children are posted first and bundled before the summary is posted.
 When the summary is posted, it is handled as a "summary without children" (potentially abusive notification), causing an empty summary notification to be shown.

Flag: android.service.notification.notification_classification

Test: atest NotificationManagerServiceTest
Bug: 429411981
Change-Id: I2139bdfe541f56fd6e55e072d75833c4e6bac3b0
parent 01418e2a
Loading
Loading
Loading
Loading
+19 −3
Original line number Original line Diff line number Diff line
@@ -840,7 +840,23 @@ public class GroupHelper {
                    Log.i(TAG, "isGroupChildWithoutSummary OR isGroupSummaryWithoutChild"
                    Log.i(TAG, "isGroupChildWithoutSummary OR isGroupSummaryWithoutChild"
                            + record);
                            + record);
                }
                }

                boolean aggregated =
                        addToUngroupedAndMaybeAggregate(record, fullAggregateGroupKey, sectioner);
                        addToUngroupedAndMaybeAggregate(record, fullAggregateGroupKey, sectioner);
                if (android.service.notification.Flags.notificationClassification()) {
                    if (!aggregated && isSummaryWithAllChildrenBundled(record, notificationList,
                            new ArrayList<>())) {
                        // Cancel the summary and cache it if does not get aggregated
                        // in order to avoid empty summaries
                        if (DEBUG) {
                            Slog.i(TAG,
                                    "Empty group summary to be canceled and cached: " + record);
                        }
                        mCallback.removeAppProvidedSummary(record.getKey());
                        cacheCanceledSummary(record);
                    }
                }

                return;
                return;
            }
            }


@@ -1660,7 +1676,7 @@ public class GroupHelper {
        // Find all posted children for this summary
        // Find all posted children for this summary
        for (NotificationRecord r : postedNotificationsList) {
        for (NotificationRecord r : postedNotificationsList) {
            if (!r.getNotification().isGroupSummary()
            if (!r.getNotification().isGroupSummary()
                    && groupKey.equals(r.getSbn().getGroup())) {
                    && groupKey.equals(r.getSbn().getNotification().getGroup())) {
                numChildren++;
                numChildren++;
                if (isInBundleSection(r)) {
                if (isInBundleSection(r)) {
                    numBundledChildren++;
                    numBundledChildren++;
@@ -1670,7 +1686,7 @@ public class GroupHelper {
        // Find all enqueued children for this summary
        // Find all enqueued children for this summary
        for (NotificationRecord r : enqueuedNotificationsList) {
        for (NotificationRecord r : enqueuedNotificationsList) {
            if (!r.getNotification().isGroupSummary()
            if (!r.getNotification().isGroupSummary()
                    && groupKey.equals(r.getSbn().getGroup())) {
                    && groupKey.equals(r.getSbn().getNotification().getGroup())) {
                numChildren++;
                numChildren++;
                if (isInBundleSection(r)) {
                if (isInBundleSection(r)) {
                    numBundledChildren++;
                    numBundledChildren++;
+113 −0
Original line number Original line Diff line number Diff line
@@ -20016,6 +20016,119 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertThat(s.getNotification().flags & FLAG_SILENT).isEqualTo(FLAG_SILENT);
        assertThat(s.getNotification().flags & FLAG_SILENT).isEqualTo(FLAG_SILENT);
    }
    }
    @Test
    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
    public void testAllChildrenBundled_summaryCanceled() throws Exception {
        when(mAssistants.isClassificationTypeAllowed(anyInt(), anyInt())).thenReturn(true);
        when(mAssistants.isAdjustmentAllowedForPackage(anyInt(), anyString(),
                anyString())).thenReturn(true);
        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
        // Create a group with 2 children and a summary
        final String originalGroupName = "originalGroup";
        final int summaryId = 0;
        // Post the child notifications first and bundle them immediately
        final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
                summaryId + 1, originalGroupName, false);
        mService.addNotification(r1);
        Bundle signals = new Bundle();
        signals.putInt(KEY_TYPE, TYPE_NEWS);
        Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
                "", r1.getUser().getIdentifier());
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r1.applyAdjustments();
        r1.setOverrideGroupKey("newsBundleGroup");
        assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
        final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
                summaryId + 2, originalGroupName, false);
        mService.addNotification(r2);
        signals.putInt(KEY_TYPE, TYPE_PROMOTION);
        adjustment = new Adjustment(r2.getSbn().getPackageName(), r2.getKey(), signals,
                "", r2.getUser().getIdentifier());
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r2.applyAdjustments();
        r2.setOverrideGroupKey("promotionsBundleGroup");
        assertThat(r2.getChannel().getId()).isEqualTo(PROMOTIONS_ID);
        // Post summary
        final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
                summaryId, originalGroupName, true);
        mService.addEnqueuedNotification(summary);
        mService.new PostNotificationRunnable(summary.getKey(), summary.getSbn().getPackageName(),
                summary.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
        waitForIdle();
        // Check that the summary has FLAG_SILENT set
        NotificationRecord s = mService.findNotificationLocked(summary.getSbn().getPackageName(),
                summary.getSbn().getTag(), summary.getSbn().getId(), summary.getSbn().getUserId());
        assertThat(s).isNotNull();
        assertThat(s.getNotification().flags & FLAG_SILENT).isEqualTo(FLAG_SILENT);
        // Advance DELAY_FORCE_REGROUP_TIME: calls GroupHelper.onNotificationPostedWithDelay
        moveTimeForwardAndWaitForIdle(DELAY_FORCE_REGROUP_TIME);
        // Check that the summary was canceled and cached in GroupHelper
        s = mService.findNotificationLocked(summary.getSbn().getPackageName(),
                summary.getSbn().getTag(), summary.getSbn().getId(), summary.getSbn().getUserId());
        assertThat(s).isNull();
        assertThat(mGroupHelper.findCanceledSummary(summary.getSbn().getPackageName(),
                summary.getSbn().getTag(), summary.getSbn().getId(),
                summary.getSbn().getUserId(),
                summary.getSbn().getNotification().getGroup())).isNotNull();
    }
    @Test
    @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
    public void testSomeChildrenBundled_summaryNotCanceled() throws Exception {
        when(mAssistants.isClassificationTypeAllowed(anyInt(), anyInt())).thenReturn(true);
        when(mAssistants.isAdjustmentAllowedForPackage(anyInt(), anyString(),
                anyString())).thenReturn(true);
        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
        // Create a group with 2 children and a summary
        final String originalGroupName = "originalGroup";
        final int summaryId = 0;
        // Post the child notifications first and bundle one of them
        final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel,
                summaryId + 1, originalGroupName, false);
        mService.addNotification(r1);
        Bundle signals = new Bundle();
        signals.putInt(KEY_TYPE, TYPE_NEWS);
        Adjustment adjustment = new Adjustment(r1.getSbn().getPackageName(), r1.getKey(), signals,
                "", r1.getUser().getIdentifier());
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r1.applyAdjustments();
        r1.setOverrideGroupKey("newsBundleGroup");
        assertThat(r1.getChannel().getId()).isEqualTo(NEWS_ID);
        final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel,
                summaryId + 2, originalGroupName, false);
        mService.addNotification(r2);
        waitForIdle();
        // Post summary
        final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel,
                summaryId, originalGroupName, true);
        mService.addEnqueuedNotification(summary);
        mService.new PostNotificationRunnable(summary.getKey(), summary.getSbn().getPackageName(),
                summary.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
        moveTimeForwardAndWaitForIdle(DELAY_FORCE_REGROUP_TIME);
        // Check that the summary was not canceled and does NOT have FLAG_SILENT set
        NotificationRecord s = mService.findNotificationLocked(summary.getSbn().getPackageName(),
                summary.getSbn().getTag(), summary.getSbn().getId(), summary.getSbn().getUserId());
        assertThat(s).isEqualTo(summary);
        assertThat(s.getNotification().flags & FLAG_SILENT).isEqualTo(0);
    }
    @Test
    @Test
    @EnableFlags({FLAG_NM_SUMMARIZATION})
    @EnableFlags({FLAG_NM_SUMMARIZATION})
    public void testDisableBundleAdjustment_unsummarizesNotifications() throws Exception {
    public void testDisableBundleAdjustment_unsummarizesNotifications() throws Exception {