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

Commit 53926ed6 authored by Valentin Iftime's avatar Valentin Iftime Committed by Iavor-Valentin Iftime
Browse files

Check sparse group child notifications for trigger section

  Do not force-group sparse groups if the child notifications are not groupable. In some groups, the summary may match a valid section but the child notifications do not: ie. conversations where the summary is not MessageMessagingStyle.

Flag: android.service.notification.notification_force_grouping
Flag: com.android.server.notification.notification_force_group_singletons

Test: atest GroupHelperTest
Bug: 377788156
Change-Id: I92a33a3a47ebbf46556e080beb356a1d5962e1b6
parent d5fcadbb
Loading
Loading
Loading
Loading
+41 −3
Original line number Diff line number Diff line
@@ -1436,7 +1436,8 @@ public class GroupHelper {
        }
    }

    private ArrayMap<String, NotificationRecord> getSparseGroups(
    @VisibleForTesting
    protected ArrayMap<String, NotificationRecord> getSparseGroups(
            final FullyQualifiedGroupKey fullAggregateGroupKey,
            final List<NotificationRecord> notificationList,
            final Map<String, NotificationRecord> summaryByGroupKey,
@@ -1448,8 +1449,8 @@ public class GroupHelper {
                        && summary.getUserId() == fullAggregateGroupKey.userId
                        && summary.getSbn().isAppGroup()
                        && !summary.getGroupKey().equals(fullAggregateGroupKey.toString())) {
                    int numChildren = getNumChildrenForGroup(summary.getSbn().getGroup(),
                            notificationList);
                    int numChildren = getNumChildrenForGroupWithSection(summary.getSbn().getGroup(),
                            notificationList, sectioner);
                    if (numChildren > 0 && numChildren < MIN_CHILD_COUNT_TO_AVOID_FORCE_GROUPING) {
                        sparseGroups.put(summary.getGroupKey(), summary);
                    }
@@ -1459,6 +1460,43 @@ public class GroupHelper {
        return sparseGroups;
    }

    /**
     *  Get the number of children of a group if all match a certain section.
     *  Used for force grouping sparse groups, where the summary may match a section but the
     *  child notifications do not: ie. conversations
     *
     * @param groupKey the group key (name)
     * @param notificationList all notifications list
     * @param sectioner the section to match
     * @return number of children in that group or -1 if section does not match
     */
    private int getNumChildrenForGroupWithSection(final String groupKey,
            final List<NotificationRecord> notificationList,
            final NotificationSectioner sectioner) {
        int numChildren = 0;
        for (NotificationRecord r : notificationList) {
            if (!r.getNotification().isGroupSummary() && groupKey.equals(r.getSbn().getGroup())) {
                NotificationSectioner childSection = getSection(r);
                if (childSection == null || childSection != sectioner) {
                    if (DEBUG) {
                        Slog.i(TAG,
                                "getNumChildrenForGroupWithSection skip because invalid section: "
                                    + groupKey + " r: " + r);
                    }
                    return -1;
                } else {
                    numChildren++;
                }
            }
        }

        if (DEBUG) {
            Slog.i(TAG,
                    "getNumChildrenForGroupWithSection " + groupKey + " numChild: " + numChildren);
        }
        return numChildren;
    }

    @GuardedBy("mAggregatedNotifications")
    private void cacheCanceledSummary(NotificationRecord record) {
        final FullyQualifiedGroupKey groupKey = new FullyQualifiedGroupKey(record.getUserId(),
+116 −0
Original line number Diff line number Diff line
@@ -84,7 +84,9 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.GroupHelper.CachedSummary;
import com.android.server.notification.GroupHelper.FullyQualifiedGroupKey;
import com.android.server.notification.GroupHelper.NotificationAttributes;
import com.android.server.notification.GroupHelper.NotificationSectioner;

import org.junit.Before;
import org.junit.Rule;
@@ -3056,6 +3058,120 @@ public class GroupHelperTest extends UiServiceTestCase {
        assertThat(cachedSummary).isNull();
    }

    @Test
    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
    @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
    public void testNonGroupableChildren_singletonGroups_disableConversations() {
        // Check that singleton groups with children that are not groupable, is not grouped
        // Even though the group summary is a regular (alerting) notification, the children are
        // conversations => the group should not be forced grouped.
        final List<NotificationRecord> notificationList = new ArrayList<>();
        final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
        final String pkg = "package";

        // Trigger notification, ungrouped
        final int triggerId = 1;
        NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
                String.valueOf(triggerId), UserHandle.SYSTEM);
        notificationList.add(triggerNotification);
        final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
        final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
                triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
                triggerSection);

        // Add singleton group with alerting child
        final String groupName_valid = "testGrp_valid";
        final int summaryId_valid = 0;
        NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
                String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
        notificationList.add(summary);
        summaryByGroup.put(summary.getGroupKey(), summary);
        final String groupKey_valid = summary.getGroupKey();
        NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
                String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
        notificationList.add(child);

        // Add singleton group with conversation child
        final String groupName_invalid = "testGrp_invalid";
        final int summaryId_invalid = 100;
        summary = getNotificationRecord(pkg, summaryId_invalid,
                String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
        notificationList.add(summary);
        final String groupKey_invalid = summary.getGroupKey();
        summaryByGroup.put(summary.getGroupKey(), summary);
        child = getNotificationRecord(pkg, summaryId_invalid + 42,
                String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
                false);
        child = spy(child);
        when(child.isConversation()).thenReturn(true);
        notificationList.add(child);

        // Check that the invalid group will not be force grouped
        final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
                triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
        assertThat(sparseGroups).containsKey(groupKey_valid);
        assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
    }

    @Test
    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
    public void testNonGroupableChildren_singletonGroups_enableConversations() {
        // Check that singleton groups with children that are not groupable, is not grouped
        // Conversations are groupable (FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS is enabled)
        // The invalid group is the alerting notifications: because the triggering notifications'
        // section is Conversations, so the alerting group should be skipped.
        final List<NotificationRecord> notificationList = new ArrayList<>();
        final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
        final String pkg = "package";

        // Trigger notification, ungrouped conversation
        final int triggerId = 1;
        NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
                String.valueOf(triggerId), UserHandle.SYSTEM);
        triggerNotification = spy(triggerNotification);
        when(triggerNotification.isConversation()).thenReturn(true);
        notificationList.add(triggerNotification);
        final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
        final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
                triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
                triggerSection);

        // Add singleton group with conversation child
        final String groupName_valid = "testGrp_valid";
        final int summaryId_valid = 0;
        NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
                String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
        summary = spy(summary);
        when(summary.isConversation()).thenReturn(true);
        notificationList.add(summary);
        summaryByGroup.put(summary.getGroupKey(), summary);
        final String groupKey_valid = summary.getGroupKey();
        NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
                String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
        child = spy(child);
        when(child.isConversation()).thenReturn(true);
        notificationList.add(child);

        // Add singleton group with non-conversation child
        final String groupName_invalid = "testGrp_invalid";
        final int summaryId_invalid = 100;
        summary = getNotificationRecord(pkg, summaryId_invalid,
                String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
        notificationList.add(summary);
        final String groupKey_invalid = summary.getGroupKey();
        summaryByGroup.put(summary.getGroupKey(), summary);
        child = getNotificationRecord(pkg, summaryId_invalid + 42,
                String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
                false);
        notificationList.add(child);

        // Check that the invalid group will not be force grouped
        final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
                triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
        assertThat(sparseGroups).containsKey(groupKey_valid);
        assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
    }

    @Test
    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
    @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)