Merge "Add safer Bundle APIs and deprecated old ones" am: c5589d2c92 am: ce09bba3fc
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1988908 Change-Id: I2f821d9de45980035ae3b4f36d83b36ad859568e
This commit is contained in:
@@ -29483,7 +29483,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);
|
||||
@@ -29724,16 +29724,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);
|
||||
|
||||
30
core/java/android/os/BadTypeParcelableException.java
Normal file
30
core/java/android/os/BadTypeParcelableException.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -3170,8 +3173,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3186,8 +3188,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3236,13 +3237,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3258,13 +3253,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4289,16 +4278,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,
|
||||
@@ -4306,25 +4296,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.
|
||||
@@ -4351,7 +4342,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 | ...}
|
||||
@@ -4383,7 +4374,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) {
|
||||
@@ -4392,7 +4383,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);
|
||||
}
|
||||
@@ -4472,14 +4463,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:
|
||||
@@ -4495,7 +4497,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:
|
||||
@@ -4526,10 +4532,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;
|
||||
@@ -4550,10 +4558,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;
|
||||
@@ -4570,14 +4580,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;
|
||||
@@ -4625,7 +4639,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");
|
||||
}
|
||||
@@ -4651,6 +4665,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
|
||||
@@ -4781,7 +4827,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");
|
||||
}
|
||||
@@ -4804,7 +4850,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");
|
||||
}
|
||||
@@ -4865,15 +4911,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4885,14 +4923,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);
|
||||
}
|
||||
@@ -4955,7 +4999,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");
|
||||
}
|
||||
@@ -4980,7 +5024,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");
|
||||
}
|
||||
@@ -5090,7 +5134,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) {
|
||||
@@ -5101,7 +5164,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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user