summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/NotificationHistory.java20
-rw-r--r--core/tests/coretests/src/android/app/NotificationHistoryTest.java34
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java48
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryManager.java18
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java18
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java47
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java14
8 files changed, 193 insertions, 7 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 16c0910f1273..19397ed3ebaa 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -113,6 +113,7 @@ interface INotificationManager
int getAppsBypassingDndCount(int uid);
ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
boolean isPackagePaused(String pkg);
+ void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
void silenceNotificationSound();
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 909a476f3c94..f26e62874347 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -346,6 +346,26 @@ public final class NotificationHistory implements Parcelable {
}
/**
+ * Removes an individual historical notification and regenerates the string pool
+ */
+ public boolean removeNotificationFromWrite(String packageName, long postedTime) {
+ boolean removed = false;
+ for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
+ HistoricalNotification hn = mNotificationsToWrite.get(i);
+ if (packageName.equals(hn.getPackage())
+ && postedTime == hn.getPostedTimeMs()) {
+ removed = true;
+ mNotificationsToWrite.remove(i);
+ }
+ }
+ if (removed) {
+ poolStringsFromNotifications();
+ }
+
+ return removed;
+ }
+
+ /**
* Gets pooled strings in order to write them to disk
*/
public @NonNull String[] getPooledStringsToWrite() {
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 0a21875fd77d..d1608d055604 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -234,6 +234,40 @@ public class NotificationHistoryTest {
}
@Test
+ public void testRemoveNotificationFromWrite() {
+ NotificationHistory history = new NotificationHistory();
+
+ List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
+ List<String> postRemoveExpectedStrings = new ArrayList<>();
+ for (int i = 1; i <= 10; i++) {
+ HistoricalNotification n = getHistoricalNotification("pkg", i);
+
+ if (987654323 != n.getPostedTimeMs()) {
+ postRemoveExpectedStrings.add(n.getPackage());
+ postRemoveExpectedStrings.add(n.getChannelName());
+ postRemoveExpectedStrings.add(n.getChannelId());
+ postRemoveExpectedEntries.add(n);
+ }
+
+ history.addNotificationToWrite(n);
+ }
+
+ history.poolStringsFromNotifications();
+
+ assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
+ // 1 package name and 10 unique channel names and ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+
+ history.removeNotificationFromWrite("pkg", 987654323);
+
+
+ // 1 package names and 9 * 2 unique channel names and ids
+ assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
+ assertThat(history.getNotificationsToWrite())
+ .containsExactlyElementsIn(postRemoveExpectedEntries);
+ }
+
+ @Test
public void testParceling() {
NotificationHistory history = new NotificationHistory();
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index e8cb1632bdc3..061cbd8a1a40 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -170,6 +170,11 @@ public class NotificationHistoryDatabase {
mFileWriteHandler.post(rpr);
}
+ public void deleteNotificationHistoryItem(String pkg, long postedTime) {
+ RemoveNotificationRunnable rnr = new RemoveNotificationRunnable(pkg, postedTime);
+ mFileWriteHandler.post(rnr);
+ }
+
public void addNotification(final HistoricalNotification notification) {
synchronized (mLock) {
mBuffer.addNewNotificationToWrite(notification);
@@ -367,6 +372,49 @@ public class NotificationHistoryDatabase {
}
}
+ final class RemoveNotificationRunnable implements Runnable {
+ private String mPkg;
+ private long mPostedTime;
+ private NotificationHistory mNotificationHistory;
+
+ public RemoveNotificationRunnable(String pkg, long postedTime) {
+ mPkg = pkg;
+ mPostedTime = postedTime;
+ }
+
+ @VisibleForTesting
+ void setNotificationHistory(NotificationHistory nh) {
+ mNotificationHistory = nh;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) Slog.d(TAG, "RemovePackageRunnable");
+ synchronized (mLock) {
+ // Remove from pending history
+ mBuffer.removeNotificationFromWrite(mPkg, mPostedTime);
+
+ 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.removeNotificationFromWrite(mPkg, mPostedTime)) {
+ writeLocked(af, notificationHistory);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot clean up file on notification removal "
+ + af.getBaseFile().getName(), e);
+ }
+ }
+ }
+ }
+ }
+
public static final class NotificationHistoryFileAttrProvider implements
NotificationHistoryDatabase.FileAttrProvider {
final static String TAG = "NotifHistoryFileDate";
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 41bc29f7a82e..9aab0fd912e8 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -130,7 +130,7 @@ public class NotificationHistoryManager {
}
}
- public void onPackageRemoved(int userId, String packageName) {
+ public void onPackageRemoved(@UserIdInt int userId, String packageName) {
synchronized (mLock) {
if (!mUserUnlockedStates.get(userId, false)) {
if (mHistoryEnabled.get(userId, false)) {
@@ -150,6 +150,22 @@ public class NotificationHistoryManager {
}
}
+ public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+ 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 notif for locked/gone/disabled user "
+ + userId);
+ return;
+ }
+ userHistory.deleteNotificationHistoryItem(pkg, postedTime);
+ }
+ }
+
// TODO: wire this up to AMS when power button is long pressed
public void triggerWriteToDisk() {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f07113591fa5..5fa536caf349 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2780,7 +2780,7 @@ public class NotificationManagerService extends SystemService {
if (!isSystemToast) {
int count = 0;
final int N = mToastQueue.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
final ToastRecord r = mToastQueue.get(i);
if (r.pkg.equals(pkg)) {
count++;
@@ -2820,7 +2820,7 @@ public class NotificationManagerService extends SystemService {
if (pkg == null || token == null) {
Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " token=" + token);
- return ;
+ return;
}
synchronized (mToastQueue) {
@@ -2933,14 +2933,14 @@ public class NotificationManagerService extends SystemService {
/**
* Updates the enabled state for notifications for the given package (and uid).
- * Additionally, this method marks the app importance as locked by the user, which means
+ * Additionally, this method marks the app importance as locked by the user, which
+ * means
* that notifications from the app will <b>not</b> be considered for showing a
* blocking helper.
*
- * @param pkg package that owns the notifications to update
- * @param uid uid of the app providing notifications
+ * @param pkg package that owns the notifications to update
+ * @param uid uid of the app providing notifications
* @param enabled whether notifications should be enabled for the app
- *
* @see #setNotificationsEnabledForPackage(String, int, boolean)
*/
@Override
@@ -3031,6 +3031,12 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+ checkCallerIsSystem();
+ mHistoryManager.deleteNotificationHistoryItem(pkg, uid, postedTime);
+ }
+
+ @Override
public int getPackageImportance(String pkg) {
checkCallerIsSystemOrSameApp(pkg);
return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 5b5ad877081c..cbb760a7a871 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -44,6 +44,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.internal.matchers.Not;
import java.io.File;
import java.util.ArrayList;
@@ -239,6 +240,52 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
verify(af2, never()).openRead();
}
+ @Test
+ public void testRemoveNotificationRunnable() throws Exception {
+ NotificationHistory nh = mock(NotificationHistory.class);
+ NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+ mDataBase.new RemoveNotificationRunnable("pkg", 123);
+ rnr.setNotificationHistory(nh);
+
+ AtomicFile af = mock(AtomicFile.class);
+ when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+ mDataBase.mHistoryFiles.addLast(af);
+
+ when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true);
+
+ mDataBase.mBuffer = mock(NotificationHistory.class);
+
+ rnr.run();
+
+ verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+ verify(af).openRead();
+ verify(nh).removeNotificationFromWrite("pkg", 123);
+ verify(af).startWrite();
+ }
+
+ @Test
+ public void testRemoveNotificationRunnable_noChanges() throws Exception {
+ NotificationHistory nh = mock(NotificationHistory.class);
+ NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+ mDataBase.new RemoveNotificationRunnable("pkg", 123);
+ rnr.setNotificationHistory(nh);
+
+ AtomicFile af = mock(AtomicFile.class);
+ when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+ mDataBase.mHistoryFiles.addLast(af);
+
+ when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false);
+
+ mDataBase.mBuffer = mock(NotificationHistory.class);
+
+ rnr.run();
+
+ verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+ verify(af).openRead();
+ verify(nh).removeNotificationFromWrite("pkg", 123);
+ verify(af, never()).startWrite();
+ }
+
private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
public Map<File, Long> creationDates = new HashMap<>();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index b5eb1bf31848..2c548be185c9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -289,6 +289,20 @@ public class NotificationHistoryManagerTest extends UiServiceTestCase {
}
@Test
+ public void testDeleteNotificationHistoryItem_userUnlocked() {
+ String pkg = "pkg";
+ long time = 235;
+ NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
+
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
+
+ mHistoryManager.deleteNotificationHistoryItem(pkg, 1, time);
+
+ verify(userHistory, times(1)).deleteNotificationHistoryItem(pkg, time);
+ }
+
+ @Test
public void testTriggerWriteToDisk() {
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);