| /* |
| * Copyright (C) 2006 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 com.android.internal.util.Preconditions.checkArgument; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.annotation.TestApi; |
| import android.app.AppOpsManager; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.ravenwood.annotation.RavenwoodKeepWholeClass; |
| import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass; |
| import android.ravenwood.annotation.RavenwoodReplace; |
| import android.ravenwood.annotation.RavenwoodThrow; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.ExceptionUtils; |
| import android.util.Log; |
| import android.util.MathUtils; |
| import android.util.Pair; |
| import android.util.Size; |
| import android.util.SizeF; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.SparseBooleanArray; |
| import android.util.SparseIntArray; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.util.ArrayUtils; |
| |
| import dalvik.annotation.optimization.CriticalNative; |
| import dalvik.annotation.optimization.FastNative; |
| |
| import libcore.util.SneakyThrow; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.ObjectStreamClass; |
| import java.io.Serializable; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| 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; |
| |
| /** |
| * Container for a message (data and object references) that can |
| * be sent through an IBinder. A Parcel can contain both flattened data |
| * that will be unflattened on the other side of the IPC (using the various |
| * methods here for writing specific types, or the general |
| * {@link Parcelable} interface), and references to live {@link IBinder} |
| * objects that will result in the other side receiving a proxy IBinder |
| * connected with the original IBinder in the Parcel. |
| * |
| * <p class="note">Parcel is <strong>not</strong> a general-purpose |
| * serialization mechanism. This class (and the corresponding |
| * {@link Parcelable} API for placing arbitrary objects into a Parcel) is |
| * designed as a high-performance IPC transport. As such, it is not |
| * appropriate to place any Parcel data in to persistent storage: changes |
| * in the underlying implementation of any of the data in the Parcel can |
| * render older data unreadable.</p> |
| * |
| * <p>The bulk of the Parcel API revolves around reading and writing data |
| * of various types. There are six major classes of such functions available.</p> |
| * |
| * <h3>Primitives</h3> |
| * |
| * <p>The most basic data functions are for writing and reading primitive |
| * data types: {@link #writeByte}, {@link #readByte}, {@link #writeDouble}, |
| * {@link #readDouble}, {@link #writeFloat}, {@link #readFloat}, {@link #writeInt}, |
| * {@link #readInt}, {@link #writeLong}, {@link #readLong}, |
| * {@link #writeString}, {@link #readString}. Most other |
| * data operations are built on top of these. The given data is written and |
| * read using the endianess of the host CPU.</p> |
| * |
| * <h3>Primitive Arrays</h3> |
| * |
| * <p>There are a variety of methods for reading and writing raw arrays |
| * of primitive objects, which generally result in writing a 4-byte length |
| * followed by the primitive data items. The methods for reading can either |
| * read the data into an existing array, or create and return a new array. |
| * These available types are:</p> |
| * |
| * <ul> |
| * <li> {@link #writeBooleanArray(boolean[])}, |
| * {@link #readBooleanArray(boolean[])}, {@link #createBooleanArray()} |
| * <li> {@link #writeByteArray(byte[])}, |
| * {@link #writeByteArray(byte[], int, int)}, {@link #readByteArray(byte[])}, |
| * {@link #createByteArray()} |
| * <li> {@link #writeCharArray(char[])}, {@link #readCharArray(char[])}, |
| * {@link #createCharArray()} |
| * <li> {@link #writeDoubleArray(double[])}, {@link #readDoubleArray(double[])}, |
| * {@link #createDoubleArray()} |
| * <li> {@link #writeFloatArray(float[])}, {@link #readFloatArray(float[])}, |
| * {@link #createFloatArray()} |
| * <li> {@link #writeIntArray(int[])}, {@link #readIntArray(int[])}, |
| * {@link #createIntArray()} |
| * <li> {@link #writeLongArray(long[])}, {@link #readLongArray(long[])}, |
| * {@link #createLongArray()} |
| * <li> {@link #writeStringArray(String[])}, {@link #readStringArray(String[])}, |
| * {@link #createStringArray()}. |
| * <li> {@link #writeSparseBooleanArray(SparseBooleanArray)}, |
| * {@link #readSparseBooleanArray()}. |
| * </ul> |
| * |
| * <h3>Parcelables</h3> |
| * |
| * <p>The {@link Parcelable} protocol provides an extremely efficient (but |
| * low-level) protocol for objects to write and read themselves from Parcels. |
| * You can use the direct methods {@link #writeParcelable(Parcelable, int)} |
| * and {@link #readParcelable(ClassLoader)} or |
| * {@link #writeParcelableArray} and |
| * {@link #readParcelableArray(ClassLoader)} to write or read. These |
| * methods write both the class type and its data to the Parcel, allowing |
| * that class to be reconstructed from the appropriate class loader when |
| * later reading.</p> |
| * |
| * <p>There are also some methods that provide a more efficient way to work |
| * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray}, |
| * {@link #writeTypedList}, {@link #readTypedObject}, |
| * {@link #createTypedArray} and {@link #createTypedArrayList}. These methods |
| * do not write the class information of the original object: instead, the |
| * caller of the read function must know what type to expect and pass in the |
| * appropriate {@link Parcelable.Creator Parcelable.Creator} instead to |
| * properly construct the new object and read its data. (To more efficient |
| * write and read a single Parcelable object that is not null, you can directly |
| * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and |
| * {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel} |
| * yourself.)</p> |
| * |
| * <h3>Bundles</h3> |
| * |
| * <p>A special type-safe container, called {@link Bundle}, is available |
| * for key/value maps of heterogeneous values. This has many optimizations |
| * for improved performance when reading and writing data, and its type-safe |
| * API avoids difficult to debug type errors when finally marshalling the |
| * data contents into a Parcel. The methods to use are |
| * {@link #writeBundle(Bundle)}, {@link #readBundle()}, and |
| * {@link #readBundle(ClassLoader)}. |
| * |
| * <h3>Active Objects</h3> |
| * |
| * <p>An unusual feature of Parcel is the ability to read and write active |
| * objects. For these objects the actual contents of the object is not |
| * written, rather a special token referencing the object is written. When |
| * reading the object back from the Parcel, you do not get a new instance of |
| * the object, but rather a handle that operates on the exact same object that |
| * was originally written. There are two forms of active objects available.</p> |
| * |
| * <p>{@link Binder} objects are a core facility of Android's general cross-process |
| * communication system. The {@link IBinder} interface describes an abstract |
| * protocol with a Binder object. Any such interface can be written in to |
| * a Parcel, and upon reading you will receive either the original object |
| * implementing that interface or a special proxy implementation |
| * that communicates calls back to the original object. The methods to use are |
| * {@link #writeStrongBinder(IBinder)}, |
| * {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()}, |
| * {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])}, |
| * {@link #createBinderArray()}, |
| * {@link #writeInterfaceArray(T[])}, {@link #readInterfaceArray(T[], Function)}, |
| * {@link #createInterfaceArray(IntFunction, Function)}, |
| * {@link #writeBinderList(List)}, {@link #readBinderList(List)}, |
| * {@link #createBinderArrayList()}, |
| * {@link #writeInterfaceList(List)}, {@link #readInterfaceList(List, Function)}, |
| * {@link #createInterfaceArrayList(Function)}.</p> |
| * |
| * <p>FileDescriptor objects, representing raw Linux file descriptor identifiers, |
| * can be written and {@link ParcelFileDescriptor} objects returned to operate |
| * on the original file descriptor. The returned file descriptor is a dup |
| * of the original file descriptor: the object and fd is different, but |
| * operating on the same underlying file stream, with the same position, etc. |
| * The methods to use are {@link #writeFileDescriptor(FileDescriptor)}, |
| * {@link #readFileDescriptor()}. |
| * |
| * <h3>Parcelable Containers</h3> |
| * |
| * <p>A final class of methods are for writing and reading standard Java |
| * containers of arbitrary types. These all revolve around the |
| * {@link #writeValue(Object)} and {@link #readValue(ClassLoader)} methods |
| * which define the types of objects allowed. The container methods are |
| * {@link #writeArray(Object[])}, {@link #readArray(ClassLoader)}, |
| * {@link #writeList(List)}, {@link #readList(List, ClassLoader)}, |
| * {@link #readArrayList(ClassLoader)}, |
| * {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)}, |
| * {@link #writeSparseArray(SparseArray)}, |
| * {@link #readSparseArray(ClassLoader)}. |
| * |
| * <h3>Restricted Parcelable Containers</h3> |
| * |
| * <p>A final class of methods are for reading standard Java containers of restricted types. |
| * These methods replace methods for reading containers of arbitrary types from previous section |
| * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. The pairing writing methods are |
| * still the same from previous section. |
| * These methods accepts additional {@code clazz} parameters as the required types. |
| * The Restricted Parcelable container methods are {@link #readArray(ClassLoader, Class)}, |
| * {@link #readList(List, ClassLoader, Class)}, |
| * {@link #readArrayList(ClassLoader, Class)}, |
| * {@link #readMap(Map, ClassLoader, Class, Class)}, |
| * {@link #readSparseArray(ClassLoader, Class)}. |
| */ |
| @RavenwoodKeepWholeClass |
| @RavenwoodNativeSubstitutionClass( |
| "com.android.platform.test.ravenwood.nativesubstitution.Parcel_host") |
| public final class Parcel { |
| |
| private static final boolean DEBUG_RECYCLE = false; |
| private static final boolean DEBUG_ARRAY_MAP = false; |
| private static final String TAG = "Parcel"; |
| |
| @UnsupportedAppUsage |
| @SuppressWarnings({"UnusedDeclaration"}) |
| private long mNativePtr; // used by native code |
| |
| /** |
| * Flag indicating if {@link #mNativePtr} was allocated by this object, |
| * indicating that we're responsible for its lifecycle. |
| */ |
| private boolean mOwnsNativeParcelObject; |
| private long mNativeSize; |
| |
| private ArrayMap<Class, Object> mClassCookies; |
| |
| private RuntimeException mStack; |
| private boolean mRecycled = false; |
| |
| /** @hide */ |
| @TestApi |
| public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1 << 0; |
| |
| /** @hide */ |
| @TestApi |
| public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 1 << 1; |
| |
| /** @hide */ |
| @IntDef(flag = true, prefix = { "FLAG_" }, value = { |
| FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT, |
| FLAG_PROPAGATE_ALLOW_BLOCKING, |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface ParcelFlags {} |
| |
| @ParcelFlags |
| private int mFlags; |
| |
| /** |
| * Whether or not to parcel the stack trace of an exception. This has a performance |
| * impact, so should only be included in specific processes and only on debug builds. |
| */ |
| private static boolean sParcelExceptionStackTrace; |
| |
| private static final Object sPoolSync = new Object(); |
| |
| /** Next item in the linked list pool, if any */ |
| @GuardedBy("sPoolSync") |
| private Parcel mPoolNext; |
| |
| /** Head of a linked list pool of {@link Parcel} objects */ |
| @GuardedBy("sPoolSync") |
| private static Parcel sOwnedPool; |
| /** Head of a linked list pool of {@link Parcel} objects */ |
| @GuardedBy("sPoolSync") |
| private static Parcel sHolderPool; |
| |
| /** Total size of pool with head at {@link #sOwnedPool} */ |
| @GuardedBy("sPoolSync") |
| private static int sOwnedPoolSize = 0; |
| /** Total size of pool with head at {@link #sHolderPool} */ |
| @GuardedBy("sPoolSync") |
| private static int sHolderPoolSize = 0; |
| |
| /** |
| * We're willing to pool up to 32 objects, which is sized to accommodate |
| * both a data and reply Parcel for the maximum of 16 Binder threads. |
| */ |
| private static final int POOL_SIZE = 32; |
| |
| // Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h. |
| 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; // length-prefixed |
| private static final int VAL_BUNDLE = 3; |
| 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; // 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; // 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; // length-prefixed |
| private static final int VAL_SPARSEBOOLEANARRAY = 22; |
| private static final int VAL_BOOLEANARRAY = 23; |
| private static final int VAL_CHARSEQUENCEARRAY = 24; |
| private static final int VAL_PERSISTABLEBUNDLE = 25; |
| private static final int VAL_SIZE = 26; |
| private static final int VAL_SIZEF = 27; |
| private static final int VAL_DOUBLEARRAY = 28; |
| private static final int VAL_CHAR = 29; |
| private static final int VAL_SHORTARRAY = 30; |
| private static final int VAL_CHARARRAY = 31; |
| private static final int VAL_FLOATARRAY = 32; |
| |
| // The initial int32 in a Binder call's reply Parcel header: |
| // Keep these in sync with libbinder's binder/Status.h. |
| private static final int EX_SECURITY = -1; |
| private static final int EX_BAD_PARCELABLE = -2; |
| private static final int EX_ILLEGAL_ARGUMENT = -3; |
| private static final int EX_NULL_POINTER = -4; |
| private static final int EX_ILLEGAL_STATE = -5; |
| private static final int EX_NETWORK_MAIN_THREAD = -6; |
| private static final int EX_UNSUPPORTED_OPERATION = -7; |
| private static final int EX_SERVICE_SPECIFIC = -8; |
| private static final int EX_PARCELABLE = -9; |
| /** @hide */ |
| // WARNING: DO NOT add more 'reply' headers. These also need to add work to native |
| // code and this encodes extra information in object statuses. If we need to expand |
| // this design, we should add a generic way to attach parcelables/structured parcelables |
| // to transactions which can work across languages. |
| public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; // special; see below |
| // WARNING: DO NOT add more 'reply' headers. These also need to add work to native |
| // code and this encodes extra information in object statuses. If we need to expand |
| // this design, we should add a generic way to attach parcelables/structured parcelables |
| // to transactions which can work across languages. |
| private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128; // special; see below |
| // EX_TRANSACTION_FAILED is used exclusively in native code. |
| // see libbinder's binder/Status.h |
| private static final int EX_TRANSACTION_FAILED = -129; |
| |
| // Allow limit of 1 MB for allocating arrays |
| private static final int ARRAY_ALLOCATION_LIMIT = 1000000; |
| |
| // Following type size are used to determine allocation size while creating arrays |
| private static final int SIZE_BYTE = 1; |
| private static final int SIZE_CHAR = 2; |
| private static final int SIZE_SHORT = 2; |
| private static final int SIZE_BOOLEAN = 4; |
| private static final int SIZE_INT = 4; |
| private static final int SIZE_FLOAT = 4; |
| private static final int SIZE_DOUBLE = 8; |
| private static final int SIZE_LONG = 8; |
| |
| // Assume the least possible size for complex objects |
| private static final int SIZE_COMPLEX_TYPE = 1; |
| |
| @CriticalNative |
| private static native void nativeMarkSensitive(long nativePtr); |
| @FastNative |
| @RavenwoodThrow |
| private static native void nativeMarkForBinder(long nativePtr, IBinder binder); |
| @CriticalNative |
| @RavenwoodThrow |
| private static native boolean nativeIsForRpc(long nativePtr); |
| @CriticalNative |
| private static native int nativeDataSize(long nativePtr); |
| @CriticalNative |
| private static native int nativeDataAvail(long nativePtr); |
| @CriticalNative |
| private static native int nativeDataPosition(long nativePtr); |
| @CriticalNative |
| private static native int nativeDataCapacity(long nativePtr); |
| @FastNative |
| private static native void nativeSetDataSize(long nativePtr, int size); |
| @CriticalNative |
| private static native void nativeSetDataPosition(long nativePtr, int pos); |
| @FastNative |
| private static native void nativeSetDataCapacity(long nativePtr, int size); |
| |
| @CriticalNative |
| private static native boolean nativePushAllowFds(long nativePtr, boolean allowFds); |
| @CriticalNative |
| private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue); |
| |
| private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len); |
| private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len); |
| @CriticalNative |
| private static native int nativeWriteInt(long nativePtr, int val); |
| @CriticalNative |
| private static native int nativeWriteLong(long nativePtr, long val); |
| @CriticalNative |
| private static native int nativeWriteFloat(long nativePtr, float val); |
| @CriticalNative |
| private static native int nativeWriteDouble(long nativePtr, double val); |
| @RavenwoodThrow |
| private static native void nativeSignalExceptionForError(int error); |
| @FastNative |
| private static native void nativeWriteString8(long nativePtr, String val); |
| @FastNative |
| private static native void nativeWriteString16(long nativePtr, String val); |
| @FastNative |
| @RavenwoodThrow |
| private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); |
| @FastNative |
| @RavenwoodThrow |
| private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); |
| |
| private static native byte[] nativeCreateByteArray(long nativePtr); |
| private static native boolean nativeReadByteArray(long nativePtr, byte[] dest, int destLen); |
| private static native byte[] nativeReadBlob(long nativePtr); |
| @CriticalNative |
| private static native int nativeReadInt(long nativePtr); |
| @CriticalNative |
| private static native long nativeReadLong(long nativePtr); |
| @CriticalNative |
| private static native float nativeReadFloat(long nativePtr); |
| @CriticalNative |
| private static native double nativeReadDouble(long nativePtr); |
| @FastNative |
| private static native String nativeReadString8(long nativePtr); |
| @FastNative |
| private static native String nativeReadString16(long nativePtr); |
| @FastNative |
| @RavenwoodThrow |
| private static native IBinder nativeReadStrongBinder(long nativePtr); |
| @FastNative |
| @RavenwoodThrow |
| private static native FileDescriptor nativeReadFileDescriptor(long nativePtr); |
| |
| private static native long nativeCreate(); |
| private static native void nativeFreeBuffer(long nativePtr); |
| private static native void nativeDestroy(long nativePtr); |
| |
| private static native byte[] nativeMarshall(long nativePtr); |
| private static native void nativeUnmarshall( |
| long nativePtr, byte[] data, int offset, int length); |
| private static native int nativeCompareData(long thisNativePtr, long otherNativePtr); |
| private static native boolean nativeCompareDataInRange( |
| long ptrA, int offsetA, long ptrB, int offsetB, int length); |
| private static native void nativeAppendFrom( |
| long thisNativePtr, long otherNativePtr, int offset, int length); |
| @CriticalNative |
| private static native boolean nativeHasFileDescriptors(long nativePtr); |
| private static native boolean nativeHasFileDescriptorsInRange( |
| long nativePtr, int offset, int length); |
| |
| private static native boolean nativeHasBinders(long nativePtr); |
| private static native boolean nativeHasBindersInRange( |
| long nativePtr, int offset, int length); |
| @RavenwoodThrow |
| private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName); |
| @RavenwoodThrow |
| private static native void nativeEnforceInterface(long nativePtr, String interfaceName); |
| |
| @CriticalNative |
| @RavenwoodThrow |
| private static native boolean nativeReplaceCallingWorkSourceUid( |
| long nativePtr, int workSourceUid); |
| @CriticalNative |
| @RavenwoodThrow |
| private static native int nativeReadCallingWorkSourceUid(long nativePtr); |
| |
| /** Last time exception with a stack trace was written */ |
| private static volatile long sLastWriteExceptionStackTrace; |
| /** Used for throttling of writing stack trace, which is costly */ |
| private static final int WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS = 1000; |
| |
| @CriticalNative |
| @RavenwoodThrow |
| private static native long nativeGetOpenAshmemSize(long nativePtr); |
| |
| public final static Parcelable.Creator<String> STRING_CREATOR |
| = new Parcelable.Creator<String>() { |
| public String createFromParcel(Parcel source) { |
| return source.readString(); |
| } |
| public String[] newArray(int size) { |
| return new String[size]; |
| } |
| }; |
| |
| /** |
| * @hide |
| */ |
| public static class ReadWriteHelper { |
| |
| @UnsupportedAppUsage |
| public ReadWriteHelper() { |
| } |
| |
| public static final ReadWriteHelper DEFAULT = new ReadWriteHelper(); |
| |
| /** |
| * Called when writing a string to a parcel. Subclasses wanting to write a string |
| * must use {@link #writeStringNoHelper(String)} to avoid |
| * infinity recursive calls. |
| */ |
| public void writeString8(Parcel p, String s) { |
| p.writeString8NoHelper(s); |
| } |
| |
| public void writeString16(Parcel p, String s) { |
| p.writeString16NoHelper(s); |
| } |
| |
| /** |
| * Called when reading a string to a parcel. Subclasses wanting to read a string |
| * must use {@link #readStringNoHelper()} to avoid |
| * infinity recursive calls. |
| */ |
| public String readString8(Parcel p) { |
| return p.readString8NoHelper(); |
| } |
| |
| public String readString16(Parcel p) { |
| return p.readString16NoHelper(); |
| } |
| } |
| |
| private ReadWriteHelper mReadWriteHelper = ReadWriteHelper.DEFAULT; |
| |
| /** |
| * Retrieve a new Parcel object from the pool. |
| */ |
| @NonNull |
| public static Parcel obtain() { |
| Parcel res = null; |
| synchronized (sPoolSync) { |
| if (sOwnedPool != null) { |
| res = sOwnedPool; |
| sOwnedPool = res.mPoolNext; |
| res.mPoolNext = null; |
| sOwnedPoolSize--; |
| } |
| } |
| |
| // When no cache found above, create from scratch; otherwise prepare the |
| // cached object to be used |
| if (res == null) { |
| res = new Parcel(0); |
| } else { |
| res.mRecycled = false; |
| if (DEBUG_RECYCLE) { |
| res.mStack = new RuntimeException(); |
| } |
| res.mReadWriteHelper = ReadWriteHelper.DEFAULT; |
| } |
| |
| if (res.mNativePtr == 0) { |
| Log.e(TAG, "Obtained Parcel object has null native pointer. Invalid state."); |
| } |
| |
| return res; |
| } |
| |
| /** |
| * Retrieve a new Parcel object from the pool for use with a specific binder. |
| * |
| * Associate this parcel with a binder object. This marks the parcel as being prepared for a |
| * transaction on this specific binder object. Based on this, the format of the wire binder |
| * protocol may change. For future compatibility, it is recommended to use this for all |
| * Parcels. |
| */ |
| @NonNull |
| public static Parcel obtain(@NonNull IBinder binder) { |
| Parcel parcel = Parcel.obtain(); |
| parcel.markForBinder(binder); |
| return parcel; |
| } |
| |
| /** |
| * Put a Parcel object back into the pool. You must not touch |
| * the object after this call. |
| */ |
| public final void recycle() { |
| if (mRecycled) { |
| Log.wtf(TAG, "Recycle called on unowned Parcel. (recycle twice?) Here: " |
| + Log.getStackTraceString(new Throwable()) |
| + " Original recycle call (if DEBUG_RECYCLE): ", mStack); |
| |
| return; |
| } |
| mRecycled = true; |
| |
| // We try to reset the entire object here, but in order to be |
| // able to print a stack when a Parcel is recycled twice, that |
| // is cleared in obtain instead. |
| |
| mClassCookies = null; |
| freeBuffer(); |
| |
| if (mOwnsNativeParcelObject) { |
| synchronized (sPoolSync) { |
| if (sOwnedPoolSize < POOL_SIZE) { |
| mPoolNext = sOwnedPool; |
| sOwnedPool = this; |
| sOwnedPoolSize++; |
| } |
| } |
| } else { |
| mNativePtr = 0; |
| synchronized (sPoolSync) { |
| if (sHolderPoolSize < POOL_SIZE) { |
| mPoolNext = sHolderPool; |
| sHolderPool = this; |
| sHolderPoolSize++; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Set a {@link ReadWriteHelper}, which can be used to avoid having duplicate strings, for |
| * example. |
| * |
| * @hide |
| */ |
| public void setReadWriteHelper(@Nullable ReadWriteHelper helper) { |
| mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT; |
| } |
| |
| /** |
| * @return whether this parcel has a {@link ReadWriteHelper}. |
| * |
| * @hide |
| */ |
| public boolean hasReadWriteHelper() { |
| return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT); |
| } |
| |
| /** @hide */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @RavenwoodThrow |
| public static native long getGlobalAllocSize(); |
| |
| /** @hide */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| @RavenwoodThrow |
| public static native long getGlobalAllocCount(); |
| |
| /** |
| * Parcel data should be zero'd before realloc'd or deleted. |
| * |
| * Note: currently this feature requires multiple things to work in concert: |
| * - markSensitive must be called on every relative Parcel |
| * - FLAG_CLEAR_BUF must be passed into the kernel |
| * This requires having code which does the right thing in every method and in every backend |
| * of AIDL. Rather than exposing this API, it should be replaced with a single API on |
| * IBinder objects which can be called once, and the information should be fed into the |
| * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is |
| * much more extensible. |
| * |
| * @hide |
| */ |
| public final void markSensitive() { |
| nativeMarkSensitive(mNativePtr); |
| } |
| |
| /** |
| * @hide |
| */ |
| private void markForBinder(@NonNull IBinder binder) { |
| nativeMarkForBinder(mNativePtr, binder); |
| } |
| |
| /** |
| * Whether this Parcel is written for an RPC transaction. |
| * |
| * @hide |
| */ |
| public final boolean isForRpc() { |
| return nativeIsForRpc(mNativePtr); |
| } |
| |
| /** @hide */ |
| @ParcelFlags |
| @TestApi |
| public int getFlags() { |
| return mFlags; |
| } |
| |
| /** @hide */ |
| public void setFlags(@ParcelFlags int flags) { |
| mFlags = flags; |
| } |
| |
| /** @hide */ |
| public void addFlags(@ParcelFlags int flags) { |
| mFlags |= flags; |
| } |
| |
| /** @hide */ |
| private boolean hasFlags(@ParcelFlags int flags) { |
| return (mFlags & flags) == flags; |
| } |
| |
| /** |
| * This method is used by the AIDL compiler for system components. Not intended to be |
| * used by non-system apps. |
| */ |
| // Note: Ideally this method should be @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES), |
| // but we need to make this method public due to the way the aidl compiler is compiled. |
| // We don't really need to protect it; even if 3p / non-system apps, nothing would happen. |
| // This would only work when used on a reply parcel by a binder object that's allowed-blocking. |
| public void setPropagateAllowBlocking() { |
| addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING); |
| } |
| |
| /** |
| * Returns the total amount of data contained in the parcel. |
| */ |
| public int dataSize() { |
| return nativeDataSize(mNativePtr); |
| } |
| |
| /** |
| * Returns the amount of data remaining to be read from the |
| * parcel. That is, {@link #dataSize}-{@link #dataPosition}. |
| */ |
| public final int dataAvail() { |
| return nativeDataAvail(mNativePtr); |
| } |
| |
| /** |
| * Returns the current position in the parcel data. Never |
| * more than {@link #dataSize}. |
| */ |
| public final int dataPosition() { |
| return nativeDataPosition(mNativePtr); |
| } |
| |
| /** |
| * Returns the total amount of space in the parcel. This is always |
| * >= {@link #dataSize}. The difference between it and dataSize() is the |
| * amount of room left until the parcel needs to re-allocate its |
| * data buffer. |
| */ |
| public final int dataCapacity() { |
| return nativeDataCapacity(mNativePtr); |
| } |
| |
| /** |
| * Change the amount of data in the parcel. Can be either smaller or |
| * larger than the current size. If larger than the current capacity, |
| * more memory will be allocated. |
| * |
| * @param size The new number of bytes in the Parcel. |
| */ |
| public final void setDataSize(int size) { |
| nativeSetDataSize(mNativePtr, size); |
| } |
| |
| /** |
| * Move the current read/write position in the parcel. |
| * @param pos New offset in the parcel; must be between 0 and |
| * {@link #dataSize}. |
| */ |
| public final void setDataPosition(int pos) { |
| nativeSetDataPosition(mNativePtr, pos); |
| } |
| |
| /** |
| * Change the capacity (current available space) of the parcel. |
| * |
| * @param size The new capacity of the parcel, in bytes. Can not be |
| * less than {@link #dataSize} -- that is, you can not drop existing data |
| * with this method. |
| */ |
| public final void setDataCapacity(int size) { |
| nativeSetDataCapacity(mNativePtr, size); |
| } |
| |
| /** @hide */ |
| public final boolean pushAllowFds(boolean allowFds) { |
| return nativePushAllowFds(mNativePtr, allowFds); |
| } |
| |
| /** @hide */ |
| public final void restoreAllowFds(boolean lastValue) { |
| nativeRestoreAllowFds(mNativePtr, lastValue); |
| } |
| |
| /** |
| * Returns the raw bytes of the parcel. |
| * |
| * <p class="note">The data you retrieve here <strong>must not</strong> |
| * be placed in any kind of persistent storage (on local disk, across |
| * a network, etc). For that, you should use standard serialization |
| * or another kind of general serialization mechanism. The Parcel |
| * marshalled representation is highly optimized for local IPC, and as |
| * such does not attempt to maintain compatibility with data created |
| * in different versions of the platform. |
| */ |
| public final byte[] marshall() { |
| return nativeMarshall(mNativePtr); |
| } |
| |
| /** |
| * Fills the raw bytes of this Parcel with the supplied data. |
| */ |
| public final void unmarshall(@NonNull byte[] data, int offset, int length) { |
| nativeUnmarshall(mNativePtr, data, offset, length); |
| } |
| |
| public final void appendFrom(Parcel parcel, int offset, int length) { |
| nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length); |
| } |
| |
| /** @hide */ |
| public int compareData(Parcel other) { |
| return nativeCompareData(mNativePtr, other.mNativePtr); |
| } |
| |
| /** @hide */ |
| public static boolean compareData(Parcel a, int offsetA, Parcel b, int offsetB, int length) { |
| return nativeCompareDataInRange(a.mNativePtr, offsetA, b.mNativePtr, offsetB, length); |
| } |
| |
| /** @hide */ |
| public final void setClassCookie(Class clz, Object cookie) { |
| if (mClassCookies == null) { |
| mClassCookies = new ArrayMap<>(); |
| } |
| mClassCookies.put(clz, cookie); |
| } |
| |
| /** @hide */ |
| @Nullable |
| public final Object getClassCookie(Class clz) { |
| return mClassCookies != null ? mClassCookies.get(clz) : null; |
| } |
| |
| /** @hide */ |
| public void removeClassCookie(Class clz, Object expectedCookie) { |
| if (mClassCookies != null) { |
| Object removedCookie = mClassCookies.remove(clz); |
| if (removedCookie != expectedCookie) { |
| Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz |
| + ") but instead removed " + removedCookie); |
| } |
| } else { |
| Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz |
| + ") but no cookies were present"); |
| } |
| } |
| |
| /** |
| * Whether {@link #setClassCookie} has been called with the specified {@code clz}. |
| * @hide |
| */ |
| public boolean hasClassCookie(Class clz) { |
| return mClassCookies != null && mClassCookies.containsKey(clz); |
| } |
| |
| /** @hide */ |
| public final void adoptClassCookies(Parcel from) { |
| mClassCookies = from.mClassCookies; |
| } |
| |
| /** @hide */ |
| public Map<Class, Object> copyClassCookies() { |
| return new ArrayMap<>(mClassCookies); |
| } |
| |
| /** @hide */ |
| public void putClassCookies(Map<Class, Object> cookies) { |
| if (cookies == null) { |
| return; |
| } |
| if (mClassCookies == null) { |
| mClassCookies = new ArrayMap<>(); |
| } |
| mClassCookies.putAll(cookies); |
| } |
| |
| /** |
| * Report whether the parcel contains any marshalled file descriptors. |
| */ |
| public boolean hasFileDescriptors() { |
| return nativeHasFileDescriptors(mNativePtr); |
| } |
| |
| /** |
| * Report whether the parcel contains any marshalled file descriptors in the range defined by |
| * {@code offset} and {@code length}. |
| * |
| * @param offset The offset from which the range starts. Should be between 0 and |
| * {@link #dataSize()}. |
| * @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code |
| * offset}. |
| * @return whether there are file descriptors or not. |
| * @throws IllegalArgumentException if the parameters are out of the permitted ranges. |
| */ |
| public boolean hasFileDescriptors(int offset, int length) { |
| return nativeHasFileDescriptorsInRange(mNativePtr, offset, length); |
| } |
| |
| /** |
| * Check if the object has file descriptors. |
| * |
| * <p>Objects supported are {@link Parcel} and objects that can be passed to {@link |
| * #writeValue(Object)}} |
| * |
| * <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method |
| * for that. |
| * |
| * @throws IllegalArgumentException if you provide any object not supported by above methods |
| * (including if the unsupported object is inside a nested container). |
| * |
| * @hide |
| */ |
| public static boolean hasFileDescriptors(Object value) { |
| if (value instanceof Parcel) { |
| Parcel parcel = (Parcel) value; |
| if (parcel.hasFileDescriptors()) { |
| return true; |
| } |
| } else if (value instanceof LazyValue) { |
| LazyValue lazy = (LazyValue) value; |
| if (lazy.hasFileDescriptors()) { |
| return true; |
| } |
| } else if (value instanceof Parcelable) { |
| Parcelable parcelable = (Parcelable) value; |
| if ((parcelable.describeContents() & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) { |
| return true; |
| } |
| } else if (value instanceof ArrayMap<?, ?>) { |
| ArrayMap<?, ?> map = (ArrayMap<?, ?>) value; |
| for (int i = 0, n = map.size(); i < n; i++) { |
| if (hasFileDescriptors(map.keyAt(i)) |
| || hasFileDescriptors(map.valueAt(i))) { |
| return true; |
| } |
| } |
| } else if (value instanceof Map<?, ?>) { |
| Map<?, ?> map = (Map<?, ?>) value; |
| for (Map.Entry<?, ?> entry : map.entrySet()) { |
| if (hasFileDescriptors(entry.getKey()) |
| || hasFileDescriptors(entry.getValue())) { |
| return true; |
| } |
| } |
| } else if (value instanceof List<?>) { |
| List<?> list = (List<?>) value; |
| for (int i = 0, n = list.size(); i < n; i++) { |
| if (hasFileDescriptors(list.get(i))) { |
| return true; |
| } |
| } |
| } else if (value instanceof SparseArray<?>) { |
| SparseArray<?> array = (SparseArray<?>) value; |
| for (int i = 0, n = array.size(); i < n; i++) { |
| if (hasFileDescriptors(array.valueAt(i))) { |
| return true; |
| } |
| } |
| } else if (value instanceof Object[]) { |
| Object[] array = (Object[]) value; |
| for (int i = 0, n = array.length; i < n; i++) { |
| if (hasFileDescriptors(array[i])) { |
| return true; |
| } |
| } |
| } else { |
| getValueType(value); // Will throw if value is not supported |
| } |
| return false; |
| } |
| |
| /** |
| * Report whether the parcel contains any marshalled IBinder objects. |
| * |
| * @throws UnsupportedOperationException if binder kernel driver was disabled or if method was |
| * invoked in case of Binder RPC protocol. |
| * @hide |
| */ |
| public boolean hasBinders() { |
| return nativeHasBinders(mNativePtr); |
| } |
| |
| /** |
| * Report whether the parcel contains any marshalled {@link IBinder} objects in the range |
| * defined by {@code offset} and {@code length}. |
| * |
| * @param offset The offset from which the range starts. Should be between 0 and |
| * {@link #dataSize()}. |
| * @param length The length of the range. Should be between 0 and {@link #dataSize()} - {@code |
| * offset}. |
| * @return whether there are binders in the range or not. |
| * @throws IllegalArgumentException if the parameters are out of the permitted ranges. |
| * |
| * @hide |
| */ |
| public boolean hasBinders(int offset, int length) { |
| return nativeHasBindersInRange(mNativePtr, offset, length); |
| } |
| |
| /** |
| * Store or read an IBinder interface token in the parcel at the current |
| * {@link #dataPosition}. This is used to validate that the marshalled |
| * transaction is intended for the target interface. This is typically written |
| * at the beginning of transactions as a header. |
| */ |
| public final void writeInterfaceToken(@NonNull String interfaceName) { |
| nativeWriteInterfaceToken(mNativePtr, interfaceName); |
| } |
| |
| /** |
| * Read the header written by writeInterfaceToken and verify it matches |
| * the interface name in question. If the wrong interface type is present, |
| * {@link SecurityException} is thrown. When used over binder, this exception |
| * should propagate to the caller. |
| */ |
| public final void enforceInterface(@NonNull String interfaceName) { |
| nativeEnforceInterface(mNativePtr, interfaceName); |
| } |
| |
| /** |
| * Verify there are no bytes left to be read on the Parcel. |
| * |
| * @throws BadParcelableException If the current position hasn't reached the end of the Parcel. |
| * When used over binder, this exception should propagate to the caller. |
| */ |
| public void enforceNoDataAvail() { |
| final int n = dataAvail(); |
| if (n > 0) { |
| throw new BadParcelableException("Parcel data not fully consumed, unread size: " + n); |
| } |
| } |
| |
| /** |
| * Writes the work source uid to the request headers. |
| * |
| * <p>It requires the headers to have been written/read already to replace the work source. |
| * |
| * @return true if the request headers have been updated. |
| * |
| * @hide |
| */ |
| public boolean replaceCallingWorkSourceUid(int workSourceUid) { |
| return nativeReplaceCallingWorkSourceUid(mNativePtr, workSourceUid); |
| } |
| |
| /** |
| * Reads the work source uid from the request headers. |
| * |
| * <p>Unlike other read methods, this method does not read the parcel at the current |
| * {@link #dataPosition}. It will set the {@link #dataPosition} before the read and restore the |
| * position after reading the request header. |
| * |
| * @return the work source uid or {@link Binder#UNSET_WORKSOURCE} if headers have not been |
| * written/parsed yet. |
| * |
| * @hide |
| */ |
| public int readCallingWorkSourceUid() { |
| return nativeReadCallingWorkSourceUid(mNativePtr); |
| } |
| |
| /** |
| * Write a byte array into the parcel at the current {@link #dataPosition}, |
| * growing {@link #dataCapacity} if needed. |
| * @param b Bytes to place into the parcel. |
| */ |
| public final void writeByteArray(@Nullable byte[] b) { |
| writeByteArray(b, 0, (b != null) ? b.length : 0); |
| } |
| |
| /** |
| * Write a byte array into the parcel at the current {@link #dataPosition}, |
| * growing {@link #dataCapacity} if needed. |
| * @param b Bytes to place into the parcel. |
| * @param offset Index of first byte to be written. |
| * @param len Number of bytes to write. |
| */ |
| public final void writeByteArray(@Nullable byte[] b, int offset, int len) { |
| if (b == null) { |
| writeInt(-1); |
| return; |
| } |
| ArrayUtils.throwsIfOutOfBounds(b.length, offset, len); |
| nativeWriteByteArray(mNativePtr, b, offset, len); |
| } |
| |
| /** |
| * Write a blob of data into the parcel at the current {@link #dataPosition}, |
| * growing {@link #dataCapacity} if needed. |
| * |
| * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of |
| * an anonymous shared memory region. If you prefer send in-place, please use |
| * {@link #writeByteArray(byte[])}. |
| * |
| * @param b Bytes to place into the parcel. |
| * |
| * @see #readBlob() |
| */ |
| public final void writeBlob(@Nullable byte[] b) { |
| writeBlob(b, 0, (b != null) ? b.length : 0); |
| } |
| |
| /** |
| * Write a blob of data into the parcel at the current {@link #dataPosition}, |
| * growing {@link #dataCapacity} if needed. |
| * |
| * <p> If the blob is small, then it is stored in-place, otherwise it is transferred by way of |
| * an anonymous shared memory region. If you prefer send in-place, please use |
| * {@link #writeByteArray(byte[], int, int)}. |
| * |
| * @param b Bytes to place into the parcel. |
| * @param offset Index of first byte to be written. |
| * @param len Number of bytes to write. |
| * |
| * @see #readBlob() |
| */ |
| public final void writeBlob(@Nullable byte[] b, int offset, int len) { |
| if (b == null) { |
| writeInt(-1); |
| return; |
| } |
| ArrayUtils.throwsIfOutOfBounds(b.length, offset, len); |
| nativeWriteBlob(mNativePtr, b, offset, len); |
| } |
| |
| // The OK status from system/core/libutils/include/utils/Errors.h . |
| // We shall pass all other error codes back to native for throwing exceptions. The error |
| // check is done in Java to allow using @CriticalNative calls for the success path. |
| private static final int OK = 0; |
| |
| /** |
| * Write an integer value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeInt(int val) { |
| int err = nativeWriteInt(mNativePtr, val); |
| if (err != OK) { |
| nativeSignalExceptionForError(err); |
| } |
| } |
| |
| /** |
| * Write a long integer value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeLong(long val) { |
| int err = nativeWriteLong(mNativePtr, val); |
| if (err != OK) { |
| nativeSignalExceptionForError(err); |
| } |
| } |
| |
| /** |
| * Write a floating point value into the parcel at the current |
| * dataPosition(), growing dataCapacity() if needed. |
| */ |
| public final void writeFloat(float val) { |
| int err = nativeWriteFloat(mNativePtr, val); |
| if (err != OK) { |
| nativeSignalExceptionForError(err); |
| } |
| } |
| |
| /** |
| * Write a double precision floating point value into the parcel at the |
| * current dataPosition(), growing dataCapacity() if needed. |
| */ |
| public final void writeDouble(double val) { |
| int err = nativeWriteDouble(mNativePtr, val); |
| if (err != OK) { |
| nativeSignalExceptionForError(err); |
| } |
| } |
| |
| /** |
| * Write a string value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeString(@Nullable String val) { |
| writeString16(val); |
| } |
| |
| /** {@hide} */ |
| public final void writeString8(@Nullable String val) { |
| mReadWriteHelper.writeString8(this, val); |
| } |
| |
| /** {@hide} */ |
| public final void writeString16(@Nullable String val) { |
| mReadWriteHelper.writeString16(this, val); |
| } |
| |
| /** |
| * Write a string without going though a {@link ReadWriteHelper}. Subclasses of |
| * {@link ReadWriteHelper} must use this method instead of {@link #writeString} to avoid |
| * infinity recursive calls. |
| * |
| * @hide |
| */ |
| public void writeStringNoHelper(@Nullable String val) { |
| writeString16NoHelper(val); |
| } |
| |
| /** {@hide} */ |
| public void writeString8NoHelper(@Nullable String val) { |
| nativeWriteString8(mNativePtr, val); |
| } |
| |
| /** {@hide} */ |
| public void writeString16NoHelper(@Nullable String val) { |
| nativeWriteString16(mNativePtr, val); |
| } |
| |
| /** |
| * Write a boolean value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| * |
| * <p>Note: This method currently delegates to writeInt with a value of 1 or 0 |
| * for true or false, respectively, but may change in the future. |
| */ |
| public final void writeBoolean(boolean val) { |
| writeInt(val ? 1 : 0); |
| } |
| |
| /** |
| * Write a CharSequence value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public final void writeCharSequence(@Nullable CharSequence val) { |
| TextUtils.writeToParcel(val, this, 0); |
| } |
| |
| /** |
| * Write an object into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeStrongBinder(IBinder val) { |
| nativeWriteStrongBinder(mNativePtr, val); |
| } |
| |
| /** |
| * Write an object into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeStrongInterface(IInterface val) { |
| writeStrongBinder(val == null ? null : val.asBinder()); |
| } |
| |
| /** |
| * Write a FileDescriptor into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| * |
| * <p class="caution">The file descriptor will not be closed, which may |
| * result in file descriptor leaks when objects are returned from Binder |
| * calls. Use {@link ParcelFileDescriptor#writeToParcel} instead, which |
| * accepts contextual flags and will close the original file descriptor |
| * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p> |
| */ |
| public final void writeFileDescriptor(@NonNull FileDescriptor val) { |
| nativeWriteFileDescriptor(mNativePtr, val); |
| } |
| |
| /** |
| * {@hide} |
| * This will be the new name for writeFileDescriptor, for consistency. |
| **/ |
| public final void writeRawFileDescriptor(@NonNull FileDescriptor val) { |
| nativeWriteFileDescriptor(mNativePtr, val); |
| } |
| |
| /** |
| * {@hide} |
| * Write an array of FileDescriptor objects into the Parcel. |
| * |
| * @param value The array of objects to be written. |
| */ |
| public final void writeRawFileDescriptorArray(@Nullable FileDescriptor[] value) { |
| if (value != null) { |
| int N = value.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeRawFileDescriptor(value[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * Write a byte value into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| * |
| * <p>Note: This method currently delegates to writeInt but may change in |
| * the future. |
| */ |
| public final void writeByte(byte val) { |
| writeInt(val); |
| } |
| |
| /** |
| * Please use {@link #writeBundle} instead. Flattens a Map into the parcel |
| * at the current dataPosition(), |
| * growing dataCapacity() if needed. The Map keys must be String objects. |
| * The Map values are written using {@link #writeValue} and must follow |
| * the specification there. |
| * |
| * <p>It is strongly recommended to use {@link #writeBundle} instead of |
| * this method, since the Bundle class provides a type-safe API that |
| * allows you to avoid mysterious type errors at the point of marshalling. |
| */ |
| public final void writeMap(@Nullable Map val) { |
| writeMapInternal((Map<String, Object>) val); |
| } |
| |
| /** |
| * Flatten a Map into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. The Map keys must be String objects. |
| */ |
| /* package */ void writeMapInternal(@Nullable Map<String,Object> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| Set<Map.Entry<String,Object>> entries = val.entrySet(); |
| int size = entries.size(); |
| writeInt(size); |
| |
| for (Map.Entry<String,Object> e : entries) { |
| writeValue(e.getKey()); |
| writeValue(e.getValue()); |
| size--; |
| } |
| |
| if (size != 0) { |
| throw new BadParcelableException("Map size does not match number of entries!"); |
| } |
| |
| } |
| |
| /** |
| * Flatten an ArrayMap into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. The Map keys must be String objects. |
| */ |
| /* package */ void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| // Keep the format of this Parcel in sync with writeToParcelInner() in |
| // frameworks/native/libs/binder/PersistableBundle.cpp. |
| final int N = val.size(); |
| writeInt(N); |
| if (DEBUG_ARRAY_MAP) { |
| RuntimeException here = new RuntimeException("here"); |
| here.fillInStackTrace(); |
| Log.d(TAG, "Writing " + N + " ArrayMap entries", here); |
| } |
| int startPos; |
| for (int i=0; i<N; i++) { |
| if (DEBUG_ARRAY_MAP) startPos = dataPosition(); |
| writeString(val.keyAt(i)); |
| writeValue(val.valueAt(i)); |
| if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " " |
| + (dataPosition()-startPos) + " bytes: key=0x" |
| + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) |
| + " " + val.keyAt(i)); |
| } |
| } |
| |
| /** |
| * @hide For testing only. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void writeArrayMap(@Nullable ArrayMap<String, Object> val) { |
| writeArrayMapInternal(val); |
| } |
| |
| /** |
| * Flatten an {@link ArrayMap} with string keys containing a particular object |
| * type into the parcel at the current dataPosition() and growing dataCapacity() |
| * if needed. The type of the objects in the array must be one that implements |
| * Parcelable. Only the raw data of the objects is written and not their type, |
| * so you must use the corresponding {@link #createTypedArrayMap(Parcelable.Creator)} |
| * |
| * @param val The map of objects to be written. |
| * @param parcelableFlags The parcelable flags to use. |
| * |
| * @see #createTypedArrayMap(Parcelable.Creator) |
| * @see Parcelable |
| */ |
| public <T extends Parcelable> void writeTypedArrayMap(@Nullable ArrayMap<String, T> val, |
| int parcelableFlags) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| final int count = val.size(); |
| writeInt(count); |
| for (int i = 0; i < count; i++) { |
| writeString(val.keyAt(i)); |
| writeTypedObject(val.valueAt(i), parcelableFlags); |
| } |
| } |
| |
| /** |
| * Write an array set to the parcel. |
| * |
| * @param val The array set to write. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void writeArraySet(@Nullable ArraySet<? extends Object> val) { |
| final int size = (val != null) ? val.size() : -1; |
| writeInt(size); |
| for (int i = 0; i < size; i++) { |
| writeValue(val.valueAt(i)); |
| } |
| } |
| |
| /** |
| * Flatten a Bundle into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeBundle(@Nullable Bundle val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| |
| val.writeToParcel(this, 0); |
| } |
| |
| /** |
| * Flatten a PersistableBundle into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writePersistableBundle(@Nullable PersistableBundle val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| |
| val.writeToParcel(this, 0); |
| } |
| |
| /** |
| * Flatten a Size into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeSize(@NonNull Size val) { |
| writeInt(val.getWidth()); |
| writeInt(val.getHeight()); |
| } |
| |
| /** |
| * Flatten a SizeF into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. |
| */ |
| public final void writeSizeF(@NonNull SizeF val) { |
| writeFloat(val.getWidth()); |
| writeFloat(val.getHeight()); |
| } |
| |
| /** |
| * Flatten a List into the parcel at the current dataPosition(), growing |
| * dataCapacity() if needed. The List values are written using |
| * {@link #writeValue} and must follow the specification there. |
| */ |
| public final void writeList(@Nullable List val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeValue(val.get(i)); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten an Object array into the parcel at the current dataPosition(), |
| * growing dataCapacity() if needed. The array values are written using |
| * {@link #writeValue} and must follow the specification there. |
| */ |
| public final void writeArray(@Nullable Object[] val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.length; |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeValue(val[i]); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a generic SparseArray into the parcel at the current |
| * dataPosition(), growing dataCapacity() if needed. The SparseArray |
| * values are written using {@link #writeValue} and must follow the |
| * specification there. |
| */ |
| public final <T> void writeSparseArray(@Nullable SparseArray<T> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| writeInt(N); |
| int i=0; |
| while (i < N) { |
| writeInt(val.keyAt(i)); |
| writeValue(val.valueAt(i)); |
| i++; |
| } |
| } |
| |
| public final void writeSparseBooleanArray(@Nullable SparseBooleanArray val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| writeInt(N); |
| int i=0; |
| while (i < N) { |
| writeInt(val.keyAt(i)); |
| writeByte((byte)(val.valueAt(i) ? 1 : 0)); |
| i++; |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| public final void writeSparseIntArray(@Nullable SparseIntArray val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| writeInt(N); |
| int i=0; |
| while (i < N) { |
| writeInt(val.keyAt(i)); |
| writeInt(val.valueAt(i)); |
| i++; |
| } |
| } |
| |
| public final void writeBooleanArray(@Nullable boolean[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeInt(val[i] ? 1 : 0); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| private static <T> int getItemTypeSize(@NonNull Class<T> arrayClass) { |
| final Class<?> componentType = arrayClass.getComponentType(); |
| // typeSize has been referred from respective create*Array functions |
| if (componentType == boolean.class) { |
| return SIZE_BOOLEAN; |
| } else if (componentType == byte.class) { |
| return SIZE_BYTE; |
| } else if (componentType == char.class) { |
| return SIZE_CHAR; |
| } else if (componentType == int.class) { |
| return SIZE_INT; |
| } else if (componentType == long.class) { |
| return SIZE_LONG; |
| } else if (componentType == float.class) { |
| return SIZE_FLOAT; |
| } else if (componentType == double.class) { |
| return SIZE_DOUBLE; |
| } |
| |
| return SIZE_COMPLEX_TYPE; |
| } |
| |
| private void ensureWithinMemoryLimit(int typeSize, @NonNull int... dimensions) { |
| // For Multidimensional arrays, Calculate total object |
| // which will be allocated. |
| int totalObjects = 1; |
| try { |
| for (int dimension : dimensions) { |
| totalObjects = Math.multiplyExact(totalObjects, dimension); |
| } |
| } catch (ArithmeticException e) { |
| Log.e(TAG, "ArithmeticException occurred while multiplying dimensions " + e); |
| BadParcelableException badParcelableException = new BadParcelableException("Estimated " |
| + "array length is too large. Array Dimensions:" + Arrays.toString(dimensions)); |
| SneakyThrow.sneakyThrow(badParcelableException); |
| } |
| ensureWithinMemoryLimit(typeSize, totalObjects); |
| } |
| |
| private void ensureWithinMemoryLimit(int typeSize, int length) { |
| int estimatedAllocationSize = 0; |
| try { |
| estimatedAllocationSize = Math.multiplyExact(typeSize, length); |
| } catch (ArithmeticException e) { |
| Log.e(TAG, "ArithmeticException occurred while multiplying values " + typeSize |
| + " and " + length + " Exception: " + e); |
| BadParcelableException badParcelableException = new BadParcelableException("Estimated " |
| + "allocation size is too large. typeSize: " + typeSize + " length: " + length); |
| SneakyThrow.sneakyThrow(badParcelableException); |
| } |
| |
| boolean isInBinderTransaction = Binder.isDirectlyHandlingTransaction(); |
| if (isInBinderTransaction && (estimatedAllocationSize > ARRAY_ALLOCATION_LIMIT)) { |
| Log.e(TAG, "Trying to Allocate " + estimatedAllocationSize |
| + " memory, In Binder Transaction : " + isInBinderTransaction); |
| BadParcelableException e = new BadParcelableException("Allocation of size " |
| + estimatedAllocationSize + " is above allowed limit of 1MB"); |
| SneakyThrow.sneakyThrow(e); |
| } |
| } |
| |
| @Nullable |
| public final boolean[] createBooleanArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_BOOLEAN, N); |
| // >>2 as a fast divide-by-4 works in the create*Array() functions |
| // because dataAvail() will never return a negative number. 4 is |
| // the size of a stored boolean in the stream. |
| if (N >= 0 && N <= (dataAvail() >> 2)) { |
| boolean[] val = new boolean[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readInt() != 0; |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readBooleanArray(@NonNull boolean[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readInt() != 0; |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** @hide */ |
| public void writeShortArray(@Nullable short[] val) { |
| if (val != null) { |
| int n = val.length; |
| writeInt(n); |
| for (int i = 0; i < n; i++) { |
| writeInt(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** @hide */ |
| @Nullable |
| public short[] createShortArray() { |
| int n = readInt(); |
| ensureWithinMemoryLimit(SIZE_SHORT, n); |
| if (n >= 0 && n <= (dataAvail() >> 2)) { |
| short[] val = new short[n]; |
| for (int i = 0; i < n; i++) { |
| val[i] = (short) readInt(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| /** @hide */ |
| public void readShortArray(@NonNull short[] val) { |
| int n = readInt(); |
| if (n == val.length) { |
| for (int i = 0; i < n; i++) { |
| val[i] = (short) readInt(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeCharArray(@Nullable char[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeInt((int)val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final char[] createCharArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_CHAR, N); |
| if (N >= 0 && N <= (dataAvail() >> 2)) { |
| char[] val = new char[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = (char)readInt(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readCharArray(@NonNull char[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = (char)readInt(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeIntArray(@Nullable int[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeInt(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final int[] createIntArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_INT, N); |
| if (N >= 0 && N <= (dataAvail() >> 2)) { |
| int[] val = new int[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readInt(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readIntArray(@NonNull int[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readInt(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeLongArray(@Nullable long[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeLong(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final long[] createLongArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_LONG, N); |
| // >>3 because stored longs are 64 bits |
| if (N >= 0 && N <= (dataAvail() >> 3)) { |
| long[] val = new long[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readLong(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readLongArray(@NonNull long[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readLong(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeFloatArray(@Nullable float[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeFloat(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final float[] createFloatArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_FLOAT, N); |
| // >>2 because stored floats are 4 bytes |
| if (N >= 0 && N <= (dataAvail() >> 2)) { |
| float[] val = new float[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readFloat(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readFloatArray(@NonNull float[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readFloat(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeDoubleArray(@Nullable double[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeDouble(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final double[] createDoubleArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_DOUBLE, N); |
| // >>3 because stored doubles are 8 bytes |
| if (N >= 0 && N <= (dataAvail() >> 3)) { |
| double[] val = new double[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readDouble(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readDoubleArray(@NonNull double[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readDouble(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeStringArray(@Nullable String[] val) { |
| writeString16Array(val); |
| } |
| |
| @Nullable |
| public final String[] createStringArray() { |
| return createString16Array(); |
| } |
| |
| public final void readStringArray(@NonNull String[] val) { |
| readString16Array(val); |
| } |
| |
| /** {@hide} */ |
| public final void writeString8Array(@Nullable String[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeString8(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** {@hide} */ |
| @Nullable |
| public final String[] createString8Array() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| if (N >= 0) { |
| String[] val = new String[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readString8(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| /** {@hide} */ |
| public final void readString8Array(@NonNull String[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readString8(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** {@hide} */ |
| public final void writeString16Array(@Nullable String[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeString16(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** {@hide} */ |
| @Nullable |
| public final String[] createString16Array() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| if (N >= 0) { |
| String[] val = new String[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readString16(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| /** {@hide} */ |
| public final void readString16Array(@NonNull String[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readString16(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| public final void writeBinderArray(@Nullable IBinder[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeStrongBinder(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * Flatten a homogeneous array containing an IInterface type into the parcel, |
| * at the current dataPosition() and growing dataCapacity() if needed. The |
| * type of the objects in the array must be one that implements IInterface. |
| * |
| * @param val The array of objects to be written. |
| * |
| * @see #createInterfaceArray |
| * @see #readInterfaceArray |
| * @see IInterface |
| */ |
| public final <T extends IInterface> void writeInterfaceArray( |
| @SuppressLint("ArrayReturn") @Nullable T[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeStrongInterface(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| public final void writeCharSequenceArray(@Nullable CharSequence[] val) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeCharSequence(val[i]); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * @hide |
| */ |
| public final void writeCharSequenceList(@Nullable ArrayList<CharSequence> val) { |
| if (val != null) { |
| int N = val.size(); |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeCharSequence(val.get(i)); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| @Nullable |
| public final IBinder[] createBinderArray() { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| if (N >= 0) { |
| IBinder[] val = new IBinder[N]; |
| for (int i=0; i<N; i++) { |
| val[i] = readStrongBinder(); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| public final void readBinderArray(@NonNull IBinder[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readStrongBinder(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** |
| * Read and return a new array of T (IInterface) from the parcel. |
| * |
| * @return the IInterface array of type T |
| * @param newArray a function to create an array of T with a given length |
| * @param asInterface a function to convert IBinder object into T (IInterface) |
| */ |
| @SuppressLint({"ArrayReturn", "NullableCollection", "SamShouldBeLast"}) |
| @Nullable |
| public final <T extends IInterface> T[] createInterfaceArray( |
| @NonNull IntFunction<T[]> newArray, @NonNull Function<IBinder, T> asInterface) { |
| int N = readInt(); |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| if (N >= 0) { |
| T[] val = newArray.apply(N); |
| for (int i=0; i<N; i++) { |
| val[i] = asInterface.apply(readStrongBinder()); |
| } |
| return val; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Read an array of T (IInterface) from a parcel. |
| * |
| * @param asInterface a function to convert IBinder object into T (IInterface) |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the length of `val` |
| * mismatches the number of items in the parcel. |
| */ |
| public final <T extends IInterface> void readInterfaceArray( |
| @SuppressLint("ArrayReturn") @NonNull T[] val, |
| @NonNull Function<IBinder, T> asInterface) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = asInterface.apply(readStrongBinder()); |
| } |
| } else { |
| throw new BadParcelableException("bad array lengths"); |
| } |
| } |
| |
| /** |
| * Flatten a List containing a particular object type into the parcel, at |
| * the current dataPosition() and growing dataCapacity() if needed. The |
| * type of the objects in the list must be one that implements Parcelable. |
| * Unlike the generic writeList() method, however, only the raw data of the |
| * objects is written and not their type, so you must use the corresponding |
| * readTypedList() to unmarshall them. |
| * |
| * @param val The list of objects to be written. |
| * |
| * @see #createTypedArrayList |
| * @see #readTypedList |
| * @see Parcelable |
| */ |
| public final <T extends Parcelable> void writeTypedList(@Nullable List<T> val) { |
| writeTypedList(val, 0); |
| } |
| |
| /** |
| * Flatten a {@link SparseArray} containing a particular object type into the parcel |
| * at the current dataPosition() and growing dataCapacity() if needed. The |
| * type of the objects in the array must be one that implements Parcelable. |
| * Unlike the generic {@link #writeSparseArray(SparseArray)} method, however, only |
| * the raw data of the objects is written and not their type, so you must use the |
| * corresponding {@link #createTypedSparseArray(Parcelable.Creator)}. |
| * |
| * @param val The list of objects to be written. |
| * @param parcelableFlags The parcelable flags to use. |
| * |
| * @see #createTypedSparseArray(Parcelable.Creator) |
| * @see Parcelable |
| */ |
| public final <T extends Parcelable> void writeTypedSparseArray(@Nullable SparseArray<T> val, |
| int parcelableFlags) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| final int count = val.size(); |
| writeInt(count); |
| for (int i = 0; i < count; i++) { |
| writeInt(val.keyAt(i)); |
| writeTypedObject(val.valueAt(i), parcelableFlags); |
| } |
| } |
| |
| /** |
| * Flatten a List containing a particular object type into the parcel, at |
| * the current dataPosition() and growing dataCapacity() if needed. The |
| * type of the objects in the list must be one that implements Parcelable. |
| * Unlike the generic writeList() method, however, only the raw data of the |
| * objects is written and not their type, so you must use the corresponding |
| * readTypedList() to unmarshall them. |
| * |
| * @param val The list of objects to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| * |
| * @see #createTypedArrayList |
| * @see #readTypedList |
| * @see Parcelable |
| */ |
| public <T extends Parcelable> void writeTypedList(@Nullable List<T> val, int parcelableFlags) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeTypedObject(val.get(i), parcelableFlags); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a List containing String objects into the parcel, at |
| * the current dataPosition() and growing dataCapacity() if needed. They |
| * can later be retrieved with {@link #createStringArrayList} or |
| * {@link #readStringList}. |
| * |
| * @param val The list of strings to be written. |
| * |
| * @see #createStringArrayList |
| * @see #readStringList |
| */ |
| public final void writeStringList(@Nullable List<String> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeString(val.get(i)); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a List containing IBinder objects into the parcel, at |
| * the current dataPosition() and growing dataCapacity() if needed. They |
| * can later be retrieved with {@link #createBinderArrayList} or |
| * {@link #readBinderList}. |
| * |
| * @param val The list of strings to be written. |
| * |
| * @see #createBinderArrayList |
| * @see #readBinderList |
| */ |
| public final void writeBinderList(@Nullable List<IBinder> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeStrongBinder(val.get(i)); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a {@code List} containing T (IInterface) objects into this parcel |
| * at the current position. They can later be retrieved with |
| * {@link #createInterfaceArrayList} or {@link #readInterfaceList}. |
| * |
| * @see #createInterfaceArrayList |
| * @see #readInterfaceList |
| */ |
| public final <T extends IInterface> void writeInterfaceList(@Nullable List<T> val) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeStrongInterface(val.get(i)); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a {@code List} containing arbitrary {@code Parcelable} objects into this parcel |
| * at the current position. They can later be retrieved using |
| * {@link #readParcelableList(List, ClassLoader)} if required. |
| * |
| * @see #readParcelableList(List, ClassLoader) |
| */ |
| public final <T extends Parcelable> void writeParcelableList(@Nullable List<T> val, int flags) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| |
| int N = val.size(); |
| int i=0; |
| writeInt(N); |
| while (i < N) { |
| writeParcelable(val.get(i), flags); |
| i++; |
| } |
| } |
| |
| /** |
| * Flatten a homogeneous array containing a particular object type into |
| * the parcel, at |
| * the current dataPosition() and growing dataCapacity() if needed. The |
| * type of the objects in the array must be one that implements Parcelable. |
| * Unlike the {@link #writeParcelableArray} method, however, only the |
| * raw data of the objects is written and not their type, so you must use |
| * {@link #readTypedArray} with the correct corresponding |
| * {@link Parcelable.Creator} implementation to unmarshall them. |
| * |
| * @param val The array of objects to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| * |
| * @see #readTypedArray |
| * @see #writeParcelableArray |
| * @see Parcelable.Creator |
| */ |
| public final <T extends Parcelable> void writeTypedArray(@Nullable T[] val, |
| int parcelableFlags) { |
| if (val != null) { |
| int N = val.length; |
| writeInt(N); |
| for (int i = 0; i < N; i++) { |
| writeTypedObject(val[i], parcelableFlags); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * Flatten the Parcelable object into the parcel. |
| * |
| * @param val The Parcelable object to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| * |
| * @see #readTypedObject |
| */ |
| public final <T extends Parcelable> void writeTypedObject(@Nullable T val, |
| int parcelableFlags) { |
| if (val != null) { |
| writeInt(1); |
| val.writeToParcel(this, parcelableFlags); |
| } else { |
| writeInt(0); |
| } |
| } |
| |
| /** |
| * Flatten a homogeneous multi-dimensional array with fixed-size. This delegates to other |
| * APIs to write a one-dimensional array. Use {@link #readFixedArray(Object)} or |
| * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal. |
| * |
| * @param val The array to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| * Used only if val is an array of Parcelable objects. |
| * @param dimensions an array of int representing length of each dimension. The array should be |
| * sized with the exact size of dimensions. |
| * |
| * @see #readFixedArray |
| * @see #createFixedArray createFixedArray(Class<T>, Parcelable.Creator<S>, int...) |
| * @see #writeBooleanArray |
| * @see #writeByteArray |
| * @see #writeCharArray |
| * @see #writeIntArray |
| * @see #writeLongArray |
| * @see #writeFloatArray |
| * @see #writeDoubleArray |
| * @see #writeBinderArray |
| * @see #writeInterfaceArray |
| * @see #writeTypedArray |
| * @throws BadParcelableException If the array's component type is not supported or if its |
| * size doesn't match with the given dimensions. |
| */ |
| public <T> void writeFixedArray(@Nullable T val, int parcelableFlags, |
| @NonNull int... dimensions) { |
| if (val == null) { |
| writeInt(-1); |
| return; |
| } |
| writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions); |
| } |
| |
| private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index, |
| int[] dimensions) { |
| if (index >= dimensions.length) { |
| throw new BadParcelableException("Array has more dimensions than expected: " |
| + dimensions.length); |
| } |
| |
| int length = dimensions[index]; |
| |
| // val should be an array of length N |
| if (val == null) { |
| throw new BadParcelableException("Non-null array shouldn't have a null array."); |
| } |
| if (!val.getClass().isArray()) { |
| throw new BadParcelableException("Not an array: " + val); |
| } |
| if (Array.getLength(val) != length) { |
| throw new BadParcelableException("bad length: expected " + length + ", but got " |
| + Array.getLength(val)); |
| } |
| |
| // Delegates to other writers if this is a one-dimensional array. |
| // Otherwise, write component arrays with recursive calls. |
| |
| final Class<?> componentType = val.getClass().getComponentType(); |
| if (!componentType.isArray() && index + 1 != dimensions.length) { |
| throw new BadParcelableException("Array has fewer dimensions than expected: " |
| + dimensions.length); |
| } |
| if (componentType == boolean.class) { |
| writeBooleanArray((boolean[]) val); |
| } else if (componentType == byte.class) { |
| writeByteArray((byte[]) val); |
| } else if (componentType == char.class) { |
| writeCharArray((char[]) val); |
| } else if (componentType == int.class) { |
| writeIntArray((int[]) val); |
| } else if (componentType == long.class) { |
| writeLongArray((long[]) val); |
| } else if (componentType == float.class) { |
| writeFloatArray((float[]) val); |
| } else if (componentType == double.class) { |
| writeDoubleArray((double[]) val); |
| } else if (componentType == IBinder.class) { |
| writeBinderArray((IBinder[]) val); |
| } else if (IInterface.class.isAssignableFrom(componentType)) { |
| writeInterfaceArray((IInterface[]) val); |
| } else if (Parcelable.class.isAssignableFrom(componentType)) { |
| writeTypedArray((Parcelable[]) val, parcelableFlags); |
| } else if (componentType.isArray()) { |
| writeInt(length); |
| for (int i = 0; i < length; i++) { |
| writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1, |
| dimensions); |
| } |
| } else { |
| throw new BadParcelableException("unknown type for fixed-size array: " + componentType); |
| } |
| } |
| |
| /** |
| * Flatten a generic object in to a parcel. The given Object value may |
| * currently be one of the following types: |
| * |
| * <ul> |
| * <li> null |
| * <li> String |
| * <li> Byte |
| * <li> Short |
| * <li> Integer |
| * <li> Long |
| * <li> Float |
| * <li> Double |
| * <li> Boolean |
| * <li> String[] |
| * <li> boolean[] |
| * <li> byte[] |
| * <li> int[] |
| * <li> long[] |
| * <li> Object[] (supporting objects of the same type defined here). |
| * <li> {@link Bundle} |
| * <li> Map (as supported by {@link #writeMap}). |
| * <li> Any object that implements the {@link Parcelable} protocol. |
| * <li> Parcelable[] |
| * <li> CharSequence (as supported by {@link TextUtils#writeToParcel}). |
| * <li> List (as supported by {@link #writeList}). |
| * <li> {@link SparseArray} (as supported by {@link #writeSparseArray(SparseArray)}). |
| * <li> {@link IBinder} |
| * <li> Any object that implements Serializable (but see |
| * {@link #writeSerializable} for caveats). Note that all of the |
| * previous types have relatively efficient implementations for |
| * writing to a Parcel; having to rely on the generic serialization |
| * approach is much less efficient and should be avoided whenever |
| * possible. |
| * </ul> |
| * |
| * <p class="caution">{@link Parcelable} objects are written with |
| * {@link Parcelable#writeToParcel} using contextual flags of 0. When |
| * serializing objects containing {@link ParcelFileDescriptor}s, |
| * this may result in file descriptor leaks when they are returned from |
| * Binder calls (where {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} |
| * should be used).</p> |
| */ |
| public final void writeValue(@Nullable Object v) { |
| if (v instanceof LazyValue) { |
| LazyValue value = (LazyValue) v; |
| value.writeToParcel(this); |
| return; |
| } |
| int type = getValueType(v); |
| writeInt(type); |
| if (isLengthPrefixed(type)) { |
| // Length |
| int length = dataPosition(); |
| writeInt(-1); // Placeholder |
| // Object |
| int start = dataPosition(); |
| writeValue(type, v); |
| int end = dataPosition(); |
| // Backpatch length |
| setDataPosition(length); |
| writeInt(end - start); |
| setDataPosition(end); |
| } else { |
| writeValue(type, v); |
| } |
| } |
| |
| /** @hide */ |
| public static int getValueType(@Nullable Object v) { |
| if (v == null) { |
| return VAL_NULL; |
| } else if (v instanceof String) { |
| return VAL_STRING; |
| } else if (v instanceof Integer) { |
| return VAL_INTEGER; |
| } else if (v instanceof Map) { |
| return VAL_MAP; |
| } else if (v instanceof Bundle) { |
| // Must be before Parcelable |
| return VAL_BUNDLE; |
| } else if (v instanceof PersistableBundle) { |
| // Must be before Parcelable |
| return VAL_PERSISTABLEBUNDLE; |
| } else if (v instanceof SizeF) { |
| // Must be before Parcelable |
| return VAL_SIZEF; |
| } else if (v instanceof Parcelable) { |
| // IMPOTANT: cases for classes that implement Parcelable must |
| // come before the Parcelable case, so that their speci fic VAL_* |
| // types will be written. |
| return VAL_PARCELABLE; |
| } else if (v instanceof Short) { |
| return VAL_SHORT; |
| } else if (v instanceof Long) { |
| return VAL_LONG; |
| } else if (v instanceof Float) { |
| return VAL_FLOAT; |
| } else if (v instanceof Double) { |
| return VAL_DOUBLE; |
| } else if (v instanceof Boolean) { |
| return VAL_BOOLEAN; |
| } else if (v instanceof CharSequence) { |
| // Must be after String |
| return VAL_CHARSEQUENCE; |
| } else if (v instanceof List) { |
| return VAL_LIST; |
| } else if (v instanceof SparseArray) { |
| return VAL_SPARSEARRAY; |
| } else if (v instanceof boolean[]) { |
| return VAL_BOOLEANARRAY; |
| } else if (v instanceof byte[]) { |
| return VAL_BYTEARRAY; |
| } else if (v instanceof String[]) { |
| return VAL_STRINGARRAY; |
| } else if (v instanceof CharSequence[]) { |
| // Must be after String[] and before Object[] |
| return VAL_CHARSEQUENCEARRAY; |
| } else if (v instanceof IBinder) { |
| return VAL_IBINDER; |
| } else if (v instanceof Parcelable[]) { |
| return VAL_PARCELABLEARRAY; |
| } else if (v instanceof int[]) { |
| return VAL_INTARRAY; |
| } else if (v instanceof long[]) { |
| return VAL_LONGARRAY; |
| } else if (v instanceof Byte) { |
| return VAL_BYTE; |
| } else if (v instanceof Size) { |
| return VAL_SIZE; |
| } else if (v instanceof double[]) { |
| return VAL_DOUBLEARRAY; |
| } else if (v instanceof Character) { |
| return VAL_CHAR; |
| } else if (v instanceof short[]) { |
| return VAL_SHORTARRAY; |
| } else if (v instanceof char[]) { |
| return VAL_CHARARRAY; |
| } else if (v instanceof float[]) { |
| return VAL_FLOATARRAY; |
| } else { |
| Class<?> clazz = v.getClass(); |
| if (clazz.isArray() && clazz.getComponentType() == Object.class) { |
| // Only pure Object[] are written here, Other arrays of non-primitive types are |
| // handled by serialization as this does not record the component type. |
| return VAL_OBJECTARRAY; |
| } else if (v instanceof Serializable) { |
| // Must be last |
| return VAL_SERIALIZABLE; |
| } else { |
| throw new IllegalArgumentException("Parcel: unknown type for value " + v); |
| } |
| } |
| } |
| /** |
| * Writes value {@code v} in the parcel. This does NOT write the int representing the type |
| * first. |
| * |
| * @hide |
| */ |
| public void writeValue(int type, @Nullable Object v) { |
| switch (type) { |
| case VAL_NULL: |
| break; |
| case VAL_STRING: |
| writeString((String) v); |
| break; |
| case VAL_INTEGER: |
| writeInt((Integer) v); |
| break; |
| case VAL_MAP: |
| writeMap((Map) v); |
| break; |
| case VAL_BUNDLE: |
| writeBundle((Bundle) v); |
| break; |
| case VAL_PERSISTABLEBUNDLE: |
| writePersistableBundle((PersistableBundle) v); |
| break; |
| case VAL_PARCELABLE: |
| writeParcelable((Parcelable) v, 0); |
| break; |
| case VAL_SHORT: |
| writeInt(((Short) v).intValue()); |
| break; |
| case VAL_LONG: |
| writeLong((Long) v); |
| break; |
| case VAL_FLOAT: |
| writeFloat((Float) v); |
| break; |
| case VAL_DOUBLE: |
| writeDouble((Double) v); |
| break; |
| case VAL_BOOLEAN: |
| writeInt((Boolean) v ? 1 : 0); |
| break; |
| case VAL_CHARSEQUENCE: |
| writeCharSequence((CharSequence) v); |
| break; |
| case VAL_LIST: |
| writeList((List) v); |
| break; |
| case VAL_SPARSEARRAY: |
| writeSparseArray((SparseArray) v); |
| break; |
| case VAL_BOOLEANARRAY: |
| writeBooleanArray((boolean[]) v); |
| break; |
| case VAL_BYTEARRAY: |
| writeByteArray((byte[]) v); |
| break; |
| case VAL_STRINGARRAY: |
| writeStringArray((String[]) v); |
| break; |
| case VAL_CHARSEQUENCEARRAY: |
| writeCharSequenceArray((CharSequence[]) v); |
| break; |
| case VAL_IBINDER: |
| writeStrongBinder((IBinder) v); |
| break; |
| case VAL_PARCELABLEARRAY: |
| writeParcelableArray((Parcelable[]) v, 0); |
| break; |
| case VAL_INTARRAY: |
| writeIntArray((int[]) v); |
| break; |
| case VAL_LONGARRAY: |
| writeLongArray((long[]) v); |
| break; |
| case VAL_BYTE: |
| writeInt((Byte) v); |
| break; |
| case VAL_SIZE: |
| writeSize((Size) v); |
| break; |
| case VAL_SIZEF: |
| writeSizeF((SizeF) v); |
| break; |
| case VAL_DOUBLEARRAY: |
| writeDoubleArray((double[]) v); |
| break; |
| case VAL_CHAR: |
| writeInt((Character) v); |
| break; |
| case VAL_SHORTARRAY: |
| writeShortArray((short[]) v); |
| break; |
| case VAL_CHARARRAY: |
| writeCharArray((char[]) v); |
| break; |
| case VAL_FLOATARRAY: |
| writeFloatArray((float[]) v); |
| break; |
| case VAL_OBJECTARRAY: |
| writeArray((Object[]) v); |
| break; |
| case VAL_SERIALIZABLE: |
| writeSerializable((Serializable) v); |
| break; |
| default: |
| throw new RuntimeException("Parcel: unable to marshal value " + v); |
| } |
| } |
| |
| /** |
| * Flatten the name of the class of the Parcelable and its contents |
| * into the parcel. |
| * |
| * @param p The Parcelable object to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| */ |
| public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) { |
| if (p == null) { |
| writeString(null); |
| return; |
| } |
| writeParcelableCreator(p); |
| p.writeToParcel(this, parcelableFlags); |
| } |
| |
| /** |
| * Flatten the name of the class of the Parcelable into this Parcel. |
| * |
| * @param p The Parcelable object to be written. |
| * @see #readParcelableCreator |
| */ |
| public final void writeParcelableCreator(@NonNull Parcelable p) { |
| String name = p.getClass().getName(); |
| writeString(name); |
| } |
| |
| /** |
| * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have |
| * been seen, and what positions they were written. The value is the absolute position of |
| * each parcelable. |
| */ |
| private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables; |
| |
| private void ensureWrittenSquashableParcelables() { |
| if (mWrittenSquashableParcelables != null) { |
| return; |
| } |
| mWrittenSquashableParcelables = new ArrayMap<>(); |
| } |
| |
| private boolean mAllowSquashing = false; |
| |
| /** |
| * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to |
| * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel. |
| * |
| * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The |
| * caller must retain the return value from this method and call {@link #restoreAllowSquashing} |
| * with it. |
| * |
| * See {@link #maybeWriteSquashed(Parcelable)} for the details. |
| * |
| * @see #restoreAllowSquashing(boolean) |
| * @see #maybeWriteSquashed(Parcelable) |
| * @see #readSquashed(SquashReadHelper) |
| * |
| * @hide |
| */ |
| @TestApi |
| public boolean allowSquashing() { |
| boolean previous = mAllowSquashing; |
| mAllowSquashing = true; |
| return previous; |
| } |
| |
| /** |
| * @see #allowSquashing() |
| * @hide |
| */ |
| @TestApi |
| public void restoreAllowSquashing(boolean previous) { |
| mAllowSquashing = previous; |
| if (!mAllowSquashing) { |
| mWrittenSquashableParcelables = null; |
| } |
| } |
| |
| private void resetSqaushingState() { |
| if (mAllowSquashing) { |
| Slog.wtf(TAG, "allowSquashing wasn't restored."); |
| } |
| mWrittenSquashableParcelables = null; |
| mReadSquashableParcelables = null; |
| mAllowSquashing = false; |
| } |
| |
| /** |
| * A map used by {@link #readSquashed} to cache parcelables. It's a map from |
| * an absolute position in a Parcel to the parcelable stored at the position. |
| */ |
| private SparseArray<Parcelable> mReadSquashableParcelables; |
| |
| private void ensureReadSquashableParcelables() { |
| if (mReadSquashableParcelables != null) { |
| return; |
| } |
| mReadSquashableParcelables = new SparseArray<>(); |
| } |
| |
| /** |
| * Write a parcelable with "squash" -- that is, when the same instance is written to the |
| * same Parcelable multiple times, instead of writing the entire instance multiple times, |
| * only write it once, and in subsequent writes we'll only write the offset to the original |
| * object. |
| * |
| * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with |
| * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable |
| * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure |
| * we only do so within each "top level" Parcelable. |
| * |
| * Usage: Use this method in {@link Parcelable#writeToParcel}. |
| * If this method returns TRUE, it's a subsequent call, and the offset is already written, |
| * so the caller doesn't have to do anything. If this method returns FALSE, it's the first |
| * time for the instance to be written to this parcel. The caller has to proceed with its |
| * {@link Parcelable#writeToParcel}. |
| * |
| * (See {@code ApplicationInfo} for the example.) |
| * |
| * @param p the target Parcelable to write. |
| * |
| * @see #allowSquashing() |
| * @see #restoreAllowSquashing(boolean) |
| * @see #readSquashed(SquashReadHelper) |
| * |
| * @hide |
| */ |
| public boolean maybeWriteSquashed(@NonNull Parcelable p) { |
| if (!mAllowSquashing) { |
| // Don't squash, and don't put it in the map either. |
| writeInt(0); |
| return false; |
| } |
| ensureWrittenSquashableParcelables(); |
| final Integer firstPos = mWrittenSquashableParcelables.get(p); |
| if (firstPos != null) { |
| // Already written. |
| // Write the relative offset from the current position to the first position. |
| final int pos = dataPosition(); |
| |
| // We want the offset from the next byte of this integer, so we need to +4. |
| writeInt(pos - firstPos + 4); |
| return true; |
| } |
| // First time seen, write a marker. |
| writeInt(0); |
| |
| // Remember the position. |
| final int pos = dataPosition(); |
| mWrittenSquashableParcelables.put(p, pos); |
| |
| // Return false and let the caller actually write the content. |
| return false; |
| } |
| |
| /** |
| * Helper function that's used by {@link #readSquashed(SquashReadHelper)} |
| * @hide |
| */ |
| public interface SquashReadHelper<T> { |
| /** Read and instantiate {@code T} from a Parcel. */ |
| @NonNull |
| T readRawParceled(@NonNull Parcel p); |
| } |
| |
| /** |
| * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}. |
| * |
| * @param reader a callback function that instantiates an instance from a parcel. |
| * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed. |
| * |
| * @see #maybeWriteSquashed(Parcelable) |
| * |
| * @hide |
| */ |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) { |
| final int offset = readInt(); |
| final int pos = dataPosition(); |
| |
| if (offset == 0) { |
| // First time read. Unparcel, and remember it. |
| final T p = reader.readRawParceled(this); |
| ensureReadSquashableParcelables(); |
| mReadSquashableParcelables.put(pos, p); |
| return p; |
| } |
| // Subsequent read. |
| final int firstAbsolutePos = pos - offset; |
| |
| final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos); |
| if (p == null) { |
| final StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < mReadSquashableParcelables.size(); i++) { |
| sb.append(mReadSquashableParcelables.keyAt(i)).append(' '); |
| } |
| Slog.wtfStack(TAG, "Map doesn't contain offset " |
| + firstAbsolutePos |
| + " : contains=" + sb.toString()); |
| } |
| return (T) p; |
| } |
| |
| /** |
| * Write a generic serializable object in to a Parcel. It is strongly |
| * recommended that this method be avoided, since the serialization |
| * overhead is extremely large, and this approach will be much slower than |
| * using the other approaches to writing data in to a Parcel. |
| */ |
| public final void writeSerializable(@Nullable Serializable s) { |
| if (s == null) { |
| writeString(null); |
| return; |
| } |
| String name = s.getClass().getName(); |
| writeString(name); |
| |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| try { |
| ObjectOutputStream oos = new ObjectOutputStream(baos); |
| oos.writeObject(s); |
| oos.close(); |
| |
| writeByteArray(baos.toByteArray()); |
| } catch (IOException ioe) { |
| throw new BadParcelableException("Parcelable encountered " |
| + "IOException writing serializable object (name = " |
| + name + ")", ioe); |
| } |
| } |
| |
| /** @hide For debugging purposes */ |
| public static void setStackTraceParceling(boolean enabled) { |
| sParcelExceptionStackTrace = enabled; |
| } |
| |
| /** |
| * Special function for writing an exception result at the header of |
| * a parcel, to be used when returning an exception from a transaction. |
| * Note that this currently only supports a few exception types; any other |
| * exception will be re-thrown by this function as a RuntimeException |
| * (to be caught by the system's last-resort exception handling when |
| * dispatching a transaction). |
| * |
| * <p>The supported exception types are: |
| * <ul> |
| * <li>{@link BadParcelableException} |
| * <li>{@link IllegalArgumentException} |
| * <li>{@link IllegalStateException} |
| * <li>{@link NullPointerException} |
| * <li>{@link SecurityException} |
| * <li>{@link UnsupportedOperationException} |
| * <li>{@link NetworkOnMainThreadException} |
| * </ul> |
| * |
| * @param e The Exception to be written. |
| * |
| * @see #writeNoException |
| * @see #readException |
| */ |
| @RavenwoodReplace |
| public final void writeException(@NonNull Exception e) { |
| AppOpsManager.prefixParcelWithAppOpsIfNeeded(this); |
| |
| int code = getExceptionCode(e); |
| writeInt(code); |
| StrictMode.clearGatheredViolations(); |
| if (code == 0) { |
| if (e instanceof RuntimeException) { |
| throw (RuntimeException) e; |
| } |
| throw new RuntimeException(e); |
| } |
| writeString(e.getMessage()); |
| final long timeNow = sParcelExceptionStackTrace ? SystemClock.elapsedRealtime() : 0; |
| if (sParcelExceptionStackTrace && (timeNow - sLastWriteExceptionStackTrace |
| > WRITE_EXCEPTION_STACK_TRACE_THRESHOLD_MS)) { |
| sLastWriteExceptionStackTrace = timeNow; |
| writeStackTrace(e); |
| } else { |
| writeInt(0); |
| } |
| switch (code) { |
| case EX_SERVICE_SPECIFIC: |
| writeInt(((ServiceSpecificException) e).errorCode); |
| break; |
| case EX_PARCELABLE: |
| // Write parceled exception prefixed by length |
| final int sizePosition = dataPosition(); |
| writeInt(0); |
| writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); |
| final int payloadPosition = dataPosition(); |
| setDataPosition(sizePosition); |
| writeInt(payloadPosition - sizePosition); |
| setDataPosition(payloadPosition); |
| break; |
| } |
| } |
| |
| /** @hide */ |
| public final void writeException$ravenwood(@NonNull Exception e) { |
| // Ravenwood doesn't support IPC, no transaction headers needed |
| writeInt(getExceptionCode(e)); |
| writeString(e.getMessage()); |
| writeInt(0); |
| } |
| |
| /** @hide */ |
| public static int getExceptionCode(@NonNull Throwable e) { |
| int code = 0; |
| if (e instanceof Parcelable |
| && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) { |
| // We only send Parcelable exceptions that are in the |
| // BootClassLoader to ensure that the receiver can unpack them |
| code = EX_PARCELABLE; |
| } else if (e instanceof SecurityException) { |
| code = EX_SECURITY; |
| } else if (e instanceof BadParcelableException) { |
| code = EX_BAD_PARCELABLE; |
| } else if (e instanceof IllegalArgumentException) { |
| code = EX_ILLEGAL_ARGUMENT; |
| } else if (e instanceof NullPointerException) { |
| code = EX_NULL_POINTER; |
| } else if (e instanceof IllegalStateException) { |
| code = EX_ILLEGAL_STATE; |
| } else if (e instanceof NetworkOnMainThreadException) { |
| code = EX_NETWORK_MAIN_THREAD; |
| } else if (e instanceof UnsupportedOperationException) { |
| code = EX_UNSUPPORTED_OPERATION; |
| } else if (e instanceof ServiceSpecificException) { |
| code = EX_SERVICE_SPECIFIC; |
| } |
| return code; |
| } |
| |
| /** @hide */ |
| public void writeStackTrace(@NonNull Throwable e) { |
| final int sizePosition = dataPosition(); |
| writeInt(0); // Header size will be filled in later |
| StackTraceElement[] stackTrace = e.getStackTrace(); |
| final int truncatedSize = Math.min(stackTrace.length, 5); |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < truncatedSize; i++) { |
| sb.append("\tat ").append(stackTrace[i]).append('\n'); |
| } |
| writeString(sb.toString()); |
| final int payloadPosition = dataPosition(); |
| setDataPosition(sizePosition); |
| // Write stack trace header size. Used in native side to skip the header |
| writeInt(payloadPosition - sizePosition); |
| setDataPosition(payloadPosition); |
| } |
| |
| /** |
| * Special function for writing information at the front of the Parcel |
| * indicating that no exception occurred. |
| * |
| * @see #writeException |
| * @see #readException |
| */ |
| @RavenwoodReplace |
| public final void writeNoException() { |
| AppOpsManager.prefixParcelWithAppOpsIfNeeded(this); |
| |
| // Despite the name of this function ("write no exception"), |
| // it should instead be thought of as "write the RPC response |
| // header", but because this function name is written out by |
| // the AIDL compiler, we're not going to rename it. |
| // |
| // The response header, in the non-exception case (see also |
| // writeException above, also called by the AIDL compiler), is |
| // either a 0 (the default case), or EX_HAS_STRICTMODE_REPLY_HEADER if |
| // StrictMode has gathered up violations that have occurred |
| // during a Binder call, in which case we write out the number |
| // of violations and their details, serialized, before the |
| // actual RPC respons data. The receiving end of this is |
| // readException(), below. |
| if (StrictMode.hasGatheredViolations()) { |
| writeInt(EX_HAS_STRICTMODE_REPLY_HEADER); |
| final int sizePosition = dataPosition(); |
| writeInt(0); // total size of fat header, to be filled in later |
| StrictMode.writeGatheredViolationsToParcel(this); |
| final int payloadPosition = dataPosition(); |
| setDataPosition(sizePosition); |
| writeInt(payloadPosition - sizePosition); // header size |
| setDataPosition(payloadPosition); |
| } else { |
| writeInt(0); |
| } |
| } |
| |
| /** @hide */ |
| public final void writeNoException$ravenwood() { |
| // Ravenwood doesn't support IPC, no transaction headers needed |
| writeInt(0); |
| } |
| |
| /** |
| * Special function for reading an exception result from the header of |
| * a parcel, to be used after receiving the result of a transaction. This |
| * will throw the exception for you if it had been written to the Parcel, |
| * otherwise return and let you read the normal result data from the Parcel. |
| * |
| * @see #writeException |
| * @see #writeNoException |
| */ |
| public final void readException() { |
| int code = readExceptionCode(); |
| if (code != 0) { |
| String msg = readString(); |
| readException(code, msg); |
| } |
| } |
| |
| /** |
| * Parses the header of a Binder call's response Parcel and |
| * returns the exception code. Deals with lite or fat headers. |
| * In the common successful case, this header is generally zero. |
| * In less common cases, it's a small negative number and will be |
| * followed by an error string. |
| * |
| * This exists purely for android.database.DatabaseUtils and |
| * insulating it from having to handle fat headers as returned by |
| * e.g. StrictMode-induced RPC responses. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| @TestApi |
| public final int readExceptionCode() { |
| int code = readInt(); |
| if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) { |
| AppOpsManager.readAndLogNotedAppops(this); |
| // Read next header or real exception if there is no more header |
| code = readInt(); |
| } |
| |
| if (code == EX_HAS_STRICTMODE_REPLY_HEADER) { |
| int headerSize = readInt(); |
| if (headerSize == 0) { |
| Log.e(TAG, "Unexpected zero-sized Parcel reply header."); |
| } else { |
| // Currently the only thing in the header is StrictMode stacks, |
| // but discussions around event/RPC tracing suggest we might |
| // put that here too. If so, switch on sub-header tags here. |
| // But for now, just parse out the StrictMode stuff. |
| StrictMode.readAndHandleBinderCallViolations(this); |
| } |
| // And fat response headers are currently only used when |
| // there are no exceptions, so return no error: |
| return 0; |
| } |
| return code; |
| } |
| |
| /** |
| * Throw an exception with the given message. Not intended for use |
| * outside the Parcel class. |
| * |
| * @param code Used to determine which exception class to throw. |
| * @param msg The exception message. |
| */ |
| public final void readException(int code, String msg) { |
| String remoteStackTrace = null; |
| final int remoteStackPayloadSize = readInt(); |
| if (remoteStackPayloadSize > 0) { |
| remoteStackTrace = readString(); |
| } |
| Exception e = createException(code, msg); |
| // Attach remote stack trace if availalble |
| if (remoteStackTrace != null) { |
| RemoteException cause = new RemoteException( |
| "Remote stack trace:\n" + remoteStackTrace, null, false, false); |
| ExceptionUtils.appendCause(e, cause); |
| } |
| SneakyThrow.sneakyThrow(e); |
| } |
| |
| /** |
| * Creates an exception with the given message. |
| * |
| * @param code Used to determine which exception class to throw. |
| * @param msg The exception message. |
| */ |
| private Exception createException(int code, String msg) { |
| Exception exception = createExceptionOrNull(code, msg); |
| return exception != null |
| ? exception |
| : new RuntimeException("Unknown exception code: " + code + " msg " + msg); |
| } |
| |
| /** @hide */ |
| public Exception createExceptionOrNull(int code, String msg) { |
| switch (code) { |
| case EX_PARCELABLE: |
| if (readInt() > 0) { |
| return (Exception) readParcelable(Parcelable.class.getClassLoader(), java.lang.Exception.class); |
| } else { |
| return new RuntimeException(msg + " [missing Parcelable]"); |
| } |
| case EX_SECURITY: |
| return new SecurityException(msg); |
| case EX_BAD_PARCELABLE: |
| return new BadParcelableException(msg); |
| case EX_ILLEGAL_ARGUMENT: |
| return new IllegalArgumentException(msg); |
| case EX_NULL_POINTER: |
| return new NullPointerException(msg); |
| case EX_ILLEGAL_STATE: |
| return new IllegalStateException(msg); |
| case EX_NETWORK_MAIN_THREAD: |
| return new NetworkOnMainThreadException(); |
| case EX_UNSUPPORTED_OPERATION: |
| return new UnsupportedOperationException(msg); |
| case EX_SERVICE_SPECIFIC: |
| return new ServiceSpecificException(readInt(), msg); |
| default: |
| return null; |
| } |
| } |
| |
| /** |
| * Read an integer value from the parcel at the current dataPosition(). |
| */ |
| public final int readInt() { |
| return nativeReadInt(mNativePtr); |
| } |
| |
| /** |
| * Read a long integer value from the parcel at the current dataPosition(). |
| */ |
| public final long readLong() { |
| return nativeReadLong(mNativePtr); |
| } |
| |
| /** |
| * Read a floating point value from the parcel at the current |
| * dataPosition(). |
| */ |
| public final float readFloat() { |
| return nativeReadFloat(mNativePtr); |
| } |
| |
| /** |
| * Read a double precision floating point value from the parcel at the |
| * current dataPosition(). |
| */ |
| public final double readDouble() { |
| return nativeReadDouble(mNativePtr); |
| } |
| |
| /** |
| * Read a string value from the parcel at the current dataPosition(). |
| */ |
| @Nullable |
| public final String readString() { |
| return readString16(); |
| } |
| |
| /** {@hide} */ |
| public final @Nullable String readString8() { |
| return mReadWriteHelper.readString8(this); |
| } |
| |
| /** {@hide} */ |
| public final @Nullable String readString16() { |
| return mReadWriteHelper.readString16(this); |
| } |
| |
| /** |
| * Read a string without going though a {@link ReadWriteHelper}. Subclasses of |
| * {@link ReadWriteHelper} must use this method instead of {@link #readString} to avoid |
| * infinity recursive calls. |
| * |
| * @hide |
| */ |
| public @Nullable String readStringNoHelper() { |
| return readString16NoHelper(); |
| } |
| |
| /** {@hide} */ |
| public @Nullable String readString8NoHelper() { |
| return nativeReadString8(mNativePtr); |
| } |
| |
| /** {@hide} */ |
| public @Nullable String readString16NoHelper() { |
| return nativeReadString16(mNativePtr); |
| } |
| |
| /** |
| * Read a boolean value from the parcel at the current dataPosition(). |
| */ |
| public final boolean readBoolean() { |
| return readInt() != 0; |
| } |
| |
| /** |
| * Read a CharSequence value from the parcel at the current dataPosition(). |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| @Nullable |
| public final CharSequence readCharSequence() { |
| return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this); |
| } |
| |
| /** |
| * Read an object from the parcel at the current dataPosition(). |
| */ |
| public final IBinder readStrongBinder() { |
| final IBinder result = nativeReadStrongBinder(mNativePtr); |
| |
| // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking |
| // from the object that returned it. |
| if (result != null && hasFlags( |
| FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT | FLAG_PROPAGATE_ALLOW_BLOCKING)) { |
| Binder.allowBlocking(result); |
| } |
| return result; |
| } |
| |
| /** |
| * Read a FileDescriptor from the parcel at the current dataPosition(). |
| */ |
| public final ParcelFileDescriptor readFileDescriptor() { |
| FileDescriptor fd = nativeReadFileDescriptor(mNativePtr); |
| return fd != null ? new ParcelFileDescriptor(fd) : null; |
| } |
| |
| /** {@hide} */ |
| @UnsupportedAppUsage |
| public final FileDescriptor readRawFileDescriptor() { |
| return nativeReadFileDescriptor(mNativePtr); |
| } |
| |
| /** |
| * {@hide} |
| * Read and return a new array of FileDescriptors from the parcel. |
| * @return the FileDescriptor array, or null if the array is null. |
| **/ |
| @Nullable |
| public final FileDescriptor[] createRawFileDescriptorArray() { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| FileDescriptor[] f = new FileDescriptor[N]; |
| for (int i = 0; i < N; i++) { |
| f[i] = readRawFileDescriptor(); |
| } |
| return f; |
| } |
| |
| /** |
| * {@hide} |
| * Read an array of FileDescriptors from a parcel. |
| * The passed array must be exactly the length of the array in the parcel. |
| * @return the FileDescriptor array, or null if the array is null. |
| **/ |
| public final void readRawFileDescriptorArray(FileDescriptor[] val) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readRawFileDescriptor(); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** |
| * Read a byte value from the parcel at the current dataPosition(). |
| */ |
| public final byte readByte() { |
| return (byte)(readInt() & 0xff); |
| } |
| |
| /** |
| * Please use {@link #readBundle(ClassLoader)} instead (whose data must have |
| * been written with {@link #writeBundle}. Read into an existing Map object |
| * from the parcel at the current dataPosition(). |
| * |
| * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this |
| * method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader, |
| * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. |
| */ |
| @Deprecated |
| public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) { |
| readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null); |
| } |
| |
| /** |
| * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and |
| * {@code clazzValue} parameter as the types required for each key and value pair. |
| * |
| * @throws BadParcelableException If the item to be deserialized is not an instance of that |
| * class or any of its children class |
| */ |
| public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal, |
| @Nullable ClassLoader loader, @NonNull Class<K> clazzKey, |
| @NonNull Class<V> clazzValue) { |
| Objects.requireNonNull(clazzKey); |
| Objects.requireNonNull(clazzValue); |
| readMapInternal(outVal, loader, clazzKey, clazzValue); |
| } |
| |
| /** |
| * Read into an existing List object from the parcel at the current |
| * dataPosition(), using the given class loader to load any enclosed |
| * Parcelables. If it is null, the default class loader is used. |
| * |
| * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting |
| * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to |
| * use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items' |
| * class is final) since this is also more performant. Note that changing to the latter |
| * also requires changing the writes. |
| */ |
| @Deprecated |
| public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) { |
| int N = readInt(); |
| readListInternal(outVal, N, loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readList(List, ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| public <T> void readList(@NonNull List<? super T> outVal, |
| @Nullable ClassLoader loader, @NonNull Class<T> clazz) { |
| Objects.requireNonNull(clazz); |
| int n = readInt(); |
| readListInternal(outVal, n, loader, clazz); |
| } |
| |
| /** |
| * Please use {@link #readBundle(ClassLoader)} instead (whose data must have |
| * been written with {@link #writeBundle}. Read and return a new HashMap |
| * object from the parcel at the current dataPosition(), using the given |
| * class loader to load any enclosed Parcelables. Returns null if |
| * the previously written map object was null. |
| * |
| * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this |
| * method is still preferred use the type-safer version {@link #readHashMap(ClassLoader, |
| * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. |
| */ |
| @Deprecated |
| @Nullable |
| public HashMap readHashMap(@Nullable ClassLoader loader) { |
| return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null); |
| } |
| |
| /** |
| * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and |
| * {@code clazzValue} parameter as the types required for each key and value pair. |
| * |
| * @throws BadParcelableException if the item to be deserialized is not an instance of that |
| * class or any of its children class |
| */ |
| @SuppressLint({"ConcreteCollection", "NullableCollection"}) |
| @Nullable |
| public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader, |
| @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) { |
| Objects.requireNonNull(clazzKey); |
| Objects.requireNonNull(clazzValue); |
| return readHashMapInternal(loader, clazzKey, clazzValue); |
| } |
| |
| /** |
| * Read and return a new Bundle object from the parcel at the current |
| * dataPosition(). Returns null if the previously written Bundle object was |
| * null. |
| */ |
| @Nullable |
| public final Bundle readBundle() { |
| return readBundle(null); |
| } |
| |
| /** |
| * Read and return a new Bundle object from the parcel at the current |
| * dataPosition(), using the given class loader to initialize the class |
| * loader of the Bundle for later retrieval of Parcelable objects. |
| * Returns null if the previously written Bundle object was null. |
| */ |
| @Nullable |
| public final Bundle readBundle(@Nullable ClassLoader loader) { |
| int length = readInt(); |
| if (length < 0) { |
| if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length); |
| return null; |
| } |
| |
| final Bundle bundle = new Bundle(this, length); |
| if (loader != null) { |
| bundle.setClassLoader(loader); |
| } |
| return bundle; |
| } |
| |
| /** |
| * Read and return a new Bundle object from the parcel at the current |
| * dataPosition(). Returns null if the previously written Bundle object was |
| * null. |
| */ |
| @Nullable |
| public final PersistableBundle readPersistableBundle() { |
| return readPersistableBundle(null); |
| } |
| |
| /** |
| * Read and return a new Bundle object from the parcel at the current |
| * dataPosition(), using the given class loader to initialize the class |
| * loader of the Bundle for later retrieval of Parcelable objects. |
| * Returns null if the previously written Bundle object was null. |
| */ |
| @Nullable |
| public final PersistableBundle readPersistableBundle(@Nullable ClassLoader loader) { |
| int length = readInt(); |
| if (length < 0) { |
| if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length); |
| return null; |
| } |
| |
| final PersistableBundle bundle = new PersistableBundle(this, length); |
| if (loader != null) { |
| bundle.setClassLoader(loader); |
| } |
| return bundle; |
| } |
| |
| /** |
| * Read a Size from the parcel at the current dataPosition(). |
| */ |
| @NonNull |
| public final Size readSize() { |
| final int width = readInt(); |
| final int height = readInt(); |
| return new Size(width, height); |
| } |
| |
| /** |
| * Read a SizeF from the parcel at the current dataPosition(). |
| */ |
| @NonNull |
| public final SizeF readSizeF() { |
| final float width = readFloat(); |
| final float height = readFloat(); |
| return new SizeF(width, height); |
| } |
| |
| /** |
| * Read and return a byte[] object from the parcel. |
| */ |
| @Nullable |
| public final byte[] createByteArray() { |
| return nativeCreateByteArray(mNativePtr); |
| } |
| |
| /** |
| * Read a byte[] object from the parcel and copy it into the |
| * given byte array. |
| */ |
| public final void readByteArray(@NonNull byte[] val) { |
| boolean valid = nativeReadByteArray(mNativePtr, val, (val != null) ? val.length : 0); |
| if (!valid) { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** |
| * Read a blob of data from the parcel and return it as a byte array. |
| * @see #writeBlob(byte[], int, int) |
| */ |
| @Nullable |
| public final byte[] readBlob() { |
| return nativeReadBlob(mNativePtr); |
| } |
| |
| /** |
| * Read and return a String[] object from the parcel. |
| * {@hide} |
| */ |
| @UnsupportedAppUsage |
| @Nullable |
| public final String[] readStringArray() { |
| return createString16Array(); |
| } |
| |
| /** |
| * Read and return a CharSequence[] object from the parcel. |
| * {@hide} |
| */ |
| @Nullable |
| public final CharSequence[] readCharSequenceArray() { |
| CharSequence[] array = null; |
| |
| int length = readInt(); |
| if (length >= 0) |
| { |
| array = new CharSequence[length]; |
| |
| for (int i = 0 ; i < length ; i++) |
| { |
| array[i] = readCharSequence(); |
| } |
| } |
| |
| return array; |
| } |
| |
| /** |
| * Read and return an ArrayList<CharSequence> object from the parcel. |
| * {@hide} |
| */ |
| @Nullable |
| public final ArrayList<CharSequence> readCharSequenceList() { |
| ArrayList<CharSequence> array = null; |
| |
| int length = readInt(); |
| if (length >= 0) { |
| array = new ArrayList<CharSequence>(length); |
| |
| for (int i = 0 ; i < length ; i++) { |
| array.add(readCharSequence()); |
| } |
| } |
| |
| return array; |
| } |
| |
| /** |
| * Read and return a new ArrayList object from the parcel at the current |
| * dataPosition(). Returns null if the previously written list object was |
| * null. The given class loader will be used to load any enclosed |
| * Parcelables. |
| * |
| * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting |
| * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to |
| * use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items' |
| * class is final) since this is also more performant. Note that changing to the latter |
| * also requires changing the writes. |
| */ |
| @Deprecated |
| @Nullable |
| public ArrayList readArrayList(@Nullable ClassLoader loader) { |
| return readArrayListInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readArrayList(ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @SuppressLint({"ConcreteCollection", "NullableCollection"}) |
| @Nullable |
| public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader, |
| @NonNull Class<? extends T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readArrayListInternal(loader, clazz); |
| } |
| |
| /** |
| * Read and return a new Object array from the parcel at the current |
| * dataPosition(). Returns null if the previously written array was |
| * null. The given class loader will be used to load any enclosed |
| * Parcelables. |
| * |
| * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from |
| * Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use |
| * {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is |
| * final) since this is also more performant. Note that changing to the latter also |
| * requires changing the writes. |
| */ |
| @Deprecated |
| @Nullable |
| public Object[] readArray(@Nullable ClassLoader loader) { |
| return readArrayInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readArray(ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @SuppressLint({"ArrayReturn", "NullableCollection"}) |
| @Nullable |
| public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readArrayInternal(loader, clazz); |
| } |
| |
| /** |
| * Read and return a new SparseArray object from the parcel at the current |
| * dataPosition(). Returns null if the previously written list object was |
| * null. The given class loader will be used to load any enclosed |
| * Parcelables. |
| * |
| * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting |
| * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to |
| * use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items' |
| * class is final) since this is also more performant. Note that changing to the latter |
| * also requires changing the writes. |
| */ |
| @Deprecated |
| @Nullable |
| public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) { |
| return readSparseArrayInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readSparseArray(ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @Nullable |
| public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader, |
| @NonNull Class<? extends T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readSparseArrayInternal(loader, clazz); |
| } |
| |
| /** |
| * Read and return a new SparseBooleanArray object from the parcel at the current |
| * dataPosition(). Returns null if the previously written list object was |
| * null. |
| */ |
| @Nullable |
| public final SparseBooleanArray readSparseBooleanArray() { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| SparseBooleanArray sa = new SparseBooleanArray(N); |
| readSparseBooleanArrayInternal(sa, N); |
| return sa; |
| } |
| |
| /** |
| * Read and return a new SparseIntArray object from the parcel at the current |
| * dataPosition(). Returns null if the previously written array object was null. |
| * @hide |
| */ |
| @Nullable |
| public final SparseIntArray readSparseIntArray() { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| SparseIntArray sa = new SparseIntArray(N); |
| readSparseIntArrayInternal(sa, N); |
| return sa; |
| } |
| |
| /** |
| * Read and return a new ArrayList containing a particular object type from |
| * the parcel that was written with {@link #writeTypedList} at the |
| * current dataPosition(). Returns null if the |
| * previously written list object was null. The list <em>must</em> have |
| * previously been written via {@link #writeTypedList} with the same object |
| * type. |
| * |
| * @return A newly created ArrayList containing objects with the same data |
| * as those that were previously written. |
| * |
| * @see #writeTypedList |
| */ |
| @Nullable |
| public final <T> ArrayList<T> createTypedArrayList(@NonNull Parcelable.Creator<T> c) { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| ArrayList<T> l = new ArrayList<T>(N); |
| while (N > 0) { |
| l.add(readTypedObject(c)); |
| N--; |
| } |
| return l; |
| } |
| |
| /** |
| * Read into the given List items containing a particular object type |
| * that were written with {@link #writeTypedList} at the |
| * current dataPosition(). The list <em>must</em> have |
| * previously been written via {@link #writeTypedList} with the same object |
| * type. |
| * |
| * @see #writeTypedList |
| */ |
| public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) { |
| int M = list.size(); |
| int N = readInt(); |
| int i = 0; |
| for (; i < M && i < N; i++) { |
| list.set(i, readTypedObject(c)); |
| } |
| for (; i<N; i++) { |
| list.add(readTypedObject(c)); |
| } |
| for (; i<M; i++) { |
| list.remove(N); |
| } |
| } |
| |
| /** |
| * Read into a new {@link SparseArray} items containing a particular object type |
| * that were written with {@link #writeTypedSparseArray(SparseArray, int)} at the |
| * current dataPosition(). The list <em>must</em> have previously been written |
| * via {@link #writeTypedSparseArray(SparseArray, int)} with the same object type. |
| * |
| * @param creator The creator to use when for instantiation. |
| * |
| * @return A newly created {@link SparseArray} containing objects with the same data |
| * as those that were previously written. |
| * |
| * @see #writeTypedSparseArray(SparseArray, int) |
| */ |
| public final @Nullable <T extends Parcelable> SparseArray<T> createTypedSparseArray( |
| @NonNull Parcelable.Creator<T> creator) { |
| final int count = readInt(); |
| if (count < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count); |
| final SparseArray<T> array = new SparseArray<>(count); |
| for (int i = 0; i < count; i++) { |
| final int index = readInt(); |
| final T value = readTypedObject(creator); |
| array.append(index, value); |
| } |
| return array; |
| } |
| |
| /** |
| * Read into a new {@link ArrayMap} with string keys items containing a particular |
| * object type that were written with {@link #writeTypedArrayMap(ArrayMap, int)} at the |
| * current dataPosition(). The list <em>must</em> have previously been written |
| * via {@link #writeTypedArrayMap(ArrayMap, int)} with the same object type. |
| * |
| * @param creator The creator to use when for instantiation. |
| * |
| * @return A newly created {@link ArrayMap} containing objects with the same data |
| * as those that were previously written. |
| * |
| * @see #writeTypedArrayMap(ArrayMap, int) |
| */ |
| public final @Nullable <T extends Parcelable> ArrayMap<String, T> createTypedArrayMap( |
| @NonNull Parcelable.Creator<T> creator) { |
| final int count = readInt(); |
| if (count < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, count); |
| final ArrayMap<String, T> map = new ArrayMap<>(count); |
| for (int i = 0; i < count; i++) { |
| final String key = readString(); |
| final T value = readTypedObject(creator); |
| map.append(key, value); |
| } |
| return map; |
| } |
| |
| /** |
| * Read and return a new ArrayList containing String objects from |
| * the parcel that was written with {@link #writeStringList} at the |
| * current dataPosition(). Returns null if the |
| * previously written list object was null. |
| * |
| * @return A newly created ArrayList containing strings with the same data |
| * as those that were previously written. |
| * |
| * @see #writeStringList |
| */ |
| @Nullable |
| public final ArrayList<String> createStringArrayList() { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| ArrayList<String> l = new ArrayList<String>(N); |
| while (N > 0) { |
| l.add(readString()); |
| N--; |
| } |
| return l; |
| } |
| |
| /** |
| * Read and return a new ArrayList containing IBinder objects from |
| * the parcel that was written with {@link #writeBinderList} at the |
| * current dataPosition(). Returns null if the |
| * previously written list object was null. |
| * |
| * @return A newly created ArrayList containing strings with the same data |
| * as those that were previously written. |
| * |
| * @see #writeBinderList |
| */ |
| @Nullable |
| public final ArrayList<IBinder> createBinderArrayList() { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| ArrayList<IBinder> l = new ArrayList<IBinder>(N); |
| while (N > 0) { |
| l.add(readStrongBinder()); |
| N--; |
| } |
| return l; |
| } |
| |
| /** |
| * Read and return a new ArrayList containing T (IInterface) objects from |
| * the parcel that was written with {@link #writeInterfaceList} at the |
| * current dataPosition(). Returns null if the |
| * previously written list object was null. |
| * |
| * @return A newly created ArrayList containing T (IInterface) |
| * |
| * @see #writeInterfaceList |
| */ |
| @SuppressLint({"ConcreteCollection", "NullableCollection"}) |
| @Nullable |
| public final <T extends IInterface> ArrayList<T> createInterfaceArrayList( |
| @NonNull Function<IBinder, T> asInterface) { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| ArrayList<T> l = new ArrayList<T>(N); |
| while (N > 0) { |
| l.add(asInterface.apply(readStrongBinder())); |
| N--; |
| } |
| return l; |
| } |
| |
| /** |
| * Read into the given List items String objects that were written with |
| * {@link #writeStringList} at the current dataPosition(). |
| * |
| * @see #writeStringList |
| */ |
| public final void readStringList(@NonNull List<String> list) { |
| int M = list.size(); |
| int N = readInt(); |
| int i = 0; |
| for (; i < M && i < N; i++) { |
| list.set(i, readString()); |
| } |
| for (; i<N; i++) { |
| list.add(readString()); |
| } |
| for (; i<M; i++) { |
| list.remove(N); |
| } |
| } |
| |
| /** |
| * Read into the given List items IBinder objects that were written with |
| * {@link #writeBinderList} at the current dataPosition(). |
| * |
| * @see #writeBinderList |
| */ |
| public final void readBinderList(@NonNull List<IBinder> list) { |
| int M = list.size(); |
| int N = readInt(); |
| int i = 0; |
| for (; i < M && i < N; i++) { |
| list.set(i, readStrongBinder()); |
| } |
| for (; i<N; i++) { |
| list.add(readStrongBinder()); |
| } |
| for (; i<M; i++) { |
| list.remove(N); |
| } |
| } |
| |
| /** |
| * Read into the given List items IInterface objects that were written with |
| * {@link #writeInterfaceList} at the current dataPosition(). |
| * |
| * @see #writeInterfaceList |
| */ |
| public final <T extends IInterface> void readInterfaceList(@NonNull List<T> list, |
| @NonNull Function<IBinder, T> asInterface) { |
| int M = list.size(); |
| int N = readInt(); |
| int i = 0; |
| for (; i < M && i < N; i++) { |
| list.set(i, asInterface.apply(readStrongBinder())); |
| } |
| for (; i<N; i++) { |
| list.add(asInterface.apply(readStrongBinder())); |
| } |
| for (; i<M; i++) { |
| list.remove(N); |
| } |
| } |
| |
| /** |
| * Read the list of {@code Parcelable} objects at the current data position into the |
| * given {@code list}. The contents of the {@code list} are replaced. If the serialized |
| * list was {@code null}, {@code list} is cleared. |
| * |
| * @see #writeParcelableList(List, int) |
| * |
| * @deprecated Use the type-safer version {@link #readParcelableList(List, ClassLoader, Class)} |
| * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the |
| * format to use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the |
| * items' class is final) since this is also more performant. Note that changing to the |
| * latter also requires changing the writes. |
| */ |
| @Deprecated |
| @NonNull |
| public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list, |
| @Nullable ClassLoader cl) { |
| return readParcelableListInternal(list, cl, /*clazz*/ null); |
| } |
| |
| /** |
| * Same as {@link #readParcelableList(List, ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readParcelableList(List, ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @NonNull |
| public <T> List<T> readParcelableList(@NonNull List<T> list, |
| @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) { |
| Objects.requireNonNull(list); |
| Objects.requireNonNull(clazz); |
| return readParcelableListInternal(list, cl, clazz); |
| } |
| |
| /** |
| * @param clazz The type of the object expected or {@code null} for performing no checks. |
| */ |
| @NonNull |
| private <T> List<T> readParcelableListInternal(@NonNull List<T> list, |
| @Nullable ClassLoader cl, @Nullable Class<? extends T> clazz) { |
| final int n = readInt(); |
| if (n == -1) { |
| list.clear(); |
| return list; |
| } |
| |
| final int m = list.size(); |
| int i = 0; |
| for (; i < m && i < n; i++) { |
| list.set(i, (T) readParcelableInternal(cl, clazz)); |
| } |
| for (; i < n; i++) { |
| list.add((T) readParcelableInternal(cl, clazz)); |
| } |
| for (; i < m; i++) { |
| list.remove(n); |
| } |
| return list; |
| } |
| |
| /** |
| * Read and return a new array containing a particular object type from |
| * the parcel at the current dataPosition(). Returns null if the |
| * previously written array was null. The array <em>must</em> have |
| * previously been written via {@link #writeTypedArray} with the same |
| * object type. |
| * |
| * @return A newly created array containing objects with the same data |
| * as those that were previously written. |
| * |
| * @see #writeTypedArray |
| */ |
| @Nullable |
| public final <T> T[] createTypedArray(@NonNull Parcelable.Creator<T> c) { |
| int N = readInt(); |
| if (N < 0) { |
| return null; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N); |
| T[] l = c.newArray(N); |
| for (int i=0; i<N; i++) { |
| l[i] = readTypedObject(c); |
| } |
| return l; |
| } |
| |
| public final <T> void readTypedArray(@NonNull T[] val, @NonNull Parcelable.Creator<T> c) { |
| int N = readInt(); |
| if (N == val.length) { |
| for (int i=0; i<N; i++) { |
| val[i] = readTypedObject(c); |
| } |
| } else { |
| throw new RuntimeException("bad array lengths"); |
| } |
| } |
| |
| /** |
| * @deprecated |
| * @hide |
| */ |
| @Deprecated |
| public final <T> T[] readTypedArray(Parcelable.Creator<T> c) { |
| return createTypedArray(c); |
| } |
| |
| /** |
| * Read and return a typed Parcelable object from a parcel. |
| * Returns null if the previous written object was null. |
| * The object <em>must</em> have previous been written via |
| * {@link #writeTypedObject} with the same object type. |
| * |
| * @return A newly created object of the type that was previously |
| * written. |
| * |
| * @see #writeTypedObject |
| */ |
| @Nullable |
| public final <T> T readTypedObject(@NonNull Parcelable.Creator<T> c) { |
| if (readInt() != 0) { |
| return c.createFromParcel(this); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Read a new multi-dimensional array from a parcel. If you want to read Parcelable or |
| * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or |
| * {@link #readFixedArray(Object, Function)}. |
| * @param val the destination array to hold the read values. |
| * |
| * @see #writeTypedArray |
| * @see #readBooleanArray |
| * @see #readByteArray |
| * @see #readCharArray |
| * @see #readIntArray |
| * @see #readLongArray |
| * @see #readFloatArray |
| * @see #readDoubleArray |
| * @see #readBinderArray |
| * @see #readInterfaceArray |
| * @see #readTypedArray |
| */ |
| public <T> void readFixedArray(@NonNull T val) { |
| Class<?> componentType = val.getClass().getComponentType(); |
| if (componentType == boolean.class) { |
| readBooleanArray((boolean[]) val); |
| } else if (componentType == byte.class) { |
| readByteArray((byte[]) val); |
| } else if (componentType == char.class) { |
| readCharArray((char[]) val); |
| } else if (componentType == int.class) { |
| readIntArray((int[]) val); |
| } else if (componentType == long.class) { |
| readLongArray((long[]) val); |
| } else if (componentType == float.class) { |
| readFloatArray((float[]) val); |
| } else if (componentType == double.class) { |
| readDoubleArray((double[]) val); |
| } else if (componentType == IBinder.class) { |
| readBinderArray((IBinder[]) val); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length != Array.getLength(val)) { |
| throw new BadParcelableException("Bad length: expected " + Array.getLength(val) |
| + ", but got " + length); |
| } |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i)); |
| } |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| } |
| |
| /** |
| * Read a new multi-dimensional array of typed interfaces from a parcel. |
| * If you want to read Parcelable values, use |
| * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use |
| * {@link #readFixedArray(Object)}. |
| * @param val the destination array to hold the read values. |
| */ |
| public <T, S extends IInterface> void readFixedArray(@NonNull T val, |
| @NonNull Function<IBinder, S> asInterface) { |
| Class<?> componentType = val.getClass().getComponentType(); |
| if (IInterface.class.isAssignableFrom(componentType)) { |
| readInterfaceArray((S[]) val, asInterface); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length != Array.getLength(val)) { |
| throw new BadParcelableException("Bad length: expected " + Array.getLength(val) |
| + ", but got " + length); |
| } |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i), asInterface); |
| } |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| } |
| |
| /** |
| * Read a new multi-dimensional array of typed parcelables from a parcel. |
| * If you want to read IInterface values, use |
| * {@link #readFixedArray(Object, Function)}. For values of other types, use |
| * {@link #readFixedArray(Object)}. |
| * @param val the destination array to hold the read values. |
| */ |
| public <T, S extends Parcelable> void readFixedArray(@NonNull T val, |
| @NonNull Parcelable.Creator<S> c) { |
| Class<?> componentType = val.getClass().getComponentType(); |
| if (Parcelable.class.isAssignableFrom(componentType)) { |
| readTypedArray((S[]) val, c); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length != Array.getLength(val)) { |
| throw new BadParcelableException("Bad length: expected " + Array.getLength(val) |
| + ", but got " + length); |
| } |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i), c); |
| } |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| } |
| |
| private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) { |
| if (numDimension <= 0) { |
| throw new BadParcelableException("Fixed-size array should have dimensions."); |
| } |
| |
| for (int i = 0; i < numDimension; i++) { |
| if (!cls.isArray()) { |
| throw new BadParcelableException("Array has fewer dimensions than expected: " |
| + numDimension); |
| } |
| cls = cls.getComponentType(); |
| } |
| if (cls.isArray()) { |
| throw new BadParcelableException("Array has more dimensions than expected: " |
| + numDimension); |
| } |
| } |
| |
| /** |
| * Read and return a new multi-dimensional array from a parcel. Returns null if the |
| * previously written array object is null. If you want to read Parcelable or |
| * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or |
| * {@link #createFixedArray(Class, Function, int[])}. |
| * @param cls the Class object for the target array type. (e.g. int[][].class) |
| * @param dimensions an array of int representing length of each dimension. |
| * |
| * @see #writeTypedArray |
| * @see #createBooleanArray |
| * @see #createByteArray |
| * @see #createCharArray |
| * @see #createIntArray |
| * @see #createLongArray |
| * @see #createFloatArray |
| * @see #createDoubleArray |
| * @see #createBinderArray |
| * @see #createInterfaceArray |
| * @see #createTypedArray |
| */ |
| @Nullable |
| public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) { |
| // Check if type matches with dimensions |
| // If type is one-dimensional array, delegate to other creators |
| // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray |
| |
| ensureClassHasExpectedDimensions(cls, dimensions.length); |
| |
| T val = null; |
| final Class<?> componentType = cls.getComponentType(); |
| if (componentType == boolean.class) { |
| val = (T) createBooleanArray(); |
| } else if (componentType == byte.class) { |
| val = (T) createByteArray(); |
| } else if (componentType == char.class) { |
| val = (T) createCharArray(); |
| } else if (componentType == int.class) { |
| val = (T) createIntArray(); |
| } else if (componentType == long.class) { |
| val = (T) createLongArray(); |
| } else if (componentType == float.class) { |
| val = (T) createFloatArray(); |
| } else if (componentType == double.class) { |
| val = (T) createDoubleArray(); |
| } else if (componentType == IBinder.class) { |
| val = (T) createBinderArray(); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length < 0) { |
| return null; |
| } |
| if (length != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] |
| + ", but got " + length); |
| } |
| |
| // Create a multi-dimensional array with an innermost component type and dimensions |
| Class<?> innermost = componentType.getComponentType(); |
| while (innermost.isArray()) { |
| innermost = innermost.getComponentType(); |
| } |
| |
| int typeSize = getItemTypeSize(innermost); |
| ensureWithinMemoryLimit(typeSize, dimensions); |
| |
| val = (T) Array.newInstance(innermost, dimensions); |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i)); |
| } |
| return val; |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| |
| // Check if val is null (which is OK) or has the expected size. |
| // This check doesn't have to be multi-dimensional because multi-dimensional arrays |
| // are created with expected dimensions. |
| if (val != null && Array.getLength(val) != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " |
| + Array.getLength(val)); |
| } |
| return val; |
| } |
| |
| /** |
| * Read and return a new multi-dimensional array of typed interfaces from a parcel. |
| * Returns null if the previously written array object is null. If you want to read |
| * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}. |
| * For values of other types use {@link #createFixedArray(Class, int[])}. |
| * @param cls the Class object for the target array type. (e.g. IFoo[][].class) |
| * @param dimensions an array of int representing length of each dimension. |
| */ |
| @Nullable |
| public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls, |
| @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) { |
| // Check if type matches with dimensions |
| // If type is one-dimensional array, delegate to other creators |
| // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray |
| |
| ensureClassHasExpectedDimensions(cls, dimensions.length); |
| |
| T val = null; |
| final Class<?> componentType = cls.getComponentType(); |
| if (IInterface.class.isAssignableFrom(componentType)) { |
| val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n), |
| asInterface); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length < 0) { |
| return null; |
| } |
| if (length != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] |
| + ", but got " + length); |
| } |
| |
| // Create a multi-dimensional array with an innermost component type and dimensions |
| Class<?> innermost = componentType.getComponentType(); |
| while (innermost.isArray()) { |
| innermost = innermost.getComponentType(); |
| } |
| |
| int typeSize = getItemTypeSize(innermost); |
| ensureWithinMemoryLimit(typeSize, dimensions); |
| |
| val = (T) Array.newInstance(innermost, dimensions); |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i), asInterface); |
| } |
| return val; |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| |
| // Check if val is null (which is OK) or has the expected size. |
| // This check doesn't have to be multi-dimensional because multi-dimensional arrays |
| // are created with expected dimensions. |
| if (val != null && Array.getLength(val) != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " |
| + Array.getLength(val)); |
| } |
| return val; |
| } |
| |
| /** |
| * Read and return a new multi-dimensional array of typed parcelables from a parcel. |
| * Returns null if the previously written array object is null. If you want to read |
| * IInterface values, use {@link #createFixedArray(Class, Function, int[])}. |
| * For values of other types use {@link #createFixedArray(Class, int[])}. |
| * @param cls the Class object for the target array type. (e.g. Foo[][].class) |
| * @param dimensions an array of int representing length of each dimension. |
| */ |
| @Nullable |
| public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls, |
| @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) { |
| // Check if type matches with dimensions |
| // If type is one-dimensional array, delegate to other creators |
| // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray |
| |
| ensureClassHasExpectedDimensions(cls, dimensions.length); |
| |
| T val = null; |
| final Class<?> componentType = cls.getComponentType(); |
| if (Parcelable.class.isAssignableFrom(componentType)) { |
| val = (T) createTypedArray(c); |
| } else if (componentType.isArray()) { |
| int length = readInt(); |
| if (length < 0) { |
| return null; |
| } |
| if (length != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] |
| + ", but got " + length); |
| } |
| |
| // Create a multi-dimensional array with an innermost component type and dimensions |
| Class<?> innermost = componentType.getComponentType(); |
| while (innermost.isArray()) { |
| innermost = innermost.getComponentType(); |
| } |
| |
| int typeSize = getItemTypeSize(innermost); |
| ensureWithinMemoryLimit(typeSize, dimensions); |
| |
| val = (T) Array.newInstance(innermost, dimensions); |
| for (int i = 0; i < length; i++) { |
| readFixedArray(Array.get(val, i), c); |
| } |
| return val; |
| } else { |
| throw new BadParcelableException("Unknown type for fixed-size array: " + componentType); |
| } |
| |
| // Check if val is null (which is OK) or has the expected size. |
| // This check doesn't have to be multi-dimensional because multi-dimensional arrays |
| // are created with expected dimensions. |
| if (val != null && Array.getLength(val) != dimensions[0]) { |
| throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got " |
| + Array.getLength(val)); |
| } |
| return val; |
| } |
| |
| /** |
| * Write a heterogeneous array of Parcelable objects into the Parcel. |
| * Each object in the array is written along with its class name, so |
| * that the correct class can later be instantiated. As a result, this |
| * has significantly more overhead than {@link #writeTypedArray}, but will |
| * correctly handle an array containing more than one type of object. |
| * |
| * @param value The array of objects to be written. |
| * @param parcelableFlags Contextual flags as per |
| * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}. |
| * |
| * @see #writeTypedArray |
| */ |
| public final <T extends Parcelable> void writeParcelableArray(@Nullable T[] value, |
| int parcelableFlags) { |
| if (value != null) { |
| int N = value.length; |
| writeInt(N); |
| for (int i=0; i<N; i++) { |
| writeParcelable(value[i], parcelableFlags); |
| } |
| } else { |
| writeInt(-1); |
| } |
| } |
| |
| /** |
| * Read a typed object from a parcel. The given class loader will be |
| * used to load any enclosed Parcelables. If it is null, the default class |
| * loader will be used. |
| */ |
| @Nullable |
| public final Object readValue(@Nullable ClassLoader loader) { |
| return readValue(loader, /* clazz */ null); |
| } |
| |
| |
| /** |
| * @see #readValue(int, ClassLoader, Class, Class[]) |
| */ |
| @Nullable |
| 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, itemTypes); |
| int actual = dataPosition() - start; |
| if (actual != length) { |
| Slog.wtfStack(TAG, |
| "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type) |
| + " consumed " + actual + " bytes, but " + length + " expected."); |
| } |
| } else { |
| object = readValue(type, loader, clazz, itemTypes); |
| } |
| return object; |
| } |
| |
| /** |
| * 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 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 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. |
| * <li>They have the same payload length. |
| * <li>Their binary content is the same. |
| * </ul> |
| * </ul> |
| * |
| * @hide |
| */ |
| @Nullable |
| public Object readLazyValue(@Nullable ClassLoader loader) { |
| int start = dataPosition(); |
| int type = readInt(); |
| if (isLengthPrefixed(type)) { |
| int objectLength = readInt(); |
| if (objectLength < 0) { |
| return null; |
| } |
| int end = MathUtils.addOrThrow(dataPosition(), objectLength); |
| int valueLength = end - start; |
| setDataPosition(end); |
| return new LazyValue(this, start, valueLength, type, loader); |
| } else { |
| return readValue(type, loader, /* clazz */ null); |
| } |
| } |
| |
| |
| private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> { |
| /** |
| * | 4B | 4B | |
| * mSource = Parcel{... | type | length | object | ...} |
| * a b c d |
| * length = d - c |
| * mPosition = a |
| * mLength = d - a |
| */ |
| private final int mPosition; |
| private final int mLength; |
| private final int mType; |
| @Nullable private final ClassLoader mLoader; |
| @Nullable private Object mObject; |
| |
| /** |
| * This goes from non-null to null once. Always check the nullability of this object before |
| * performing any operations, either involving itself or mObject since the happens-before |
| * established by this volatile will guarantee visibility of either. We can assume this |
| * parcel won't change anymore. |
| */ |
| @Nullable private volatile Parcel mSource; |
| |
| LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) { |
| mSource = requireNonNull(source); |
| mPosition = position; |
| mLength = length; |
| mType = type; |
| mLoader = loader; |
| } |
| |
| @Override |
| public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) { |
| Parcel source = mSource; |
| if (source != null) { |
| synchronized (source) { |
| // Check mSource != null guarantees callers won't ever see different objects. |
| if (mSource != null) { |
| int restore = source.dataPosition(); |
| try { |
| source.setDataPosition(mPosition); |
| mObject = source.readValue(mLoader, clazz, itemTypes); |
| } finally { |
| source.setDataPosition(restore); |
| } |
| mSource = null; |
| } |
| } |
| } |
| return mObject; |
| } |
| |
| public void writeToParcel(Parcel out) { |
| Parcel source = mSource; |
| if (source != null) { |
| synchronized (source) { |
| if (mSource != null) { |
| out.appendFrom(source, mPosition, mLength); |
| return; |
| } |
| } |
| } |
| |
| out.writeValue(mObject); |
| } |
| |
| public boolean hasFileDescriptors() { |
| Parcel source = mSource; |
| if (source != null) { |
| synchronized (source) { |
| if (mSource != null) { |
| return source.hasFileDescriptors(mPosition, mLength); |
| } |
| } |
| } |
| |
| return Parcel.hasFileDescriptors(mObject); |
| } |
| |
| @Override |
| public String toString() { |
| return (mSource != null) |
| ? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}' |
| : "Supplier{" + mObject + "}"; |
| } |
| |
| /** |
| * We're checking if the *lazy value* is equal to another one, not if the *object* |
| * represented by the lazy value is equal to the other one. So, if there are two lazy values |
| * and one of them has been deserialized but the other hasn't this will always return false. |
| */ |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } |
| if (!(other instanceof LazyValue)) { |
| return false; |
| } |
| LazyValue value = (LazyValue) other; |
| // Check if they are either both serialized or both deserialized. |
| Parcel source = mSource; |
| Parcel otherSource = value.mSource; |
| if ((source == null) != (otherSource == null)) { |
| return false; |
| } |
| // If both are deserialized, compare the live objects. |
| if (source == null) { |
| // Note that here it's guaranteed that both mObject references contain valid values |
| // (possibly null) since mSource will have provided the memory barrier for those and |
| // once deserialized we never go back to serialized state. |
| return Objects.equals(mObject, value.mObject); |
| } |
| // Better safely fail here since this could mean we get different objects. |
| if (!Objects.equals(mLoader, value.mLoader)) { |
| return false; |
| } |
| // Otherwise compare metadata prior to comparing payload. |
| if (mType != value.mType || mLength != value.mLength) { |
| return false; |
| } |
| // Finally we compare the payload. |
| return Parcel.compareData(source, mPosition, otherSource, value.mPosition, mLength); |
| } |
| |
| @Override |
| public int hashCode() { |
| // Accessing mSource first to provide memory barrier for mObject |
| return Objects.hash(mSource == null, mObject, mLoader, mType, mLength); |
| } |
| } |
| |
| /** 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, |
| @Nullable Class<?>... itemTypes) { |
| final Object object; |
| switch (type) { |
| case VAL_NULL: |
| object = null; |
| break; |
| |
| case VAL_STRING: |
| object = readString(); |
| break; |
| |
| case VAL_INTEGER: |
| object = readInt(); |
| break; |
| |
| case VAL_MAP: |
| 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: |
| object = readParcelableInternal(loader, clazz); |
| break; |
| |
| case VAL_SHORT: |
| object = (short) readInt(); |
| break; |
| |
| case VAL_LONG: |
| object = readLong(); |
| break; |
| |
| case VAL_FLOAT: |
| object = readFloat(); |
| break; |
| |
| case VAL_DOUBLE: |
| object = readDouble(); |
| break; |
| |
| case VAL_BOOLEAN: |
| object = readInt() == 1; |
| break; |
| |
| case VAL_CHARSEQUENCE: |
| object = readCharSequence(); |
| break; |
| |
| case VAL_LIST: { |
| checkTypeToUnparcel(clazz, ArrayList.class); |
| Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); |
| object = readArrayListInternal(loader, itemType); |
| break; |
| } |
| case VAL_BOOLEANARRAY: |
| object = createBooleanArray(); |
| break; |
| |
| case VAL_BYTEARRAY: |
| object = createByteArray(); |
| break; |
| |
| case VAL_STRINGARRAY: |
| object = readStringArray(); |
| break; |
| |
| case VAL_CHARSEQUENCEARRAY: |
| object = readCharSequenceArray(); |
| break; |
| |
| case VAL_IBINDER: |
| object = readStrongBinder(); |
| break; |
| |
| 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; |
| |
| case VAL_LONGARRAY: |
| object = createLongArray(); |
| break; |
| |
| case VAL_BYTE: |
| object = readByte(); |
| break; |
| |
| case VAL_SERIALIZABLE: |
| object = readSerializableInternal(loader, clazz); |
| break; |
| |
| case VAL_PARCELABLEARRAY: { |
| Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); |
| checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class); |
| object = readParcelableArrayInternal(loader, itemType); |
| break; |
| } |
| case VAL_SPARSEARRAY: { |
| checkTypeToUnparcel(clazz, SparseArray.class); |
| Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0); |
| object = readSparseArrayInternal(loader, itemType); |
| break; |
| } |
| case VAL_SPARSEBOOLEANARRAY: |
| object = readSparseBooleanArray(); |
| break; |
| |
| case VAL_BUNDLE: |
| object = readBundle(loader); // loading will be deferred |
| break; |
| |
| case VAL_PERSISTABLEBUNDLE: |
| object = readPersistableBundle(loader); |
| break; |
| |
| case VAL_SIZE: |
| object = readSize(); |
| break; |
| |
| case VAL_SIZEF: |
| object = readSizeF(); |
| break; |
| |
| case VAL_DOUBLEARRAY: |
| object = createDoubleArray(); |
| break; |
| |
| case VAL_CHAR: |
| object = (char) readInt(); |
| break; |
| |
| case VAL_SHORTARRAY: |
| object = createShortArray(); |
| break; |
| |
| case VAL_CHARARRAY: |
| object = createCharArray(); |
| break; |
| |
| case VAL_FLOATARRAY: |
| object = createFloatArray(); |
| break; |
| |
| default: |
| int off = dataPosition() - 4; |
| throw new BadParcelableException( |
| "Parcel " + this + ": Unmarshalling unknown type code " + type |
| + " at offset " + off); |
| } |
| if (object != null && clazz != null && !clazz.isInstance(object)) { |
| throw new BadTypeParcelableException("Unparcelled object " + object |
| + " is not an instance of required class " + clazz.getName() |
| + " provided in the parameter"); |
| } |
| return (T) object; |
| } |
| |
| private boolean isLengthPrefixed(int type) { |
| // In general, we want custom types and containers of custom types to be length-prefixed, |
| // this allows clients (eg. Bundle) to skip their content during deserialization. The |
| // exception to this is Bundle, since Bundle is already length-prefixed and already copies |
| // the correspondent section of the parcel internally. |
| switch (type) { |
| case VAL_MAP: |
| case VAL_PARCELABLE: |
| case VAL_LIST: |
| case VAL_SPARSEARRAY: |
| case VAL_PARCELABLEARRAY: |
| case VAL_OBJECTARRAY: |
| case VAL_SERIALIZABLE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| /** |
| * 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 |
| * class loader will be used. |
| * @param loader A ClassLoader from which to instantiate the Parcelable |
| * object, or null for the default class loader. |
| * @return Returns the newly created Parcelable, or null if a null |
| * object has been written. |
| * @throws BadParcelableException Throws BadParcelableException if there |
| * was an error trying to instantiate the Parcelable. |
| * |
| * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting |
| * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to |
| * use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also |
| * more performant. Note that changing to the latter also requires changing the writes. |
| */ |
| @Deprecated |
| @Nullable |
| public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) { |
| return readParcelableInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type |
| * required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readParcelable(ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @Nullable |
| public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readParcelableInternal(loader, clazz); |
| } |
| |
| /** |
| * @param clazz The type of the parcelable expected or {@code null} for performing no checks. |
| */ |
| @SuppressWarnings("unchecked") |
| @Nullable |
| private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) { |
| Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz); |
| if (creator == null) { |
| return null; |
| } |
| if (creator instanceof Parcelable.ClassLoaderCreator<?>) { |
| Parcelable.ClassLoaderCreator<?> classLoaderCreator = |
| (Parcelable.ClassLoaderCreator<?>) creator; |
| return (T) classLoaderCreator.createFromParcel(this, loader); |
| } |
| return (T) creator.createFromParcel(this); |
| } |
| |
| /** @hide */ |
| @UnsupportedAppUsage |
| @SuppressWarnings("unchecked") |
| @Nullable |
| public final <T extends Parcelable> T readCreator(@NonNull Parcelable.Creator<?> creator, |
| @Nullable ClassLoader loader) { |
| if (creator instanceof Parcelable.ClassLoaderCreator<?>) { |
| Parcelable.ClassLoaderCreator<?> classLoaderCreator = |
| (Parcelable.ClassLoaderCreator<?>) creator; |
| return (T) classLoaderCreator.createFromParcel(this, loader); |
| } |
| return (T) creator.createFromParcel(this); |
| } |
| |
| /** |
| * Read and return a Parcelable.Creator from the parcel. The given class loader will be used to |
| * load the {@link Parcelable.Creator}. If it is null, the default class loader will be used. |
| * |
| * @param loader A ClassLoader from which to instantiate the {@link Parcelable.Creator} |
| * object, or null for the default class loader. |
| * @return the previously written {@link Parcelable.Creator}, or null if a null Creator was |
| * written. |
| * @throws BadParcelableException Throws BadParcelableException if there was an error trying to |
| * read the {@link Parcelable.Creator}. |
| * |
| * @see #writeParcelableCreator |
| * |
| * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)} |
| * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. |
| */ |
| @Deprecated |
| @Nullable |
| public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) { |
| return readParcelableCreatorInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter |
| * as the required type. |
| * |
| * <p><b>Warning: </b> 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 #readParcelableCreator(ClassLoader) instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there there was an error |
| * trying to read the {@link Parcelable.Creator}. |
| */ |
| @Nullable |
| public <T> Parcelable.Creator<T> readParcelableCreator( |
| @Nullable ClassLoader loader, @NonNull Class<T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readParcelableCreatorInternal(loader, clazz); |
| } |
| |
| /** |
| * @param clazz The type of the parcelable expected or {@code null} for performing no checks. |
| */ |
| @SuppressWarnings("unchecked") |
| @Nullable |
| private <T> Parcelable.Creator<T> readParcelableCreatorInternal( |
| @Nullable ClassLoader loader, @Nullable Class<T> clazz) { |
| String name = readString(); |
| if (name == null) { |
| return null; |
| } |
| |
| Pair<Parcelable.Creator<?>, Class<?>> creatorAndParcelableClass; |
| synchronized (sPairedCreators) { |
| HashMap<String, Pair<Parcelable.Creator<?>, Class<?>>> map = |
| sPairedCreators.get(loader); |
| if (map == null) { |
| sPairedCreators.put(loader, new HashMap<>()); |
| mCreators.put(loader, new HashMap<>()); |
| creatorAndParcelableClass = null; |
| } else { |
| creatorAndParcelableClass = map.get(name); |
| } |
| } |
| |
| if (creatorAndParcelableClass != null) { |
| Parcelable.Creator<?> creator = creatorAndParcelableClass.first; |
| Class<?> parcelableClass = creatorAndParcelableClass.second; |
| if (clazz != null) { |
| if (!clazz.isAssignableFrom(parcelableClass)) { |
| throw new BadTypeParcelableException("Parcelable creator " + name + " is not " |
| + "a subclass of required class " + clazz.getName() |
| + " provided in the parameter"); |
| } |
| } |
| |
| return (Parcelable.Creator<T>) creator; |
| } |
| |
| Parcelable.Creator<?> creator; |
| Class<?> parcelableClass; |
| try { |
| // If loader == null, explicitly emulate Class.forName(String) "caller |
| // classloader" behavior. |
| ClassLoader parcelableClassLoader = |
| (loader == null ? getClass().getClassLoader() : loader); |
| // Avoid initializing the Parcelable class until we know it implements |
| // Parcelable and has the necessary CREATOR field. http://b/1171613. |
| parcelableClass = Class.forName(name, false /* initialize */, |
| parcelableClassLoader); |
| if (!Parcelable.class.isAssignableFrom(parcelableClass)) { |
| throw new BadParcelableException("Parcelable protocol requires subclassing " |
| + "from Parcelable on class " + name); |
| } |
| if (clazz != null) { |
| if (!clazz.isAssignableFrom(parcelableClass)) { |
| throw new BadTypeParcelableException("Parcelable creator " + name + " is not " |
| + "a subclass of required class " + clazz.getName() |
| + " provided in the parameter"); |
| } |
| } |
| |
| Field f = parcelableClass.getField("CREATOR"); |
| if ((f.getModifiers() & Modifier.STATIC) == 0) { |
| throw new BadParcelableException("Parcelable protocol requires " |
| + "the CREATOR object to be static on class " + name); |
| } |
| Class<?> creatorType = f.getType(); |
| if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) { |
| // Fail before calling Field.get(), not after, to avoid initializing |
| // parcelableClass unnecessarily. |
| throw new BadParcelableException("Parcelable protocol requires a " |
| + "Parcelable.Creator object called " |
| + "CREATOR on class " + name); |
| } |
| creator = (Parcelable.Creator<?>) f.get(null); |
| } catch (IllegalAccessException e) { |
| Log.e(TAG, "Illegal access when unmarshalling: " + name, e); |
| throw new BadParcelableException( |
| "IllegalAccessException when unmarshalling: " + name, e); |
| } catch (ClassNotFoundException e) { |
| Log.e(TAG, "Class not found when unmarshalling: " + name, e); |
| throw new BadParcelableException( |
| "ClassNotFoundException when unmarshalling: " + name, e); |
| } catch (NoSuchFieldException e) { |
| throw new BadParcelableException("Parcelable protocol requires a " |
| + "Parcelable.Creator object called " |
| + "CREATOR on class " + name, e); |
| } |
| if (creator == null) { |
| throw new BadParcelableException("Parcelable protocol requires a " |
| + "non-null Parcelable.Creator object called " |
| + "CREATOR on class " + name); |
| } |
| |
| synchronized (sPairedCreators) { |
| sPairedCreators.get(loader).put(name, Pair.create(creator, parcelableClass)); |
| mCreators.get(loader).put(name, creator); |
| } |
| |
| return (Parcelable.Creator<T>) creator; |
| } |
| |
| /** |
| * Read and return a new Parcelable array from the parcel. |
| * The given class loader will be used to load any enclosed |
| * Parcelables. |
| * @return the Parcelable array, or null if the array is null |
| * |
| * @deprecated Use the type-safer version {@link #readParcelableArray(ClassLoader, Class)} |
| * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the |
| * format to use {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the |
| * items' class is final) since this is also more performant. Note that changing to the |
| * latter also requires changing the writes. |
| */ |
| @Deprecated |
| @Nullable |
| public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) { |
| return readParcelableArrayInternal(loader, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readParcelableArray(ClassLoader)} but accepts {@code clazz} parameter as |
| * the type required for each item. |
| * |
| * <p><b>Warning: </b> 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 #readParcelableArray(ClassLoader)} instead. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children classes or there was an error |
| * trying to instantiate an element. |
| */ |
| @SuppressLint({"ArrayReturn", "NullableCollection"}) |
| @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; |
| } |
| ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, 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); |
| } |
| return p; |
| } |
| |
| /** |
| * Read and return a new Serializable object from the parcel. |
| * @return the Serializable object, or null if the Serializable name |
| * wasn't found in the parcel. |
| * |
| * Unlike {@link #readSerializable(ClassLoader, Class)}, it uses the nearest valid class loader |
| * up the execution stack to instantiate the Serializable object. |
| * |
| * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting |
| * from Android {@link Build.VERSION_CODES#TIRAMISU}. |
| */ |
| @Deprecated |
| @Nullable |
| public Serializable readSerializable() { |
| return readSerializableInternal(/* loader */ null, /* clazz */ null); |
| } |
| |
| /** |
| * Same as {@link #readSerializable()} but accepts {@code loader} and {@code clazz} parameters. |
| * |
| * @param loader A ClassLoader from which to instantiate the Serializable object, |
| * or null for the default class loader. |
| * @param clazz The type of the object expected. |
| * |
| * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized |
| * is not an instance of that class or any of its children class or there there was an error |
| * deserializing the object. |
| */ |
| @Nullable |
| public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) { |
| Objects.requireNonNull(clazz); |
| return readSerializableInternal( |
| loader == null ? getClass().getClassLoader() : loader, clazz); |
| } |
| |
| /** |
| * @param clazz The type of the serializable expected or {@code null} for performing no checks |
| */ |
| @Nullable |
| private <T> T readSerializableInternal(@Nullable final ClassLoader loader, |
| @Nullable Class<T> clazz) { |
| String name = readString(); |
| if (name == null) { |
| // For some reason we were unable to read the name of the Serializable (either there |
| // is nothing left in the Parcel to read, or the next value wasn't a String), so |
| // return null, which indicates that the name wasn't found in the parcel. |
| return null; |
| } |
| |
| try { |
| if (clazz != null && loader != null) { |
| // If custom classloader is provided, resolve the type of serializable using the |
| // name, then check the type before deserialization. As in this case we can resolve |
| // the class the same way as ObjectInputStream, using the provided classloader. |
| Class<?> cl = Class.forName(name, false, loader); |
| if (!clazz.isAssignableFrom(cl)) { |
| throw new BadTypeParcelableException("Serializable object " |
| + cl.getName() + " is not a subclass of required class " |
| + clazz.getName() + " provided in the parameter"); |
| } |
| } |
| byte[] serializedData = createByteArray(); |
| ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); |
| ObjectInputStream ois = new ObjectInputStream(bais) { |
| @Override |
| protected Class<?> resolveClass(ObjectStreamClass osClass) |
| throws IOException, ClassNotFoundException { |
| // try the custom classloader if provided |
| if (loader != null) { |
| Class<?> c = Class.forName(osClass.getName(), false, loader); |
| return Objects.requireNonNull(c); |
| } |
| return super.resolveClass(osClass); |
| } |
| }; |
| T object = (T) ois.readObject(); |
| if (clazz != null && loader == null) { |
| // If custom classloader is not provided, check the type of the serializable using |
| // the deserialized object, as we cannot resolve the class the same way as |
| // ObjectInputStream. |
| if (!clazz.isAssignableFrom(object.getClass())) { |
| throw new BadTypeParcelableException("Serializable object " |
| + object.getClass().getName() + " is not a subclass of required class " |
| + clazz.getName() + " provided in the parameter"); |
| } |
| } |
| return object; |
| } catch (IOException ioe) { |
| throw new BadParcelableException("Parcelable encountered " |
| + "IOException reading a Serializable object (name = " |
| + name + ")", ioe); |
| } catch (ClassNotFoundException cnfe) { |
| throw new BadParcelableException("Parcelable encountered " |
| + "ClassNotFoundException reading a Serializable object (name = " |
| + name + ")", cnfe); |
| } |
| } |
| |
| |
| // Left due to the UnsupportedAppUsage. Do not use anymore - use sPairedCreators instead |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) |
| private static final HashMap<ClassLoader, HashMap<String, Parcelable.Creator<?>>> |
| mCreators = new HashMap<>(); |
| |
| // Cache of previously looked up CREATOR.createFromParcel() methods for particular classes. |
| // Keys are the names of the classes, values are a pair consisting of a parcelable creator, |
| // and the class of the parcelable type for the object. |
| private static final HashMap<ClassLoader, HashMap<String, |
| Pair<Parcelable.Creator<?>, Class<?>>>> sPairedCreators = new HashMap<>(); |
| |
| /** @hide for internal use only. */ |
| static protected final Parcel obtain(int obj) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** @hide */ |
| static protected final Parcel obtain(long obj) { |
| Parcel res = null; |
| synchronized (sPoolSync) { |
| if (sHolderPool != null) { |
| res = sHolderPool; |
| sHolderPool = res.mPoolNext; |
| res.mPoolNext = null; |
| sHolderPoolSize--; |
| } |
| } |
| |
| // When no cache found above, create from scratch; otherwise prepare the |
| // cached object to be used |
| if (res == null) { |
| res = new Parcel(obj); |
| } else { |
| res.mRecycled = false; |
| if (DEBUG_RECYCLE) { |
| res.mStack = new RuntimeException(); |
| } |
| res.init(obj); |
| } |
| return res; |
| } |
| |
| private Parcel(long nativePtr) { |
| if (DEBUG_RECYCLE) { |
| mStack = new RuntimeException(); |
| } |
| //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack); |
| init(nativePtr); |
| } |
| |
| private void init(long nativePtr) { |
| if (nativePtr != 0) { |
| mNativePtr = nativePtr; |
| mOwnsNativeParcelObject = false; |
| } else { |
| mNativePtr = nativeCreate(); |
| mOwnsNativeParcelObject = true; |
| } |
| } |
| |
| private void freeBuffer() { |
| mFlags = 0; |
| resetSqaushingState(); |
| if (mOwnsNativeParcelObject) { |
| nativeFreeBuffer(mNativePtr); |
| } |
| mReadWriteHelper = ReadWriteHelper.DEFAULT; |
| } |
| |
| private void destroy() { |
| resetSqaushingState(); |
| if (mNativePtr != 0) { |
| if (mOwnsNativeParcelObject) { |
| nativeDestroy(mNativePtr); |
| } |
| mNativePtr = 0; |
| } |
| } |
| |
| @Override |
| protected void finalize() throws Throwable { |
| if (DEBUG_RECYCLE) { |
| // we could always have this log on, but it's spammy |
| if (!mRecycled) { |
| Log.w(TAG, "Client did not call Parcel.recycle()", mStack); |
| } |
| } |
| destroy(); |
| } |
| |
| /** |
| * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep |
| * the old API for compatibility usages. |
| */ |
| /* package */ void readMapInternal(@NonNull Map outVal, int n, |
| @Nullable ClassLoader loader) { |
| readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null); |
| } |
| |
| @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) { |
| K key = readValue(loader, clazzKey); |
| V value = readValue(loader, clazzValue); |
| outVal.put(key, value); |
| n--; |
| } |
| } |
| |
| private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal, |
| int size, @Nullable ClassLoader loader) { |
| readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader); |
| } |
| |
| /** |
| * Reads a map into {@code map}. |
| * |
| * @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path. |
| * @param lazy Whether to populate the map with lazy {@link Function} objects for |
| * length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more |
| * details. |
| * @return a count of the lazy values in the map |
| * @hide |
| */ |
| int readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted, |
| boolean lazy, @Nullable ClassLoader loader) { |
| int lazyValues = 0; |
| while (size > 0) { |
| String key = readString(); |
| Object value = (lazy) ? readLazyValue(loader) : readValue(loader); |
| if (value instanceof LazyValue) { |
| lazyValues++; |
| } |
| if (sorted) { |
| map.append(key, value); |
| } else { |
| map.put(key, value); |
| } |
| size--; |
| } |
| if (sorted) { |
| map.validate(); |
| } |
| return lazyValues; |
| } |
| |
| /** |
| * @hide For testing only. |
| */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal, |
| @Nullable ClassLoader loader) { |
| final int N = readInt(); |
| if (N < 0) { |
| return; |
| } |
| readArrayMapInternal(outVal, N, loader); |
| } |
| |
| /** |
| * Reads an array set. |
| * |
| * @param loader The class loader to use. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public @Nullable ArraySet<? extends Object> readArraySet(@Nullable ClassLoader loader) { |
| final int size = readInt(); |
| if (size < 0) { |
| return null; |
| } |
| ArraySet<Object> result = new ArraySet<>(size); |
| for (int i = 0; i < size; i++) { |
| Object value = readValue(loader); |
| result.append(value); |
| } |
| return result; |
| } |
| |
| /** |
| * The method is replaced by {@link #readListInternal(List, int, ClassLoader, Class)}, however |
| * we are keeping this unused method here to allow unsupported app usages. |
| */ |
| private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) { |
| readListInternal(outVal, n, loader, /* clazz */ null); |
| } |
| |
| /** |
| * @param clazz The type of the object expected or {@code null} for performing no checks. |
| */ |
| private <T> void readListInternal(@NonNull List<? super T> outVal, int n, |
| @Nullable ClassLoader loader, @Nullable Class<T> clazz) { |
| while (n > 0) { |
| T value = readValue(loader, clazz); |
| //Log.d(TAG, "Unmarshalling value=" + value); |
| outVal.add(value); |
| n--; |
| } |
| } |
| |
| /** |
| * @param clazz The type of the object expected or {@code null} for performing no checks. |
| */ |
| @SuppressLint({"ConcreteCollection", "NullableCollection"}) |
| @Nullable |
| private <T> ArrayList<T> readArrayListInternal(@Nullable ClassLoader loader, |
| @Nullable Class<? extends T> clazz) { |
| int n = readInt(); |
| if (n < 0) { |
| return null; |
| } |
| ArrayList<T> l = new ArrayList<>(n); |
| readListInternal(l, n, loader, clazz); |
| return l; |
| } |
| |
| /** |
| * The method is replaced by {@link #readArrayInternal(ClassLoader, Class)}, however |
| * we are keeping this unused method here to allow unsupported app usages. |
| */ |
| private void readArrayInternal(@NonNull Object[] outVal, int N, |
| @Nullable ClassLoader loader) { |
| for (int i = 0; i < N; i++) { |
| Object value = readValue(loader, /* clazz */ null); |
| outVal[i] = value; |
| } |
| } |
| |
| /** |
| * @param clazz The type of the object expected or {@code null} for performing no checks. |
| */ |
| @SuppressWarnings("unchecked") |
| @Nullable |
| private <T> T[] readArrayInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) { |
| int n = readInt(); |
| if (n < 0) { |
| return null; |
| } |
| T[] outVal = (T[]) ((clazz == null) ? new Object[n] : Array.newInstance(clazz, n)); |
| |
| for (int i = 0; i < n; i++) { |
| T value = readValue(loader, clazz); |
| outVal[i] = value; |
| } |
| return outVal; |
| } |
| |
| /** |
| * The method is replaced by {@link #readSparseArray(ClassLoader, Class)}, however |
| * we are keeping this unused method here to allow unsupported app usages. |
| */ |
| private void readSparseArrayInternal(@NonNull SparseArray outVal, int N, |
| @Nullable ClassLoader loader) { |
| while (N > 0) { |
| int key = readInt(); |
| Object value = readValue(loader); |
| outVal.append(key, value); |
| N--; |
| } |
| } |
| |
| /** |
| * @param clazz The type of the object expected or {@code null} for performing no checks. |
| */ |
| @Nullable |
| private <T> SparseArray<T> readSparseArrayInternal(@Nullable ClassLoader loader, |
| @Nullable Class<? extends T> clazz) { |
| int n = readInt(); |
| if (n < 0) { |
| return null; |
| } |
| SparseArray<T> outVal = new SparseArray<>(n); |
| |
| while (n > 0) { |
| int key = readInt(); |
| T value = readValue(loader, clazz); |
| outVal.append(key, value); |
| n--; |
| } |
| return outVal; |
| } |
| |
| |
| private void readSparseBooleanArrayInternal(@NonNull SparseBooleanArray outVal, int N) { |
| while (N > 0) { |
| int key = readInt(); |
| boolean value = this.readByte() == 1; |
| //Log.i(TAG, "Unmarshalling key=" + key + " value=" + value); |
| outVal.append(key, value); |
| N--; |
| } |
| } |
| |
| private void readSparseIntArrayInternal(@NonNull SparseIntArray outVal, int N) { |
| while (N > 0) { |
| int key = readInt(); |
| int value = readInt(); |
| outVal.append(key, value); |
| N--; |
| } |
| } |
| |
| /** |
| * @hide For testing |
| */ |
| public long getOpenAshmemSize() { |
| return nativeGetOpenAshmemSize(mNativePtr); |
| } |
| |
| private static String valueTypeToString(int type) { |
| switch (type) { |
| case VAL_NULL: return "VAL_NULL"; |
| case VAL_INTEGER: return "VAL_INTEGER"; |
| case VAL_MAP: return "VAL_MAP"; |
| case VAL_BUNDLE: return "VAL_BUNDLE"; |
| case VAL_PERSISTABLEBUNDLE: return "VAL_PERSISTABLEBUNDLE"; |
| case VAL_PARCELABLE: return "VAL_PARCELABLE"; |
| case VAL_SHORT: return "VAL_SHORT"; |
| case VAL_LONG: return "VAL_LONG"; |
| case VAL_FLOAT: return "VAL_FLOAT"; |
| case VAL_DOUBLE: return "VAL_DOUBLE"; |
| case VAL_BOOLEAN: return "VAL_BOOLEAN"; |
| case VAL_CHARSEQUENCE: return "VAL_CHARSEQUENCE"; |
| case VAL_LIST: return "VAL_LIST"; |
| case VAL_SPARSEARRAY: return "VAL_SPARSEARRAY"; |
| case VAL_BOOLEANARRAY: return "VAL_BOOLEANARRAY"; |
| case VAL_BYTEARRAY: return "VAL_BYTEARRAY"; |
| case VAL_STRINGARRAY: return "VAL_STRINGARRAY"; |
| case VAL_CHARSEQUENCEARRAY: return "VAL_CHARSEQUENCEARRAY"; |
| case VAL_IBINDER: return "VAL_IBINDER"; |
| case VAL_PARCELABLEARRAY: return "VAL_PARCELABLEARRAY"; |
| case VAL_INTARRAY: return "VAL_INTARRAY"; |
| case VAL_LONGARRAY: return "VAL_LONGARRAY"; |
| case VAL_BYTE: return "VAL_BYTE"; |
| case VAL_SIZE: return "VAL_SIZE"; |
| case VAL_SIZEF: return "VAL_SIZEF"; |
| case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY"; |
| case VAL_CHAR: return "VAL_CHAR"; |
| case VAL_SHORTARRAY: return "VAL_SHORTARRAY"; |
| case VAL_CHARARRAY: return "VAL_CHARARRAY"; |
| case VAL_FLOATARRAY: return "VAL_FLOATARRAY"; |
| case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY"; |
| case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE"; |
| default: return "UNKNOWN(" + type + ")"; |
| } |
| } |
| } |