Merge "Add @hide Bundle.getParcelable() with explicit type" am: 58b4c57393
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1988907 Change-Id: I64dcfca33f85e355346b0fe6880ba58413a98308
This commit is contained in:
@@ -31,7 +31,7 @@ import com.android.internal.util.IndentingPrintWriter;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping from String keys to values of various types. In most cases, you
|
* A mapping from String keys to values of various types. In most cases, you
|
||||||
@@ -252,11 +252,10 @@ public class BaseBundle {
|
|||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Object o = getValueAt(0);
|
|
||||||
try {
|
try {
|
||||||
return (String) o;
|
return getValueAt(0, String.class);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException | BadParcelableException e) {
|
||||||
typeWarning("getPairValue()", o, "String", e);
|
typeWarning("getPairValue()", /* value */ null, "String", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,7 +308,7 @@ public class BaseBundle {
|
|||||||
}
|
}
|
||||||
for (int i = 0, n = mMap.size(); i < n; i++) {
|
for (int i = 0, n = mMap.size(); i < n; i++) {
|
||||||
// Triggers deserialization of i-th item, if needed
|
// Triggers deserialization of i-th item, if needed
|
||||||
getValueAt(i);
|
getValueAt(i, /* clazz */ null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,8 +323,21 @@ public class BaseBundle {
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
final Object getValue(String key) {
|
final Object getValue(String key) {
|
||||||
|
return getValue(key, /* clazz */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
|
||||||
|
* null} for no type check).
|
||||||
|
*
|
||||||
|
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
|
||||||
|
* {@code mMap} is not null.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
final <T> T getValue(String key, @Nullable Class<T> clazz) {
|
||||||
int i = mMap.indexOfKey(key);
|
int i = mMap.indexOfKey(key);
|
||||||
return (i >= 0) ? getValueAt(i) : null;
|
return (i >= 0) ? getValueAt(i, clazz) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -336,11 +348,12 @@ public class BaseBundle {
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
final Object getValueAt(int i) {
|
@SuppressWarnings("unchecked")
|
||||||
|
final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
|
||||||
Object object = mMap.valueAt(i);
|
Object object = mMap.valueAt(i);
|
||||||
if (object instanceof Supplier<?>) {
|
if (object instanceof Function<?, ?>) {
|
||||||
try {
|
try {
|
||||||
object = ((Supplier<?>) object).get();
|
object = ((Function<Class<?>, ?>) object).apply(clazz);
|
||||||
} catch (BadParcelableException e) {
|
} catch (BadParcelableException e) {
|
||||||
if (sShouldDefuse) {
|
if (sShouldDefuse) {
|
||||||
Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
|
Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
|
||||||
@@ -351,7 +364,7 @@ public class BaseBundle {
|
|||||||
}
|
}
|
||||||
mMap.setValueAt(i, object);
|
mMap.setValueAt(i, object);
|
||||||
}
|
}
|
||||||
return object;
|
return (clazz != null) ? clazz.cast(object) : (T) object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
|
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
|
||||||
@@ -528,7 +541,7 @@ public class BaseBundle {
|
|||||||
} else {
|
} else {
|
||||||
// Following semantic above of failing in case we get a serialized value vs a
|
// Following semantic above of failing in case we get a serialized value vs a
|
||||||
// deserialized one, we'll compare the map. If a certain element hasn't been
|
// deserialized one, we'll compare the map. If a certain element hasn't been
|
||||||
// deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's
|
// deserialized yet, it's a function object (or more specifically a LazyValue, but let's
|
||||||
// pretend we don't know that here :P), we'll use that element's equality comparison as
|
// pretend we don't know that here :P), we'll use that element's equality comparison as
|
||||||
// map naturally does. That will takes care of comparing the payload if needed (see
|
// map naturally does. That will takes care of comparing the payload if needed (see
|
||||||
// Parcel.readLazyValue() for details).
|
// Parcel.readLazyValue() for details).
|
||||||
@@ -982,15 +995,19 @@ public class BaseBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log a message if the value was non-null but not of the expected type
|
// Log a message if the value was non-null but not of the expected type
|
||||||
void typeWarning(String key, Object value, String className,
|
void typeWarning(String key, @Nullable Object value, String className,
|
||||||
Object defaultValue, ClassCastException e) {
|
Object defaultValue, RuntimeException e) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Key ");
|
sb.append("Key ");
|
||||||
sb.append(key);
|
sb.append(key);
|
||||||
sb.append(" expected ");
|
sb.append(" expected ");
|
||||||
sb.append(className);
|
sb.append(className);
|
||||||
|
if (value != null) {
|
||||||
sb.append(" but value was a ");
|
sb.append(" but value was a ");
|
||||||
sb.append(value.getClass().getName());
|
sb.append(value.getClass().getName());
|
||||||
|
} else {
|
||||||
|
sb.append(" but value was of a different type ");
|
||||||
|
}
|
||||||
sb.append(". The default value ");
|
sb.append(". The default value ");
|
||||||
sb.append(defaultValue);
|
sb.append(defaultValue);
|
||||||
sb.append(" was returned.");
|
sb.append(" was returned.");
|
||||||
@@ -998,8 +1015,7 @@ public class BaseBundle {
|
|||||||
Log.w(TAG, "Attempt to cast generated internal exception:", e);
|
Log.w(TAG, "Attempt to cast generated internal exception:", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void typeWarning(String key, Object value, String className,
|
void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) {
|
||||||
ClassCastException e) {
|
|
||||||
typeWarning(key, value, className, "<null>", e);
|
typeWarning(key, value, className, "<null>", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package android.os;
|
package android.os;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
@@ -912,6 +915,33 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value associated with the given key, or {@code null} if
|
||||||
|
* no mapping of the desired type exists for the given key or a {@code null}
|
||||||
|
* value is explicitly associated with the key.
|
||||||
|
*
|
||||||
|
* <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
|
||||||
|
* you must call {@link #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
|
||||||
|
* Otherwise, this method might throw an exception or return {@code null}.
|
||||||
|
*
|
||||||
|
* @param key a String, or {@code null}
|
||||||
|
* @param clazz The type of the object expected or {@code null} for performing no checks.
|
||||||
|
* @return a Parcelable value, or {@code null}
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
|
||||||
|
unparcel();
|
||||||
|
try {
|
||||||
|
return getValue(key, requireNonNull(clazz));
|
||||||
|
} catch (ClassCastException | BadParcelableException e) {
|
||||||
|
typeWarning(key, /* value */ null, "Parcelable", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value associated with the given key, or {@code null} if
|
* Returns the value associated with the given key, or {@code null} if
|
||||||
* no mapping of the desired type exists for the given key or a null
|
* no mapping of the desired type exists for the given key or a null
|
||||||
|
|||||||
@@ -4312,18 +4312,19 @@ public final class Parcel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will return a {@link Supplier} for length-prefixed types that deserializes the object
|
* This will return a {@link Function} for length-prefixed types that deserializes the object
|
||||||
* when {@link Supplier#get()} is called, for other types it will return the object itself.
|
* when {@link Function#apply} is called with the expected class of the return object (or {@code
|
||||||
|
* null} for no type check), for other types it will return the object itself.
|
||||||
*
|
*
|
||||||
* <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you
|
* <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
|
||||||
* shouldn't recycle the parcel, not at least until all objects have been retrieved. No
|
* you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
|
||||||
* synchronization attempts are made.
|
* synchronization attempts are made.
|
||||||
*
|
*
|
||||||
* </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
|
* </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
|
||||||
* suppliers are equal if either of the following is true:
|
* function objects are equal if either of the following is true:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link Supplier#get()} has been called on both and both objects returned are equal.
|
* <li>{@link Function#apply} has been called on both and both objects returned are equal.
|
||||||
* <li>{@link Supplier#get()} hasn't been called on either one and everything below is true:
|
* <li>{@link Function#apply} hasn't been called on either one and everything below is true:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>The {@code loader} parameters used to retrieve each are equal.
|
* <li>The {@code loader} parameters used to retrieve each are equal.
|
||||||
* <li>They both have the same type.
|
* <li>They both have the same type.
|
||||||
@@ -4350,7 +4351,7 @@ public final class Parcel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final class LazyValue implements Supplier<Object> {
|
private static final class LazyValue implements Function<Class<?>, Object> {
|
||||||
/**
|
/**
|
||||||
* | 4B | 4B |
|
* | 4B | 4B |
|
||||||
* mSource = Parcel{... | type | length | object | ...}
|
* mSource = Parcel{... | type | length | object | ...}
|
||||||
@@ -4382,7 +4383,7 @@ public final class Parcel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object get() {
|
public Object apply(@Nullable Class<?> clazz) {
|
||||||
Parcel source = mSource;
|
Parcel source = mSource;
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
synchronized (source) {
|
synchronized (source) {
|
||||||
@@ -4391,7 +4392,7 @@ public final class Parcel {
|
|||||||
int restore = source.dataPosition();
|
int restore = source.dataPosition();
|
||||||
try {
|
try {
|
||||||
source.setDataPosition(mPosition);
|
source.setDataPosition(mPosition);
|
||||||
mObject = source.readValue(mLoader);
|
mObject = source.readValue(mLoader, clazz);
|
||||||
} finally {
|
} finally {
|
||||||
source.setDataPosition(restore);
|
source.setDataPosition(restore);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user