blob: 4178309450bd14d462d3482f3a359b019e23e6d1 [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
Mathew Inwood5c0d3542018-08-14 13:54:31 +010019import android.annotation.UnsupportedAppUsage;
Robin Leeabaa0692017-02-20 20:54:22 +000020import android.os.Binder;
21import android.os.IBinder;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.RemoteException;
25import android.util.Log;
26
27import java.util.ArrayList;
28import java.util.List;
29
30/**
31 * Transfer a large list of Parcelable objects across an IPC. Splits into
32 * multiple transactions if needed.
33 *
34 * Caveat: for efficiency and security, all elements must be the same concrete type.
35 * In order to avoid writing the class name of each object, we must ensure that
36 * each object is the same type, or else unparceling then reparceling the data may yield
37 * a different result if the class name encoded in the Parcelable is a Base type.
38 * See b/17671747.
39 *
40 * @hide
41 */
42abstract class BaseParceledListSlice<T> implements Parcelable {
43 private static String TAG = "ParceledListSlice";
44 private static boolean DEBUG = false;
45
46 /*
47 * TODO get this number from somewhere else. For now set it to a quarter of
48 * the 1MB limit.
49 */
50 private static final int MAX_IPC_SIZE = IBinder.MAX_IPC_SIZE;
51
52 private final List<T> mList;
53
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -070054 private int mInlineCountLimit = Integer.MAX_VALUE;
55
Robin Leeabaa0692017-02-20 20:54:22 +000056 public BaseParceledListSlice(List<T> list) {
57 mList = list;
58 }
59
60 @SuppressWarnings("unchecked")
61 BaseParceledListSlice(Parcel p, ClassLoader loader) {
62 final int N = p.readInt();
63 mList = new ArrayList<T>(N);
64 if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
65 if (N <= 0) {
66 return;
67 }
68
69 Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
70 Class<?> listElementClass = null;
71
72 int i = 0;
73 while (i < N) {
74 if (p.readInt() == 0) {
75 break;
76 }
77
78 final T parcelable = readCreator(creator, p, loader);
79 if (listElementClass == null) {
80 listElementClass = parcelable.getClass();
81 } else {
82 verifySameType(listElementClass, parcelable.getClass());
83 }
84
85 mList.add(parcelable);
86
87 if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
88 i++;
89 }
90 if (i >= N) {
91 return;
92 }
93 final IBinder retriever = p.readStrongBinder();
94 while (i < N) {
95 if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
96 Parcel data = Parcel.obtain();
97 Parcel reply = Parcel.obtain();
98 data.writeInt(i);
99 try {
100 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
101 } catch (RemoteException e) {
102 Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
103 return;
104 }
105 while (i < N && reply.readInt() != 0) {
Alexandru-Andrei Rotaru2682fa72017-08-18 15:54:36 +0100106 final T parcelable = readCreator(creator, reply, loader);
Robin Leeabaa0692017-02-20 20:54:22 +0000107 verifySameType(listElementClass, parcelable.getClass());
108
109 mList.add(parcelable);
110
111 if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
112 i++;
113 }
114 reply.recycle();
115 data.recycle();
116 }
117 }
118
119 private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
120 if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
121 Parcelable.ClassLoaderCreator<?> classLoaderCreator =
122 (Parcelable.ClassLoaderCreator<?>) creator;
123 return (T) classLoaderCreator.createFromParcel(p, loader);
124 }
125 return (T) creator.createFromParcel(p);
126 }
127
128 private static void verifySameType(final Class<?> expected, final Class<?> actual) {
129 if (!actual.equals(expected)) {
130 throw new IllegalArgumentException("Can't unparcel type "
Adam Hec78e5d72019-02-28 12:55:26 -0800131 + (actual == null ? null : actual.getName()) + " in list of type "
132 + (expected == null ? null : expected.getName()));
Robin Leeabaa0692017-02-20 20:54:22 +0000133 }
134 }
135
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100136 @UnsupportedAppUsage
Robin Leeabaa0692017-02-20 20:54:22 +0000137 public List<T> getList() {
138 return mList;
139 }
140
141 /**
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -0700142 * Set a limit on the maximum number of entries in the array that will be included
143 * inline in the initial parcelling of this object.
144 */
145 public void setInlineCountLimit(int maxCount) {
146 mInlineCountLimit = maxCount;
147 }
148
149 /**
Robin Leeabaa0692017-02-20 20:54:22 +0000150 * Write this to another Parcel. Note that this discards the internal Parcel
151 * and should not be used anymore. This is so we can pass this to a Binder
152 * where we won't have a chance to call recycle on this.
153 */
154 @Override
155 public void writeToParcel(Parcel dest, int flags) {
156 final int N = mList.size();
157 final int callFlags = flags;
158 dest.writeInt(N);
159 if (DEBUG) Log.d(TAG, "Writing " + N + " items");
160 if (N > 0) {
161 final Class<?> listElementClass = mList.get(0).getClass();
162 writeParcelableCreator(mList.get(0), dest);
163 int i = 0;
Dianne Hackborn3f7c9f22017-04-04 15:36:33 -0700164 while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
Robin Leeabaa0692017-02-20 20:54:22 +0000165 dest.writeInt(1);
166
167 final T parcelable = mList.get(i);
168 verifySameType(listElementClass, parcelable.getClass());
169 writeElement(parcelable, dest, callFlags);
170
171 if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
172 i++;
173 }
174 if (i < N) {
175 dest.writeInt(0);
176 Binder retriever = new Binder() {
177 @Override
178 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
179 throws RemoteException {
180 if (code != FIRST_CALL_TRANSACTION) {
181 return super.onTransact(code, data, reply, flags);
182 }
183 int i = data.readInt();
184 if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
185 while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
186 reply.writeInt(1);
187
188 final T parcelable = mList.get(i);
189 verifySameType(listElementClass, parcelable.getClass());
190 writeElement(parcelable, reply, callFlags);
191
192 if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
193 i++;
194 }
195 if (i < N) {
196 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
197 reply.writeInt(0);
198 }
199 return true;
200 }
201 };
202 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
203 dest.writeStrongBinder(retriever);
204 }
205 }
206 }
207
208 protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);
209
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100210 @UnsupportedAppUsage
Robin Leeabaa0692017-02-20 20:54:22 +0000211 protected abstract void writeParcelableCreator(T parcelable, Parcel dest);
212
213 protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
214}