diff options
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/Notification.java | 65 | ||||
| -rw-r--r-- | core/java/android/app/PendingIntent.java | 35 | ||||
| -rw-r--r-- | core/java/android/os/Parcel.java | 37 | ||||
| -rw-r--r-- | core/java/android/util/ArraySet.java | 26 |
4 files changed, 160 insertions, 3 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6fc1820c3c36..3c3da7804a11 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -39,6 +39,7 @@ import android.media.AudioManager; import android.media.session.MediaSession; import android.net.Uri; import android.os.BadParcelableException; +import android.os.BaseBundle; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -53,6 +54,7 @@ import android.text.style.AbsoluteSizeSpan; import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; import android.text.style.TextAppearanceSpan; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.view.Gravity; @@ -63,6 +65,7 @@ import android.widget.ProgressBar; import android.widget.RemoteViews; import com.android.internal.R; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.NotificationColorUtil; import java.lang.annotation.Retention; @@ -70,6 +73,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -758,6 +762,16 @@ public class Notification implements Parcelable public Bundle extras = new Bundle(); /** + * All pending intents in the notification extras (notification extras, actions extras, + * and remote input extras) as the system needs to be able to access them but touching + * the extras bundle in the system process is not safe because the bundle may contain + * custom parcelable objects. + * + * @hide + */ + public ArraySet<PendingIntent> extrasPendingIntents; + + /** * {@link #extras} key: this is the title of the notification, * as supplied to {@link Builder#setContentTitle(CharSequence)}. */ @@ -1549,7 +1563,16 @@ public class Notification implements Parcelable /** * Unflatten the notification from a parcel. */ - public Notification(Parcel parcel) + @SuppressWarnings("unchecked") + public Notification(Parcel parcel) { + // IMPORTANT: Add unmarshaling code in readFromParcel as the pending + // intents in extras are always written as the last entry. + readFromParcelImpl(parcel); + // Must be read last! + extrasPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null); + } + + private void readFromParcelImpl(Parcel parcel) { int version = parcel.readInt(); @@ -1704,6 +1727,10 @@ public class Notification implements Parcelable } } + if (!ArrayUtils.isEmpty(extrasPendingIntents)) { + that.extrasPendingIntents = new ArraySet<>(extrasPendingIntents); + } + if (this.actions != null) { that.actions = new Action[this.actions.length]; for(int i=0; i<this.actions.length; i++) { @@ -1819,8 +1846,40 @@ public class Notification implements Parcelable /** * Flatten this notification into a parcel. */ - public void writeToParcel(Parcel parcel, int flags) - { + public void writeToParcel(Parcel parcel, int flags) { + // We need to mark all pending intents getting into the notification + // system as being put there to later allow the notification ranker + // to launch them and by doing so add the app to the battery saver white + // list for a short period of time. The problem is that the system + // cannot look into the extras as there may be parcelables there that + // the platform does not know how to handle. To go around that we have + // an explicit list of the pending intents in the extras bundle. + final boolean collectPendingIntents = (extrasPendingIntents == null); + if (collectPendingIntents) { + PendingIntent.setOnMarshaledListener( + (PendingIntent intent, Parcel out, int outFlags) -> { + if (parcel == out) { + if (extrasPendingIntents == null) { + extrasPendingIntents = new ArraySet<>(); + } + extrasPendingIntents.add(intent); + } + }); + } + try { + // IMPORTANT: Add marshaling code in writeToParcelImpl as we + // want to intercept all pending events written to the pacel. + writeToParcelImpl(parcel, flags); + // Must be written last! + parcel.writeArraySet(extrasPendingIntents); + } finally { + if (collectPendingIntents) { + PendingIntent.setOnMarshaledListener(null); + } + } + } + + private void writeToParcelImpl(Parcel parcel, int flags) { parcel.writeInt(1); parcel.writeLong(when); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index cb15392b6a8a..cfa242be02aa 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -242,6 +242,36 @@ public final class PendingIntent implements Parcelable { } /** + * Listener for observing when pending intents are written to a parcel. + * + * @hide + */ + public interface OnMarshaledListener { + /** + * Called when a pending intent is written to a parcel. + * + * @param intent The pending intent. + * @param parcel The parcel to which it was written. + * @param flags The parcel flags when it was written. + */ + void onMarshaled(PendingIntent intent, Parcel parcel, int flags); + } + + private static final ThreadLocal<OnMarshaledListener> sOnMarshaledListener + = new ThreadLocal<>(); + + /** + * Registers an listener for pending intents being written to a parcel. + * + * @param listener The listener, null to clear. + * + * @hide + */ + public static void setOnMarshaledListener(OnMarshaledListener listener) { + sOnMarshaledListener.set(listener); + } + + /** * Retrieve a PendingIntent that will start a new activity, like calling * {@link Context#startActivity(Intent) Context.startActivity(Intent)}. * Note that the activity will be started outside of the context of an @@ -1016,6 +1046,11 @@ public final class PendingIntent implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeStrongBinder(mTarget.asBinder()); + OnMarshaledListener listener = sOnMarshaledListener.get(); + if (listener != null) { + listener.onMarshaled(this, out, flags); + } + } public static final Parcelable.Creator<PendingIntent> CREATOR diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 26312475297c..74dcc0787b3b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -17,8 +17,10 @@ package android.os; import android.annotation.IntegerRes; +import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Size; import android.util.SizeF; @@ -734,6 +736,21 @@ public final class Parcel { } /** + * Write an array set to the parcel. + * + * @param val The array set to write. + * + * @hide + */ + public void writeArraySet(@Nullable ArraySet<? extends Object> val) { + final int size = (val != null) ? val.size() : -1; + writeInt(size); + for (int i = 0; i < size; i++) { + writeValue(val.valueAt(i)); + } + } + + /** * Flatten a Bundle into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ @@ -2735,6 +2752,26 @@ public final class Parcel { readArrayMapInternal(outVal, N, loader); } + /** + * Reads an array set. + * + * @param loader The class loader to use. + * + * @hide + */ + public @Nullable ArraySet<? extends Object> readArraySet(ClassLoader loader) { + final int size = readInt(); + if (size < 0) { + return null; + } + ArraySet<Object> result = new ArraySet<>(size); + for (int i = 0; i < size; i++) { + Object value = readValue(loader); + result.append(value); + } + return result; + } + private void readListInternal(List outVal, int N, ClassLoader loader) { while (N > 0) { diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 9e9314fba4c4..d39e91fd98b2 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -390,6 +390,32 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } /** + * Special fast path for appending items to the end of the array without validation. + * The array must already be large enough to contain the item. + * @hide + */ + public void append(E value) { + final int index = mSize; + final int hash = value == null ? 0 + : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode()); + if (index >= mHashes.length) { + throw new IllegalStateException("Array is full"); + } + if (index > 0 && mHashes[index - 1] > hash) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Log.w(TAG, "New hash " + hash + + " is before end of array hash " + mHashes[index - 1] + + " at index " + index, e); + add(value); + return; + } + mSize = index + 1; + mHashes[index] = hash; + mArray[index] = value; + } + + /** * Perform a {@link #add(Object)} of all values in <var>array</var> * @param array The array whose contents are to be retrieved. */ |
