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 * @hide 34 */ 35 public class ParceledListSlice<T extends Parcelable> implements Parcelable { 36 private static String TAG = "ParceledListSlice"; 37 private static boolean DEBUG = false; 38 39 /* 40 * TODO get this number from somewhere else. For now set it to a quarter of 41 * the 1MB limit. 42 */ 43 private static final int MAX_IPC_SIZE = 256 * 1024; 44 private static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2; 45 46 private final List<T> mList; 47 48 public ParceledListSlice(List<T> list) { 49 mList = list; 50 } 51 52 private ParceledListSlice(Parcel p, ClassLoader loader) { 53 final int N = p.readInt(); 54 mList = new ArrayList<T>(N); 55 if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); 56 if (N <= 0) { 57 return; 58 } 59 Parcelable.Creator<T> creator = p.readParcelableCreator(loader); 60 int i = 0; 61 while (i < N) { 62 if (p.readInt() == 0) { 63 break; 64 } 65 mList.add(p.readCreator(creator, loader)); 66 if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1)); 67 i++; 68 } 69 if (i >= N) { 70 return; 71 } 72 final IBinder retriever = p.readStrongBinder(); 73 while (i < N) { 74 if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever); 75 Parcel data = Parcel.obtain(); 76 Parcel reply = Parcel.obtain(); 77 data.writeInt(i); 78 try { 79 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0); 80 } catch (RemoteException e) { 81 Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e); 82 return; 83 } 84 while (i < N && reply.readInt() != 0) { 85 mList.add(reply.readCreator(creator, loader)); 86 if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1)); 87 i++; 88 } 89 reply.recycle(); 90 data.recycle(); 91 } 92 } 93 94 public List<T> getList() { 95 return mList; 96 } 97 98 @Override 99 public int describeContents() { 100 int contents = 0; 101 for (int i=0; i<mList.size(); i++) { 102 contents |= mList.get(i).describeContents(); 103 } 104 return contents; 105 } 106 107 /** 108 * Write this to another Parcel. Note that this discards the internal Parcel 109 * and should not be used anymore. This is so we can pass this to a Binder 110 * where we won't have a chance to call recycle on this. 111 */ 112 @Override 113 public void writeToParcel(Parcel dest, int flags) { 114 final int N = mList.size(); 115 final int callFlags = flags; 116 dest.writeInt(N); 117 if (DEBUG) Log.d(TAG, "Writing " + N + " items"); 118 if (N > 0) { 119 dest.writeParcelableCreator(mList.get(0)); 120 int i = 0; 121 while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) { 122 dest.writeInt(1); 123 mList.get(i).writeToParcel(dest, callFlags); 124 if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i)); 125 i++; 126 } 127 if (i < N) { 128 dest.writeInt(0); 129 Binder retriever = new Binder() { 130 @Override 131 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 132 throws RemoteException { 133 if (code != FIRST_CALL_TRANSACTION) { 134 return super.onTransact(code, data, reply, flags); 135 } 136 int i = data.readInt(); 137 if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); 138 while (i < N && reply.dataSize() < MAX_IPC_SIZE) { 139 reply.writeInt(1); 140 mList.get(i).writeToParcel(reply, callFlags); 141 if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i)); 142 i++; 143 } 144 if (i < N) { 145 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); 146 reply.writeInt(0); 147 } 148 return true; 149 } 150 }; 151 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever); 152 dest.writeStrongBinder(retriever); 153 } 154 } 155 } 156 157 @SuppressWarnings("unchecked") 158 public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR = 159 new Parcelable.ClassLoaderCreator<ParceledListSlice>() { 160 public ParceledListSlice createFromParcel(Parcel in) { 161 return new ParceledListSlice(in, null); 162 } 163 164 @Override 165 public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) { 166 return new ParceledListSlice(in, loader); 167 } 168 169 public ParceledListSlice[] newArray(int size) { 170 return new ParceledListSlice[size]; 171 } 172 }; 173 } 174