diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0d402e57d3353..ecab3565cfb4a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -772,7 +772,7 @@ public class NotificationManagerService extends SystemService { parser, mAllowedManagedServicePackages, forRestore, userId); migratedManagedServices = true; } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) { - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, System.currentTimeMillis()); } if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) { if (forRestore && userId != UserHandle.USER_SYSTEM) { @@ -2322,6 +2322,8 @@ public class NotificationManagerService extends SystemService { mConditionProviders.onBootPhaseAppsCanStart(); mHistoryManager.onBootPhaseAppsCanStart(); registerDeviceConfigChange(); + } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis()); } } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index bae1dd3c6cb60..d60c29119c182 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -36,6 +36,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -43,9 +44,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; -import java.sql.Array; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -172,7 +171,7 @@ public class SnoozeHelper { for (int i = 0; i < allRecords.size(); i++) { NotificationRecord r = allRecords.valueAt(i); String currentGroupKey = r.getSbn().getGroup(); - if (currentGroupKey.equals(groupKey)) { + if (Objects.equals(currentGroupKey, groupKey)) { records.add(r); } } @@ -217,7 +216,7 @@ public class SnoozeHelper { snooze(record); scheduleRepost(pkg, key, userId, duration); - Long activateAt = System.currentTimeMillis() + duration; + Long activateAt = SystemClock.elapsedRealtime() + duration; synchronized (mPersistedSnoozedNotifications) { storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); } @@ -244,8 +243,6 @@ public class SnoozeHelper { } storeRecord(record.getSbn().getPackageName(), record.getKey(), userId, mSnoozedNotifications, record); - mPackages.put(record.getKey(), record.getSbn().getPackageName()); - mUsers.put(record.getKey(), userId); } private void storeRecord(String pkg, String key, Integer userId, @@ -258,6 +255,8 @@ public class SnoozeHelper { keyToValue.put(key, object); targets.put(getPkgKey(userId, pkg), keyToValue); + mPackages.put(key, pkg); + mUsers.put(key, userId); } private T removeRecord(String pkg, String key, Integer userId, @@ -425,12 +424,34 @@ public class SnoozeHelper { PendingIntent.FLAG_UPDATE_CURRENT); } + public void scheduleRepostsForPersistedNotifications(long currentTime) { + for (ArrayMap snoozed : mPersistedSnoozedNotifications.values()) { + for (int i = 0; i < snoozed.size(); i++) { + String key = snoozed.keyAt(i); + Long time = snoozed.valueAt(i); + String pkg = mPackages.get(key); + Integer userId = mUsers.get(key); + if (time == null || pkg == null || userId == null) { + Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId); + continue; + } + if (time != null && time > currentTime) { + scheduleRepostAtTime(pkg, key, userId, time); + } + } + + } + } + private void scheduleRepost(String pkg, String key, int userId, long duration) { + scheduleRepostAtTime(pkg, key, userId, SystemClock.elapsedRealtime() + duration); + } + + private void scheduleRepostAtTime(String pkg, String key, int userId, long time) { long identity = Binder.clearCallingIdentity(); try { final PendingIntent pi = createPendingIntent(pkg, key, userId); mAm.cancel(pi); - long time = SystemClock.elapsedRealtime() + duration; if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time)); mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi); } finally { @@ -496,6 +517,7 @@ public class SnoozeHelper { private interface Inserter { void insert(T t) throws IOException; } + private void writeXml(XmlSerializer out, ArrayMap> targets, String tag, Inserter attributeInserter) @@ -503,12 +525,13 @@ public class SnoozeHelper { synchronized (targets) { final int M = targets.size(); for (int i = 0; i < M; i++) { - String userIdPkgKey = targets.keyAt(i); // T is a String (snoozed until context) or Long (snoozed until time) ArrayMap keyToValue = targets.valueAt(i); for (int j = 0; j < keyToValue.size(); j++) { String key = keyToValue.keyAt(j); T value = keyToValue.valueAt(j); + String pkg = mPackages.get(key); + Integer userId = mUsers.get(key); out.startTag(null, tag); @@ -518,8 +541,7 @@ public class SnoozeHelper { XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - String pkg = mPackages.get(key); - int userId = mUsers.get(key); + out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, String.valueOf(userId)); @@ -530,7 +552,7 @@ public class SnoozeHelper { } } - protected void readXml(XmlPullParser parser) + protected void readXml(XmlPullParser parser, long currentTime) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -547,16 +569,15 @@ public class SnoozeHelper { try { final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY); final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG); - final int userId = Integer.parseInt( - parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID)); + final int userId = XmlUtils.readIntAttribute( + parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); if (tag.equals(XML_SNOOZED_NOTIFICATION)) { - final Long time = Long.parseLong( - parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME)); - if (time > System.currentTimeMillis()) { //only read new stuff + final Long time = XmlUtils.readLongAttribute( + parser, XML_SNOOZED_NOTIFICATION_TIME, 0); + if (time > currentTime) { //only read new stuff synchronized (mPersistedSnoozedNotifications) { storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time); } - scheduleRepost(pkg, key, userId, time - System.currentTimeMillis()); } } if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index ccce0436ea595..d0283f78338da 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3246,7 +3246,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, UserHandle.USER_ALL); - verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class)); + verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 1dd0b1a6f3596..816e8e57acc6f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; @@ -96,10 +97,33 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); - mSnoozeHelper.readXml(parser); - assertTrue("Should read the notification time from xml and it should be more than zero", - 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification( - 0, "pkg", "key").doubleValue()); + mSnoozeHelper.readXml(parser, 1); + assertEquals((long) Long.MAX_VALUE, (long) mSnoozeHelper + .getSnoozeTimeForUnpostedNotification(0, "pkg", "key")); + verify(mAm, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); + } + + @Test + public void testWriteXML_afterReading_noNPE() + throws XmlPullParserException, IOException { + final String max_time_str = Long.toString(Long.MAX_VALUE); + final String xml_string = "" + + "" + + "" + + ""; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml_string.getBytes())), null); + mSnoozeHelper.readXml(parser, 1); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); } @Test @@ -115,7 +139,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 1); assertEquals("Should read the notification context from xml and it should be `uri", "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification( 0, "pkg", "key")); @@ -137,7 +161,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 1); assertTrue("Should read the notification time from xml and it should be more than zero", 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification( 0, "pkg", r.getKey()).doubleValue()); @@ -161,7 +185,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { XmlPullParser parser = Xml.newPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); - mSnoozeHelper.readXml(parser); + mSnoozeHelper.readXml(parser, 2); int systemUser = UserHandle.SYSTEM.getIdentifier(); assertTrue("Should see a past time returned", System.currentTimeMillis() > mSnoozeHelper.getSnoozeTimeForUnpostedNotification( @@ -194,6 +218,30 @@ public class SnoozeHelperTest extends UiServiceTestCase { "not_my_package", r.getKey()).longValue()); } + @Test + public void testScheduleRepostsForPersistedNotifications() throws Exception { + final String xml_string = "" + + "" + + "" + + ""; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(xml_string.getBytes())), null); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 10), captor.capture()); + assertEquals("key", captor.getValue().getIntent().getStringExtra(EXTRA_KEY)); + + ArgumentCaptor captor2 = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 15), captor2.capture()); + assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -413,6 +461,23 @@ public class SnoozeHelperTest extends UiServiceTestCase { mSnoozeHelper.getNotifications("pkg", "group", UserHandle.USER_CURRENT).size()); } + @Test + public void testGetSnoozedGroupNotifications_nonGrouped() throws Exception { + IntArray profileIds = new IntArray(); + profileIds.add(UserHandle.USER_CURRENT); + when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds); + NotificationRecord r = getNotificationRecord("pkg", 1, "tag", + UserHandle.CURRENT, "group", true); + NotificationRecord r2 = getNotificationRecord("pkg", 2, "tag", + UserHandle.CURRENT, null, true); + mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(r2, 1000); + + assertEquals(1, + mSnoozeHelper.getNotifications("pkg", "group", UserHandle.USER_CURRENT).size()); + // and no NPE + } + @Test public void testGetSnoozedNotificationByKey() throws Exception { IntArray profileIds = new IntArray();