Loading core/java/android/app/NotificationHistory.java +20 −0 Original line number Diff line number Diff line Loading @@ -403,6 +403,26 @@ public final class NotificationHistory implements Parcelable { return removed; } /** * Removes all notifications from a channel and regenerates the string pool */ public boolean removeChannelFromWrite(String packageName, String channelId) { boolean removed = false; for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) { HistoricalNotification hn = mNotificationsToWrite.get(i); if (packageName.equals(hn.getPackage()) && Objects.equals(channelId, hn.getChannelId())) { removed = true; mNotificationsToWrite.remove(i); } } if (removed) { poolStringsFromNotifications(); } return removed; } /** * Gets pooled strings in order to write them to disk */ Loading core/tests/coretests/src/android/app/NotificationHistoryTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; Loading Loading @@ -326,6 +328,48 @@ public class NotificationHistoryTest { .containsExactlyElementsIn(postRemoveExpectedEntries); } @Test public void testRemoveChannelFromWrite() { NotificationHistory history = new NotificationHistory(); List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>(); Set<String> postRemoveExpectedStrings = new HashSet<>(); for (int i = 1; i <= 10; i++) { HistoricalNotification n = getHistoricalNotification("pkg", i); // Remove channel numbers 5 and 6 if (i != 5 && i != 6) { postRemoveExpectedStrings.add(n.getPackage()); postRemoveExpectedStrings.add(n.getChannelName()); postRemoveExpectedStrings.add(n.getChannelId()); if (n.getConversationId() != null) { postRemoveExpectedStrings.add(n.getConversationId()); } postRemoveExpectedEntries.add(n); } history.addNotificationToWrite(n); } // add second notification with the same channel id that will also be removed history.addNotificationToWrite(getHistoricalNotification("pkg", 6)); history.poolStringsFromNotifications(); assertThat(history.getNotificationsToWrite().size()).isEqualTo(11); // 1 package name and 20 unique channel names and ids and 5 conversation ids assertThat(history.getPooledStringsToWrite().length).isEqualTo(26); history.removeChannelFromWrite("pkg", "channelId5"); history.removeChannelFromWrite("pkg", "channelId6"); // 1 package names and 8 * 2 unique channel names and ids and 4 conversation ids assertThat(history.getPooledStringsToWrite().length).isEqualTo(21); assertThat(Arrays.asList(history.getPooledStringsToWrite())) .containsExactlyElementsIn(postRemoveExpectedStrings); assertThat(history.getNotificationsToWrite()) .containsExactlyElementsIn(postRemoveExpectedEntries); } @Test public void testParceling() { NotificationHistory history = new NotificationHistory(); Loading services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +48 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,11 @@ public class NotificationHistoryDatabase { mFileWriteHandler.post(rcr); } public void deleteNotificationChannel(String pkg, String channelId) { RemoveChannelRunnable rcr = new RemoveChannelRunnable(pkg, channelId); mFileWriteHandler.post(rcr); } public void addNotification(final HistoricalNotification notification) { synchronized (mLock) { mBuffer.addNewNotificationToWrite(notification); Loading Loading @@ -505,4 +510,47 @@ public class NotificationHistoryDatabase { } } } final class RemoveChannelRunnable implements Runnable { private String mPkg; private String mChannelId; private NotificationHistory mNotificationHistory; RemoveChannelRunnable(String pkg, String channelId) { mPkg = pkg; mChannelId = channelId; } @VisibleForTesting void setNotificationHistory(NotificationHistory nh) { mNotificationHistory = nh; } @Override public void run() { if (DEBUG) Slog.d(TAG, "RemoveChannelRunnable"); synchronized (mLock) { // Remove from pending history mBuffer.removeChannelFromWrite(mPkg, mChannelId); Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator(); while (historyFileItr.hasNext()) { final AtomicFile af = historyFileItr.next(); try { NotificationHistory notificationHistory = mNotificationHistory != null ? mNotificationHistory : new NotificationHistory(); readLocked(af, notificationHistory, new NotificationHistoryFilter.Builder().build()); if (notificationHistory.removeChannelFromWrite(mPkg, mChannelId)) { writeLocked(af, notificationHistory); } } catch (Exception e) { Slog.e(TAG, "Cannot clean up file on channel removal " + af.getBaseFile().getName(), e); } } } } } } services/core/java/com/android/server/notification/NotificationHistoryManager.java +16 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,22 @@ public class NotificationHistoryManager { } } public void deleteNotificationChannel(String pkg, int uid, String channelId) { synchronized (mLock) { int userId = UserHandle.getUserId(uid); final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); // TODO: it shouldn't be possible to delete a notification entry while the user is // locked but we should handle it if (userHistory == null) { Slog.w(TAG, "Attempted to remove channel for locked/gone/disabled user " + userId); return; } userHistory.deleteNotificationChannel(pkg, channelId); } } public void triggerWriteToDisk() { synchronized (mLock) { final int userCount = mUserState.size(); Loading services/core/java/com/android/server/notification/NotificationManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -3623,6 +3623,7 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, callingUser, REASON_CHANNEL_REMOVED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId); mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true), Loading Loading
core/java/android/app/NotificationHistory.java +20 −0 Original line number Diff line number Diff line Loading @@ -403,6 +403,26 @@ public final class NotificationHistory implements Parcelable { return removed; } /** * Removes all notifications from a channel and regenerates the string pool */ public boolean removeChannelFromWrite(String packageName, String channelId) { boolean removed = false; for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) { HistoricalNotification hn = mNotificationsToWrite.get(i); if (packageName.equals(hn.getPackage()) && Objects.equals(channelId, hn.getChannelId())) { removed = true; mNotificationsToWrite.remove(i); } } if (removed) { poolStringsFromNotifications(); } return removed; } /** * Gets pooled strings in order to write them to disk */ Loading
core/tests/coretests/src/android/app/NotificationHistoryTest.java +44 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; Loading Loading @@ -326,6 +328,48 @@ public class NotificationHistoryTest { .containsExactlyElementsIn(postRemoveExpectedEntries); } @Test public void testRemoveChannelFromWrite() { NotificationHistory history = new NotificationHistory(); List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>(); Set<String> postRemoveExpectedStrings = new HashSet<>(); for (int i = 1; i <= 10; i++) { HistoricalNotification n = getHistoricalNotification("pkg", i); // Remove channel numbers 5 and 6 if (i != 5 && i != 6) { postRemoveExpectedStrings.add(n.getPackage()); postRemoveExpectedStrings.add(n.getChannelName()); postRemoveExpectedStrings.add(n.getChannelId()); if (n.getConversationId() != null) { postRemoveExpectedStrings.add(n.getConversationId()); } postRemoveExpectedEntries.add(n); } history.addNotificationToWrite(n); } // add second notification with the same channel id that will also be removed history.addNotificationToWrite(getHistoricalNotification("pkg", 6)); history.poolStringsFromNotifications(); assertThat(history.getNotificationsToWrite().size()).isEqualTo(11); // 1 package name and 20 unique channel names and ids and 5 conversation ids assertThat(history.getPooledStringsToWrite().length).isEqualTo(26); history.removeChannelFromWrite("pkg", "channelId5"); history.removeChannelFromWrite("pkg", "channelId6"); // 1 package names and 8 * 2 unique channel names and ids and 4 conversation ids assertThat(history.getPooledStringsToWrite().length).isEqualTo(21); assertThat(Arrays.asList(history.getPooledStringsToWrite())) .containsExactlyElementsIn(postRemoveExpectedStrings); assertThat(history.getNotificationsToWrite()) .containsExactlyElementsIn(postRemoveExpectedEntries); } @Test public void testParceling() { NotificationHistory history = new NotificationHistory(); Loading
services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +48 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,11 @@ public class NotificationHistoryDatabase { mFileWriteHandler.post(rcr); } public void deleteNotificationChannel(String pkg, String channelId) { RemoveChannelRunnable rcr = new RemoveChannelRunnable(pkg, channelId); mFileWriteHandler.post(rcr); } public void addNotification(final HistoricalNotification notification) { synchronized (mLock) { mBuffer.addNewNotificationToWrite(notification); Loading Loading @@ -505,4 +510,47 @@ public class NotificationHistoryDatabase { } } } final class RemoveChannelRunnable implements Runnable { private String mPkg; private String mChannelId; private NotificationHistory mNotificationHistory; RemoveChannelRunnable(String pkg, String channelId) { mPkg = pkg; mChannelId = channelId; } @VisibleForTesting void setNotificationHistory(NotificationHistory nh) { mNotificationHistory = nh; } @Override public void run() { if (DEBUG) Slog.d(TAG, "RemoveChannelRunnable"); synchronized (mLock) { // Remove from pending history mBuffer.removeChannelFromWrite(mPkg, mChannelId); Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator(); while (historyFileItr.hasNext()) { final AtomicFile af = historyFileItr.next(); try { NotificationHistory notificationHistory = mNotificationHistory != null ? mNotificationHistory : new NotificationHistory(); readLocked(af, notificationHistory, new NotificationHistoryFilter.Builder().build()); if (notificationHistory.removeChannelFromWrite(mPkg, mChannelId)) { writeLocked(af, notificationHistory); } } catch (Exception e) { Slog.e(TAG, "Cannot clean up file on channel removal " + af.getBaseFile().getName(), e); } } } } } }
services/core/java/com/android/server/notification/NotificationHistoryManager.java +16 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,22 @@ public class NotificationHistoryManager { } } public void deleteNotificationChannel(String pkg, int uid, String channelId) { synchronized (mLock) { int userId = UserHandle.getUserId(uid); final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); // TODO: it shouldn't be possible to delete a notification entry while the user is // locked but we should handle it if (userHistory == null) { Slog.w(TAG, "Attempted to remove channel for locked/gone/disabled user " + userId); return; } userHistory.deleteNotificationChannel(pkg, channelId); } } public void triggerWriteToDisk() { synchronized (mLock) { final int userCount = mUserState.size(); Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -3623,6 +3623,7 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true, callingUser, REASON_CHANNEL_REMOVED, null); mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId); mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId); mListeners.notifyNotificationChannelChanged(pkg, UserHandle.getUserHandleForUid(callingUid), mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true), Loading