package com.lody.virtual.remote; import java.util.ArrayList; import java.util.List; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; /** * Transfer a large list of Parcelable objects across an IPC. Splits into * multiple transactions if needed. * * Caveat: for efficiency and security, all elements must be the same concrete * type. In order to avoid writing the class name of each object, we must ensure * that each object is the same type, or else unparceling then reparceling the * data may yield a different result if the class name encoded in the Parcelable * is a Base type. See b/17671747. * * @hide * * OpenSilk: Modified to remove the creator optimization which uses hidden * apis. this means we write an extra string for every list item. Class * verification left in place since the Base type issue still applies */ public class VParceledListSlice<T extends Parcelable> implements Parcelable { @SuppressWarnings("unchecked") public static final Parcelable.ClassLoaderCreator<VParceledListSlice> CREATOR = new Parcelable.ClassLoaderCreator<VParceledListSlice>() { public VParceledListSlice createFromParcel(Parcel in) { return new VParceledListSlice(in, null); } @Override public VParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { return new VParceledListSlice(in, loader); } public VParceledListSlice[] newArray(int size) { return new VParceledListSlice[size]; } }; /* * TODO get this number from somewhere else. For now set it to a quarter of * the 1MB limit. */ private static final int MAX_IPC_SIZE = 256 * 1024; private static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2; private static String TAG = "ParceledListSlice"; private static boolean DEBUG = false; private final List<T> mList; public VParceledListSlice(List<T> list) { mList = list; } private VParceledListSlice(Parcel p, ClassLoader loader) { final int N = p.readInt(); mList = new ArrayList<T>(N); if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); if (N <= 0) { return; } // Parcelable.Creator<T> creator = p.readParcelableCreator(loader); Class<?> listElementClass = null; int i = 0; while (i < N) { if (p.readInt() == 0) { break; } // final T parcelable = p.readCreator(creator, loader); final T parcelable = p.readParcelable(loader); if (listElementClass == null) { listElementClass = parcelable.getClass(); } else { verifySameType(listElementClass, parcelable.getClass()); } mList.add(parcelable); if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size() - 1)); i++; } if (i >= N) { return; } final IBinder retriever = p.readStrongBinder(); while (i < N) { if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInt(i); try { retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); } catch (RemoteException e) { Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); return; } while (i < N && reply.readInt() != 0) { // final T parcelable = reply.readCreator(creator, loader); final T parcelable = reply.readParcelable(loader); verifySameType(listElementClass, parcelable.getClass()); mList.add(parcelable); if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size() - 1)); i++; } reply.recycle(); data.recycle(); } } private static void verifySameType(final Class<?> expected, final Class<?> actual) { if (!actual.equals(expected)) { throw new IllegalArgumentException( "Can't unparcel type " + actual.getName() + " in list of type " + expected.getName()); } } public List<T> getList() { return mList; } @Override public int describeContents() { int contents = 0; for (int i = 0; i < mList.size(); i++) { contents |= mList.get(i).describeContents(); } return contents; } /** * Write this to another Parcel. Note that this discards the internal Parcel * and should not be used anymore. This is so we can pass this to a Binder * where we won't have a chance to call recycle on this. */ @Override public void writeToParcel(Parcel dest, int flags) { final int N = mList.size(); final int callFlags = flags; dest.writeInt(N); if (DEBUG) Log.d(TAG, "Writing " + N + " items"); if (N > 0) { final Class<?> listElementClass = mList.get(0).getClass(); // dest.writeParcelableCreator(mList.get(0)); int i = 0; while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) { dest.writeInt(1); final T parcelable = mList.get(i); verifySameType(listElementClass, parcelable.getClass()); // parcelable.writeToParcel(dest, callFlags); dest.writeParcelable(parcelable, callFlags); if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); i++; } if (i < N) { dest.writeInt(0); Binder retriever = new Binder() { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { if (code != FIRST_CALL_TRANSACTION) { return super.onTransact(code, data, reply, flags); } int i = data.readInt(); if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); while (i < N && reply.dataSize() < MAX_IPC_SIZE) { reply.writeInt(1); final T parcelable = mList.get(i); verifySameType(listElementClass, parcelable.getClass()); // parcelable.writeToParcel(reply, callFlags); reply.writeParcelable(parcelable, callFlags); if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); i++; } if (i < N) { if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); reply.writeInt(0); } return true; } }; if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); dest.writeStrongBinder(retriever); } } } }