From 31f403284e2b2fa88cca68974b2ad902dca01521 Mon Sep 17 00:00:00 2001
From: Julia Tuttle <juliatuttle@google.com>
Date: Mon, 18 Dec 2023 13:13:21 -0500
Subject: [PATCH] BigPictureStyle: set unused big picture extra to null

ag/24426105 changed BigPictureStyle so that whichever of EXTRA_PICTURE
or EXTRA_PICTURE_ICON wasn't set to the big picture would be *removed*
instead of set to null.

This was an app-visible behavior change, and turned out to break one of
our own tests, b/315128763. Since it has the potential to affect apps
that interact with notifications, I'm modifying it to preserve the old
null value behavior, while leaving the other bitmap expiration work in
that change alone.

Bug: 290381858
Bug: 315128763
Test: atest NotificationTest
Test: atest NotificationManagerServiceTest
Flag: NA
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:297baf11fc11c065f9661efc3dd253c0c332c803)
Merged-In: Ifef48da0ba6c0314dffef9ac04e5018bfb85b91b
Change-Id: Ifef48da0ba6c0314dffef9ac04e5018bfb85b91b
---
 core/java/android/app/Notification.java       |  7 +++---
 .../src/android/app/NotificationTest.java     | 24 ++++++++++++-------
 .../NotificationManagerService.java           | 13 ++++++----
 .../NotificationManagerServiceTest.java       |  9 +++++--
 4 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 013bcddbb7f3..7454e52b50ef 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7702,11 +7702,12 @@ public class Notification implements Parcelable
             } else if (mPictureIcon.getType() == Icon.TYPE_BITMAP) {
                 // If the icon contains a bitmap, use the old extra so that listeners which look
                 // for that extra can still find the picture. Don't include the new extra in
-                // that case, to avoid duplicating data.
+                // that case, to avoid duplicating data. Leave the unused extra set to null to avoid
+                // crashing apps that came to expect it to be present but null.
                 extras.putParcelable(EXTRA_PICTURE, mPictureIcon.getBitmap());
-                extras.remove(EXTRA_PICTURE_ICON);
+                extras.putParcelable(EXTRA_PICTURE_ICON, null);
             } else {
-                extras.remove(EXTRA_PICTURE);
+                extras.putParcelable(EXTRA_PICTURE, null);
                 extras.putParcelable(EXTRA_PICTURE_ICON, mPictureIcon);
             }
         }
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 1577d9c1c1ab..5b0502da1bdf 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -1244,29 +1244,33 @@ public class NotificationTest {
     }
 
     @Test
-    public void testBigPictureStyle_setExtras_pictureIconNull_noPictureIconKey() {
+    public void testBigPictureStyle_setExtras_pictureIconNull_pictureIconKeyNull() {
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture((Bitmap) null);
 
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+        final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+        assertThat(pictureIcon).isNull();
     }
 
     @Test
-    public void testBigPictureStyle_setExtras_pictureIconNull_noPictureKey() {
+    public void testBigPictureStyle_setExtras_pictureIconNull_pictureKeyNull() {
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture((Bitmap) null);
 
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
+        assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
+        final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
+        assertThat(picture).isNull();
     }
 
     @Test
-    public void testBigPictureStyle_setExtras_pictureIconTypeBitmap_noPictureIconKey() {
+    public void testBigPictureStyle_setExtras_pictureIconTypeBitmap_pictureIconKeyNull() {
         Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture(bitmap);
@@ -1274,11 +1278,13 @@ public class NotificationTest {
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+        assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+        final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+        assertThat(pictureIcon).isNull();
     }
 
     @Test
-    public void testBigPictureStyle_setExtras_pictureIconTypeIcon_noPictureKey() {
+    public void testBigPictureStyle_setExtras_pictureIconTypeIcon_pictureKeyNull() {
         Icon icon = Icon.createWithResource(mContext, R.drawable.btn_plus);
         Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
         bpStyle.bigPicture(icon);
@@ -1286,7 +1292,9 @@ public class NotificationTest {
         Bundle extras = new Bundle();
         bpStyle.addExtras(extras);
 
-        assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
+        assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
+        final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
+        assertThat(picture).isNull();
     }
 
     @Test
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e7ae61072db4..6b956e2ae9d7 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6985,12 +6985,14 @@ public class NotificationManagerService extends SystemService {
             return false;
         }
 
-        final boolean hasBitmap = n.extras.containsKey(Notification.EXTRA_PICTURE);
+        final boolean hasBitmap = n.extras.containsKey(Notification.EXTRA_PICTURE)
+                && n.extras.getParcelable(Notification.EXTRA_PICTURE) != null;
         if (hasBitmap) {
             return true;
         }
 
-        final boolean hasIcon = n.extras.containsKey(Notification.EXTRA_PICTURE_ICON);
+        final boolean hasIcon = n.extras.containsKey(Notification.EXTRA_PICTURE_ICON)
+                && n.extras.getParcelable(Notification.EXTRA_PICTURE_ICON) != null;
         if (hasIcon) {
             return true;
         }
@@ -7006,9 +7008,10 @@ public class NotificationManagerService extends SystemService {
         if (!isBigPictureWithBitmapOrIcon(r.getNotification())) {
             return;
         }
-        // Remove Notification object's reference to picture bitmap or URI
-        r.getNotification().extras.remove(Notification.EXTRA_PICTURE);
-        r.getNotification().extras.remove(Notification.EXTRA_PICTURE_ICON);
+        // Remove Notification object's reference to picture bitmap or URI. Leave the extras set to
+        // null to avoid crashing apps that came to expect them to be present but null.
+        r.getNotification().extras.putParcelable(Notification.EXTRA_PICTURE, null);
+        r.getNotification().extras.putParcelable(Notification.EXTRA_PICTURE_ICON, null);
 
         // Make Notification silent
         r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
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 776189eeb7c3..b93fffcb1869 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -11978,7 +11978,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                 /* isImageBitmap= */ true,
                 /* isExpired= */ true);
         addRecordAndRemoveBitmaps(record);
-        assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE)).isFalse();
+        assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE)).isTrue();
+        final Parcelable picture = record.getNotification().extras.getParcelable(EXTRA_PICTURE);
+        assertThat(picture).isNull();
     }
 
     @Test
@@ -12012,7 +12014,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                 /* isImageBitmap= */ false,
                 /* isExpired= */ true);
         addRecordAndRemoveBitmaps(record);
-        assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
+        assertThat(record.getNotification().extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
+        final Parcelable pictureIcon =
+                record.getNotification().extras.getParcelable(EXTRA_PICTURE_ICON);
+        assertThat(pictureIcon).isNull();
     }
 
     @Test
-- 
GitLab