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 abstract class BaseParceledListSlice<T> 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 = IBinder.MAX_IPC_SIZE; 50 51 private final List<T> mList; 52 53 private int mInlineCountLimit = Integer.MAX_VALUE; 54 BaseParceledListSlice(List<T> list)55 public BaseParceledListSlice(List<T> list) { 56 mList = list; 57 } 58 59 @SuppressWarnings("unchecked") BaseParceledListSlice(Parcel p, ClassLoader loader)60 BaseParceledListSlice(Parcel p, ClassLoader loader) { 61 final int N = p.readInt(); 62 mList = new ArrayList<T>(N); 63 if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); 64 if (N <= 0) { 65 return; 66 } 67 68 Parcelable.Creator<?> creator = readParcelableCreator(p, loader); 69 Class<?> listElementClass = null; 70 71 int i = 0; 72 while (i < N) { 73 if (p.readInt() == 0) { 74 break; 75 } 76 77 final T parcelable = readCreator(creator, p, loader); 78 if (listElementClass == null) { 79 listElementClass = parcelable.getClass(); 80 } else { 81 verifySameType(listElementClass, parcelable.getClass()); 82 } 83 84 mList.add(parcelable); 85 86 if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); 87 i++; 88 } 89 if (i >= N) { 90 return; 91 } 92 final IBinder retriever = p.readStrongBinder(); 93 while (i < N) { 94 if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); 95 Parcel data = Parcel.obtain(); 96 Parcel reply = Parcel.obtain(); 97 data.writeInt(i); 98 try { 99 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); 100 } catch (RemoteException e) { 101 Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); 102 return; 103 } 104 while (i < N && reply.readInt() != 0) { 105 final T parcelable = readCreator(creator, reply, loader); 106 verifySameType(listElementClass, parcelable.getClass()); 107 108 mList.add(parcelable); 109 110 if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); 111 i++; 112 } 113 reply.recycle(); 114 data.recycle(); 115 } 116 } 117 readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader)118 private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) { 119 if (creator instanceof Parcelable.ClassLoaderCreator<?>) { 120 Parcelable.ClassLoaderCreator<?> classLoaderCreator = 121 (Parcelable.ClassLoaderCreator<?>) creator; 122 return (T) classLoaderCreator.createFromParcel(p, loader); 123 } 124 return (T) creator.createFromParcel(p); 125 } 126 verifySameType(final Class<?> expected, final Class<?> actual)127 private static void verifySameType(final Class<?> expected, final Class<?> actual) { 128 if (!actual.equals(expected)) { 129 throw new IllegalArgumentException("Can't unparcel type " 130 + actual.getName() + " in list of type " 131 + expected.getName()); 132 } 133 } 134 getList()135 public List<T> getList() { 136 return mList; 137 } 138 139 /** 140 * Set a limit on the maximum number of entries in the array that will be included 141 * inline in the initial parcelling of this object. 142 */ setInlineCountLimit(int maxCount)143 public void setInlineCountLimit(int maxCount) { 144 mInlineCountLimit = maxCount; 145 } 146 147 /** 148 * Write this to another Parcel. Note that this discards the internal Parcel 149 * and should not be used anymore. This is so we can pass this to a Binder 150 * where we won't have a chance to call recycle on this. 151 */ 152 @Override writeToParcel(Parcel dest, int flags)153 public void writeToParcel(Parcel dest, int flags) { 154 final int N = mList.size(); 155 final int callFlags = flags; 156 dest.writeInt(N); 157 if (DEBUG) Log.d(TAG, "Writing " + N + " items"); 158 if (N > 0) { 159 final Class<?> listElementClass = mList.get(0).getClass(); 160 writeParcelableCreator(mList.get(0), dest); 161 int i = 0; 162 while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) { 163 dest.writeInt(1); 164 165 final T parcelable = mList.get(i); 166 verifySameType(listElementClass, parcelable.getClass()); 167 writeElement(parcelable, dest, callFlags); 168 169 if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); 170 i++; 171 } 172 if (i < N) { 173 dest.writeInt(0); 174 Binder retriever = new Binder() { 175 @Override 176 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 177 throws RemoteException { 178 if (code != FIRST_CALL_TRANSACTION) { 179 return super.onTransact(code, data, reply, flags); 180 } 181 int i = data.readInt(); 182 if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); 183 while (i < N && reply.dataSize() < MAX_IPC_SIZE) { 184 reply.writeInt(1); 185 186 final T parcelable = mList.get(i); 187 verifySameType(listElementClass, parcelable.getClass()); 188 writeElement(parcelable, reply, callFlags); 189 190 if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); 191 i++; 192 } 193 if (i < N) { 194 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); 195 reply.writeInt(0); 196 } 197 return true; 198 } 199 }; 200 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); 201 dest.writeStrongBinder(retriever); 202 } 203 } 204 } 205 writeElement(T parcelable, Parcel reply, int callFlags)206 protected abstract void writeElement(T parcelable, Parcel reply, int callFlags); 207 writeParcelableCreator(T parcelable, Parcel dest)208 protected abstract void writeParcelableCreator(T parcelable, Parcel dest); 209 readParcelableCreator(Parcel from, ClassLoader loader)210 protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader); 211 } 212