Merge "Add safer Bundle APIs and deprecated old ones" am: c5589d2c92 am: ce09bba3fc am: aed366dc04

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1988908

Change-Id: I684471f938a4400fae3309ad2c9759d808075aea
This commit is contained in:
Bernardo Rufino
2022-02-22 14:31:52 +00:00
committed by Automerger Merge Worker
6 changed files with 424 additions and 136 deletions

View File

@@ -30684,7 +30684,7 @@ package android.os {
public class BaseBundle {
method public void clear();
method public boolean containsKey(String);
method @Nullable public Object get(String);
method @Deprecated @Nullable public Object get(String);
method public boolean getBoolean(String);
method public boolean getBoolean(String, boolean);
method @Nullable public boolean[] getBooleanArray(@Nullable String);
@@ -30926,16 +30926,21 @@ package android.os {
method public float getFloat(String, float);
method @Nullable public float[] getFloatArray(@Nullable String);
method @Nullable public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(@Nullable String);
method @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
method @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
method @Nullable public java.io.Serializable getSerializable(@Nullable String);
method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
method @Nullable public <T> T getParcelable(@Nullable String, @NonNull Class<T>);
method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
method @Nullable public <T> T[] getParcelableArray(@Nullable String, @NonNull Class<T>);
method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayList(@Nullable String, @NonNull Class<T>);
method @Deprecated @Nullable public java.io.Serializable getSerializable(@Nullable String);
method @Nullable public <T extends java.io.Serializable> T getSerializable(@Nullable String, @NonNull Class<T>);
method public short getShort(String);
method public short getShort(String, short);
method @Nullable public short[] getShortArray(@Nullable String);
method @Nullable public android.util.Size getSize(@Nullable String);
method @Nullable public android.util.SizeF getSizeF(@Nullable String);
method @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
method @Deprecated @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
method @Nullable public <T> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String, @NonNull Class<T>);
method @Nullable public java.util.ArrayList<java.lang.String> getStringArrayList(@Nullable String);
method public boolean hasFileDescriptors();
method public void putAll(android.os.Bundle);

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/** Used by Parcel to signal that the type on the payload was not expected by the caller. */
class BadTypeParcelableException extends BadParcelableException {
BadTypeParcelableException(String msg) {
super(msg);
}
BadTypeParcelableException(Exception cause) {
super(cause);
}
BadTypeParcelableException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -16,6 +16,8 @@
package android.os;
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -31,7 +33,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
import java.util.function.Function;
import java.util.function.BiFunction;
/**
* A mapping from String keys to values of various types. In most cases, you
@@ -254,8 +256,8 @@ public class BaseBundle {
}
try {
return getValueAt(0, String.class);
} catch (ClassCastException | BadParcelableException e) {
typeWarning("getPairValue()", /* value */ null, "String", e);
} catch (ClassCastException | BadTypeParcelableException e) {
typeWarning("getPairValue()", "String", e);
return null;
}
}
@@ -320,28 +322,46 @@ public class BaseBundle {
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
* {@code mMap} is not null.
*
* @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in
* other deprecated APIs.
*
* @hide
*/
@Deprecated
@Nullable
final Object getValue(String key) {
return getValue(key, /* clazz */ null);
}
/** Same as {@link #getValue(String, Class, Class[])} with no item types. */
@Nullable
final <T> T getValue(String key, @Nullable Class<T> clazz) {
// Avoids allocating Class[0] array
return getValue(key, clazz, (Class<?>[]) null);
}
/**
* Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
* Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code
* null} for no type check).
*
* For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
*
* 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) {
@Nullable
final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
int i = mMap.indexOfKey(key);
return (i >= 0) ? getValueAt(i, clazz) : null;
return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null;
}
/**
* Returns the value for a certain position in the array map.
* Returns the value for a certain position in the array map for expected return type {@code
* clazz} (or pass {@code null} for no type check).
*
* For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
*
* This call should always be made after {@link #unparcel()} or inside a lock after making sure
* {@code mMap} is not null.
@@ -349,11 +369,12 @@ public class BaseBundle {
* @hide
*/
@SuppressWarnings("unchecked")
final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
@Nullable
final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
Object object = mMap.valueAt(i);
if (object instanceof Function<?, ?>) {
if (object instanceof BiFunction<?, ?, ?>) {
try {
object = ((Function<Class<?>, ?>) object).apply(clazz);
object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -615,13 +636,43 @@ public class BaseBundle {
*
* @param key a String key
* @return an Object, or null
*
* @deprecated Use the type-safe specific APIs depending on the type of the item to be
* retrieved, eg. {@link #getString(String)}.
*/
@Deprecated
@Nullable
public Object get(String key) {
unparcel();
return getValue(key);
}
/**
* Returns the object of type {@code clazz} for the given {@code key}, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* <p>Use the more specific APIs where possible, especially in the case of containers such as
* lists, since those APIs allow you to specify the type of the items.
*
* @param key String key
* @param clazz The type of the object expected
* @return an Object, or null
*/
@Nullable
<T> T get(@Nullable String key, @NonNull Class<T> clazz) {
unparcel();
try {
return getValue(key, requireNonNull(clazz));
} catch (ClassCastException | BadTypeParcelableException e) {
typeWarning(key, clazz.getCanonicalName(), e);
return null;
}
}
/**
* Removes any entry with the given key from the mapping of this Bundle.
*
@@ -1006,7 +1057,7 @@ public class BaseBundle {
sb.append(" but value was a ");
sb.append(value.getClass().getName());
} else {
sb.append(" but value was of a different type ");
sb.append(" but value was of a different type");
}
sb.append(". The default value ");
sb.append(defaultValue);
@@ -1019,6 +1070,10 @@ public class BaseBundle {
typeWarning(key, value, className, "<null>", e);
}
void typeWarning(String key, String className, RuntimeException e) {
typeWarning(key, /* value */ null, className, "<null>", e);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
@@ -1358,7 +1413,11 @@ public class BaseBundle {
*
* @param key a String, or null
* @return a Serializable value, or null
*
* @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in
* other deprecated APIs.
*/
@Deprecated
@Nullable
Serializable getSerializable(@Nullable String key) {
unparcel();
@@ -1374,6 +1433,36 @@ public class BaseBundle {
}
}
/**
* Returns the value associated with the given key, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* @param key a String, or null
* @param clazz The expected class of the returned type
* @return a Serializable value, or null
*/
@Nullable
<T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) {
return get(key, clazz);
}
@SuppressWarnings("unchecked")
@Nullable
<T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<T> clazz) {
unparcel();
try {
return getValue(key, ArrayList.class, requireNonNull(clazz));
} catch (ClassCastException | BadTypeParcelableException e) {
typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e);
return null;
}
}
/**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
@@ -1384,17 +1473,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
unparcel();
Object o = getValue(key);
if (o == null) {
return null;
}
try {
return (ArrayList<Integer>) o;
} catch (ClassCastException e) {
typeWarning(key, o, "ArrayList<Integer>", e);
return null;
}
return getArrayList(key, Integer.class);
}
/**
@@ -1407,17 +1486,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<String> getStringArrayList(@Nullable String key) {
unparcel();
Object o = getValue(key);
if (o == null) {
return null;
}
try {
return (ArrayList<String>) o;
} catch (ClassCastException e) {
typeWarning(key, o, "ArrayList<String>", e);
return null;
}
return getArrayList(key, String.class);
}
/**
@@ -1430,17 +1499,7 @@ public class BaseBundle {
*/
@Nullable
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
unparcel();
Object o = getValue(key);
if (o == null) {
return null;
}
try {
return (ArrayList<CharSequence>) o;
} catch (ClassCastException e) {
typeWarning(key, o, "ArrayList<CharSequence>", e);
return null;
}
return getArrayList(key, CharSequence.class);
}
/**

View File

@@ -20,6 +20,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Size;
@@ -876,7 +877,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
@Nullable
public Bundle getBundle(@Nullable String key) {
unparcel();
Object o = getValue(key);
Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -899,7 +900,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return a Parcelable value, or {@code null}
*
* @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
* {@link Build.VERSION_CODES#TIRAMISU}.
*/
@Deprecated
@Nullable
public <T extends Parcelable> T getParcelable(@Nullable String key) {
unparcel();
@@ -916,30 +921,28 @@ 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.
* Returns the value associated with the given key or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* <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.
* @param clazz The type of the object expected
* @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;
}
// The reason for not using <T extends Parcelable> is because the caller could provide a
// super class to restrict the children that doesn't implement Parcelable itself while the
// children do, more details at b/210800751 (same reasoning applies here).
return get(key, clazz);
}
/**
@@ -953,7 +956,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return a Parcelable[] value, or {@code null}
*
* @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from
* Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
@Deprecated
@Nullable
public Parcelable[] getParcelableArray(@Nullable String key) {
unparcel();
@@ -969,6 +976,39 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
}
/**
* Returns the value associated with the given key, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* <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 items inside the array
* @return a Parcelable[] value, or {@code null}
*/
@SuppressLint({"ArrayReturn", "NullableCollection"})
@SuppressWarnings("unchecked")
@Nullable
public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) {
// The reason for not using <T extends Parcelable> is because the caller could provide a
// super class to restrict the children that doesn't implement Parcelable itself while the
// children do, more details at b/210800751 (same reasoning applies here).
unparcel();
try {
// In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting.
return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz));
} catch (ClassCastException | BadTypeParcelableException e) {
typeWarning(key, clazz.getCanonicalName() + "[]", e);
return null;
}
}
/**
* 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}
@@ -980,7 +1020,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or {@code null}
* @return an ArrayList<T> value, or {@code null}
*
* @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
* {@link Build.VERSION_CODES#TIRAMISU}.
*/
@Deprecated
@Nullable
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
unparcel();
@@ -996,15 +1040,44 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
}
/**
* Returns the value associated with the given key, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* <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 items inside the array list
* @return an ArrayList<T> value, or {@code null}
*/
@SuppressLint("NullableCollection")
@SuppressWarnings("unchecked")
@Nullable
public <T> ArrayList<T> getParcelableArrayList(@Nullable String key, @NonNull Class<T> clazz) {
// The reason for not using <T extends Parcelable> is because the caller could provide a
// super class to restrict the children that doesn't implement Parcelable itself while the
// children do, more details at b/210800751 (same reasoning applies here).
return getArrayList(key, clazz);
}
/**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
* value is explicitly associated with the key.
*
* @param key a String, or null
*
* @return a SparseArray of T values, or null
*
* @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from
* Android {@link Build.VERSION_CODES#TIRAMISU}.
*/
@Deprecated
@Nullable
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
unparcel();
@@ -1020,6 +1093,33 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
}
}
/**
* Returns the value associated with the given key, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* @param key a String, or null
* @return a SparseArray of T values, or null
*/
@SuppressWarnings("unchecked")
@Nullable
public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key,
@NonNull Class<T> clazz) {
// The reason for not using <T extends Parcelable> is because the caller could provide a
// super class to restrict the children that doesn't implement Parcelable itself while the
// children do, more details at b/210800751 (same reasoning applies here).
unparcel();
try {
return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz));
} catch (ClassCastException | BadTypeParcelableException e) {
typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e);
return null;
}
}
/**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null
@@ -1027,13 +1127,35 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
*
* @param key a String, or null
* @return a Serializable value, or null
*
* @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android
* {@link Build.VERSION_CODES#TIRAMISU}.
*/
@Deprecated
@Override
@Nullable
public Serializable getSerializable(@Nullable String key) {
return super.getSerializable(key);
}
/**
* Returns the value associated with the given key, or {@code null} if:
* <ul>
* <li>No mapping of the desired type exists for the given key.
* <li>A {@code null} value is explicitly associated with the key.
* <li>The object is not of type {@code clazz}.
* </ul>
*
* @param key a String, or null
* @param clazz The expected class of the returned type
* @return a Serializable value, or null
*/
@Nullable
public <T extends Serializable> T getSerializable(@Nullable String key,
@NonNull Class<T> clazz) {
return super.getSerializable(key, requireNonNull(clazz));
}
/**
* Returns the value associated with the given key, or null if
* no mapping of the desired type exists for the given key or a null

View File

@@ -16,6 +16,8 @@
package android.os;
import static com.android.internal.util.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
@@ -65,6 +67,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
@@ -287,26 +290,26 @@ public final class Parcel {
private static final int VAL_NULL = -1;
private static final int VAL_STRING = 0;
private static final int VAL_INTEGER = 1;
private static final int VAL_MAP = 2;
private static final int VAL_MAP = 2; // length-prefixed
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_PARCELABLE = 4; // length-prefixed
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;
private static final int VAL_LIST = 11;
private static final int VAL_SPARSEARRAY = 12;
private static final int VAL_LIST = 11; // length-prefixed
private static final int VAL_SPARSEARRAY = 12; // length-prefixed
private static final int VAL_BYTEARRAY = 13;
private static final int VAL_STRINGARRAY = 14;
private static final int VAL_IBINDER = 15;
private static final int VAL_PARCELABLEARRAY = 16;
private static final int VAL_OBJECTARRAY = 17;
private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
private static final int VAL_OBJECTARRAY = 17; // length-prefixed
private static final int VAL_INTARRAY = 18;
private static final int VAL_LONGARRAY = 19;
private static final int VAL_BYTE = 20;
private static final int VAL_SERIALIZABLE = 21;
private static final int VAL_SERIALIZABLE = 21; // length-prefixed
private static final int VAL_SPARSEBOOLEANARRAY = 22;
private static final int VAL_BOOLEANARRAY = 23;
private static final int VAL_CHARSEQUENCEARRAY = 24;
@@ -3179,8 +3182,7 @@ public final class Parcel {
*/
@Deprecated
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
int n = readInt();
readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
}
/**
@@ -3195,8 +3197,7 @@ public final class Parcel {
@NonNull Class<V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
int n = readInt();
readMapInternal(outVal, n, loader, clazzKey, clazzValue);
readMapInternal(outVal, loader, clazzKey, clazzValue);
}
/**
@@ -3245,13 +3246,7 @@ public final class Parcel {
@Deprecated
@Nullable
public HashMap readHashMap(@Nullable ClassLoader loader) {
int n = readInt();
if (n < 0) {
return null;
}
HashMap m = new HashMap(n);
readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
return m;
return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
}
/**
@@ -3267,13 +3262,7 @@ public final class Parcel {
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
Objects.requireNonNull(clazzKey);
Objects.requireNonNull(clazzValue);
int n = readInt();
if (n < 0) {
return null;
}
HashMap<K, V> map = new HashMap<>(n);
readMapInternal(map, n, loader, clazzKey, clazzValue);
return map;
return readHashMapInternal(loader, clazzKey, clazzValue);
}
/**
@@ -4296,16 +4285,17 @@ public final class Parcel {
/**
* @param clazz The type of the object expected or {@code null} for performing no checks.
* @see #readValue(int, ClassLoader, Class, Class[])
*/
@Nullable
private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
@Nullable Class<?>... itemTypes) {
int type = readInt();
final T object;
if (isLengthPrefixed(type)) {
int length = readInt();
int start = dataPosition();
object = readValue(type, loader, clazz);
object = readValue(type, loader, clazz, itemTypes);
int actual = dataPosition() - start;
if (actual != length) {
Slog.wtfStack(TAG,
@@ -4313,25 +4303,26 @@ public final class Parcel {
+ " consumed " + actual + " bytes, but " + length + " expected.");
}
} else {
object = readValue(type, loader, clazz);
object = readValue(type, loader, clazz, itemTypes);
}
return object;
}
/**
* This will return a {@link Function} for length-prefixed types that deserializes the object
* 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.
* This will return a {@link BiFunction} for length-prefixed types that deserializes the object
* when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link
* #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it
* will return the object itself.
*
* <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
* you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
* <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you
* shouldn't recycle the parcel, not at least until all objects have been retrieved. No
* synchronization attempts are made.
*
* </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
* function objects are equal if either of the following is true:
* <ul>
* <li>{@link Function#apply} has been called on both and both objects returned are equal.
* <li>{@link Function#apply} hasn't been called on either one and everything below is true:
* <li>{@link BiFunction#apply} has been called on both and both objects returned are equal.
* <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true:
* <ul>
* <li>The {@code loader} parameters used to retrieve each are equal.
* <li>They both have the same type.
@@ -4358,7 +4349,7 @@ public final class Parcel {
}
private static final class LazyValue implements Function<Class<?>, Object> {
private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
/**
* | 4B | 4B |
* mSource = Parcel{... | type | length | object | ...}
@@ -4390,7 +4381,7 @@ public final class Parcel {
}
@Override
public Object apply(@Nullable Class<?> clazz) {
public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
Parcel source = mSource;
if (source != null) {
synchronized (source) {
@@ -4399,7 +4390,7 @@ public final class Parcel {
int restore = source.dataPosition();
try {
source.setDataPosition(mPosition);
mObject = source.readValue(mLoader, clazz);
mObject = source.readValue(mLoader, clazz, itemTypes);
} finally {
source.setDataPosition(restore);
}
@@ -4479,14 +4470,25 @@ public final class Parcel {
}
}
/** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
// Avoids allocating Class[0] array
return readValue(type, loader, clazz, (Class<?>[]) null);
}
/**
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
* type first.
*
* @param clazz The type of the object expected or {@code null} for performing no checks.
* @param itemTypes If the value is a container, these represent the item types (eg. for a list
* it's the item type, for a map, it's the key type, followed by the value
* type).
*/
@SuppressWarnings("unchecked")
@Nullable
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz,
@Nullable Class<?>... itemTypes) {
final Object object;
switch (type) {
case VAL_NULL:
@@ -4502,7 +4504,11 @@ public final class Parcel {
break;
case VAL_MAP:
object = readHashMap(loader);
checkTypeToUnparcel(clazz, HashMap.class);
Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0);
Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1);
checkArgument((keyType == null) == (valueType == null));
object = readHashMapInternal(loader, keyType, valueType);
break;
case VAL_PARCELABLE:
@@ -4533,10 +4539,12 @@ public final class Parcel {
object = readCharSequence();
break;
case VAL_LIST:
object = readArrayList(loader);
case VAL_LIST: {
checkTypeToUnparcel(clazz, ArrayList.class);
Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
object = readArrayListInternal(loader, itemType);
break;
}
case VAL_BOOLEANARRAY:
object = createBooleanArray();
break;
@@ -4557,10 +4565,12 @@ public final class Parcel {
object = readStrongBinder();
break;
case VAL_OBJECTARRAY:
object = readArray(loader);
case VAL_OBJECTARRAY: {
Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class);
object = readArrayInternal(loader, itemType);
break;
}
case VAL_INTARRAY:
object = createIntArray();
break;
@@ -4577,14 +4587,18 @@ public final class Parcel {
object = readSerializableInternal(loader, clazz);
break;
case VAL_PARCELABLEARRAY:
object = readParcelableArray(loader);
case VAL_PARCELABLEARRAY: {
Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class);
object = readParcelableArrayInternal(loader, itemType);
break;
case VAL_SPARSEARRAY:
object = readSparseArray(loader);
}
case VAL_SPARSEARRAY: {
checkTypeToUnparcel(clazz, SparseArray.class);
Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
object = readSparseArrayInternal(loader, itemType);
break;
}
case VAL_SPARSEBOOLEANARRAY:
object = readSparseBooleanArray();
break;
@@ -4632,7 +4646,7 @@ public final class Parcel {
+ " at offset " + off);
}
if (object != null && clazz != null && !clazz.isInstance(object)) {
throw new BadParcelableException("Unparcelled object " + object
throw new BadTypeParcelableException("Unparcelled object " + object
+ " is not an instance of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4658,6 +4672,38 @@ public final class Parcel {
}
}
/**
* Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of
* {@code requiredArrayType}.
*/
private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType,
Class<?> componentTypeToUnparcel) {
if (requiredArrayType != null) {
// In Java 12, we could use componentTypeToUnparcel.arrayType() for the check
Class<?> requiredComponentType = requiredArrayType.getComponentType();
if (requiredComponentType == null) {
throw new BadTypeParcelableException(
"About to unparcel an array but type "
+ requiredArrayType.getCanonicalName()
+ " required by caller is not an array.");
}
checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel);
}
}
/**
* Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code
* requiredType} is not {@code null}.
*/
private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) {
if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) {
throw new BadTypeParcelableException(
"About to unparcel a " + typeToUnparcel.getCanonicalName()
+ ", which is not a subtype of type " + requiredType.getCanonicalName()
+ " required by caller.");
}
}
/**
* Read and return a new Parcelable from the parcel. The given class loader
* will be used to load any enclosed Parcelables. If it is null, the default
@@ -4788,7 +4834,7 @@ public final class Parcel {
if (clazz != null) {
Class<?> parcelableClass = creator.getClass().getEnclosingClass();
if (!clazz.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable creator " + name + " is not "
throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4811,7 +4857,7 @@ public final class Parcel {
}
if (clazz != null) {
if (!clazz.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable creator " + name + " is not "
throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
@@ -4872,15 +4918,7 @@ public final class Parcel {
@Deprecated
@Nullable
public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
}
Parcelable[] p = new Parcelable[N];
for (int i = 0; i < N; i++) {
p[i] = readParcelable(loader);
}
return p;
return readParcelableArrayInternal(loader, /* clazz */ null);
}
/**
@@ -4892,14 +4930,20 @@ public final class Parcel {
* trying to instantiate an element.
*/
@SuppressLint({"ArrayReturn", "NullableCollection"})
@SuppressWarnings("unchecked")
@Nullable
public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
return readParcelableArrayInternal(loader, requireNonNull(clazz));
}
@SuppressWarnings("unchecked")
@Nullable
private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader,
@Nullable Class<T> clazz) {
int n = readInt();
if (n < 0) {
return null;
}
T[] p = (T[]) Array.newInstance(clazz, n);
T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
for (int i = 0; i < n; i++) {
p[i] = readParcelableInternal(loader, clazz);
}
@@ -4962,7 +5006,7 @@ public final class Parcel {
// the class the same way as ObjectInputStream, using the provided classloader.
Class<?> cl = Class.forName(name, false, loader);
if (!clazz.isAssignableFrom(cl)) {
throw new BadParcelableException("Serializable object "
throw new BadTypeParcelableException("Serializable object "
+ cl.getName() + " is not a subclass of required class "
+ clazz.getName() + " provided in the parameter");
}
@@ -4987,7 +5031,7 @@ public final class Parcel {
// the deserialized object, as we cannot resolve the class the same way as
// ObjectInputStream.
if (!clazz.isAssignableFrom(object.getClass())) {
throw new BadParcelableException("Serializable object "
throw new BadTypeParcelableException("Serializable object "
+ object.getClass().getName() + " is not a subclass of required class "
+ clazz.getName() + " provided in the parameter");
}
@@ -5097,7 +5141,26 @@ public final class Parcel {
readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
}
/* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
@Nullable
private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader,
@NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
int n = readInt();
if (n < 0) {
return null;
}
HashMap<K, V> map = new HashMap<>(n);
readMapInternal(map, n, loader, clazzKey, clazzValue);
return map;
}
private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal,
@Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
@Nullable Class<V> clazzValue) {
int n = readInt();
readMapInternal(outVal, n, loader, clazzKey, clazzValue);
}
private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
@Nullable Class<V> clazzValue) {
while (n > 0) {
@@ -5108,7 +5171,7 @@ public final class Parcel {
}
}
/* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
int size, @Nullable ClassLoader loader) {
readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
}

View File

@@ -888,6 +888,15 @@ public class ArrayUtils {
}
}
/**
* Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code
* null}, otherwise returns {@code null}.
*/
@Nullable
public static <T> T getOrNull(@Nullable T[] items, int i) {
return (items != null && items.length > i) ? items[i] : null;
}
public static @Nullable <T> T firstOrNull(T[] items) {
return items.length > 0 ? items[0] : null;
}