Merge \\"Mark app pending intents in notification extras\\" into nyc-dev am: b2cd9c95bf

am: 5f7f3fa7a9

Change-Id: Id4bc3ae0c1d79174a22d669dccab3391c231357d
This commit is contained in:
Svetoslav Ganov
2016-06-28 00:52:01 +00:00
committed by android-build-merger
5 changed files with 166 additions and 36 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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.

View File

@@ -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;