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