/* * Copyright (C) 2007 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; import static java.util.Objects.requireNonNull; import android.annotation.IntDef; 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; import android.util.SizeF; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; /** * A mapping from String keys to various {@link Parcelable} values. * *
Warning: Note that {@link Bundle} is a lazy container and as such it does NOT implement * {@link #equals(Object)} or {@link #hashCode()}. * * @see PersistableBundle */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public final class Bundle extends BaseBundle implements Cloneable, Parcelable { @VisibleForTesting static final int FLAG_HAS_FDS = 1 << 8; @VisibleForTesting static final int FLAG_HAS_FDS_KNOWN = 1 << 9; @VisibleForTesting static final int FLAG_ALLOW_FDS = 1 << 10; @VisibleForTesting static final int FLAG_HAS_BINDERS_KNOWN = 1 << 11; @VisibleForTesting static final int FLAG_HAS_BINDERS = 1 << 12; /** * Status when the Bundle can assert that the underlying Parcel DOES NOT contain * Binder object(s). * * @hide */ public static final int STATUS_BINDERS_NOT_PRESENT = 0; /** * Status when the Bundle can assert that there are Binder object(s) in the Parcel. * * @hide */ public static final int STATUS_BINDERS_PRESENT = 1; /** * Status when the Bundle cannot be checked for Binders and there is no parcelled data * available to check either. *
This could happen when a Bundle is unparcelled or was never parcelled, and modified such * that it is not possible to assert if the Bundle has any Binder objects in the current state. * * For e.g. calling {@link #putParcelable} or {@link #putBinder} could have added a Binder * object to the Bundle but it is not possible to assert this fact unless the Bundle is written * to a Parcel. *
* * @hide */ public static final int STATUS_BINDERS_UNKNOWN = 2; /** @hide */ @IntDef(flag = true, prefix = {"STATUS_BINDERS_"}, value = { STATUS_BINDERS_PRESENT, STATUS_BINDERS_UNKNOWN, STATUS_BINDERS_NOT_PRESENT }) @Retention(RetentionPolicy.SOURCE) public @interface HasBinderStatus { } /** An unmodifiable {@code Bundle} that is always {@link #isEmpty() empty}. */ public static final Bundle EMPTY; /** * Special extras used to denote extras have been stripped off. * @hide */ public static final Bundle STRIPPED; static { EMPTY = new Bundle(); EMPTY.mMap = ArrayMap.EMPTY; STRIPPED = new Bundle(); STRIPPED.putInt("STRIPPED", 1); } /** * Constructs a new, empty Bundle. */ public Bundle() { super(); mFlags = FLAG_HAS_FDS_KNOWN | FLAG_HAS_BINDERS_KNOWN | FLAG_ALLOW_FDS; } /** * Constructs a Bundle whose data is stored as a Parcel. The data * will be unparcelled on first contact, using the assigned ClassLoader. * * @param parcelledData a Parcel containing a Bundle * * @hide */ @VisibleForTesting public Bundle(Parcel parcelledData) { super(parcelledData); mFlags = FLAG_ALLOW_FDS; maybePrefillHasFds(); } /** * Constructor from a parcel for when the length is known *and is not stored in the parcel.* * The other constructor that takes a parcel assumes the length is in the parcel. * * @hide */ @VisibleForTesting public Bundle(Parcel parcelledData, int length) { super(parcelledData, length); mFlags = FLAG_ALLOW_FDS; maybePrefillHasFds(); } /** * Constructs a {@link Bundle} containing a copy of {@code from}. * * @param from The bundle to be copied. * @param deep Whether is a deep or shallow copy. * @hide */ Bundle(Bundle from, boolean deep) { super(from, deep); } /** * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast. * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN} * unset, because scanning a map is slower. We'll do it lazily in * {@link #hasFileDescriptors()}. */ private void maybePrefillHasFds() { if (mParcelledData != null) { if (mParcelledData.hasFileDescriptors()) { mFlags |= FLAG_HAS_FDS | FLAG_HAS_FDS_KNOWN; } else { mFlags |= FLAG_HAS_FDS_KNOWN; } } } /** * Constructs a new, empty Bundle that uses a specific ClassLoader for * instantiating Parcelable and Serializable objects. * * @param loader An explicit ClassLoader to use when instantiating objects * inside of the Bundle. */ public Bundle(ClassLoader loader) { super(loader); mFlags = FLAG_HAS_FDS_KNOWN | FLAG_HAS_BINDERS_KNOWN | FLAG_ALLOW_FDS; } /** * Constructs a new, empty Bundle sized to hold the given number of * elements. The Bundle will grow as needed. * * @param capacity the initial capacity of the Bundle */ public Bundle(int capacity) { super(capacity); mFlags = FLAG_HAS_FDS_KNOWN | FLAG_HAS_BINDERS_KNOWN | FLAG_ALLOW_FDS; } /** * Constructs a Bundle containing a copy of the mappings from the given * Bundle. Does only a shallow copy of the original Bundle -- see * {@link #deepCopy()} if that is not what you want. * * @param b a Bundle to be copied. * * @see #deepCopy() */ public Bundle(Bundle b) { super(b); mFlags = b.mFlags; } /** * Constructs a Bundle containing a copy of the mappings from the given * PersistableBundle. Does only a shallow copy of the PersistableBundle -- see * {@link PersistableBundle#deepCopy()} if you don't want that. * * @param b a PersistableBundle to be copied. */ public Bundle(PersistableBundle b) { super(b); mFlags = FLAG_HAS_FDS_KNOWN | FLAG_HAS_BINDERS_KNOWN | FLAG_ALLOW_FDS; } /** * Make a Bundle for a single key/value pair. * * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static Bundle forPair(String key, String value) { Bundle b = new Bundle(1); b.putString(key, value); return b; } /** * Changes the ClassLoader this Bundle uses when instantiating objects. * * @param loader An explicit ClassLoader to use when instantiating objects * inside of the Bundle. */ @Override public void setClassLoader(ClassLoader loader) { super.setClassLoader(loader); } /** * Return the ClassLoader currently associated with this Bundle. */ @Override public ClassLoader getClassLoader() { return super.getClassLoader(); } /** {@hide} */ public boolean setAllowFds(boolean allowFds) { final boolean orig = (mFlags & FLAG_ALLOW_FDS) != 0; if (allowFds) { mFlags |= FLAG_ALLOW_FDS; } else { mFlags &= ~FLAG_ALLOW_FDS; } return orig; } /** * Mark if this Bundle is okay to "defuse." That is, it's okay for system * processes to ignore any {@link BadParcelableException} encountered when * unparceling it, leaving an empty bundle in its place. *
* This should only be set when the Bundle reaches its final
* destination, otherwise a system process may clobber contents that were
* destined for an app that could have unparceled them.
*
* @hide
*/
public void setDefusable(boolean defusable) {
if (defusable) {
mFlags |= FLAG_DEFUSABLE;
} else {
mFlags &= ~FLAG_DEFUSABLE;
}
}
/** {@hide} */
@UnsupportedAppUsage
public static Bundle setDefusable(Bundle bundle, boolean defusable) {
if (bundle != null) {
bundle.setDefusable(defusable);
}
return bundle;
}
/**
* Clones the current Bundle. The internal map is cloned, but the keys and
* values to which it refers are copied by reference.
*/
@Override
public Object clone() {
return new Bundle(this);
}
/**
* Make a deep copy of the given bundle. Traverses into inner containers and copies
* them as well, so they are not shared across bundles. Will traverse in to
* {@link Bundle}, {@link PersistableBundle}, {@link ArrayList}, and all types of
* primitive arrays. Other types of objects (such as Parcelable or Serializable)
* are referenced as-is and not copied in any way.
*/
public Bundle deepCopy() {
return new Bundle(this, /* deep */ true);
}
/**
* Removes all elements from the mapping of this Bundle.
*/
@Override
public void clear() {
super.clear();
mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
}
/**
* Removes any entry with the given key from the mapping of this Bundle.
*
* @param key a String key
*/
public void remove(String key) {
super.remove(key);
if ((mFlags & FLAG_HAS_FDS) != 0) {
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
if ((mFlags & FLAG_HAS_BINDERS) != 0) {
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
}
/**
* Inserts all mappings from the given Bundle into this Bundle.
*
* @param bundle a Bundle
*/
public void putAll(Bundle bundle) {
unparcel();
bundle.unparcel();
mOwnsLazyValues = false;
bundle.mOwnsLazyValues = false;
mMap.putAll(bundle.mMap);
// FD and Binders state is now known if and only if both bundles already knew
if ((bundle.mFlags & FLAG_HAS_FDS) != 0) {
mFlags |= FLAG_HAS_FDS;
}
if ((bundle.mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
mFlags &= ~FLAG_HAS_FDS_KNOWN;
}
if ((bundle.mFlags & FLAG_HAS_BINDERS) != 0) {
mFlags |= FLAG_HAS_BINDERS;
}
if ((bundle.mFlags & FLAG_HAS_BINDERS_KNOWN) == 0) {
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
}
/**
* Return the size of {@link #mParcelledData} in bytes if available, otherwise {@code 0}.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int getSize() {
if (mParcelledData != null) {
return mParcelledData.dataSize();
} else {
return 0;
}
}
/**
* Reports whether the bundle contains any parcelled file descriptors.
*/
public boolean hasFileDescriptors() {
if ((mFlags & FLAG_HAS_FDS_KNOWN) == 0) {
Parcel p = mParcelledData;
mFlags = (Parcel.hasFileDescriptors((p != null) ? p : mMap))
? mFlags | FLAG_HAS_FDS
: mFlags & ~FLAG_HAS_FDS;
mFlags |= FLAG_HAS_FDS_KNOWN;
}
return (mFlags & FLAG_HAS_FDS) != 0;
}
/**
* Returns a status indicating whether the bundle contains any parcelled Binder objects.
* @hide
*/
public @HasBinderStatus int hasBinders() {
if ((mFlags & FLAG_HAS_BINDERS_KNOWN) != 0) {
if ((mFlags & FLAG_HAS_BINDERS) != 0) {
return STATUS_BINDERS_PRESENT;
} else {
return STATUS_BINDERS_NOT_PRESENT;
}
}
final Parcel p = mParcelledData;
if (p == null) {
return STATUS_BINDERS_UNKNOWN;
}
if (p.hasBinders()) {
mFlags = mFlags | FLAG_HAS_BINDERS | FLAG_HAS_BINDERS_KNOWN;
return STATUS_BINDERS_PRESENT;
} else {
mFlags = mFlags & ~FLAG_HAS_BINDERS;
mFlags |= FLAG_HAS_BINDERS_KNOWN;
return STATUS_BINDERS_NOT_PRESENT;
}
}
/** {@hide} */
@Override
public void putObject(@Nullable String key, @Nullable Object value) {
if (value instanceof Byte) {
putByte(key, (Byte) value);
} else if (value instanceof Character) {
putChar(key, (Character) value);
} else if (value instanceof Short) {
putShort(key, (Short) value);
} else if (value instanceof Float) {
putFloat(key, (Float) value);
} else if (value instanceof CharSequence) {
putCharSequence(key, (CharSequence) value);
} else if (value instanceof Parcelable) {
putParcelable(key, (Parcelable) value);
} else if (value instanceof Size) {
putSize(key, (Size) value);
} else if (value instanceof SizeF) {
putSizeF(key, (SizeF) value);
} else if (value instanceof Parcelable[]) {
putParcelableArray(key, (Parcelable[]) value);
} else if (value instanceof ArrayList) {
putParcelableArrayList(key, (ArrayList) value);
} else if (value instanceof List) {
putParcelableList(key, (List) value);
} else if (value instanceof SparseArray) {
putSparseParcelableArray(key, (SparseArray) value);
} else if (value instanceof Serializable) {
putSerializable(key, (Serializable) value);
} else if (value instanceof byte[]) {
putByteArray(key, (byte[]) value);
} else if (value instanceof short[]) {
putShortArray(key, (short[]) value);
} else if (value instanceof char[]) {
putCharArray(key, (char[]) value);
} else if (value instanceof float[]) {
putFloatArray(key, (float[]) value);
} else if (value instanceof CharSequence[]) {
putCharSequenceArray(key, (CharSequence[]) value);
} else if (value instanceof Bundle) {
putBundle(key, (Bundle) value);
} else if (value instanceof Binder) {
putBinder(key, (Binder) value);
} else if (value instanceof IBinder) {
putIBinder(key, (IBinder) value);
} else {
super.putObject(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
*/
@Override
public void putByte(@Nullable String key, byte value) {
super.putByte(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
*/
@Override
public void putChar(@Nullable String key, char value) {
super.putChar(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
*/
@Override
public void putShort(@Nullable String key, short value) {
super.putShort(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
*/
@Override
public void putFloat(@Nullable String key, float value) {
super.putFloat(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
*/
@Override
public void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
super.putCharSequence(key, value);
}
/**
* Inserts a Parcelable 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 Parcelable object, or null
*/
public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
/**
* Inserts a Size 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 Size object, or null
*/
public void putSize(@Nullable String key, @Nullable Size value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts a SizeF 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 SizeF object, or null
*/
public void putSizeF(@Nullable String key, @Nullable SizeF value) {
unparcel();
mMap.put(key, value);
}
/**
* Inserts an array of Parcelable values 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 array of Parcelable objects, or null
*/
public void putParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
/**
* Inserts a List of Parcelable values 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 of Parcelable objects, or null
*/
public void putParcelableArrayList(@Nullable String key,
@Nullable ArrayList extends Parcelable> value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
/** {@hide} */
@UnsupportedAppUsage
public void putParcelableList(String key, List extends Parcelable> value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
/**
* Inserts a SparceArray of Parcelable values 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 SparseArray of Parcelable objects, or null
*/
public void putSparseParcelableArray(@Nullable String key,
@Nullable SparseArray extends Parcelable> value) {
unparcel();
mMap.put(key, value);
mFlags &= ~FLAG_HAS_FDS_KNOWN;
mFlags &= ~FLAG_HAS_BINDERS_KNOWN;
}
/**
* Inserts an ArrayList You should be very careful when using this function. In many
* places where Bundles are used (such as inside of Intent objects), the Bundle
* can live longer inside of another process than the process that had originally
* created it. In that case, the IBinder you supply here will become invalid
* when your process goes away, and no longer usable, even if a new process is
* created for you later on. Note: 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}
* @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 Note: 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}.
*
* Warning: the class that implements {@link Parcelable} has to be the immediately
* enclosing class of the runtime type of its CREATOR field (that is,
* {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
* otherwise this method might throw an exception. If the Parcelable class does not enclose the
* CREATOR, use the deprecated {@link #getParcelable(String)} instead.
*
* @param key a String, or {@code null}
* @param clazz The type of the object expected
* @return a Parcelable value, or {@code null}
*/
@SuppressWarnings("unchecked")
@Nullable
public Note: 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}
* @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();
Object o = getValue(key);
if (o == null) {
return null;
}
try {
return (Parcelable[]) o;
} catch (ClassCastException e) {
typeWarning(key, o, "Parcelable[]", e);
return null;
}
}
/**
* Returns the value associated with the given key, or {@code null} if:
* Note: 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}.
*
* Warning: if the list contains items implementing the {@link Parcelable} interface,
* the class that implements {@link Parcelable} has to be the immediately
* enclosing class of the runtime type of its CREATOR field (that is,
* {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
* otherwise this method might throw an exception. If the Parcelable class does not enclose the
* CREATOR, use the deprecated {@link #getParcelableArray(String)} instead.
*
* @param key a String, or {@code null}
* @param clazz The type of the items inside the array. This is only verified when unparceling.
* @return a Parcelable[] value, or {@code null}
*/
@SuppressLint({"ArrayReturn", "NullableCollection"})
@SuppressWarnings("unchecked")
@Nullable
public Note: 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}
* @return an ArrayList Note: 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}.
*
* Warning: if the list contains items implementing the {@link Parcelable} interface,
* the class that implements {@link Parcelable} has to be the immediately
* enclosing class of the runtime type of its CREATOR field (that is,
* {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
* otherwise this method might throw an exception. If the Parcelable class does not enclose the
* CREATOR, use the deprecated {@link #getParcelableArrayList(String)} instead.
*
* @param key a String, or {@code null}
* @param clazz The type of the items inside the array list. This is only verified when
* unparceling.
* @return an ArrayList Warning: if the list contains items implementing the {@link Parcelable} interface,
* the class that implements {@link Parcelable} has to be the immediately
* enclosing class of the runtime type of its CREATOR field (that is,
* {@link Class#getEnclosingClass()} has to return the parcelable implementing class),
* otherwise this method might throw an exception. If the Parcelable class does not enclose the
* CREATOR, use the deprecated {@link #getSparseParcelableArray(String)} instead.
*
* @param key a String, or null
* @param clazz The type of the items inside the sparse array. This is only verified when
* unparceling.
* @return a SparseArray of T values, or null
*/
@SuppressWarnings("unchecked")
@Nullable
public
*
*
*
*
*
*
*
*
*
*
*
*
*
*
* @param key a String, or null
* @param clazz The expected class of the returned type
* @return a Serializable value, or null
*/
@Nullable
public