1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.content.pm; 18 19import android.os.Binder; 20import android.os.IBinder; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.os.RemoteException; 24import android.util.Log; 25 26import java.util.ArrayList; 27import java.util.Collections; 28import java.util.List; 29 30/** 31 * Transfer a large list of Parcelable objects across an IPC. Splits into 32 * multiple transactions if needed. 33 * 34 * Caveat: for efficiency and security, all elements must be the same concrete type. 35 * In order to avoid writing the class name of each object, we must ensure that 36 * each object is the same type, or else unparceling then reparceling the data may yield 37 * a different result if the class name encoded in the Parcelable is a Base type. 38 * See b/17671747. 39 * 40 * @hide 41 */ 42public class ParceledListSlice<T extends Parcelable> implements Parcelable { 43 private static String TAG = "ParceledListSlice"; 44 private static boolean DEBUG = false; 45 46 /* 47 * TODO get this number from somewhere else. For now set it to a quarter of 48 * the 1MB limit. 49 */ 50 private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE; 51 52 private final List<T> mList; 53 54 public static <T extends Parcelable> ParceledListSlice<T> emptyList() { 55 return new ParceledListSlice<T>(Collections.<T> emptyList()); 56 } 57 58 public ParceledListSlice(List<T> list) { 59 mList = list; 60 } 61 62 @SuppressWarnings("unchecked") 63 private ParceledListSlice(Parcel p, ClassLoader loader) { 64 final int N = p.readInt(); 65 mList = new ArrayList<T>(N); 66 if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); 67 if (N <= 0) { 68 return; 69 } 70 71 Parcelable.Creator<?> creator = p.readParcelableCreator(loader); 72 Class<?> listElementClass = null; 73 74 int i = 0; 75 while (i < N) { 76 if (p.readInt() == 0) { 77 break; 78 } 79 80 final T parcelable = p.readCreator(creator, loader); 81 if (listElementClass == null) { 82 listElementClass = parcelable.getClass(); 83 } else { 84 verifySameType(listElementClass, parcelable.getClass()); 85 } 86 87 mList.add(parcelable); 88 89 if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); 90 i++; 91 } 92 if (i >= N) { 93 return; 94 } 95 final IBinder retriever = p.readStrongBinder(); 96 while (i < N) { 97 if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); 98 Parcel data = Parcel.obtain(); 99 Parcel reply = Parcel.obtain(); 100 data.writeInt(i); 101 try { 102 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); 103 } catch (RemoteException e) { 104 Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); 105 return; 106 } 107 while (i < N && reply.readInt() != 0) { 108 final T parcelable = reply.readCreator(creator, loader); 109 verifySameType(listElementClass, parcelable.getClass()); 110 111 mList.add(parcelable); 112 113 if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); 114 i++; 115 } 116 reply.recycle(); 117 data.recycle(); 118 } 119 } 120 121 private static void verifySameType(final Class<?> expected, final Class<?> actual) { 122 if (!actual.equals(expected)) { 123 throw new IllegalArgumentException("Can't unparcel type " 124 + actual.getName() + " in list of type " 125 + expected.getName()); 126 } 127 } 128 129 public List<T> getList() { 130 return mList; 131 } 132 133 @Override 134 public int describeContents() { 135 int contents = 0; 136 for (int i=0; i<mList.size(); i++) { 137 contents |= mList.get(i).describeContents(); 138 } 139 return contents; 140 } 141 142 /** 143 * Write this to another Parcel. Note that this discards the internal Parcel 144 * and should not be used anymore. This is so we can pass this to a Binder 145 * where we won't have a chance to call recycle on this. 146 */ 147 @Override 148 public void writeToParcel(Parcel dest, int flags) { 149 final int N = mList.size(); 150 final int callFlags = flags; 151 dest.writeInt(N); 152 if (DEBUG) Log.d(TAG, "Writing " + N + " items"); 153 if (N > 0) { 154 final Class<?> listElementClass = mList.get(0).getClass(); 155 dest.writeParcelableCreator(mList.get(0)); 156 int i = 0; 157 while (i < N && dest.dataSize() < MAX_IPC_SIZE) { 158 dest.writeInt(1); 159 160 final T parcelable = mList.get(i); 161 verifySameType(listElementClass, parcelable.getClass()); 162 parcelable.writeToParcel(dest, callFlags); 163 164 if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); 165 i++; 166 } 167 if (i < N) { 168 dest.writeInt(0); 169 Binder retriever = new Binder() { 170 @Override 171 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 172 throws RemoteException { 173 if (code != FIRST_CALL_TRANSACTION) { 174 return super.onTransact(code, data, reply, flags); 175 } 176 int i = data.readInt(); 177 if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); 178 while (i < N && reply.dataSize() < MAX_IPC_SIZE) { 179 reply.writeInt(1); 180 181 final T parcelable = mList.get(i); 182 verifySameType(listElementClass, parcelable.getClass()); 183 parcelable.writeToParcel(reply, callFlags); 184 185 if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); 186 i++; 187 } 188 if (i < N) { 189 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); 190 reply.writeInt(0); 191 } 192 return true; 193 } 194 }; 195 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); 196 dest.writeStrongBinder(retriever); 197 } 198 } 199 } 200 201 @SuppressWarnings("unchecked") 202 public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR = 203 new Parcelable.ClassLoaderCreator<ParceledListSlice>() { 204 public ParceledListSlice createFromParcel(Parcel in) { 205 return new ParceledListSlice(in, null); 206 } 207 208 @Override 209 public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { 210 return new ParceledListSlice(in, loader); 211 } 212 213 public ParceledListSlice[] newArray(int size) { 214 return new ParceledListSlice[size]; 215 } 216 }; 217} 218