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

Commit 3d898423 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Unsummarize notifications when feature is turned off

Test: NotificationManagerServiceTest
Test: ConversationNotificationProcessorTest
Flag: android.app.nm_summarization_ui
Fixes: 404220768
Change-Id: I8c7aa7ab7dd5797ac9aac18c312b6605a7b68bf4
parent 2ba3a051
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -135,11 +135,30 @@ class ConversationNotificationProcessorTest : SysuiTestCase() {
            .isNotNull()
    }

    @Test
    @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
    fun processNotification_messagingStyleUpdateSummarizationToNull() {
        val nb = getMessagingNotification()
        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
        newRow.entry.setRanking(
            RankingBuilder(newRow.entry.ranking).setSummarization("hello").build()
        )
        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
            .isNotNull()

        newRow.entry.setRanking(RankingBuilder(newRow.entry.ranking).setSummarization(null).build())

        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
            .isNotNull()
        assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
    }

    @Test
    @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
    fun processNotification_messagingStyleWithoutSummarization() {
        val nb = getMessagingNotification()
        val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())

        assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
            .isNotNull()
        assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
+35 −32
Original line number Diff line number Diff line
@@ -71,14 +71,14 @@ constructor(
                Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
            else if (entry.ranking.isConversation)
                Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
            else
                Notification.MessagingStyle.CONVERSATION_TYPE_LEGACY
            else Notification.MessagingStyle.CONVERSATION_TYPE_LEGACY
        entry.ranking.conversationShortcutInfo?.let { shortcutInfo ->
            logger.logAsyncTaskProgress(entry.logKey, "getting shortcut icon")
            messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
            shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label }
        }
        if (NmSummarizationUiFlag.isEnabled && !TextUtils.isEmpty(entry.ranking.summarization)) {
        if (NmSummarizationUiFlag.isEnabled) {
            if (!TextUtils.isEmpty(entry.ranking.summarization)) {
                val icon = context.getDrawable(R.drawable.ic_notification_summarization)?.mutate()
                val imageSpan =
                    icon?.let {
@@ -111,6 +111,9 @@ constructor(
                    EXTRA_SUMMARIZED_CONTENT,
                    decoratedSummary,
                )
            } else {
                entry.sbn.notification.extras.putCharSequence(EXTRA_SUMMARIZED_CONTENT, null)
            }
        }

        messagingStyle.unreadMessageCount =
+100 −107
Original line number Diff line number Diff line
@@ -1877,73 +1877,35 @@ public class NotificationManagerService extends SystemService {
        }
    };
    private void unclassifyNotificationsForUser(final int userId) {
        if (DBG) {
            Slog.v(TAG, "unclassifyForUser: " + userId);
        }
        unclassifyNotificationsFiltered((r) -> r.getUserId() == userId);
    private void applyNotificationUpdateForUser(final int userId,
            NotificationUpdate notificationUpdate) {
        applyUpdateForNotificationsFiltered((r) -> r.getUserId() == userId,
                notificationUpdate);
    }
    private void unclassifyNotificationsForUid(final int userId, @NonNull final String pkg) {
        if (DBG) {
            Slog.v(TAG, "unclassifyForUid userId: " + userId + " pkg: " + pkg);
        }
        unclassifyNotificationsFiltered((r) ->
    private void applyNotificationUpdateForUid(final int userId, @NonNull final String pkg,
            NotificationUpdate notificationUpdate) {
        applyUpdateForNotificationsFiltered((r) ->
                r.getUserId() == userId
                && Objects.equals(r.getSbn().getPackageName(), pkg));
                && Objects.equals(r.getSbn().getPackageName(), pkg),
                notificationUpdate);
    }
    private void unclassifyNotificationsForUserAndType(final int userId,
            final @Types int bundleType) {
        if (DBG) {
            Slog.v(TAG,
                    "unclassifyForUserAndType userId: " + userId + " bundleType: " + bundleType);
        }
    private void applyNotificationUpdateForUserAndChannelType(final int userId,
            final @Types int bundleType, NotificationUpdate notificationUpdate) {
        final String bundleChannelId = NotificationChannel.getChannelIdForBundleType(bundleType);
        unclassifyNotificationsFiltered((r) ->
        applyUpdateForNotificationsFiltered((r) ->
                r.getUserId() == userId
                && r.getChannel() != null
                && Objects.equals(bundleChannelId, r.getChannel().getId()));
    }
    private void unclassifyNotificationsFiltered(Predicate<NotificationRecord> filter) {
        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
            return;
        }
        synchronized (mNotificationLock) {
            for (int i = 0; i < mEnqueuedNotifications.size(); i++) {
                final NotificationRecord r = mEnqueuedNotifications.get(i);
                if (filter.test(r)) {
                    unclassifyNotificationLocked(r);
                }
                && Objects.equals(bundleChannelId, r.getChannel().getId()),
                notificationUpdate);
    }
            for (int i = 0; i < mNotificationList.size(); i++) {
                final NotificationRecord r = mNotificationList.get(i);
                if (filter.test(r)) {
                    unclassifyNotificationLocked(r);
                }
            }
        }
    }
    @GuardedBy("mNotificationLock")
    private void unclassifyNotificationLocked(@NonNull final NotificationRecord r) {
        if (DBG) {
            Slog.v(TAG, "unclassifyNotification: " + r);
        }
        // Only NotificationRecord's mChannel is updated when bundled, the Notification
        // mChannelId will always be the original channel.
        String origChannelId = r.getNotification().getChannelId();
        NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
                r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
        String currChannelId = r.getChannel().getId();
        boolean isClassified = NotificationChannel.SYSTEM_RESERVED_IDS.contains(currChannelId);
        if (originalChannel != null && !origChannelId.equals(currChannelId) && isClassified) {
            r.updateNotificationChannel(originalChannel);
            mGroupHelper.onNotificationUnbundled(r,
                    GroupHelper.isOriginalGroupSummaryPresent(r, mSummaryByGroupKey));
        }
    private void applyNotificationUpdateForUserAndType(final int userId,
            final @Types int bundleType, NotificationUpdate notificationUpdate) {
        applyUpdateForNotificationsFiltered(
                (r) -> r.getUserId() == userId && r.getBundleType() == bundleType,
                notificationUpdate);
    }
    @VisibleForTesting
@@ -1956,7 +1918,7 @@ public class NotificationManagerService extends SystemService {
            if (r == null) {
                return;
            }
            unclassifyNotificationLocked(r);
            unclassifyNotificationLocked(r, true);
        }
    }
@@ -1974,50 +1936,36 @@ public class NotificationManagerService extends SystemService {
        }
    }
    private void reclassifyNotificationsFiltered(Predicate<NotificationRecord> filter) {
        if (!(notificationClassificationUi() && notificationRegroupOnClassification())) {
            return;
        }
        synchronized (mNotificationLock) {
            for (int i = 0; i < mEnqueuedNotifications.size(); i++) {
                final NotificationRecord r = mEnqueuedNotifications.get(i);
                if (filter.test(r)) {
                    reclassifyNotificationLocked(r, false);
                }
            }
            for (int i = 0; i < mNotificationList.size(); i++) {
                final NotificationRecord r = mNotificationList.get(i);
                if (filter.test(r)) {
                    reclassifyNotificationLocked(r, true);
                }
            }
        }
    }
    private void reclassifyNotificationsForUserAndType(final int userId,
            final @Types int bundleType) {
    @GuardedBy("mNotificationLock")
    private void unclassifyNotificationLocked(@NonNull final NotificationRecord r,
            boolean isPosted) {
        if (DBG) {
            Slog.v(TAG, "reclassifyNotificationsForUserAndType userId: " + userId + " bundleType: "
                    + bundleType);
        }
        reclassifyNotificationsFiltered(
                (r) -> r.getUserId() == userId && r.getBundleType() == bundleType);
            Slog.v(TAG, "unclassifyNotification: " + r);
        }
    private void reclassifyNotificationsForUid(final int userId, final String pkg) {
        if (DBG) {
            Slog.v(TAG, "reclassifyNotificationsForUid userId: " + userId + " pkg: " + pkg);
        // Only NotificationRecord's mChannel is updated when bundled, the Notification
        // mChannelId will always be the original channel.
        String origChannelId = r.getNotification().getChannelId();
        NotificationChannel originalChannel = mPreferencesHelper.getNotificationChannel(
                r.getSbn().getPackageName(), r.getUid(), origChannelId, false);
        String currChannelId = r.getChannel().getId();
        boolean isClassified = NotificationChannel.SYSTEM_RESERVED_IDS.contains(currChannelId);
        if (originalChannel != null && !origChannelId.equals(currChannelId) && isClassified) {
            r.updateNotificationChannel(originalChannel);
            mGroupHelper.onNotificationUnbundled(r,
                    GroupHelper.isOriginalGroupSummaryPresent(r, mSummaryByGroupKey));
        }
        reclassifyNotificationsFiltered((r) ->
                r.getUserId() == userId && Objects.equals(r.getSbn().getPackageName(), pkg));
    }
    private void reclassifyNotificationsForUser(final int userId) {
        if (DBG) {
            Slog.v(TAG, "reclassifyAllNotificationsForUser: " + userId);
        }
        reclassifyNotificationsFiltered((r) -> r.getUserId() == userId);
    @GuardedBy("mNotificationLock")
    private void unsummarizeNotificationLocked(@NonNull final NotificationRecord r,
            boolean isPosted) {
        Bundle signals = new Bundle();
        signals.putString(KEY_SUMMARIZATION, null);
        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
                r.getSbn().getUserId());
        r.addAdjustment(adjustment);
        mRankingHandler.requestSort();
    }
    @GuardedBy("mNotificationLock")
@@ -2043,6 +1991,33 @@ public class NotificationManagerService extends SystemService {
        }
    }
    /**
     * Given a filter and a function to update a notification record, runs that function on all
     * enqueued and posted notifications that match the filter
     */
    private void applyUpdateForNotificationsFiltered(Predicate<NotificationRecord> filter,
            NotificationUpdate notificationUpdate) {
        synchronized (mNotificationLock) {
            for (int i = 0; i < mEnqueuedNotifications.size(); i++) {
                final NotificationRecord r = mEnqueuedNotifications.get(i);
                if (filter.test(r)) {
                    notificationUpdate.apply(r, false);
                }
            }
            for (int i = 0; i < mNotificationList.size(); i++) {
                final NotificationRecord r = mNotificationList.get(i);
                if (filter.test(r)) {
                    notificationUpdate.apply(r, true);
                }
            }
        }
    }
    private interface NotificationUpdate {
        void apply(NotificationRecord r, boolean isPosted);
    }
    NotificationManagerPrivate mNotificationManagerPrivate = new NotificationManagerPrivate() {
        @Nullable
        @Override
@@ -4475,9 +4450,11 @@ public class NotificationManagerService extends SystemService {
        public void allowAssistantAdjustment(String adjustmentType) {
            checkCallerIsSystemOrSystemUiOrShell();
            mAssistants.allowAdjustmentType(adjustmentType);
            int userId = UserHandle.getUserId(Binder.getCallingUid());
            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
                if (KEY_TYPE.equals(adjustmentType)) {
                    reclassifyNotificationsForUser(UserHandle.getUserId(Binder.getCallingUid()));
                    applyNotificationUpdateForUser(userId,
                            NotificationManagerService.this::reclassifyNotificationLocked);
                }
            }
            handleSavePolicyFile();
@@ -4488,9 +4465,17 @@ public class NotificationManagerService extends SystemService {
        public void disallowAssistantAdjustment(String adjustmentType) {
            checkCallerIsSystemOrSystemUiOrShell();
            mAssistants.disallowAdjustmentType(adjustmentType);
            int userId = UserHandle.getUserId(Binder.getCallingUid());
            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
                if (KEY_TYPE.equals(adjustmentType)) {
                    unclassifyNotificationsForUser(UserHandle.getUserId(Binder.getCallingUid()));
                    applyNotificationUpdateForUser(userId,
                            NotificationManagerService.this::unclassifyNotificationLocked);
                }
            }
            if (nmSummarizationUi() || nmSummarization()) {
                if (KEY_SUMMARIZATION.equals(adjustmentType)) {
                    applyNotificationUpdateForUser(userId,
                            NotificationManagerService.this::unsummarizeNotificationLocked);
                }
            }
            handleSavePolicyFile();
@@ -4539,11 +4524,13 @@ public class NotificationManagerService extends SystemService {
            mAssistants.setAssistantAdjustmentKeyTypeState(type, enabled);
            if ((notificationClassificationUi() && notificationRegroupOnClassification())) {
                if (enabled) {
                    reclassifyNotificationsForUserAndType(
                            UserHandle.getUserId(Binder.getCallingUid()), type);
                    applyNotificationUpdateForUserAndType(
                            UserHandle.getUserId(Binder.getCallingUid()), type,
                            NotificationManagerService.this::reclassifyNotificationLocked);
                } else {
                    unclassifyNotificationsForUserAndType(
                            UserHandle.getUserId(Binder.getCallingUid()), type);
                    applyNotificationUpdateForUserAndChannelType(
                            UserHandle.getUserId(Binder.getCallingUid()), type,
                            NotificationManagerService.this::unclassifyNotificationLocked);
                }
            }
            handleSavePolicyFile();
@@ -4569,11 +4556,17 @@ public class NotificationManagerService extends SystemService {
            if (notificationClassificationUi() && notificationRegroupOnClassification()
                    && key.equals(KEY_TYPE)) {
                if (enabled) {
                    reclassifyNotificationsForUid(UserHandle.getUserId(Binder.getCallingUid()),
                            pkg);
                    applyNotificationUpdateForUid(UserHandle.getUserId(Binder.getCallingUid()),
                            pkg, NotificationManagerService.this::reclassifyNotificationLocked);
                } else {
                    unclassifyNotificationsForUid(UserHandle.getUserId(Binder.getCallingUid()),
                            pkg);
                    applyNotificationUpdateForUid(UserHandle.getUserId(Binder.getCallingUid()),
                            pkg, NotificationManagerService.this::unclassifyNotificationLocked);
                }
            }
            if (nmSummarization() || nmSummarizationUi()) {
                if (KEY_SUMMARIZATION.equals(key) && !enabled) {
                    applyNotificationUpdateForUid(UserHandle.getUserId(Binder.getCallingUid()),
                            pkg, NotificationManagerService.this::unsummarizeNotificationLocked);
                }
            }
            handleSavePolicyFile();
+1 −1
Original line number Diff line number Diff line
@@ -999,7 +999,7 @@ public final class NotificationRecord {
        return null;
    }

    public String getSummarization() {
    public @Nullable String getSummarization() {
        if ((android.app.Flags.nmSummarizationUi() || android.app.Flags.nmSummarization())) {
            return mSummarization;
        }
+68 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.app.Flags.FLAG_NM_SUMMARIZATION;
import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
import static android.app.Notification.EXTRA_PICTURE;
@@ -105,6 +106,7 @@ import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_SUMMARIZATION;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
@@ -18308,9 +18310,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Post some notifications and classify in different bundles
        final int numNotifications = NotificationChannel.SYSTEM_RESERVED_IDS.size();
        final int numNewsNotifications = 1;
        List<String> postedNotificationKeys = new ArrayList();
        for (int i = 0; i < numNotifications; i++) {
            NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, i, mUserId);
            mService.addNotification(r);
            postedNotificationKeys.add(r.getKey());
            Bundle signals = new Bundle();
            final int adjustmentType = i + 1;
            signals.putInt(Adjustment.KEY_TYPE, adjustmentType);
@@ -18330,7 +18334,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        waitForIdle();
        //Check that all notifications classified as TYPE_NEWS have been unbundled
        for (NotificationRecord record : mService.mNotificationList) {
        for (String key : postedNotificationKeys) {
            NotificationRecord record= mService.mNotificationsByKey.get(key);
            // Check that the original channel was restored
            // for notifications classified as TYPE_NEWS
            if (record.getBundleType() == TYPE_NEWS) {
@@ -18355,7 +18360,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Check that the bundle channel was restored
        verify(mRankingHandler, times(numNewsNotifications)).requestSort();
        for (NotificationRecord record : mService.mNotificationList) {
        for (String key : postedNotificationKeys) {
            NotificationRecord record= mService.mNotificationsByKey.get(key);
            assertThat(record.getChannel().getId()).isIn(NotificationChannel.SYSTEM_RESERVED_IDS);
        }
    }
@@ -18424,6 +18430,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        }
    }
    @Test
    @EnableFlags({FLAG_NM_SUMMARIZATION})
    public void testDisableBundleAdjustmentByPkg_unsummarizesNotifications() throws Exception {
        NotificationManagerService.WorkerHandler handler = mock(
                NotificationManagerService.WorkerHandler.class);
        mService.setHandler(handler);
        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, mUserId);
        mService.addNotification(r);
        Bundle signals = new Bundle();
        signals.putCharSequence(Adjustment.KEY_SUMMARIZATION, "hello");
        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
                "", r.getUser().getIdentifier());
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r.applyAdjustments();
        Mockito.clearInvocations(mRankingHandler);
        // Disable summarization for package
        mBinderService.setAdjustmentSupportedForPackage(KEY_SUMMARIZATION, mPkg, false);
        verify(mRankingHandler).requestSort();
        mService.handleRankingSort();
        assertThat(mService.mNotificationsByKey.get(r.getKey()).getSummarization()).isNull();
    }
    @Test
    @EnableFlags({FLAG_NOTIFICATION_CLASSIFICATION,
            FLAG_NOTIFICATION_FORCE_GROUPING,
@@ -18626,6 +18662,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        }
    }
    @Test
    @EnableFlags({FLAG_NM_SUMMARIZATION})
    public void testDisableBundleAdjustment_unsummarizesNotifications() throws Exception {
        NotificationManagerService.WorkerHandler handler = mock(
                NotificationManagerService.WorkerHandler.class);
        mService.setHandler(handler);
        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
        when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
        when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
        when(mAssistants.isAdjustmentAllowedForPackage(anyString(), anyString())).thenReturn(true);
        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, mUserId);
        mService.addNotification(r);
        Bundle signals = new Bundle();
        signals.putCharSequence(Adjustment.KEY_SUMMARIZATION, "hello");
        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
                "", r.getUser().getIdentifier());
        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
        waitForIdle();
        r.applyAdjustments();
        Mockito.clearInvocations(mRankingHandler);
        // Disable summarization for package
        mBinderService.disallowAssistantAdjustment(KEY_SUMMARIZATION);
        verify(mRankingHandler).requestSort();
        mService.handleRankingSort();
        assertThat(mService.mNotificationsByKey.get(r.getKey()).getSummarization()).isNull();
    }
    @Test
    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
    public void clearAll_fromUser_willSendDeleteIntentForCachedSummaries() throws Exception {