/* * 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.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Field; 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 #writeTypedArray}, * {@link #writeTypedList(List)}, * {@link #readTypedArray} and {@link #readTypedList}. 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 Parceable object, 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; @SuppressWarnings({"UnusedDeclaration"}) private int mObject; // used by native code @SuppressWarnings({"UnusedDeclaration"}) private int mOwnObject; // used by native code private RuntimeException mStack; 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]; 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 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; 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]; } }; /** * Retrieve a new Parcel object from the pool. */ 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(); } 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 = mOwnObject != 0 ? sOwnedPool : sHolderPool; synchronized (pool) { for (int i=0; i<POOL_SIZE; i++) { if (pool[i] == null) { pool[i] = this; return; } } } } /** * Returns the total amount of data contained in the parcel. */ public final native int dataSize(); /** * Returns the amount of data remaining to be read from the * parcel. That is, {@link #dataSize}-{@link #dataPosition}. */ public final native int dataAvail(); /** * Returns the current position in the parcel data. Never * more than {@link #dataSize}. */ public final native int dataPosition(); /** * 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 native int dataCapacity(); /** * 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 native void setDataSize(int 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 native void setDataPosition(int 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 native void setDataCapacity(int size); /** * 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 native byte[] marshall(); /** * Set the bytes in data to be the raw bytes of this Parcel. */ public final native void unmarshall(byte[] data, int offest, int length); public final native void appendFrom(Parcel parcel, int offset, int length); /** * Report whether the parcel contains any marshalled file descriptors. */ public final native boolean hasFileDescriptors(); /** * 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 native void writeInterfaceToken(String interfaceName); public final native void enforceInterface(String interfaceName); /** * 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(byte[] b) { writeByteArray(b, 0, (b != null) ? b.length : 0); } /** * Write an 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(byte[] b, int offset, int len) { if (b == null) { writeInt(-1); return; } if (b.length < offset + len || len < 0 || offset < 0) { throw new ArrayIndexOutOfBoundsException(); } writeNative(b, offset, len); } private native void writeNative(byte[] b, int offset, int len); /** * Write an integer value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final native void writeInt(int val); /** * Write a long integer value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final native void writeLong(long val); /** * Write a floating point value into the parcel at the current * dataPosition(), growing dataCapacity() if needed. */ public final native void writeFloat(float val); /** * Write a double precision floating point value into the parcel at the * current dataPosition(), growing dataCapacity() if needed. */ public final native void writeDouble(double val); /** * Write a string value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final native void writeString(String val); /** * Write an object into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final native void writeStrongBinder(IBinder 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. */ public final native void writeFileDescriptor(FileDescriptor val); /** * Write an byte value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ 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(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. */ private void writeMapInternal(Map<String,Object> val) { if (val == null) { writeInt(-1); return; } Set<Map.Entry<String,Object>> entries = val.entrySet(); writeInt(entries.size()); for (Map.Entry<String,Object> e : entries) { writeValue(e.getKey()); writeValue(e.getValue()); } } /** * Flatten a Bundle into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final void writeBundle(Bundle val) { if (val == null) { writeInt(-1); return; } if (val.mParcelledData != null) { int length = val.mParcelledData.dataSize(); appendFrom(val.mParcelledData, 0, length); } else { writeInt(-1); // dummy, will hold length int oldPos = dataPosition(); writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' writeMapInternal(val.mMap); int newPos = dataPosition(); // Backpatch length setDataPosition(oldPos - 4); int length = newPos - oldPos; writeInt(length); setDataPosition(newPos); } } /** * 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(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(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 void writeSparseArray(SparseArray<Object> 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(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++; } } public final void writeBooleanArray(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); } } 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(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(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); } } 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(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(int[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeInt(val[i]); } } else { writeInt(-1); } } 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(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(long[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeLong(val[i]); } } else { writeInt(-1); } } 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(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(float[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeFloat(val[i]); } } else { writeInt(-1); } } 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(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(double[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeDouble(val[i]); } } else { writeInt(-1); } } 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(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(String[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeString(val[i]); } } else { writeInt(-1); } } public final String[] createStringArray() { int N = readInt(); if (N >= 0) { String[] val = new String[N]; for (int i=0; i<N; i++) { val[i] = readString(); } return val; } else { return null; } } public final void readStringArray(String[] val) { int N = readInt(); if (N == val.length) { for (int i=0; i<N; i++) { val[i] = readString(); } } else { throw new RuntimeException("bad array lengths"); } } public final void writeBinderArray(IBinder[] val) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { writeStrongBinder(val[i]); } } else { writeInt(-1); } } 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(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(List<T> val) { if (val == null) { writeInt(-1); return; } int N = val.size(); int i=0; writeInt(N); while (i < N) { T item = val.get(i); if (item != null) { writeInt(1); item.writeToParcel(this, 0); } else { writeInt(0); } 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(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(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 heterogeneous 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(T[] val, int parcelableFlags) { if (val != null) { int N = val.length; writeInt(N); for (int i=0; i<N; i++) { T item = val[i]; if (item != null) { writeInt(1); item.writeToParcel(this, parcelableFlags); } else { writeInt(0); } } } else { writeInt(-1); } } /** * 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}). * <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> */ public final void writeValue(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 Parcelable) { 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); TextUtils.writeToParcel((CharSequence) v, this, 0); } 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 IBinder) { writeInt(VAL_IBINDER); writeStrongBinder((IBinder) v); } else if (v instanceof Parcelable[]) { writeInt(VAL_PARCELABLEARRAY); writeParcelableArray((Parcelable[]) v, 0); } else if (v instanceof Object[]) { writeInt(VAL_OBJECTARRAY); writeArray((Object[]) v); } 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 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(Parcelable p, int parcelableFlags) { if (p == null) { writeString(null); return; } String name = p.getClass().getName(); writeString(name); p.writeToParcel(this, parcelableFlags); } /** * 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(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); } } /** * 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} * </ul> * * @param e The Exception to be written. * * @see #writeNoException * @see #readException */ public final void writeException(Exception e) { int code = 0; 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; } writeInt(code); if (code == 0) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } writeString(e.getMessage()); } /** * Special function for writing information at the front of the Parcel * indicating that no exception occurred. * * @see #writeException * @see #readException */ public final void writeNoException() { 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 = readInt(); if (code == 0) return; String msg = readString(); readException(code, msg); } /** * Use this function for customized exception handling. * customized method call this method for all unknown case * @param code exception code * @param msg exception message */ public final void readException(int code, String msg) { switch (code) { case EX_SECURITY: throw new SecurityException(msg); case EX_BAD_PARCELABLE: throw new BadParcelableException(msg); case EX_ILLEGAL_ARGUMENT: throw new IllegalArgumentException(msg); case EX_NULL_POINTER: throw new NullPointerException(msg); case EX_ILLEGAL_STATE: throw new IllegalStateException(msg); } throw new RuntimeException("Unknown exception code: " + code + " msg " + msg); } /** * Read an integer value from the parcel at the current dataPosition(). */ public final native int readInt(); /** * Read a long integer value from the parcel at the current dataPosition(). */ public final native long readLong(); /** * Read a floating point value from the parcel at the current * dataPosition(). */ public final native float readFloat(); /** * Read a double precision floating point value from the parcel at the * current dataPosition(). */ public final native double readDouble(); /** * Read a string value from the parcel at the current dataPosition(). */ public final native String readString(); /** * Read an object from the parcel at the current dataPosition(). */ public final native IBinder readStrongBinder(); /** * Read a FileDescriptor from the parcel at the current dataPosition(). */ public final ParcelFileDescriptor readFileDescriptor() { FileDescriptor fd = internalReadFileDescriptor(); return fd != null ? new ParcelFileDescriptor(fd) : null; } private native FileDescriptor internalReadFileDescriptor(); /*package*/ static native FileDescriptor openFileDescriptor(String file, int mode) throws FileNotFoundException; /*package*/ static native void closeFileDescriptor(FileDescriptor desc) throws IOException; /** * 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(Map outVal, 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(List outVal, 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. */ public final HashMap readHashMap(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. */ 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. */ public final Bundle readBundle(ClassLoader loader) { int offset = dataPosition(); int length = readInt(); if (length < 0) { return null; } int magic = readInt(); if (magic != 0x4C444E42) { //noinspection ThrowableInstanceNeverThrown String st = Log.getStackTraceString(new RuntimeException()); Log.e("Bundle", "readBundle: bad magic number"); Log.e("Bundle", "readBundle: trace = " + st); } // Advance within this Parcel setDataPosition(offset + length + 4); Parcel p = new Parcel(0); p.setDataPosition(0); p.appendFrom(this, offset, length + 4); p.setDataPosition(0); final Bundle bundle = new Bundle(p); 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. The returned bundle will have its contents fully unpacked using * the given ClassLoader. */ /* package */ Bundle readBundleUnpacked(ClassLoader loader) { int length = readInt(); if (length == -1) { return null; } int magic = readInt(); if (magic != 0x4C444E42) { //noinspection ThrowableInstanceNeverThrown String st = Log.getStackTraceString(new RuntimeException()); Log.e("Bundle", "readBundleUnpacked: bad magic number"); Log.e("Bundle", "readBundleUnpacked: trace = " + st); } Bundle m = new Bundle(loader); int N = readInt(); if (N < 0) { return null; } readMapInternal(m.mMap, N, loader); return m; } /** * Read and return a byte[] object from the parcel. */ public final native byte[] createByteArray(); /** * Read a byte[] object from the parcel and copy it into the * given byte array. */ public final void readByteArray(byte[] val) { // TODO: make this a native method to avoid the extra copy. byte[] ba = createByteArray(); if (ba.length == val.length) { System.arraycopy(ba, 0, val, 0, ba.length); } else { throw new RuntimeException("bad array lengths"); } } /** * Read and return a String[] object from the parcel. * {@hide} */ public final String[] readStringArray() { String[] array = null; int length = readInt(); if (length >= 0) { array = new String[length]; for (int i = 0 ; i < length ; i++) { array[i] = readString(); } } 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. */ public final ArrayList readArrayList(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. */ public final Object[] readArray(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. */ public final SparseArray readSparseArray(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. */ 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 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 */ public final <T> ArrayList<T> createTypedArrayList(Parcelable.Creator<T> c) { int N = readInt(); if (N < 0) { return null; } ArrayList<T> l = new ArrayList<T>(N); while (N > 0) { if (readInt() != 0) { l.add(c.createFromParcel(this)); } else { l.add(null); } 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(List<T> list, Parcelable.Creator<T> c) { int M = list.size(); int N = readInt(); int i = 0; for (; i < M && i < N; i++) { if (readInt() != 0) { list.set(i, c.createFromParcel(this)); } else { list.set(i, null); } } for (; i<N; i++) { if (readInt() != 0) { list.add(c.createFromParcel(this)); } else { list.add(null); } } for (; i<M; i++) { list.remove(N); } } /** * 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 */ 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 */ 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(). * * @return A newly created ArrayList containing strings with the same data * as those that were previously written. * * @see #writeStringList */ public final void readStringList(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(). * * @return A newly created ArrayList containing strings with the same data * as those that were previously written. * * @see #writeBinderList */ public final void readBinderList(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 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 */ public final <T> T[] createTypedArray(Parcelable.Creator<T> c) { int N = readInt(); if (N < 0) { return null; } T[] l = c.newArray(N); for (int i=0; i<N; i++) { if (readInt() != 0) { l[i] = c.createFromParcel(this); } } return l; } public final <T> void readTypedArray(T[] val, Parcelable.Creator<T> c) { int N = readInt(); if (N == val.length) { for (int i=0; i<N; i++) { if (readInt() != 0) { val[i] = c.createFromParcel(this); } else { val[i] = null; } } } else { throw new RuntimeException("bad array lengths"); } } /** * @deprecated * @hide */ @Deprecated public final <T> T[] readTypedArray(Parcelable.Creator<T> c) { return createTypedArray(c); } /** * 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(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. */ public final Object readValue(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 TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(this); case VAL_LIST: return readArrayList(loader); case VAL_BOOLEANARRAY: return createBooleanArray(); case VAL_BYTEARRAY: return createByteArray(); case VAL_STRINGARRAY: return readStringArray(); 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(); 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 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. */ public final <T extends Parcelable> T readParcelable(ClassLoader loader) { String name = readString(); if (name == null) { return null; } Parcelable.Creator<T> creator; synchronized (mCreators) { HashMap<String,Parcelable.Creator> map = mCreators.get(loader); if (map == null) { map = new HashMap<String,Parcelable.Creator>(); mCreators.put(loader, map); } creator = map.get(name); if (creator == null) { try { Class c = loader == null ? Class.forName(name) : Class.forName(name, true, loader); Field f = c.getField("CREATOR"); creator = (Parcelable.Creator)f.get(null); } catch (IllegalAccessException e) { Log.e("Parcel", "Class not found when unmarshalling: " + name + ", e: " + e); throw new BadParcelableException( "IllegalAccessException when unmarshalling: " + name); } catch (ClassNotFoundException e) { Log.e("Parcel", "Class not found when unmarshalling: " + name + ", e: " + e); throw new BadParcelableException( "ClassNotFoundException when unmarshalling: " + name); } catch (ClassCastException e) { throw new BadParcelableException("Parcelable protocol requires a " + "Parcelable.Creator object called " + " CREATOR on class " + 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 " + "Parcelable.Creator object called " + " CREATOR on class " + name); } map.put(name, creator); } } return creator.createFromParcel(this); } /** * 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 */ public final Parcelable[] readParcelableArray(ClassLoader loader) { int N = readInt(); if (N < 0) { return null; } Parcelable[] p = new Parcelable[N]; for (int i = 0; i < N; i++) { p[i] = (Parcelable) 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. */ public final Serializable readSerializable() { 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); 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. private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator>> mCreators = new HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>(); static protected final Parcel obtain(int 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(int obj) { if (DEBUG_RECYCLE) { mStack = new RuntimeException(); } //Log.i("Parcel", "Initializing obj=0x" + Integer.toHexString(obj), mStack); init(obj); } @Override protected void finalize() throws Throwable { if (DEBUG_RECYCLE) { if (mStack != null) { Log.w("Parcel", "Client did not call Parcel.recycle()", mStack); } } destroy(); } private native void freeBuffer(); private native void init(int obj); private native void destroy(); private void readMapInternal(Map outVal, int N, ClassLoader loader) { while (N > 0) { Object key = readValue(loader); Object value = readValue(loader); outVal.put(key, value); N--; } } private void readListInternal(List outVal, int N, ClassLoader loader) { while (N > 0) { Object value = readValue(loader); //Log.d("Parcel", "Unmarshalling value=" + value); outVal.add(value); N--; } } private void readArrayInternal(Object[] outVal, int N, ClassLoader loader) { for (int i = 0; i < N; i++) { Object value = readValue(loader); //Log.d("Parcel", "Unmarshalling value=" + value); outVal[i] = value; } } private void readSparseArrayInternal(SparseArray outVal, int N, ClassLoader loader) { while (N > 0) { int key = readInt(); Object value = readValue(loader); //Log.i("Parcel", "Unmarshalling key=" + key + " value=" + value); outVal.append(key, value); N--; } } private void readSparseBooleanArrayInternal(SparseBooleanArray outVal, int N) { while (N > 0) { int key = readInt(); boolean value = this.readByte() == 1; //Log.i("Parcel", "Unmarshalling key=" + key + " value=" + value); outVal.append(key, value); N--; } } }