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

Commit 434fd329 authored by Alexander Roederer's avatar Alexander Roederer
Browse files

Clear recent notifs in History on package removed

Clears recently dismissed notifications from Notification History when
an app package is removed. We already did this when a notification
channel was removed, but not when the whole package was removed. We also
already were clearing these from the 24 hour history, but not from the
recent history.

Bug: 308319922
Test: atest ArchiveTest, atest NotificationManagerServiceTest
Change-Id: Id1fbbf3fc9453d6d7bdc24aab331a58e52194f0b
parent bb6a1542
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -828,6 +828,22 @@ public class NotificationManagerService extends SystemService {
            }
        }
        // Removes all notifications with the specified user & package.
        public void removePackageNotifications(String pkg, @UserIdInt int userId) {
            synchronized (mBufferLock) {
                Iterator<Pair<StatusBarNotification, Integer>> bufferIter = descendingIterator();
                while (bufferIter.hasNext()) {
                    final Pair<StatusBarNotification, Integer> pair = bufferIter.next();
                    if (pair.first != null
                            && userId == pair.first.getNormalizedUserId()
                            && pkg != null && pkg.equals(pair.first.getPackageName())
                            && pair.first.getNotification() != null) {
                        bufferIter.remove();
                    }
                }
            }
        }
        void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
            synchronized (mBufferLock) {
                Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
@@ -1902,7 +1918,6 @@ public class NotificationManagerService extends SystemService {
                        unhideNotificationsForPackages(pkgList, uidList);
                    }
                }
                mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
            }
        }
@@ -4216,7 +4231,8 @@ public class NotificationManagerService extends SystemService {
            boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
                    pkg, callingUid, channelId, callingUid, isSystemOrSystemUi);
            if (previouslyExisted) {
                // Remove from both recent notification archive and notification history
                // Remove from both recent notification archive (recently dismissed notifications)
                // and notification history
                mArchive.removeChannelNotifications(pkg, callingUser, channelId);
                mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
                mListeners.notifyNotificationChannelChanged(pkg,
@@ -9418,7 +9434,11 @@ public class NotificationManagerService extends SystemService {
            for (int i = 0; i < size; i++) {
                final String pkg = pkgList[i];
                final int uid = uidList[i];
                mHistoryManager.onPackageRemoved(UserHandle.getUserId(uid), pkg);
                final int userHandle = UserHandle.getUserId(uid);
                // Removes this package's notifications from both recent notification archive
                // (recently dismissed notifications) and notification history.
                mArchive.removePackageNotifications(pkg, userHandle);
                mHistoryManager.onPackageRemoved(userHandle, pkg);
            }
        }
        if (preferencesChanged) {
+30 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;

@@ -39,6 +40,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -55,6 +57,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ArchiveTest extends UiServiceTestCase {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private static final int SIZE = 5;

    private NotificationManagerService.Archive mArchive;
@@ -249,4 +254,29 @@ public class ArchiveTest extends UiServiceTestCase {
            assertThat(expected).contains(sbn.getKey());
        }
    }

    @Test
    public void testRemoveNotificationsByPackage() {
        List<String> expected = new ArrayList<>();

        StatusBarNotification sbn_remove = getNotification("pkg_remove", 0,
                UserHandle.of(USER_CURRENT));
        mArchive.record(sbn_remove, REASON_CANCEL);

        StatusBarNotification sbn_keep = getNotification("pkg_keep", 1,
                UserHandle.of(USER_CURRENT));
        mArchive.record(sbn_keep, REASON_CANCEL);
        expected.add(sbn_keep.getKey());

        StatusBarNotification sbn_remove2 = getNotification("pkg_remove", 2,
                UserHandle.of(USER_CURRENT));
        mArchive.record(sbn_remove2, REASON_CANCEL);

        mArchive.removePackageNotifications("pkg_remove", USER_CURRENT);
        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(mUm, SIZE, true));
        assertThat(actual).hasSize(expected.size());
        for (StatusBarNotification sbn : actual) {
            assertThat(expected).contains(sbn.getKey());
        }
    }
}
+68 −2
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -72,7 +73,6 @@ import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
@@ -307,7 +307,6 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@@ -813,6 +812,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mPackageIntentReceiver.onReceive(getContext(), intent);
    }
    private void simulatePackageRemovedBroadcast(String pkg, int uid) {
        // mimics receive broadcast that package is removed, but doesn't remove the package.
        final Bundle extras = new Bundle();
        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
                new String[]{pkg});
        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid});
        final Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
        intent.setData(Uri.parse("package:" + pkg));
        intent.putExtras(extras);
        mPackageIntentReceiver.onReceive(getContext(), intent);
    }
    private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) {
        // mimics receive broadcast that package is (un)distracting
        // but does not actually register that info with packagemanager
@@ -878,6 +891,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mTestNotificationChannel.setAllowBubbles(channelEnabled);
    }
    private void setUpPrefsForHistory(int uid, boolean globalEnabled) {
        // Sets NOTIFICATION_HISTORY_ENABLED setting for calling process uid
        Settings.Secure.putIntForUser(mContext.getContentResolver(),
                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0, uid);
        // Sets NOTIFICATION_HISTORY_ENABLED setting for uid 0
        Settings.Secure.putInt(mContext.getContentResolver(),
                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, globalEnabled ? 1 : 0);
        // Forces an update by calling observe on mSettingsObserver, which picks up the settings
        // changes above.
        mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper);
        assertEquals(globalEnabled, Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0 /* =def */, uid) != 0);
    }
    private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
        Notification.Builder nb = new Notification.Builder(mContext, "a")
                .setContentTitle("foo")
@@ -9830,6 +9859,43 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        verify(mHistoryManager, times(1)).onPackageRemoved(UserHandle.getUserId(uids[1]), pkgs[1]);
    }
    @Test
    public void testHandleOnPackageRemoved_ClearsHistory() throws RemoteException {
        // Enables Notification History setting
        setUpPrefsForHistory(mUid, true /* =enabled */);
        // Posts a notification to the mTestNotificationChannel.
        final NotificationRecord notif = generateNotificationRecord(
                mTestNotificationChannel, 1, null, false);
        mService.addNotification(notif);
        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(
                notif.getSbn().getPackageName());
        assertEquals(1, notifs.length);
        // Cancels all notifications.
        mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0,
                notif.getUserId(), REASON_CANCEL);
        waitForIdle();
        notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
        assertEquals(0, notifs.length);
        // Checks that notification history's recently canceled archive contains the notification.
        notifs = mBinderService.getHistoricalNotificationsWithAttribution(PKG,
                        mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */);
        waitForIdle();
        assertEquals(1, notifs.length);
        // Remove sthe package that contained the channel
        simulatePackageRemovedBroadcast(PKG, mUid);
        waitForIdle();
        // Checks that notification history no longer contains the notification.
        notifs = mBinderService.getHistoricalNotificationsWithAttribution(
                PKG, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */);
        waitForIdle();
        assertEquals(0, notifs.length);
    }
    @Test
    public void testNotificationHistory_addNoisyNotification() throws Exception {
        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,