blob: c4e4e06be74979f86de7c7190d8686d46856623f [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
53 public BaseParceledListSlice(List<T> list) {
54 mList = list;
55 }
56
57 @SuppressWarnings("unchecked")
58 BaseParceledListSlice(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<?> creator = readParcelableCreator(p, 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 = readCreator(creator, p, 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 T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
117 if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
118 Parcelable.ClassLoaderCreator<?> classLoaderCreator =
119 (Parcelable.ClassLoaderCreator<?>) creator;
120 return (T) classLoaderCreator.createFromParcel(p, loader);
121 }
122 return (T) creator.createFromParcel(p);
123 }
124
125 private static void verifySameType(final Class<?> expected, final Class<?> actual) {
126 if (!actual.equals(expected)) {
127 throw new IllegalArgumentException("Can't unparcel type "
128 + actual.getName() + " in list of type "
129 + expected.getName());
130 }
131 }
132
133 public List<T> getList() {
134 return mList;
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 writeParcelableCreator(mList.get(0), dest);
151 int i = 0;
152 while (i < N && dest.dataSize() < MAX_IPC_SIZE) {
153 dest.writeInt(1);
154
155 final T parcelable = mList.get(i);
156 verifySameType(listElementClass, parcelable.getClass());
157 writeElement(parcelable, 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 writeElement(parcelable, 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 protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);
197
198 protected abstract void writeParcelableCreator(T parcelable, Parcel dest);
199
200 protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
201}