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