Merge "notification: fix Alarm & PendingIntent leak" into main
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e633ba6..db04093 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8237,19 +8237,26 @@
         }
     }
 
+    private PendingIntent getNotificationTimeoutPendingIntent(NotificationRecord record,
+            int flags) {
+        flags |= PendingIntent.FLAG_IMMUTABLE;
+        return PendingIntent.getBroadcast(getContext(),
+                REQUEST_CODE_TIMEOUT,
+                new Intent(ACTION_NOTIFICATION_TIMEOUT)
+                        .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
+                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+                                .appendPath(record.getKey()).build())
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                        .putExtra(EXTRA_KEY, record.getKey()),
+                flags);
+    }
+
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
     void scheduleTimeoutLocked(NotificationRecord record) {
         if (record.getNotification().getTimeoutAfter() > 0) {
-            final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                    REQUEST_CODE_TIMEOUT,
-                    new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                            .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
-                            .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                    .appendPath(record.getKey()).build())
-                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
-                            .putExtra(EXTRA_KEY, record.getKey()),
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+            final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                    record, PendingIntent.FLAG_UPDATE_CURRENT);
             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
         }
@@ -8257,6 +8264,16 @@
 
     @VisibleForTesting
     @GuardedBy("mNotificationLock")
+    void cancelScheduledTimeoutLocked(NotificationRecord record) {
+        final PendingIntent pi = getNotificationTimeoutPendingIntent(
+                record, PendingIntent.FLAG_CANCEL_CURRENT);
+        if (pi != null) {
+            mAlarmManager.cancel(pi);
+        }
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mNotificationLock")
     /**
      * Determine whether this notification should attempt to make noise, vibrate, or flash the LED
      * @return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
@@ -9249,21 +9266,7 @@
             int rank, int count, boolean wasPosted, String listenerName,
             @ElapsedRealtimeLong long cancellationElapsedTimeMs) {
         final String canceledKey = r.getKey();
-
-        // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
-        // does not already exist, then null will be returned.
-        final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
-                REQUEST_CODE_TIMEOUT,
-                new Intent(ACTION_NOTIFICATION_TIMEOUT)
-                        .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
-                                .appendPath(r.getKey()).build())
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
-                PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
-
-        // Cancel alarm corresponding to pi.
-        if (pi != null) {
-            mAlarmManager.cancel(pi);
-        }
+        cancelScheduledTimeoutLocked(r);
 
         // Record caller.
         recordCallerLocked(r);
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 6a6fa3f..189dbdc 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1104,6 +1104,11 @@
         verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
         assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
                 captor.getValue().getIntent().getPackage());
+
+        mService.cancelScheduledTimeoutLocked(r);
+        verify(mAlarmManager).cancel(captor.capture());
+        assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+                captor.getValue().getIntent().getPackage());
     }
 
     @Test