Loading core/java/android/app/Notification.java +26 −55 Original line number Diff line number Diff line Loading @@ -1870,7 +1870,7 @@ public class Notification implements Parcelable * You can test if a RemoteInput matches these constraints using * {@link RemoteInput#isDataOnly}. */ private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; /** * No semantic action defined. Loading Loading @@ -2089,7 +2089,7 @@ public class Notification implements Parcelable * of non-textual RemoteInputs do not access these remote inputs. */ public RemoteInput[] getDataOnlyRemoteInputs() { return getParcelableArrayFromBundle(mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); return mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); } /** Loading Loading @@ -2330,8 +2330,8 @@ public class Notification implements Parcelable checkContextualActionNullFields(); ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>(); RemoteInput[] previousDataInputs = getParcelableArrayFromBundle( mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); RemoteInput[] previousDataInputs = mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); if (previousDataInputs != null) { for (RemoteInput input : previousDataInputs) { dataOnlyInputs.add(input); Loading Loading @@ -3091,7 +3091,8 @@ public class Notification implements Parcelable visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI))); } ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); if (people != null && !people.isEmpty()) { for (Person p : people) { p.visitUris(visitor); Loading @@ -3117,8 +3118,8 @@ public class Notification implements Parcelable person.visitUris(visitor); } final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { Loading @@ -3126,8 +3127,8 @@ public class Notification implements Parcelable } } final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Parcelable.class); final Bundle[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(historic)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(historic)) { Loading Loading @@ -3838,17 +3839,9 @@ public class Notification implements Parcelable */ private void fixDuplicateExtras() { if (extras != null) { fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON); } if (mLargeIcon != null) { extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); } /** * If we find an extra that's exactly the same as one of the "real" fields but refers to a * separate object, replace it with the field's version to avoid holding duplicate copies. */ private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) { if (original != null && extras.getParcelable(extraName, Parcelable.class) != null) { extras.putParcelable(extraName, original); } } Loading Loading @@ -6622,8 +6615,8 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle( mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] replyText = mN.extras.getParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { Loading Loading @@ -8027,8 +8020,7 @@ public class Notification implements Parcelable */ public boolean hasImage() { if (isStyle(MessagingStyle.class) && extras != null) { final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message m : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { Loading Loading @@ -9348,10 +9340,10 @@ public class Notification implements Parcelable mUser = user; } mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE); Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); mMessages = Message.getMessagesFromBundleArray(messages); Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Parcelable.class); Bundle.class); mHistoricMessages = Message.getMessagesFromBundleArray(histMessages); mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION); mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); Loading Loading @@ -10218,8 +10210,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle( mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem[] remoteInputHistory = mBuilder.mN.extras.getParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { Loading Loading @@ -13070,7 +13062,8 @@ public class Notification implements Parcelable public WearableExtender(Notification notif) { Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS); if (wearableBundle != null) { List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS, android.app.Notification.Action.class); List<Action> actions = wearableBundle.getParcelableArrayList( KEY_ACTIONS, Notification.Action.class); if (actions != null) { mActions.addAll(actions); } Loading @@ -13079,8 +13072,8 @@ public class Notification implements Parcelable mDisplayIntent = wearableBundle.getParcelable( KEY_DISPLAY_INTENT, PendingIntent.class); Notification[] pages = getParcelableArrayFromBundle( wearableBundle, KEY_PAGES, Notification.class); Notification[] pages = wearableBundle.getParcelableArray(KEY_PAGES, Notification.class); if (pages != null) { Collections.addAll(mPages, pages); } Loading Loading @@ -14015,7 +14008,7 @@ public class Notification implements Parcelable if (mParticipants != null && mParticipants.length > 1) { author = mParticipants[0]; } Parcelable[] messages = new Parcelable[mMessages.length]; Bundle[] messages = new Bundle[mMessages.length]; for (int i = 0; i < messages.length; i++) { Bundle m = new Bundle(); m.putString(KEY_TEXT, mMessages[i]); Loading @@ -14037,8 +14030,7 @@ public class Notification implements Parcelable if (b == null) { return null; } Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, Parcelable.class); Bundle[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, Bundle.class); String[] messages = null; if (parcelableMessages != null) { String[] tmp = new String[parcelableMessages.length]; Loading Loading @@ -14402,27 +14394,6 @@ public class Notification implements Parcelable } } /** * Get an array of Parcelable objects from a parcelable array bundle field. * Update the bundle to have a typed array so fetches in the future don't need * to do an array copy. */ @Nullable private static <T extends Parcelable> T[] getParcelableArrayFromBundle( Bundle bundle, String key, Class<T> itemClass) { final Parcelable[] array = bundle.getParcelableArray(key, Parcelable.class); final Class<?> arrayClass = Array.newInstance(itemClass, 0).getClass(); if (arrayClass.isInstance(array) || array == null) { return (T[]) array; } final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length); for (int i = 0; i < array.length; i++) { typedArray[i] = (T) array[i]; } bundle.putParcelableArray(key, typedArray); return typedArray; } private static class BuilderRemoteViews extends RemoteViews { public BuilderRemoteViews(Parcel parcel) { super(parcel); core/tests/coretests/src/android/app/NotificationTest.java +83 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import static android.app.Notification.Action.EXTRA_DATA_ONLY_INPUTS; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_READ; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_REPLY; import static android.app.Notification.CarExtender.UnreadConversation.KEY_REMOTE_INPUT; Loading Loading @@ -97,6 +98,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.TextAppearanceSpan; import android.util.Pair; import android.view.View; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; Loading @@ -106,10 +108,10 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; import junit.framework.Assert; import libcore.junit.util.compat.CoreCompatChangeRule; import junit.framework.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; Loading Loading @@ -544,6 +546,22 @@ public class NotificationTest { assertSame(q.getLargeIcon(), q.extras.getParcelable(EXTRA_LARGE_ICON)); } @Test public void largeIconMultipleReferences_ignoreBadData() { Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build(); assertSame(n.getLargeIcon(), originalIcon); n.extras.putParcelable(EXTRA_LARGE_ICON, new NotificationChannelGroup("hi", "hi")); Notification q = writeAndReadParcelable(n); assertNotSame(q.getLargeIcon(), n.getLargeIcon()); assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap())); assertSame(q.getLargeIcon(), q.extras.getParcelable(EXTRA_LARGE_ICON)); } @Test public void largeIconReferenceInExtrasOnly_keptAfterParcelling() { Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( Loading Loading @@ -2444,6 +2462,69 @@ public class NotificationTest { assertThat(progressStyle1.isStyledByProgress()).isTrue(); } @Test public void getDataOnlyRemoteInputs_invalidData() { Notification.Action action = new Notification.Action.Builder(0, "title", null) .addRemoteInput(new RemoteInput.Builder("result") .setAllowFreeFormInput(false) .setAllowDataType("mimeType", true) .build()).build(); action.getExtras().putParcelable(EXTRA_DATA_ONLY_INPUTS, new NotificationChannelGroup("hi", "hi")); assertThat(action.getDataOnlyRemoteInputs()).isNull(); } @Test public void actionAddExtras_invalidData() { Bundle extras = new Bundle(); extras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, new NotificationChannelGroup[]{new NotificationChannelGroup("hi", "hi")}); Notification.Action action = new Notification.Action.Builder(0, "title", null) .addRemoteInput(new RemoteInput.Builder("result") .setAllowFreeFormInput(false) .setAllowDataType("mimeType", true) .addExtras(extras) .build()).build(); assertThat(action.getDataOnlyRemoteInputs()[0].getClass()).isEqualTo(RemoteInput.class); } @Test public void makeBigTemplate_invalidData() { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(mContext, NotificationTest.class), PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); RemoteInput remoteInput = new RemoteInput.Builder("key_text_reply") .setLabel("Reply") .setChoices(new String[]{"Choice 1", "Choice 2"}) .addExtras(new Bundle()) .build(); Notification.Action replyAction = new Notification.Action.Builder( R.drawable.stat_notify_chat, "Reply", pendingIntent) .addRemoteInput(remoteInput) .build(); Bundle bundle = new Bundle(); bundle.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, new NotificationChannelGroup[]{}); Notification.Builder nb = new Notification.Builder(mContext, "channel") .setContentTitle("title") .setVisibility(android.app.Notification.VISIBILITY_PUBLIC) .setStyle(new Notification.InboxStyle()) .setExtras(bundle) .setSmallIcon(R.drawable.stat_notify_chat) .addAction(replyAction); RemoteViews views = nb.createBigContentView(); View view = views.apply(mContext, null); assertThat(view.findViewById(R.id.notification_material_reply_container).getVisibility()) .isNotEqualTo(View.VISIBLE); assertThat(view.findViewById(R.id.inbox_text0).getVisibility()) .isNotEqualTo(View.VISIBLE); } private void assertValid(Notification.Colors c) { // Assert that all colors are populated assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID); Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java +27 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; import static android.app.Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; Loading @@ -23,6 +25,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.app.Notification; import android.app.NotificationChannelGroup; import android.app.RemoteInputHistoryItem; import android.net.Uri; import android.os.UserHandle; Loading Loading @@ -70,6 +73,25 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mEntry.setRow(mRow); } @Test public void testRebuildWithRemoteInput_invalidData() { Uri uri = mock(Uri.class); String mimeType = "image/jpeg"; String text = "image inserted"; mEntry.getSbn().getNotification().extras.putParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, new NotificationChannelGroup[]{}); StatusBarNotification newSbn = mRebuilder.rebuildWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals(text, messages[0].getText()); assertEquals(mimeType, messages[0].getMimeType()); assertEquals(uri, messages[0].getUri()); } @Test public void testRebuildWithRemoteInput_noExistingInput_image() { Uri uri = mock(Uri.class); Loading @@ -79,7 +101,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals(text, messages[0].getText()); assertEquals(mimeType, messages[0].getMimeType()); Loading @@ -92,7 +114,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", false, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertFalse(newSbn.getNotification().extras Loading @@ -107,7 +129,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertTrue(newSbn.getNotification().extras Loading @@ -130,7 +152,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals("A Reply", messages[1].getText()); Loading @@ -153,7 +175,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals(text, messages[1].getText()); Loading packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java +4 −2 Original line number Diff line number Diff line Loading @@ -128,7 +128,8 @@ public class RemoteInputNotificationRebuilder { // Read the whole remoteInputs list from the entry, then append all of those to the sbn. Parcelable[] oldHistoryItems = sbn.getNotification().extras .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Loading @@ -144,7 +145,8 @@ public class RemoteInputNotificationRebuilder { ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) : new RemoteInputHistoryItem(remoteInputText); Parcelable[] oldHistoryItems = sbn.getNotification().extras .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Stream.of(newItem), Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +2 −1 Original line number Diff line number Diff line Loading @@ -586,7 +586,8 @@ public final class NotificationEntry extends ListEntry { } Bundle extras = mSbn.getNotification().extras; Parcelable[] replyTexts = extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } Loading Loading
core/java/android/app/Notification.java +26 −55 Original line number Diff line number Diff line Loading @@ -1870,7 +1870,7 @@ public class Notification implements Parcelable * You can test if a RemoteInput matches these constraints using * {@link RemoteInput#isDataOnly}. */ private static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; static final String EXTRA_DATA_ONLY_INPUTS = "android.extra.DATA_ONLY_INPUTS"; /** * No semantic action defined. Loading Loading @@ -2089,7 +2089,7 @@ public class Notification implements Parcelable * of non-textual RemoteInputs do not access these remote inputs. */ public RemoteInput[] getDataOnlyRemoteInputs() { return getParcelableArrayFromBundle(mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); return mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); } /** Loading Loading @@ -2330,8 +2330,8 @@ public class Notification implements Parcelable checkContextualActionNullFields(); ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>(); RemoteInput[] previousDataInputs = getParcelableArrayFromBundle( mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); RemoteInput[] previousDataInputs = mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS, RemoteInput.class); if (previousDataInputs != null) { for (RemoteInput input : previousDataInputs) { dataOnlyInputs.add(input); Loading Loading @@ -3091,7 +3091,8 @@ public class Notification implements Parcelable visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI))); } ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); if (people != null && !people.isEmpty()) { for (Person p : people) { p.visitUris(visitor); Loading @@ -3117,8 +3118,8 @@ public class Notification implements Parcelable person.visitUris(visitor); } final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { Loading @@ -3126,8 +3127,8 @@ public class Notification implements Parcelable } } final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Parcelable.class); final Bundle[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(historic)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(historic)) { Loading Loading @@ -3838,17 +3839,9 @@ public class Notification implements Parcelable */ private void fixDuplicateExtras() { if (extras != null) { fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON); } if (mLargeIcon != null) { extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon); } /** * If we find an extra that's exactly the same as one of the "real" fields but refers to a * separate object, replace it with the field's version to avoid holding duplicate copies. */ private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) { if (original != null && extras.getParcelable(extraName, Parcelable.class) != null) { extras.putParcelable(extraName, original); } } Loading Loading @@ -6622,8 +6615,8 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.actions_container, View.GONE); } RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle( mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] replyText = mN.extras.getParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (validRemoteInput && replyText != null && replyText.length > 0 && !TextUtils.isEmpty(replyText[0].getText()) && p.maxRemoteInputHistory > 0) { Loading Loading @@ -8027,8 +8020,7 @@ public class Notification implements Parcelable */ public boolean hasImage() { if (isStyle(MessagingStyle.class) && extras != null) { final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); final Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message m : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { Loading Loading @@ -9348,10 +9340,10 @@ public class Notification implements Parcelable mUser = user; } mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE); Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Parcelable.class); Bundle[] messages = extras.getParcelableArray(EXTRA_MESSAGES, Bundle.class); mMessages = Message.getMessagesFromBundleArray(messages); Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, Parcelable.class); Bundle.class); mHistoricMessages = Message.getMessagesFromBundleArray(histMessages); mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION); mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); Loading Loading @@ -10218,8 +10210,8 @@ public class Notification implements Parcelable if (mBuilder.mActions.size() > 0) { maxRows--; } RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle( mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem[] remoteInputHistory = mBuilder.mN.extras.getParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (remoteInputHistory != null && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) { Loading Loading @@ -13070,7 +13062,8 @@ public class Notification implements Parcelable public WearableExtender(Notification notif) { Bundle wearableBundle = notif.extras.getBundle(EXTRA_WEARABLE_EXTENSIONS); if (wearableBundle != null) { List<Action> actions = wearableBundle.getParcelableArrayList(KEY_ACTIONS, android.app.Notification.Action.class); List<Action> actions = wearableBundle.getParcelableArrayList( KEY_ACTIONS, Notification.Action.class); if (actions != null) { mActions.addAll(actions); } Loading @@ -13079,8 +13072,8 @@ public class Notification implements Parcelable mDisplayIntent = wearableBundle.getParcelable( KEY_DISPLAY_INTENT, PendingIntent.class); Notification[] pages = getParcelableArrayFromBundle( wearableBundle, KEY_PAGES, Notification.class); Notification[] pages = wearableBundle.getParcelableArray(KEY_PAGES, Notification.class); if (pages != null) { Collections.addAll(mPages, pages); } Loading Loading @@ -14015,7 +14008,7 @@ public class Notification implements Parcelable if (mParticipants != null && mParticipants.length > 1) { author = mParticipants[0]; } Parcelable[] messages = new Parcelable[mMessages.length]; Bundle[] messages = new Bundle[mMessages.length]; for (int i = 0; i < messages.length; i++) { Bundle m = new Bundle(); m.putString(KEY_TEXT, mMessages[i]); Loading @@ -14037,8 +14030,7 @@ public class Notification implements Parcelable if (b == null) { return null; } Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, Parcelable.class); Bundle[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES, Bundle.class); String[] messages = null; if (parcelableMessages != null) { String[] tmp = new String[parcelableMessages.length]; Loading Loading @@ -14402,27 +14394,6 @@ public class Notification implements Parcelable } } /** * Get an array of Parcelable objects from a parcelable array bundle field. * Update the bundle to have a typed array so fetches in the future don't need * to do an array copy. */ @Nullable private static <T extends Parcelable> T[] getParcelableArrayFromBundle( Bundle bundle, String key, Class<T> itemClass) { final Parcelable[] array = bundle.getParcelableArray(key, Parcelable.class); final Class<?> arrayClass = Array.newInstance(itemClass, 0).getClass(); if (arrayClass.isInstance(array) || array == null) { return (T[]) array; } final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length); for (int i = 0; i < array.length; i++) { typedArray[i] = (T) array[i]; } bundle.putParcelableArray(key, typedArray); return typedArray; } private static class BuilderRemoteViews extends RemoteViews { public BuilderRemoteViews(Parcel parcel) { super(parcel);
core/tests/coretests/src/android/app/NotificationTest.java +83 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import static android.app.Notification.Action.EXTRA_DATA_ONLY_INPUTS; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_READ; import static android.app.Notification.CarExtender.UnreadConversation.KEY_ON_REPLY; import static android.app.Notification.CarExtender.UnreadConversation.KEY_REMOTE_INPUT; Loading Loading @@ -97,6 +98,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.text.style.TextAppearanceSpan; import android.util.Pair; import android.view.View; import android.widget.RemoteViews; import androidx.test.InstrumentationRegistry; Loading @@ -106,10 +108,10 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; import junit.framework.Assert; import libcore.junit.util.compat.CoreCompatChangeRule; import junit.framework.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; Loading Loading @@ -544,6 +546,22 @@ public class NotificationTest { assertSame(q.getLargeIcon(), q.extras.getParcelable(EXTRA_LARGE_ICON)); } @Test public void largeIconMultipleReferences_ignoreBadData() { Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( mContext.getResources(), com.android.frameworks.coretests.R.drawable.test128x96)); Notification n = new Notification.Builder(mContext).setLargeIcon(originalIcon).build(); assertSame(n.getLargeIcon(), originalIcon); n.extras.putParcelable(EXTRA_LARGE_ICON, new NotificationChannelGroup("hi", "hi")); Notification q = writeAndReadParcelable(n); assertNotSame(q.getLargeIcon(), n.getLargeIcon()); assertTrue(q.getLargeIcon().getBitmap().sameAs(n.getLargeIcon().getBitmap())); assertSame(q.getLargeIcon(), q.extras.getParcelable(EXTRA_LARGE_ICON)); } @Test public void largeIconReferenceInExtrasOnly_keptAfterParcelling() { Icon originalIcon = Icon.createWithBitmap(BitmapFactory.decodeResource( Loading Loading @@ -2444,6 +2462,69 @@ public class NotificationTest { assertThat(progressStyle1.isStyledByProgress()).isTrue(); } @Test public void getDataOnlyRemoteInputs_invalidData() { Notification.Action action = new Notification.Action.Builder(0, "title", null) .addRemoteInput(new RemoteInput.Builder("result") .setAllowFreeFormInput(false) .setAllowDataType("mimeType", true) .build()).build(); action.getExtras().putParcelable(EXTRA_DATA_ONLY_INPUTS, new NotificationChannelGroup("hi", "hi")); assertThat(action.getDataOnlyRemoteInputs()).isNull(); } @Test public void actionAddExtras_invalidData() { Bundle extras = new Bundle(); extras.putParcelableArray(EXTRA_DATA_ONLY_INPUTS, new NotificationChannelGroup[]{new NotificationChannelGroup("hi", "hi")}); Notification.Action action = new Notification.Action.Builder(0, "title", null) .addRemoteInput(new RemoteInput.Builder("result") .setAllowFreeFormInput(false) .setAllowDataType("mimeType", true) .addExtras(extras) .build()).build(); assertThat(action.getDataOnlyRemoteInputs()[0].getClass()).isEqualTo(RemoteInput.class); } @Test public void makeBigTemplate_invalidData() { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(mContext, NotificationTest.class), PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); RemoteInput remoteInput = new RemoteInput.Builder("key_text_reply") .setLabel("Reply") .setChoices(new String[]{"Choice 1", "Choice 2"}) .addExtras(new Bundle()) .build(); Notification.Action replyAction = new Notification.Action.Builder( R.drawable.stat_notify_chat, "Reply", pendingIntent) .addRemoteInput(remoteInput) .build(); Bundle bundle = new Bundle(); bundle.putParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, new NotificationChannelGroup[]{}); Notification.Builder nb = new Notification.Builder(mContext, "channel") .setContentTitle("title") .setVisibility(android.app.Notification.VISIBILITY_PUBLIC) .setStyle(new Notification.InboxStyle()) .setExtras(bundle) .setSmallIcon(R.drawable.stat_notify_chat) .addAction(replyAction); RemoteViews views = nb.createBigContentView(); View view = views.apply(mContext, null); assertThat(view.findViewById(R.id.notification_material_reply_container).getVisibility()) .isNotEqualTo(View.VISIBLE); assertThat(view.findViewById(R.id.inbox_text0).getVisibility()) .isNotEqualTo(View.VISIBLE); } private void assertValid(Notification.Colors c) { // Assert that all colors are populated assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID); Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java +27 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; import static android.app.Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; Loading @@ -23,6 +25,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.app.Notification; import android.app.NotificationChannelGroup; import android.app.RemoteInputHistoryItem; import android.net.Uri; import android.os.UserHandle; Loading Loading @@ -70,6 +73,25 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mEntry.setRow(mRow); } @Test public void testRebuildWithRemoteInput_invalidData() { Uri uri = mock(Uri.class); String mimeType = "image/jpeg"; String text = "image inserted"; mEntry.getSbn().getNotification().extras.putParcelableArray( EXTRA_REMOTE_INPUT_HISTORY_ITEMS, new NotificationChannelGroup[]{}); StatusBarNotification newSbn = mRebuilder.rebuildWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals(text, messages[0].getText()); assertEquals(mimeType, messages[0].getMimeType()); assertEquals(uri, messages[0].getUri()); } @Test public void testRebuildWithRemoteInput_noExistingInput_image() { Uri uri = mock(Uri.class); Loading @@ -79,7 +101,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, text, false, mimeType, uri); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals(text, messages[0].getText()); assertEquals(mimeType, messages[0].getMimeType()); Loading @@ -92,7 +114,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", false, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertFalse(newSbn.getNotification().extras Loading @@ -107,7 +129,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { mRebuilder.rebuildWithRemoteInputInserted( mEntry, "A Reply", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(1, messages.length); assertEquals("A Reply", messages[0].getText()); assertTrue(newSbn.getNotification().extras Loading @@ -130,7 +152,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals("A Reply", messages[1].getText()); Loading @@ -153,7 +175,7 @@ public class RemoteInputNotificationRebuilderTest extends SysuiTestCase { newSbn = mRebuilder.rebuildWithRemoteInputInserted( entry, "Reply 2", true, null, null); RemoteInputHistoryItem[] messages = (RemoteInputHistoryItem[]) newSbn.getNotification() .extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS); assertEquals(2, messages.length); assertEquals("Reply 2", messages[0].getText()); assertEquals(text, messages[1].getText()); Loading
packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java +4 −2 Original line number Diff line number Diff line Loading @@ -128,7 +128,8 @@ public class RemoteInputNotificationRebuilder { // Read the whole remoteInputs list from the entry, then append all of those to the sbn. Parcelable[] oldHistoryItems = sbn.getNotification().extras .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Loading @@ -144,7 +145,8 @@ public class RemoteInputNotificationRebuilder { ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText) : new RemoteInputHistoryItem(remoteInputText); Parcelable[] oldHistoryItems = sbn.getNotification().extras .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null ? Stream.concat( Stream.of(newItem), Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +2 −1 Original line number Diff line number Diff line Loading @@ -586,7 +586,8 @@ public final class NotificationEntry extends ListEntry { } Bundle extras = mSbn.getNotification().extras; Parcelable[] replyTexts = extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (!ArrayUtils.isEmpty(replyTexts)) { return true; } Loading