blob: 40eceb8a04e1f14f60736753a1129a43f23f5fd0 [file] [log] [blame]
Craig Mautner719e6b12014-04-04 20:29:41 -07001/*
2 * Copyright (C) 2014 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.os;
18
Scott Kennedyc6a65dff2015-03-01 17:10:10 -080019import android.annotation.Nullable;
Craig Mautner719e6b12014-04-04 20:29:41 -070020import android.util.ArrayMap;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080021import android.util.proto.ProtoOutputStream;
Jeff Sharkeyd136e512016-03-09 22:30:56 -070022
Craig Mautner21d24a22014-04-23 11:45:37 -070023import com.android.internal.util.XmlUtils;
Jeff Sharkeyd136e512016-03-09 22:30:56 -070024
Craig Mautner21d24a22014-04-23 11:45:37 -070025import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27import org.xmlpull.v1.XmlSerializer;
Craig Mautner719e6b12014-04-04 20:29:41 -070028
Craig Mautner21d24a22014-04-23 11:45:37 -070029import java.io.IOException;
Dianne Hackbornba604732016-02-10 17:05:10 -080030import java.util.ArrayList;
Craig Mautner719e6b12014-04-04 20:29:41 -070031
32/**
Jeff Sharkeyd136e512016-03-09 22:30:56 -070033 * A mapping from String keys to values of various types. The set of types
34 * supported by this class is purposefully restricted to simple objects that can
35 * safely be persisted to and restored from disk.
Craig Mautner719e6b12014-04-04 20:29:41 -070036 *
Jeff Sharkeyd136e512016-03-09 22:30:56 -070037 * @see Bundle
Craig Mautner719e6b12014-04-04 20:29:41 -070038 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -070039public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
40 XmlUtils.WriteMapCallback {
Craig Mautner21d24a22014-04-23 11:45:37 -070041 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
Craig Mautner719e6b12014-04-04 20:29:41 -070042 public static final PersistableBundle EMPTY;
Craig Mautner719e6b12014-04-04 20:29:41 -070043
44 static {
45 EMPTY = new PersistableBundle();
46 EMPTY.mMap = ArrayMap.EMPTY;
Craig Mautner719e6b12014-04-04 20:29:41 -070047 }
48
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070049 /** @hide */
50 public static boolean isValidType(Object value) {
51 return (value instanceof Integer) || (value instanceof Long) ||
52 (value instanceof Double) || (value instanceof String) ||
53 (value instanceof int[]) || (value instanceof long[]) ||
54 (value instanceof double[]) || (value instanceof String[]) ||
55 (value instanceof PersistableBundle) || (value == null) ||
56 (value instanceof Boolean) || (value instanceof boolean[]);
57 }
58
Craig Mautner719e6b12014-04-04 20:29:41 -070059 /**
60 * Constructs a new, empty PersistableBundle.
61 */
62 public PersistableBundle() {
63 super();
Jeff Sharkeyd136e512016-03-09 22:30:56 -070064 mFlags = FLAG_DEFUSABLE;
Craig Mautner719e6b12014-04-04 20:29:41 -070065 }
66
67 /**
Craig Mautner719e6b12014-04-04 20:29:41 -070068 * Constructs a new, empty PersistableBundle sized to hold the given number of
69 * elements. The PersistableBundle will grow as needed.
70 *
71 * @param capacity the initial capacity of the PersistableBundle
72 */
73 public PersistableBundle(int capacity) {
74 super(capacity);
Jeff Sharkeyd136e512016-03-09 22:30:56 -070075 mFlags = FLAG_DEFUSABLE;
Craig Mautner719e6b12014-04-04 20:29:41 -070076 }
77
78 /**
79 * Constructs a PersistableBundle containing a copy of the mappings from the given
Dianne Hackborn2510b372017-03-03 17:01:38 -080080 * PersistableBundle. Does only a shallow copy of the original PersistableBundle -- see
81 * {@link #deepCopy()} if that is not what you want.
Craig Mautner719e6b12014-04-04 20:29:41 -070082 *
83 * @param b a PersistableBundle to be copied.
Dianne Hackborn2510b372017-03-03 17:01:38 -080084 *
85 * @see #deepCopy()
Craig Mautner719e6b12014-04-04 20:29:41 -070086 */
87 public PersistableBundle(PersistableBundle b) {
88 super(b);
Jeff Sharkeyd136e512016-03-09 22:30:56 -070089 mFlags = b.mFlags;
Craig Mautner719e6b12014-04-04 20:29:41 -070090 }
91
Makoto Onuki6f7362d92016-03-04 13:39:41 -080092
93 /**
Dianne Hackborn2510b372017-03-03 17:01:38 -080094 * Constructs a PersistableBundle from a Bundle. Does only a shallow copy of the Bundle.
Makoto Onuki6f7362d92016-03-04 13:39:41 -080095 *
96 * @param b a Bundle to be copied.
97 *
98 * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
Makoto Onuki55046222016-03-08 10:49:47 -080099 *
100 * @hide
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800101 */
102 public PersistableBundle(Bundle b) {
103 this(b.getMap());
104 }
105
Craig Mautner719e6b12014-04-04 20:29:41 -0700106 /**
Craig Mautner21d24a22014-04-23 11:45:37 -0700107 * Constructs a PersistableBundle containing the mappings passed in.
108 *
109 * @param map a Map containing only those items that can be persisted.
110 * @throws IllegalArgumentException if any element of #map cannot be persisted.
111 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700112 private PersistableBundle(ArrayMap<String, Object> map) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700113 super();
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700114 mFlags = FLAG_DEFUSABLE;
Craig Mautner21d24a22014-04-23 11:45:37 -0700115
116 // First stuff everything in.
117 putAll(map);
118
119 // Now verify each item throwing an exception if there is a violation.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700120 final int N = mMap.size();
121 for (int i=0; i<N; i++) {
122 Object value = mMap.valueAt(i);
123 if (value instanceof ArrayMap) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700124 // Fix up any Maps by replacing them with PersistableBundles.
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700125 mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800126 } else if (value instanceof Bundle) {
127 mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700128 } else if (!isValidType(value)) {
129 throw new IllegalArgumentException("Bad value in PersistableBundle key="
130 + mMap.keyAt(i) + " value=" + value);
Craig Mautner21d24a22014-04-23 11:45:37 -0700131 }
132 }
133 }
134
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700135 /* package */ PersistableBundle(Parcel parcelledData, int length) {
136 super(parcelledData, length);
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700137 mFlags = FLAG_DEFUSABLE;
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700138 }
139
Craig Mautner21d24a22014-04-23 11:45:37 -0700140 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800141 * Constructs a PersistableBundle without initializing it.
142 */
143 PersistableBundle(boolean doInit) {
144 super(doInit);
145 }
146
147 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700148 * Make a PersistableBundle for a single key/value pair.
149 *
150 * @hide
151 */
152 public static PersistableBundle forPair(String key, String value) {
153 PersistableBundle b = new PersistableBundle(1);
154 b.putString(key, value);
155 return b;
156 }
157
158 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700159 * Clones the current PersistableBundle. The internal map is cloned, but the keys and
160 * values to which it refers are copied by reference.
161 */
162 @Override
163 public Object clone() {
164 return new PersistableBundle(this);
165 }
166
167 /**
Dianne Hackbornba604732016-02-10 17:05:10 -0800168 * Make a deep copy of the given bundle. Traverses into inner containers and copies
169 * them as well, so they are not shared across bundles. Will traverse in to
170 * {@link Bundle}, {@link PersistableBundle}, {@link ArrayList}, and all types of
171 * primitive arrays. Other types of objects (such as Parcelable or Serializable)
172 * are referenced as-is and not copied in any way.
173 */
Dianne Hackborn2510b372017-03-03 17:01:38 -0800174 public PersistableBundle deepCopy() {
Dianne Hackbornba604732016-02-10 17:05:10 -0800175 PersistableBundle b = new PersistableBundle(false);
176 b.copyInternal(this, true);
177 return b;
178 }
179
180 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700181 * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
182 * any existing value for the given key. Either key or value may be null.
183 *
184 * @param key a String, or null
185 * @param value a Bundle object, or null
186 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800187 public void putPersistableBundle(@Nullable String key, @Nullable PersistableBundle value) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700188 unparcel();
189 mMap.put(key, value);
Craig Mautner719e6b12014-04-04 20:29:41 -0700190 }
191
192 /**
193 * Returns the value associated with the given key, or null if
194 * no mapping of the desired type exists for the given key or a null
195 * value is explicitly associated with the key.
196 *
197 * @param key a String, or null
198 * @return a Bundle value, or null
199 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800200 @Nullable
201 public PersistableBundle getPersistableBundle(@Nullable String key) {
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700202 unparcel();
203 Object o = mMap.get(key);
204 if (o == null) {
205 return null;
206 }
207 try {
208 return (PersistableBundle) o;
209 } catch (ClassCastException e) {
210 typeWarning(key, o, "Bundle", e);
211 return null;
212 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700213 }
214
215 public static final Parcelable.Creator<PersistableBundle> CREATOR =
216 new Parcelable.Creator<PersistableBundle>() {
217 @Override
218 public PersistableBundle createFromParcel(Parcel in) {
219 return in.readPersistableBundle();
220 }
221
222 @Override
223 public PersistableBundle[] newArray(int size) {
224 return new PersistableBundle[size];
225 }
226 };
227
Craig Mautner21d24a22014-04-23 11:45:37 -0700228 /** @hide */
229 @Override
230 public void writeUnknownObject(Object v, String name, XmlSerializer out)
231 throws XmlPullParserException, IOException {
232 if (v instanceof PersistableBundle) {
233 out.startTag(null, TAG_PERSISTABLEMAP);
234 out.attribute(null, "name", name);
235 ((PersistableBundle) v).saveToXml(out);
236 out.endTag(null, TAG_PERSISTABLEMAP);
237 } else {
238 throw new XmlPullParserException("Unknown Object o=" + v);
239 }
240 }
241
242 /** @hide */
243 public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
244 unparcel();
245 XmlUtils.writeMapXml(mMap, out, this);
246 }
247
248 /** @hide */
249 static class MyReadMapCallback implements XmlUtils.ReadMapCallback {
250 @Override
251 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
252 throws XmlPullParserException, IOException {
253 if (TAG_PERSISTABLEMAP.equals(tag)) {
254 return restoreFromXml(in);
255 }
256 throw new XmlPullParserException("Unknown tag=" + tag);
257 }
258 }
259
260 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700261 * Report the nature of this Parcelable's contents
Craig Mautner21d24a22014-04-23 11:45:37 -0700262 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700263 @Override
264 public int describeContents() {
265 return 0;
266 }
267
268 /**
269 * Writes the PersistableBundle contents to a Parcel, typically in order for
270 * it to be passed through an IBinder connection.
271 * @param parcel The parcel to copy this bundle to.
272 */
273 @Override
274 public void writeToParcel(Parcel parcel, int flags) {
275 final boolean oldAllowFds = parcel.pushAllowFds(false);
276 try {
277 writeToParcelInner(parcel, flags);
278 } finally {
279 parcel.restoreAllowFds(oldAllowFds);
280 }
281 }
282
283 /** @hide */
Craig Mautner21d24a22014-04-23 11:45:37 -0700284 public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
285 XmlPullParserException {
286 final int outerDepth = in.getDepth();
287 final String startTag = in.getName();
288 final String[] tagName = new String[1];
289 int event;
290 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
291 (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
292 if (event == XmlPullParser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700293 return new PersistableBundle((ArrayMap<String, Object>)
294 XmlUtils.readThisArrayMapXml(in, startTag, tagName,
295 new MyReadMapCallback()));
Craig Mautner21d24a22014-04-23 11:45:37 -0700296 }
297 }
298 return EMPTY;
299 }
300
Craig Mautner719e6b12014-04-04 20:29:41 -0700301 @Override
302 synchronized public String toString() {
303 if (mParcelledData != null) {
Andreas Gampe52764cb2016-04-19 20:46:43 -0700304 if (isEmptyParcel()) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700305 return "PersistableBundle[EMPTY_PARCEL]";
306 } else {
307 return "PersistableBundle[mParcelledData.dataSize=" +
308 mParcelledData.dataSize() + "]";
309 }
310 }
311 return "PersistableBundle[" + mMap.toString() + "]";
312 }
Dianne Hackborna47223f2017-03-30 13:49:13 -0700313
314 /** @hide */
315 synchronized public String toShortString() {
316 if (mParcelledData != null) {
317 if (isEmptyParcel()) {
318 return "EMPTY_PARCEL";
319 } else {
320 return "mParcelledData.dataSize=" + mParcelledData.dataSize();
321 }
322 }
323 return mMap.toString();
324 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800325
326 /** @hide */
327 public void writeToProto(ProtoOutputStream proto, long fieldId) {
328 final long token = proto.start(fieldId);
329
330 if (mParcelledData != null) {
331 if (isEmptyParcel()) {
332 proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, 0);
333 } else {
334 proto.write(PersistableBundleProto.PARCELLED_DATA_SIZE, mParcelledData.dataSize());
335 }
336 } else {
337 proto.write(PersistableBundleProto.MAP_DATA, mMap.toString());
338 }
339
340 proto.end(token);
341 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700342}