map = mMap;
if (map == null) {
map = new ArrayMap<>(count);
} else {
map.erase();
map.ensureCapacity(count);
}
int numLazyValues = 0;
try {
numLazyValues = parcelledData.readArrayMap(map, count, !parcelledByNative,
/* lazy */ ownsParcel, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
map.erase();
} else {
throw e;
}
} finally {
mWeakParcelledData = null;
if (ownsParcel) {
if (numLazyValues == 0) {
recycleParcel(parcelledData);
} else {
mWeakParcelledData = new WeakReference<>(parcelledData);
}
}
mLazyValues = numLazyValues;
mParcelledByNative = false;
mMap = map;
// Set field last as it is volatile
mParcelledData = null;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
/**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean isParcelled() {
return mParcelledData != null;
}
/**
* @hide
*/
public boolean isEmptyParcel() {
return isEmptyParcel(mParcelledData);
}
/**
* @hide
*/
private static boolean isEmptyParcel(Parcel p) {
return p == NoImagePreloadHolder.EMPTY_PARCEL;
}
private static void recycleParcel(Parcel p) {
if (p != null && !isEmptyParcel(p)) {
p.recycle();
}
}
/**
* Returns the backing map of this bundle after deserializing every item.
*
* Warning: This method will deserialize every item on the bundle, including custom
* types such as {@link Parcelable} and {@link Serializable}, so only use this when you trust
* the source. Specifically don't use this method on app-provided bundles.
*
* @hide
*/
ArrayMap getItemwiseMap() {
unparcel(/* itemwise */ true);
return mMap;
}
/**
* Returns the number of mappings contained in this Bundle.
*
* @return the number of mappings as an int.
*/
public int size() {
unparcel();
return mMap.size();
}
/**
* Returns true if the mapping of this Bundle is empty, false otherwise.
*/
public boolean isEmpty() {
unparcel();
return mMap.isEmpty();
}
/**
* This method returns true when the parcel is 'definitely' empty.
* That is, it may return false for an empty parcel. But will never return true for a non-empty
* one.
*
* @hide this should probably be the implementation of isEmpty(). To do that we
* need to ensure we always use the special empty parcel form when the bundle is
* empty. (This may already be the case, but to be safe we'll do this later when
* we aren't trying to stabilize.)
*/
public boolean isDefinitelyEmpty() {
if (isParcelled()) {
return isEmptyParcel();
} else {
return isEmpty();
}
}
/**
* Does a loose equality check between two given {@link BaseBundle} objects.
* Returns {@code true} if both are {@code null}, or if both are equal as per
* {@link #kindofEquals(BaseBundle)}
*
* @param a A {@link BaseBundle} object
* @param b Another {@link BaseBundle} to compare with a
* @return {@code true} if both are the same, {@code false} otherwise
*
* @see #kindofEquals(BaseBundle)
*
* @hide
*/
public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) {
return (a == b) || (a != null && a.kindofEquals(b));
}
/**
* Performs a loose equality check, which means there can be false negatives but if the method
* returns true than both objects are guaranteed to be equal.
*
* The point is that this method is a light-weight check in performance terms.
*
* @hide
*/
public boolean kindofEquals(BaseBundle other) {
if (other == null) {
return false;
}
if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
return true;
}
if (isParcelled() != other.isParcelled()) {
// Big kind-of here!
return false;
} else if (isParcelled()) {
return mParcelledData.compareData(other.mParcelledData) == 0;
} else {
// 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 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
// map naturally does. That will takes care of comparing the payload if needed (see
// Parcel.readLazyValue() for details).
return mMap.equals(other.mMap);
}
}
/**
* Removes all elements from the mapping of this Bundle.
* Recycles the underlying parcel if it is still present.
*/
public void clear() {
unparcel();
if (mOwnsLazyValues && mWeakParcelledData != null) {
recycleParcel(mWeakParcelledData.get());
}
mWeakParcelledData = null;
mLazyValues = 0;
mOwnsLazyValues = true;
mMap.clear();
}
private Object deepCopyValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof Bundle) {
return ((Bundle)value).deepCopy();
} else if (value instanceof PersistableBundle) {
return ((PersistableBundle)value).deepCopy();
} else if (value instanceof ArrayList) {
return deepcopyArrayList((ArrayList) value);
} else if (value.getClass().isArray()) {
if (value instanceof int[]) {
return ((int[])value).clone();
} else if (value instanceof long[]) {
return ((long[])value).clone();
} else if (value instanceof float[]) {
return ((float[])value).clone();
} else if (value instanceof double[]) {
return ((double[])value).clone();
} else if (value instanceof Object[]) {
return ((Object[])value).clone();
} else if (value instanceof byte[]) {
return ((byte[])value).clone();
} else if (value instanceof short[]) {
return ((short[])value).clone();
} else if (value instanceof char[]) {
return ((char[]) value).clone();
}
}
return value;
}
private ArrayList deepcopyArrayList(ArrayList from) {
final int N = from.size();
ArrayList out = new ArrayList(N);
for (int i=0; i
* No mapping of the desired type exists for the given key.
* A {@code null} value is explicitly associated with the key.
* The object is not of type {@code clazz}.
*
*
* 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 get(@Nullable String key, @NonNull Class 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.
*
* @param key a String key
*/
public void remove(String key) {
unparcel();
mMap.remove(key);
}
/**
* Inserts all mappings from the given PersistableBundle into this BaseBundle.
*
* @param bundle a PersistableBundle
*/
public void putAll(PersistableBundle bundle) {
unparcel();
bundle.unparcel();
mMap.putAll(bundle.mMap);
}
/**
* Inserts all mappings from the given Map into this BaseBundle.
*
* @param map a Map
*/
void putAll(ArrayMap map) {
unparcel();
mMap.putAll(map);
}
/**
* Returns a Set containing the Strings used as keys in this Bundle.
*
* @return a Set of String keys
*/
public Set keySet() {
unparcel();
return mMap.keySet();
}
/** {@hide} */
public void putObject(@Nullable String key, @Nullable Object value) {
if (value == null) {
putString(key, null);
} else if (value instanceof Boolean) {
putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
putInt(key, (Integer) value);
} else if (value instanceof Long) {
putLong(key, (Long) value);
} else if (value instanceof Double) {
putDouble(key, (Double) value);
} else if (value instanceof String) {
putString(key, (String) value);
} else if (value instanceof boolean[]) {
putBooleanArray(key, (boolean[]) value);
} else if (value instanceof int[]) {
putIntArray(key, (int[]) value);
} else if (value instanceof long[]) {
putLongArray(key, (long[]) value);
} else if (value instanceof double[]) {
putDoubleArray(key, (double[]) value);
} else if (value instanceof String[]) {
putStringArray(key, (String[]) value);
} else {
throw new IllegalArgumentException("Unsupported type " + value.getClass());
}
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean
*/
public void putBoolean(@Nullable String key, boolean value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a byte value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a byte
*/
void putByte(@Nullable String key, byte value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a char value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a char
*/
void putChar(@Nullable String key, char value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a short value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a short
*/
void putShort(@Nullable String key, short value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an int value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value an int
*/
public void putInt(@Nullable String key, int value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a long value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a long
*/
public void putLong(@Nullable String key, long value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a float value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a float
*/
void putFloat(@Nullable String key, float value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a double value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a double
*/
public void putDouble(@Nullable String key, double value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a String value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a String, or null
*/
public void putString(@Nullable String key, @Nullable String value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a CharSequence value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence, or null
*/
void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
*/
void putIntegerArrayList(@Nullable String key, @Nullable ArrayList value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
*/
void putStringArrayList(@Nullable String key, @Nullable ArrayList value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
*/
void putCharSequenceArrayList(@Nullable String key, @Nullable ArrayList value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a Serializable value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Serializable object, or null
*/
void putSerializable(@Nullable String key, @Nullable Serializable value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a boolean array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean array object, or null
*/
public void putBooleanArray(@Nullable String key, @Nullable boolean[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a byte array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a byte array object, or null
*/
void putByteArray(@Nullable String key, @Nullable byte[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a short array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a short array object, or null
*/
void putShortArray(@Nullable String key, @Nullable short[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a char array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a char array object, or null
*/
void putCharArray(@Nullable String key, @Nullable char[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an int array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an int array object, or null
*/
public void putIntArray(@Nullable String key, @Nullable int[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a long array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a long array object, or null
*/
public void putLongArray(@Nullable String key, @Nullable long[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a float array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a float array object, or null
*/
void putFloatArray(@Nullable String key, @Nullable float[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a double array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a double array object, or null
*/
public void putDoubleArray(@Nullable String key, @Nullable double[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a String array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a String array object, or null
*/
public void putStringArray(@Nullable String key, @Nullable String[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a CharSequence array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence array object, or null
*/
void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
unparcel();
mMap.put(key, value);
}
/**
* Returns the value associated with the given key, or false if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a boolean value
*/
public boolean getBoolean(String key) {
unparcel();
if (DEBUG) Log.d(TAG, "Getting boolean in "
+ Integer.toHexString(System.identityHashCode(this)));
return getBoolean(key, false);
}
// Log a message if the value was non-null but not of the expected type
void typeWarning(String key, @Nullable Object value, String className,
Object defaultValue, RuntimeException e) {
StringBuilder sb = new StringBuilder();
sb.append("Key ");
sb.append(key);
sb.append(" expected ");
sb.append(className);
if (value != null) {
sb.append(" but value was a ");
sb.append(value.getClass().getName());
} else {
sb.append(" but value was of a different type");
}
sb.append(". The default value ");
sb.append(defaultValue);
sb.append(" was returned.");
Log.w(TAG, sb.toString());
Log.w(TAG, "Attempt to cast generated internal exception:", e);
}
void typeWarning(String key, @Nullable Object value, String className, RuntimeException e) {
typeWarning(key, value, className, "", e);
}
void typeWarning(String key, String className, RuntimeException e) {
typeWarning(key, /* value */ null, className, "", e);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a boolean value
*/
public boolean getBoolean(String key, boolean defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Boolean) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Boolean", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or (byte) 0 if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a byte value
*/
byte getByte(String key) {
unparcel();
return getByte(key, (byte) 0);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a byte value
*/
Byte getByte(String key, byte defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Byte) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Byte", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or (char) 0 if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a char value
*/
char getChar(String key) {
unparcel();
return getChar(key, (char) 0);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a char value
*/
char getChar(String key, char defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Character) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Character", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or (short) 0 if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a short value
*/
short getShort(String key) {
unparcel();
return getShort(key, (short) 0);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a short value
*/
short getShort(String key, short defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Short) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Short", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or 0 if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return an int value
*/
public int getInt(String key) {
unparcel();
return getInt(key, 0);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return an int value
*/
public int getInt(String key, int defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Integer) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Integer", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or 0L if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a long value
*/
public long getLong(String key) {
unparcel();
return getLong(key, 0L);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a long value
*/
public long getLong(String key, long defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Long) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Long", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or 0.0f if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a float value
*/
float getFloat(String key) {
unparcel();
return getFloat(key, 0.0f);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a float value
*/
float getFloat(String key, float defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Float) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Float", defaultValue, e);
return defaultValue;
}
}
/**
* Returns the value associated with the given key, or 0.0 if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @return a double value
*/
public double getDouble(String key) {
unparcel();
return getDouble(key, 0.0);
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key.
*
* @param key a String
* @param defaultValue Value to return if key does not exist
* @return a double value
*/
public double getDouble(String key, double defaultValue) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
try {
return (Double) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Double", defaultValue, e);
return defaultValue;
}
}
/**
* 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 String value, or null
*/
@Nullable
public String getString(@Nullable String key) {
unparcel();
final Object o = mMap.get(key);
try {
return (String) o;
} catch (ClassCastException e) {
typeWarning(key, o, "String", e);
return null;
}
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key or if a null
* value is explicitly associated with the given key.
*
* @param key a String, or null
* @param defaultValue Value to return if key does not exist or if a null
* value is associated with the given key.
* @return the String value associated with the given key, or defaultValue
* if no valid String object is currently mapped to that key.
*/
public String getString(@Nullable String key, String defaultValue) {
final String s = getString(key);
return (s == null) ? defaultValue : s;
}
/**
* 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 CharSequence value, or null
*/
@Nullable
CharSequence getCharSequence(@Nullable String key) {
unparcel();
final Object o = mMap.get(key);
try {
return (CharSequence) o;
} catch (ClassCastException e) {
typeWarning(key, o, "CharSequence", e);
return null;
}
}
/**
* Returns the value associated with the given key, or defaultValue if
* no mapping of the desired type exists for the given key or if a null
* value is explicitly associated with the given key.
*
* @param key a String, or null
* @param defaultValue Value to return if key does not exist or if a null
* value is associated with the given key.
* @return the CharSequence value associated with the given key, or defaultValue
* if no valid CharSequence object is currently mapped to that key.
*/
CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
final CharSequence cs = getCharSequence(key);
return (cs == null) ? defaultValue : cs;
}
/**
* 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 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();
Object o = getValue(key);
if (o == null) {
return null;
}
try {
return (Serializable) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Serializable", 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.
*
- A {@code null} value is explicitly associated with the key.
*
- The object is not of type {@code clazz}.
*
*
* @param key a String, or null
* @param clazz The expected class of the returned type
* @return a Serializable value, or null
*/
@Nullable
T getSerializable(@Nullable String key, @NonNull Class clazz) {
return get(key, clazz);
}
@SuppressWarnings("unchecked")
@Nullable
ArrayList getArrayList(@Nullable String key, @NonNull Class extends 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return an ArrayList value, or null
*/
@Nullable
ArrayList getIntegerArrayList(@Nullable String key) {
return getArrayList(key, Integer.class);
}
/**
* 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 an ArrayList value, or null
*/
@Nullable
ArrayList getStringArrayList(@Nullable String key) {
return getArrayList(key, String.class);
}
/**
* 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 an ArrayList value, or null
*/
@Nullable
ArrayList getCharSequenceArrayList(@Nullable String key) {
return getArrayList(key, CharSequence.class);
}
/**
* 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 boolean[] value, or null
*/
@Nullable
public boolean[] getBooleanArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (boolean[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "byte[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a byte[] value, or null
*/
@Nullable
byte[] getByteArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (byte[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "byte[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a short[] value, or null
*/
@Nullable
short[] getShortArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (short[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "short[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a char[] value, or null
*/
@Nullable
char[] getCharArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (char[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "char[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return an int[] value, or null
*/
@Nullable
public int[] getIntArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (int[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "int[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a long[] value, or null
*/
@Nullable
public long[] getLongArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (long[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "long[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a float[] value, or null
*/
@Nullable
float[] getFloatArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (float[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "float[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a double[] value, or null
*/
@Nullable
public double[] getDoubleArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (double[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "double[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a String[] value, or null
*/
@Nullable
public String[] getStringArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (String[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "String[]", 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
* value is explicitly associated with the key.
*
* @param key a String, or null
* @return a CharSequence[] value, or null
*/
@Nullable
CharSequence[] getCharSequenceArray(@Nullable String key) {
unparcel();
Object o = mMap.get(key);
if (o == null) {
return null;
}
try {
return (CharSequence[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "CharSequence[]", e);
return null;
}
}
/**
* Writes the Bundle contents to a Parcel, typically in order for
* it to be passed through an IBinder connection.
* @param parcel The parcel to copy this bundle to.
*/
void writeToParcelInner(Parcel parcel, int flags) {
// If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
if (parcel.hasReadWriteHelper()) {
unparcel(/* itemwise */ true);
}
// Keep implementation in sync with writeToParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
final ArrayMap map;
synchronized (this) {
// unparcel() can race with this method and cause the parcel to recycle
// at the wrong time. So synchronize access the mParcelledData's content.
if (mParcelledData != null) {
if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
parcel.writeInt(0);
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
parcel.appendFrom(mParcelledData, 0, length);
}
return;
}
map = mMap;
}
// Special case for empty bundles.
if (map == null || map.size() <= 0) {
parcel.writeInt(0);
return;
}
int lengthPos = parcel.dataPosition();
parcel.writeInt(-1); // placeholder, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
/**
* Reads the Parcel contents into this Bundle, typically in order for
* it to be passed through an IBinder connection.
* @param parcel The parcel to overwrite this bundle from.
*/
void readFromParcelInner(Parcel parcel) {
// Keep implementation in sync with readFromParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
int length = parcel.readInt();
readFromParcelInner(parcel, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);
} else if (length == 0) {
mParcelledByNative = false;
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
return;
} else if (length % 4 != 0) {
throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
}
final int magic = parcel.readInt();
final boolean isJavaBundle = magic == BUNDLE_MAGIC;
final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
if (!isJavaBundle && !isNativeBundle) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
if (parcel.hasReadWriteHelper()) {
// If the parcel has a read-write helper, it's better to deserialize immediately
// otherwise the helper would have to either maintain valid state long after the bundle
// had been constructed with parcel or to make sure they trigger deserialization of the
// bundle immediately; neither of which is obvious.
synchronized (this) {
mOwnsLazyValues = false;
initializeFromParcelLocked(parcel, /*ownsParcel*/ false, isNativeBundle);
}
return;
}
// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.adoptClassCookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
mOwnsLazyValues = true;
mParcelledByNative = isNativeBundle;
mParcelledData = p;
}
/** {@hide} */
public static void dumpStats(IndentingPrintWriter pw, String key, Object value) {
final Parcel tmp = Parcel.obtain();
tmp.writeValue(value);
final int size = tmp.dataPosition();
tmp.recycle();
// We only really care about logging large values
if (size > 1024) {
pw.println(key + " [size=" + size + "]");
if (value instanceof BaseBundle) {
dumpStats(pw, (BaseBundle) value);
} else if (value instanceof SparseArray) {
dumpStats(pw, (SparseArray) value);
}
}
}
/** {@hide} */
public static void dumpStats(IndentingPrintWriter pw, SparseArray array) {
pw.increaseIndent();
if (array == null) {
pw.println("[null]");
return;
}
for (int i = 0; i < array.size(); i++) {
dumpStats(pw, "0x" + Integer.toHexString(array.keyAt(i)), array.valueAt(i));
}
pw.decreaseIndent();
}
/** {@hide} */
public static void dumpStats(IndentingPrintWriter pw, BaseBundle bundle) {
pw.increaseIndent();
if (bundle == null) {
pw.println("[null]");
return;
}
final ArrayMap map = bundle.getItemwiseMap();
for (int i = 0; i < map.size(); i++) {
dumpStats(pw, map.keyAt(i), map.valueAt(i));
}
pw.decreaseIndent();
}
}