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.Parcel; 20 import android.os.Parcelable; 21 22 import java.util.List; 23 24 /** 25 * Builds up a parcel that is discarded when written to another parcel or 26 * written to a list. This is useful for API that sends huge lists across a 27 * Binder that may be larger than the IPC limit. 28 * 29 * @hide 30 */ 31 public class ParceledListSlice<T extends Parcelable> implements Parcelable { 32 /* 33 * TODO get this number from somewhere else. For now set it to a quarter of 34 * the 1MB limit. 35 */ 36 private static final int MAX_IPC_SIZE = 256 * 1024; 37 38 private Parcel mParcel; 39 40 private int mNumItems; 41 42 private boolean mIsLastSlice; 43 44 public ParceledListSlice() { 45 mParcel = Parcel.obtain(); 46 } 47 48 private ParceledListSlice(Parcel p, int numItems, boolean lastSlice) { 49 mParcel = p; 50 mNumItems = numItems; 51 mIsLastSlice = lastSlice; 52 } 53 54 @Override 55 public int describeContents() { 56 return 0; 57 } 58 59 /** 60 * Write this to another Parcel. Note that this discards the internal Parcel 61 * and should not be used anymore. This is so we can pass this to a Binder 62 * where we won't have a chance to call recycle on this. 63 */ 64 @Override 65 public void writeToParcel(Parcel dest, int flags) { 66 dest.writeInt(mNumItems); 67 dest.writeInt(mIsLastSlice ? 1 : 0); 68 69 if (mNumItems > 0) { 70 final int parcelSize = mParcel.dataSize(); 71 dest.writeInt(parcelSize); 72 dest.appendFrom(mParcel, 0, parcelSize); 73 } 74 75 mNumItems = 0; 76 mParcel.recycle(); 77 mParcel = null; 78 } 79 80 /** 81 * Appends a parcel to this list slice. 82 * 83 * @param item Parcelable item to append to this list slice 84 * @return true when the list slice is full and should not be appended to 85 * anymore 86 */ 87 public boolean append(T item) { 88 if (mParcel == null) { 89 throw new IllegalStateException("ParceledListSlice has already been recycled"); 90 } 91 92 item.writeToParcel(mParcel, PARCELABLE_WRITE_RETURN_VALUE); 93 mNumItems++; 94 95 return mParcel.dataSize() > MAX_IPC_SIZE; 96 } 97 98 /** 99 * Populates a list and discards the internal state of the 100 * ParceledListSlice in the process. The instance should 101 * not be used anymore. 102 * 103 * @param list list to insert items from this slice. 104 * @param creator creator that knows how to unparcel the 105 * target object type. 106 * @return the last item inserted into the list or null if none. 107 */ 108 public T populateList(List<T> list, Creator<T> creator) { 109 mParcel.setDataPosition(0); 110 111 T item = null; 112 for (int i = 0; i < mNumItems; i++) { 113 item = creator.createFromParcel(mParcel); 114 list.add(item); 115 } 116 117 mParcel.recycle(); 118 mParcel = null; 119 120 return item; 121 } 122 123 /** 124 * Sets whether this is the last list slice in the series. 125 * 126 * @param lastSlice 127 */ 128 public void setLastSlice(boolean lastSlice) { 129 mIsLastSlice = lastSlice; 130 } 131 132 /** 133 * Returns whether this is the last slice in a series of slices. 134 * 135 * @return true if this is the last slice in the series. 136 */ 137 public boolean isLastSlice() { 138 return mIsLastSlice; 139 } 140 141 @SuppressWarnings("unchecked") 142 public static final Parcelable.Creator<ParceledListSlice> CREATOR = 143 new Parcelable.Creator<ParceledListSlice>() { 144 public ParceledListSlice createFromParcel(Parcel in) { 145 final int numItems = in.readInt(); 146 final boolean lastSlice = in.readInt() == 1; 147 148 if (numItems > 0) { 149 final int parcelSize = in.readInt(); 150 151 // Advance within this Parcel 152 int offset = in.dataPosition(); 153 in.setDataPosition(offset + parcelSize); 154 155 Parcel p = Parcel.obtain(); 156 p.setDataPosition(0); 157 p.appendFrom(in, offset, parcelSize); 158 p.setDataPosition(0); 159 160 return new ParceledListSlice(p, numItems, lastSlice); 161 } else { 162 return new ParceledListSlice(); 163 } 164 } 165 166 public ParceledListSlice[] newArray(int size) { 167 return new ParceledListSlice[size]; 168 } 169 }; 170 } 171