blob: aaa5f19c3fca34cd78e1886cb71481e4717a80bf [file] [log] [blame]
Robin Leeabaa0692017-02-20 20:54:22 +00001/*
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.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 */
41abstract 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
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -070053 private int mInlineCountLimit = Integer.MAX_VALUE;
54
Robin Leeabaa0692017-02-20 20:54:22 +000055 public BaseParceledListSlice(List<T> list) {
56 mList = list;
57 }
58
59 @SuppressWarnings("unchecked")
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 = reply.readCreator(creator, 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
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
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
135 public List<T> getList() {
136 return mList;
137 }
138
139 /**
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -0700140 * 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 */
143 public void setInlineCountLimit(int maxCount) {
144 mInlineCountLimit = maxCount;
145 }
146
147 /**
Robin Leeabaa0692017-02-20 20:54:22 +0000148 * 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
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;
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -0700162 while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
Robin Leeabaa0692017-02-20 20:54:22 +0000163 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
206 protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);
207
208 protected abstract void writeParcelableCreator(T parcelable, Parcel dest);
209
210 protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
211}