blob: e5bab6fc9230e51864d62a248d195cc4336722a1 [file] [log] [blame]
/*
* 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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.Log;
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 dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.VMRuntime;
import libcore.util.ArrayUtils;
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.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 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 #writeBinderList(List)}, {@link #readBinderList(List)},
* {@link #createBinderArrayList()}.</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>Untyped 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)}.
*/
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;
/**
* 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 int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
private static final Parcel[] sHolderPool = new Parcel[POOL_SIZE];
// 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;
private static final int VAL_BUNDLE = 3;
private static final int VAL_PARCELABLE = 4;
private static final int VAL_SHORT = 5;
private static final int VAL_LONG = 6;
private static final int VAL_FLOAT = 7;
private static final int VAL_DOUBLE = 8;
private static final int VAL_BOOLEAN = 9;
private static final int VAL_CHARSEQUENCE = 10;
private static final int VAL_LIST = 11;
private static final int VAL_SPARSEARRAY = 12;
private static final int VAL_BYTEARRAY = 13;
private static final int VAL_STRINGARRAY = 14;
private static final int VAL_IBINDER = 15;
private static final int VAL_PARCELABLEARRAY = 16;
private static final int VAL_OBJECTARRAY = 17;
private static final int VAL_INTARRAY = 18;
private static final int VAL_LONGARRAY = 19;
private static final int VAL_BYTE = 20;
private static final int VAL_SERIALIZABLE = 21;
private static final int VAL_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;
// 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 */
public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; // special; see below
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;
@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 long 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);
@FastNative
private static native void nativeWriteInt(long nativePtr, int val);
@FastNative
private static native void nativeWriteLong(long nativePtr, long val);
@FastNative
private static native void nativeWriteFloat(long nativePtr, float val);
@FastNative
private static native void nativeWriteDouble(long nativePtr, double val);
@FastNative
private static native void nativeWriteString8(long nativePtr, String val);
@FastNative
private static native void nativeWriteString16(long nativePtr, String val);
@FastNative
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
private static native long 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
private static native IBinder nativeReadStrongBinder(long nativePtr);
@FastNative
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
private static native long nativeFreeBuffer(long nativePtr);
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
private static native long nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
private static native long nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
@CriticalNative
private static native boolean nativeHasFileDescriptors(long nativePtr);
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
@CriticalNative
private static native boolean nativeReplaceCallingWorkSourceUid(
long nativePtr, int workSourceUid);
@CriticalNative
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
private static native long nativeGetBlobAshmemSize(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() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
return p;
}
}
}
return new Parcel(0);
}
/**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
*/
public final void recycle() {
if (DEBUG_RECYCLE) mStack = null;
freeBuffer();
final Parcel[] pool;
if (mOwnsNativeParcelObject) {
pool = sOwnedPool;
} else {
mNativePtr = 0;
pool = sHolderPool;
}
synchronized (pool) {
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
pool[i] = this;
return;
}
}
}
}
/**
* 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
public static native long getGlobalAllocSize();
/** @hide */
@UnsupportedAppUsage
public static native long getGlobalAllocCount();
/**
* Returns the total amount of data contained in the parcel.
*/
public final 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) {
updateNativeSize(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);
}
/**
* Set the bytes in data to be the raw bytes of this Parcel.
*/
public final void unmarshall(@NonNull byte[] data, int offset, int length) {
updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
}
public final void appendFrom(Parcel parcel, int offset, int length) {
updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
}
/** @hide */
public final int compareData(Parcel other) {
return nativeCompareData(mNativePtr, other.mNativePtr);
}
/** @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 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 final boolean hasFileDescriptors() {
return nativeHasFileDescriptors(mNativePtr);
}
/**
* 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.
*/
public final void writeInterfaceToken(String interfaceName) {
nativeWriteInterfaceToken(mNativePtr, interfaceName);
}
public final void enforceInterface(String interfaceName) {
nativeEnforceInterface(mNativePtr, interfaceName);
}
/**
* 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.
* @param b Bytes to place into the parcel.
* {@hide}
* {@SystemApi}
*/
@UnsupportedAppUsage
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.
* @param b Bytes to place into the parcel.
* @param offset Index of first byte to be written.
* @param len Number of bytes to write.
* {@hide}
* {@SystemApi}
*/
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);
}
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {
nativeWriteInt(mNativePtr, val);
}
/**
* Write a long integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeLong(long val) {
nativeWriteLong(mNativePtr, val);
}
/**
* Write a floating point value into the parcel at the current
* dataPosition(), growing dataCapacity() if needed.
*/
public final void writeFloat(float val) {
nativeWriteFloat(mNativePtr, val);
}
/**
* Write a double precision floating point value into the parcel at the
* current dataPosition(), growing dataCapacity() if needed.
*/
public final void writeDouble(double val) {
nativeWriteDouble(mNativePtr, val);
}
/**
* 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) {
updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
}
private void updateNativeSize(long newNativeSize) {
if (mOwnsNativeParcelObject) {
if (newNativeSize > Integer.MAX_VALUE) {
newNativeSize = Integer.MAX_VALUE;
}
if (newNativeSize != mNativeSize) {
int delta = (int) (newNativeSize - mNativeSize);
if (delta > 0) {
VMRuntime.getRuntime().registerNativeAllocation(delta);
} else {
VMRuntime.getRuntime().registerNativeFree(-delta);
}
mNativeSize = newNativeSize;
}
}
}
/**
* {@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
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
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);
}
}
@Nullable
public final boolean[] createBooleanArray() {
int N = readInt();
// >>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");
}
}
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();
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();
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();
// >>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();
// >>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();
// >>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();
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();
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);
}
}
/**
* @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();
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");
}
}
/**
* 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);
}
}
/**
* @hide
*/
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 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 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 == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
} else if (v instanceof Map) {
writeInt(VAL_MAP);
writeMap((Map) v);
} else if (v instanceof Bundle) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
// come before the Parcelable case, so that their specific VAL_*
// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} else if (v instanceof Short) {
writeInt(VAL_SHORT);
writeInt(((Short) v).intValue());
} else if (v instanceof Long) {
writeInt(VAL_LONG);
writeLong((Long) v);
} else if (v instanceof Float) {
writeInt(VAL_FLOAT);
writeFloat((Float) v);
} else if (v instanceof Double) {
writeInt(VAL_DOUBLE);
writeDouble((Double) v);
} else if (v instanceof Boolean) {
writeInt(VAL_BOOLEAN);
writeInt((Boolean) v ? 1 : 0);
} else if (v instanceof CharSequence) {
// Must be after String
writeInt(VAL_CHARSEQUENCE);
writeCharSequence((CharSequence) v);
} else if (v instanceof List) {
writeInt(VAL_LIST);
writeList((List) v);
} else if (v instanceof SparseArray) {
writeInt(VAL_SPARSEARRAY);
writeSparseArray((SparseArray) v);
} else if (v instanceof boolean[]) {
writeInt(VAL_BOOLEANARRAY);
writeBooleanArray((boolean[]) v);
} else if (v instanceof byte[]) {
writeInt(VAL_BYTEARRAY);
writeByteArray((byte[]) v);
} else if (v instanceof String[]) {
writeInt(VAL_STRINGARRAY);
writeStringArray((String[]) v);
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
writeInt(VAL_CHARSEQUENCEARRAY);
writeCharSequenceArray((CharSequence[]) v);
} else if (v instanceof IBinder) {
writeInt(VAL_IBINDER);
writeStrongBinder((IBinder) v);
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
} else if (v instanceof long[]) {
writeInt(VAL_LONGARRAY);
writeLongArray((long[]) v);
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
} else if (v instanceof SizeF) {
writeInt(VAL_SIZEF);
writeSizeF((SizeF) v);
} else if (v instanceof double[]) {
writeInt(VAL_DOUBLEARRAY);
writeDoubleArray((double[]) v);
} 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.
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);
} else {
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 ArrayMap<Integer, Parcelable> mReadSquashableParcelables;
private void ensureReadSquashableParcelables() {
if (mReadSquashableParcelables != null) {
return;
}
mReadSquashableParcelables = new ArrayMap<>();
}
/**
* 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) {
Slog.wtfStack(TAG, "Map doesn't contain offset "
+ firstAbsolutePos
+ " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet()));
}
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 RuntimeException("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
*/
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 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
*/
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);
}
}
/**
* 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());
} 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() {
return nativeReadStrongBinder(mNativePtr);
}
/**
* 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;
}
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().
*/
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
int N = readInt();
readMapInternal(outVal, N, loader);
}
/**
* 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.
*/
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
int N = readInt();
readListInternal(outVal, N, loader);
}
/**
* 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.
*/
@Nullable
public final HashMap readHashMap(@Nullable ClassLoader loader)
{
int N = readInt();
if (N < 0) {
return null;
}
HashMap m = new HashMap(N);
readMapInternal(m, N, loader);
return m;
}
/**
* 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.
* {@hide}
* {@SystemApi}
*/
@UnsupportedAppUsage
@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&lt;CharSequence&gt; 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.
*/
@Nullable
public final ArrayList readArrayList(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
}
ArrayList l = new ArrayList(N);
readListInternal(l, N, loader);
return l;
}
/**
* 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.
*/
@Nullable
public final Object[] readArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
}
Object[] l = new Object[N];
readArrayInternal(l, N, loader);
return l;
}
/**
* 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.
*/
@Nullable
public final <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
}
SparseArray sa = new SparseArray(N);
readSparseArrayInternal(sa, N, loader);
return sa;
}
/**
* 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;
}
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.
*
* @return A newly created ArrayList containing objects with the same data
* as those that were previously written.
*
* @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;
}
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;
}
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;
}
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;
}
ArrayList<IBinder> l = new ArrayList<IBinder>(N);
while (N > 0) {
l.add(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 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)
*/
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
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) readParcelable(cl));
}
for (; i<N; i++) {
list.add((T) readParcelable(cl));
}
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;
}
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;
}
}
/**
* 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) {
int type = readInt();
switch (type) {
case VAL_NULL:
return null;
case VAL_STRING:
return readString();
case VAL_INTEGER:
return readInt();
case VAL_MAP:
return readHashMap(loader);
case VAL_PARCELABLE:
return readParcelable(loader);
case VAL_SHORT:
return (short) readInt();
case VAL_LONG:
return readLong();
case VAL_FLOAT:
return readFloat();
case VAL_DOUBLE:
return readDouble();
case VAL_BOOLEAN:
return readInt() == 1;
case VAL_CHARSEQUENCE:
return readCharSequence();
case VAL_LIST:
return readArrayList(loader);
case VAL_BOOLEANARRAY:
return createBooleanArray();
case VAL_BYTEARRAY:
return createByteArray();
case VAL_STRINGARRAY:
return readStringArray();
case VAL_CHARSEQUENCEARRAY:
return readCharSequenceArray();
case VAL_IBINDER:
return readStrongBinder();
case VAL_OBJECTARRAY:
return readArray(loader);
case VAL_INTARRAY:
return createIntArray();
case VAL_LONGARRAY:
return createLongArray();
case VAL_BYTE:
return readByte();
case VAL_SERIALIZABLE:
return readSerializable(loader);
case VAL_PARCELABLEARRAY:
return readParcelableArray(loader);
case VAL_SPARSEARRAY:
return readSparseArray(loader);
case VAL_SPARSEBOOLEANARRAY:
return readSparseBooleanArray();
case VAL_BUNDLE:
return readBundle(loader); // loading will be deferred
case VAL_PERSISTABLEBUNDLE:
return readPersistableBundle(loader);
case VAL_SIZE:
return readSize();
case VAL_SIZEF:
return readSizeF();
case VAL_DOUBLEARRAY:
return createDoubleArray();
default:
int off = dataPosition() - 4;
throw new RuntimeException(
"Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
}
}
/**
* 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.
*/
@SuppressWarnings("unchecked")
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
Parcelable.Creator<?> creator = readParcelableCreator(loader);
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
*/
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
String name = readString();
if (name == null) {
return null;
}
Parcelable.Creator<?> creator;
HashMap<String, Parcelable.Creator<?>> map;
synchronized (mCreators) {
map = mCreators.get(loader);
if (map == null) {
map = new HashMap<>();
mCreators.put(loader, map);
}
creator = map.get(name);
}
if (creator != null) {
return creator;
}
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.
Class<?> parcelableClass = Class.forName(name, false /* initialize */,
parcelableClassLoader);
if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable protocol requires subclassing "
+ "from Parcelable on class " + name);
}
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);
} catch (ClassNotFoundException e) {
Log.e(TAG, "Class not found when unmarshalling: " + name, e);
throw new BadParcelableException(
"ClassNotFoundException when unmarshalling: " + name);
} catch (NoSuchFieldException e) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "Parcelable.Creator object called "
+ "CREATOR on class " + name);
}
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
+ "non-null Parcelable.Creator object called "
+ "CREATOR on class " + name);
}
synchronized (mCreators) {
map.put(name, creator);
}
return 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
*/
@Nullable
public final Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
int N = readInt();
if (N < 0) {
return null;
}
Parcelable[] p = new Parcelable[N];
for (int i = 0; i < N; i++) {
p[i] = readParcelable(loader);
}
return p;
}
/** @hide */
@Nullable
public final <T extends Parcelable> T[] readParcelableArray(@Nullable ClassLoader loader,
@NonNull Class<T> clazz) {
int N = readInt();
if (N < 0) {
return null;
}
T[] p = (T[]) Array.newInstance(clazz, N);
for (int i = 0; i < N; i++) {
p[i] = readParcelable(loader);
}
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.
*/
@Nullable
public final Serializable readSerializable() {
return readSerializable(null);
}
@Nullable
private final Serializable readSerializable(@Nullable final ClassLoader loader) {
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;
}
byte[] serializedData = createByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
try {
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);
if (c != null) {
return c;
}
}
return super.resolveClass(osClass);
}
};
return (Serializable) ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException reading a Serializable object (name = " + name +
")", ioe);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Parcelable encountered " +
"ClassNotFoundException reading a Serializable object (name = "
+ name + ")", cnfe);
}
}
// Cache of previously looked up CREATOR.createFromParcel() methods for
// particular classes. Keys are the names of the classes, values are
// Method objects.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator<?>>>
mCreators = 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) {
final Parcel[] pool = sHolderPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.init(obj);
return p;
}
}
}
return new Parcel(obj);
}
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() {
resetSqaushingState();
if (mOwnsNativeParcelObject) {
updateNativeSize(nativeFreeBuffer(mNativePtr));
}
mReadWriteHelper = ReadWriteHelper.DEFAULT;
}
private void destroy() {
resetSqaushingState();
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
updateNativeSize(0);
}
mNativePtr = 0;
}
}
@Override
protected void finalize() throws Throwable {
if (DEBUG_RECYCLE) {
if (mStack != null) {
Log.w(TAG, "Client did not call Parcel.recycle()", mStack);
}
}
destroy();
}
/* package */ void readMapInternal(@NonNull Map outVal, int N,
@Nullable ClassLoader loader) {
while (N > 0) {
Object key = readValue(loader);
Object value = readValue(loader);
outVal.put(key, value);
N--;
}
}
/* package */ void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
@Nullable ClassLoader loader) {
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
}
int startPos;
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
outVal.append(key, value);
N--;
}
outVal.validate();
}
/* package */ void readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N,
@Nullable ClassLoader loader) {
if (DEBUG_ARRAY_MAP) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here);
}
while (N > 0) {
String key = readString();
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x"
+ (key != null ? key.hashCode() : 0) + " " + key);
Object value = readValue(loader);
outVal.put(key, value);
N--;
}
}
/**
* @hide For testing only.
*/
@UnsupportedAppUsage
public void readArrayMap(@NonNull ArrayMap 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;
}
private void readListInternal(@NonNull List outVal, int N,
@Nullable ClassLoader loader) {
while (N > 0) {
Object value = readValue(loader);
//Log.d(TAG, "Unmarshalling value=" + value);
outVal.add(value);
N--;
}
}
private void readArrayInternal(@NonNull Object[] outVal, int N,
@Nullable ClassLoader loader) {
for (int i = 0; i < N; i++) {
Object value = readValue(loader);
//Log.d(TAG, "Unmarshalling value=" + value);
outVal[i] = value;
}
}
private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
@Nullable ClassLoader loader) {
while (N > 0) {
int key = readInt();
Object value = readValue(loader);
//Log.i(TAG, "Unmarshalling key=" + key + " value=" + value);
outVal.append(key, value);
N--;
}
}
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 getBlobAshmemSize() {
return nativeGetBlobAshmemSize(mNativePtr);
}
}