Merge \\"Mark app pending intents in notification extras\\" into nyc-dev am: b2cd9c95bf
am: 5f7f3fa7a9
Change-Id: Id4bc3ae0c1d79174a22d669dccab3391c231357d
This commit is contained in:
@@ -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;
|
||||
@@ -757,6 +761,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)}.
|
||||
@@ -1573,7 +1587,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();
|
||||
|
||||
@@ -1728,6 +1751,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++) {
|
||||
@@ -1843,8 +1870,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);
|
||||
|
||||
@@ -241,6 +241,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)}.
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -733,6 +735,21 @@ public final class Parcel {
|
||||
writeArrayMapInternal(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
@@ -389,6 +389,32 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -2598,7 +2598,6 @@ public class NotificationManagerService extends SystemService {
|
||||
final long duration = LocalServices.getService(DeviceIdleController.LocalService.class)
|
||||
.getNotificationWhitelistDuration();
|
||||
|
||||
int size = 0;
|
||||
if (notification.contentIntent != null) {
|
||||
am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration);
|
||||
}
|
||||
@@ -2615,45 +2614,19 @@ public class NotificationManagerService extends SystemService {
|
||||
continue;
|
||||
}
|
||||
am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration);
|
||||
setPendingIntentWhitelistDuration(am, duration, action.getExtras());
|
||||
final RemoteInput[] remoteInputs = action.getRemoteInputs();
|
||||
if (remoteInputs != null) {
|
||||
for (RemoteInput remoteInput : remoteInputs) {
|
||||
setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
|
||||
Bundle extras) {
|
||||
for (String key : extras.keySet()) {
|
||||
final Object value = extras.get(key);
|
||||
if (value instanceof Parcelable) {
|
||||
setPendingIntentWhitelistDuration(am, duration, (Parcelable) value);
|
||||
} else if (value instanceof Parcelable[]) {
|
||||
for (Parcelable parcelable : (Parcelable[]) value) {
|
||||
setPendingIntentWhitelistDuration(am, duration, parcelable);
|
||||
}
|
||||
} else if (value instanceof List) {
|
||||
for (Object element : (List <?>) value) {
|
||||
if (element instanceof Parcelable) {
|
||||
setPendingIntentWhitelistDuration(am, duration, (Parcelable) element);
|
||||
}
|
||||
if (notification.extrasPendingIntents != null) {
|
||||
final int intentCount = notification.extrasPendingIntents.size();
|
||||
for (int i = 0; i < intentCount; i++) {
|
||||
PendingIntent pendingIntent = notification.extrasPendingIntents.valueAt(i);
|
||||
if (pendingIntent != null) {
|
||||
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
|
||||
Parcelable parcelable) {
|
||||
if (parcelable instanceof PendingIntent) {
|
||||
am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(),
|
||||
duration);
|
||||
}
|
||||
}
|
||||
|
||||
private class EnqueueNotificationRunnable implements Runnable {
|
||||
private final NotificationRecord r;
|
||||
private final int userId;
|
||||
|
||||
Reference in New Issue
Block a user