PersistableBundle.java revision 21d24a21ea4aaadd78e73de54168e8a8a8973e4d
1/*
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
19import android.util.ArrayMap;
20import com.android.internal.util.XmlUtils;
21import org.xmlpull.v1.XmlPullParser;
22import org.xmlpull.v1.XmlPullParserException;
23import org.xmlpull.v1.XmlSerializer;
24
25import java.io.IOException;
26import java.util.Iterator;
27import java.util.Map;
28import java.util.Set;
29
30/**
31 * A mapping from String values to various types that can be saved to persistent and later
32 * restored.
33 *
34 */
35public final class PersistableBundle extends CommonBundle implements XmlUtils.WriteMapCallback {
36    private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
37    public static final PersistableBundle EMPTY;
38    static final Parcel EMPTY_PARCEL;
39
40    static {
41        EMPTY = new PersistableBundle();
42        EMPTY.mMap = ArrayMap.EMPTY;
43        EMPTY_PARCEL = CommonBundle.EMPTY_PARCEL;
44    }
45
46    /**
47     * Constructs a new, empty PersistableBundle.
48     */
49    public PersistableBundle() {
50        super();
51    }
52
53    /**
54     * Constructs a PersistableBundle whose data is stored as a Parcel.  The data
55     * will be unparcelled on first contact, using the assigned ClassLoader.
56     *
57     * @param parcelledData a Parcel containing a PersistableBundle
58     */
59    PersistableBundle(Parcel parcelledData) {
60        super(parcelledData);
61    }
62
63    /* package */ PersistableBundle(Parcel parcelledData, int length) {
64        super(parcelledData, length);
65    }
66
67    /**
68     * Constructs a new, empty PersistableBundle that uses a specific ClassLoader for
69     * instantiating Parcelable and Serializable objects.
70     *
71     * @param loader An explicit ClassLoader to use when instantiating objects
72     * inside of the PersistableBundle.
73     */
74    public PersistableBundle(ClassLoader loader) {
75        super(loader);
76    }
77
78    /**
79     * Constructs a new, empty PersistableBundle sized to hold the given number of
80     * elements. The PersistableBundle will grow as needed.
81     *
82     * @param capacity the initial capacity of the PersistableBundle
83     */
84    public PersistableBundle(int capacity) {
85        super(capacity);
86    }
87
88    /**
89     * Constructs a PersistableBundle containing a copy of the mappings from the given
90     * PersistableBundle.
91     *
92     * @param b a PersistableBundle to be copied.
93     */
94    public PersistableBundle(PersistableBundle b) {
95        super(b);
96    }
97
98    /**
99     * Constructs a PersistableBundle containing the mappings passed in.
100     *
101     * @param map a Map containing only those items that can be persisted.
102     * @throws IllegalArgumentException if any element of #map cannot be persisted.
103     */
104    private PersistableBundle(Map<String, Object> map) {
105        super();
106
107        // First stuff everything in.
108        putAll(map);
109
110        // Now verify each item throwing an exception if there is a violation.
111        Set<String> keys = map.keySet();
112        Iterator<String> iterator = keys.iterator();
113        while (iterator.hasNext()) {
114            String key = iterator.next();
115            Object value = map.get(key);
116            if (value instanceof Map) {
117                // Fix up any Maps by replacing them with PersistableBundles.
118                putPersistableBundle(key, new PersistableBundle((Map<String, Object>) value));
119            } else if (!(value instanceof Integer) && !(value instanceof Long) &&
120                    !(value instanceof Double) && !(value instanceof String) &&
121                    !(value instanceof int[]) && !(value instanceof long[]) &&
122                    !(value instanceof double[]) && !(value instanceof String[]) &&
123                    !(value instanceof PersistableBundle) && (value != null)) {
124                throw new IllegalArgumentException("Bad value in PersistableBundle key=" + key +
125                        " value=" + value);
126            }
127        }
128    }
129
130    /**
131     * Make a PersistableBundle for a single key/value pair.
132     *
133     * @hide
134     */
135    public static PersistableBundle forPair(String key, String value) {
136        PersistableBundle b = new PersistableBundle(1);
137        b.putString(key, value);
138        return b;
139    }
140
141    /**
142     * @hide
143     */
144    @Override
145    public String getPairValue() {
146        return super.getPairValue();
147    }
148
149    /**
150     * Changes the ClassLoader this PersistableBundle uses when instantiating objects.
151     *
152     * @param loader An explicit ClassLoader to use when instantiating objects
153     * inside of the PersistableBundle.
154     */
155    @Override
156    public void setClassLoader(ClassLoader loader) {
157        super.setClassLoader(loader);
158    }
159
160    /**
161     * Return the ClassLoader currently associated with this PersistableBundle.
162     */
163    @Override
164    public ClassLoader getClassLoader() {
165        return super.getClassLoader();
166    }
167
168    /**
169     * Clones the current PersistableBundle. The internal map is cloned, but the keys and
170     * values to which it refers are copied by reference.
171     */
172    @Override
173    public Object clone() {
174        return new PersistableBundle(this);
175    }
176
177    /**
178     * @hide
179     */
180    @Override
181    public boolean isParcelled() {
182        return super.isParcelled();
183    }
184
185    /**
186     * Returns the number of mappings contained in this PersistableBundle.
187     *
188     * @return the number of mappings as an int.
189     */
190    @Override
191    public int size() {
192        return super.size();
193    }
194
195    /**
196     * Returns true if the mapping of this PersistableBundle is empty, false otherwise.
197     */
198    @Override
199    public boolean isEmpty() {
200        return super.isEmpty();
201    }
202
203    /**
204     * Removes all elements from the mapping of this PersistableBundle.
205     */
206    @Override
207    public void clear() {
208        super.clear();
209    }
210
211    /**
212     * Returns true if the given key is contained in the mapping
213     * of this PersistableBundle.
214     *
215     * @param key a String key
216     * @return true if the key is part of the mapping, false otherwise
217     */
218    @Override
219    public boolean containsKey(String key) {
220        return super.containsKey(key);
221    }
222
223    /**
224     * Returns the entry with the given key as an object.
225     *
226     * @param key a String key
227     * @return an Object, or null
228     */
229    @Override
230    public Object get(String key) {
231        return super.get(key);
232    }
233
234    /**
235     * Removes any entry with the given key from the mapping of this PersistableBundle.
236     *
237     * @param key a String key
238     */
239    @Override
240    public void remove(String key) {
241        super.remove(key);
242    }
243
244    /**
245     * Inserts all mappings from the given PersistableBundle into this Bundle.
246     *
247     * @param bundle a PersistableBundle
248     */
249    @Override
250    public void putAll(PersistableBundle bundle) {
251        super.putAll(bundle);
252    }
253
254    /**
255     * Returns a Set containing the Strings used as keys in this PersistableBundle.
256     *
257     * @return a Set of String keys
258     */
259    @Override
260    public Set<String> keySet() {
261        return super.keySet();
262    }
263
264    /**
265     * Inserts an int value into the mapping of this PersistableBundle, replacing
266     * any existing value for the given key.
267     *
268     * @param key a String, or null
269     * @param value an int, or null
270     */
271    @Override
272    public void putInt(String key, int value) {
273        super.putInt(key, value);
274    }
275
276    /**
277     * Inserts a long value into the mapping of this PersistableBundle, replacing
278     * any existing value for the given key.
279     *
280     * @param key a String, or null
281     * @param value a long
282     */
283    @Override
284    public void putLong(String key, long value) {
285        super.putLong(key, value);
286    }
287
288    /**
289     * Inserts a double value into the mapping of this PersistableBundle, replacing
290     * any existing value for the given key.
291     *
292     * @param key a String, or null
293     * @param value a double
294     */
295    @Override
296    public void putDouble(String key, double value) {
297        super.putDouble(key, value);
298    }
299
300    /**
301     * Inserts a String value into the mapping of this PersistableBundle, replacing
302     * any existing value for the given key.  Either key or value may be null.
303     *
304     * @param key a String, or null
305     * @param value a String, or null
306     */
307    @Override
308    public void putString(String key, String value) {
309        super.putString(key, value);
310    }
311
312    /**
313     * Inserts an int array value into the mapping of this PersistableBundle, replacing
314     * any existing value for the given key.  Either key or value may be null.
315     *
316     * @param key a String, or null
317     * @param value an int array object, or null
318     */
319    @Override
320    public void putIntArray(String key, int[] value) {
321        super.putIntArray(key, value);
322    }
323
324    /**
325     * Inserts a long array value into the mapping of this PersistableBundle, replacing
326     * any existing value for the given key.  Either key or value may be null.
327     *
328     * @param key a String, or null
329     * @param value a long array object, or null
330     */
331    @Override
332    public void putLongArray(String key, long[] value) {
333        super.putLongArray(key, value);
334    }
335
336    /**
337     * Inserts a double array value into the mapping of this PersistableBundle, replacing
338     * any existing value for the given key.  Either key or value may be null.
339     *
340     * @param key a String, or null
341     * @param value a double array object, or null
342     */
343    @Override
344    public void putDoubleArray(String key, double[] value) {
345        super.putDoubleArray(key, value);
346    }
347
348    /**
349     * Inserts a String array value into the mapping of this PersistableBundle, replacing
350     * any existing value for the given key.  Either key or value may be null.
351     *
352     * @param key a String, or null
353     * @param value a String array object, or null
354     */
355    @Override
356    public void putStringArray(String key, String[] value) {
357        super.putStringArray(key, value);
358    }
359
360    /**
361     * Inserts a PersistableBundle value into the mapping of this Bundle, replacing
362     * any existing value for the given key.  Either key or value may be null.
363     *
364     * @param key a String, or null
365     * @param value a Bundle object, or null
366     */
367    @Override
368    public void putPersistableBundle(String key, PersistableBundle value) {
369        super.putPersistableBundle(key, value);
370    }
371
372    /**
373     * Returns the value associated with the given key, or 0 if
374     * no mapping of the desired type exists for the given key.
375     *
376     * @param key a String
377     * @return an int value
378     */
379    @Override
380    public int getInt(String key) {
381        return super.getInt(key);
382    }
383
384    /**
385     * Returns the value associated with the given key, or defaultValue if
386     * no mapping of the desired type exists for the given key.
387     *
388     * @param key a String
389     * @param defaultValue Value to return if key does not exist
390     * @return an int value
391     */
392    @Override
393    public int getInt(String key, int defaultValue) {
394        return super.getInt(key, defaultValue);
395    }
396
397    /**
398     * Returns the value associated with the given key, or 0L if
399     * no mapping of the desired type exists for the given key.
400     *
401     * @param key a String
402     * @return a long value
403     */
404    @Override
405    public long getLong(String key) {
406        return super.getLong(key);
407    }
408
409    /**
410     * Returns the value associated with the given key, or defaultValue if
411     * no mapping of the desired type exists for the given key.
412     *
413     * @param key a String
414     * @param defaultValue Value to return if key does not exist
415     * @return a long value
416     */
417    @Override
418    public long getLong(String key, long defaultValue) {
419        return super.getLong(key, defaultValue);
420    }
421
422    /**
423     * Returns the value associated with the given key, or 0.0 if
424     * no mapping of the desired type exists for the given key.
425     *
426     * @param key a String
427     * @return a double value
428     */
429    @Override
430    public double getDouble(String key) {
431        return super.getDouble(key);
432    }
433
434    /**
435     * Returns the value associated with the given key, or defaultValue if
436     * no mapping of the desired type exists for the given key.
437     *
438     * @param key a String
439     * @param defaultValue Value to return if key does not exist
440     * @return a double value
441     */
442    @Override
443    public double getDouble(String key, double defaultValue) {
444        return super.getDouble(key, defaultValue);
445    }
446
447    /**
448     * Returns the value associated with the given key, or null if
449     * no mapping of the desired type exists for the given key or a null
450     * value is explicitly associated with the key.
451     *
452     * @param key a String, or null
453     * @return a String value, or null
454     */
455    @Override
456    public String getString(String key) {
457        return super.getString(key);
458    }
459
460    /**
461     * Returns the value associated with the given key, or defaultValue if
462     * no mapping of the desired type exists for the given key.
463     *
464     * @param key a String, or null
465     * @param defaultValue Value to return if key does not exist
466     * @return the String value associated with the given key, or defaultValue
467     *     if no valid String object is currently mapped to that key.
468     */
469    @Override
470    public String getString(String key, String defaultValue) {
471        return super.getString(key, defaultValue);
472    }
473
474    /**
475     * Returns the value associated with the given key, or null if
476     * no mapping of the desired type exists for the given key or a null
477     * value is explicitly associated with the key.
478     *
479     * @param key a String, or null
480     * @return a Bundle value, or null
481     */
482    @Override
483    public PersistableBundle getPersistableBundle(String key) {
484        return super.getPersistableBundle(key);
485    }
486
487    /**
488     * Returns the value associated with the given key, or null if
489     * no mapping of the desired type exists for the given key or a null
490     * value is explicitly associated with the key.
491     *
492     * @param key a String, or null
493     * @return an int[] value, or null
494     */
495    @Override
496    public int[] getIntArray(String key) {
497        return super.getIntArray(key);
498    }
499
500    /**
501     * Returns the value associated with the given key, or null if
502     * no mapping of the desired type exists for the given key or a null
503     * value is explicitly associated with the key.
504     *
505     * @param key a String, or null
506     * @return a long[] value, or null
507     */
508    @Override
509    public long[] getLongArray(String key) {
510        return super.getLongArray(key);
511    }
512
513    /**
514     * Returns the value associated with the given key, or null if
515     * no mapping of the desired type exists for the given key or a null
516     * value is explicitly associated with the key.
517     *
518     * @param key a String, or null
519     * @return a double[] value, or null
520     */
521    @Override
522    public double[] getDoubleArray(String key) {
523        return super.getDoubleArray(key);
524    }
525
526    /**
527     * Returns the value associated with the given key, or null if
528     * no mapping of the desired type exists for the given key or a null
529     * value is explicitly associated with the key.
530     *
531     * @param key a String, or null
532     * @return a String[] value, or null
533     */
534    @Override
535    public String[] getStringArray(String key) {
536        return super.getStringArray(key);
537    }
538
539    public static final Parcelable.Creator<PersistableBundle> CREATOR =
540            new Parcelable.Creator<PersistableBundle>() {
541                @Override
542                public PersistableBundle createFromParcel(Parcel in) {
543                    return in.readPersistableBundle();
544                }
545
546                @Override
547                public PersistableBundle[] newArray(int size) {
548                    return new PersistableBundle[size];
549                }
550            };
551
552    /**
553     * Report the nature of this Parcelable's contents
554     */
555    @Override
556    public int describeContents() {
557        return 0;
558    }
559
560    /**
561     * Writes the PersistableBundle contents to a Parcel, typically in order for
562     * it to be passed through an IBinder connection.
563     * @param parcel The parcel to copy this bundle to.
564     */
565    @Override
566    public void writeToParcel(Parcel parcel, int flags) {
567        final boolean oldAllowFds = parcel.pushAllowFds(false);
568        try {
569            super.writeToParcelInner(parcel, flags);
570        } finally {
571            parcel.restoreAllowFds(oldAllowFds);
572        }
573    }
574
575    /**
576     * Reads the Parcel contents into this PersistableBundle, typically in order for
577     * it to be passed through an IBinder connection.
578     * @param parcel The parcel to overwrite this bundle from.
579     */
580    public void readFromParcel(Parcel parcel) {
581        super.readFromParcelInner(parcel);
582    }
583
584    /** @hide */
585    @Override
586    public void writeUnknownObject(Object v, String name, XmlSerializer out)
587            throws XmlPullParserException, IOException {
588        if (v instanceof PersistableBundle) {
589            out.startTag(null, TAG_PERSISTABLEMAP);
590            out.attribute(null, "name", name);
591            ((PersistableBundle) v).saveToXml(out);
592            out.endTag(null, TAG_PERSISTABLEMAP);
593        } else {
594            throw new XmlPullParserException("Unknown Object o=" + v);
595        }
596    }
597
598    /** @hide */
599    public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
600        unparcel();
601        XmlUtils.writeMapXml(mMap, out, this);
602    }
603
604    /** @hide */
605    static class MyReadMapCallback implements  XmlUtils.ReadMapCallback {
606        @Override
607        public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
608                throws XmlPullParserException, IOException {
609            if (TAG_PERSISTABLEMAP.equals(tag)) {
610                return restoreFromXml(in);
611            }
612            throw new XmlPullParserException("Unknown tag=" + tag);
613        }
614    }
615
616    /**
617     * @hide
618     */
619    public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
620            XmlPullParserException {
621        final int outerDepth = in.getDepth();
622        final String startTag = in.getName();
623        final String[] tagName = new String[1];
624        int event;
625        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
626                (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
627            if (event == XmlPullParser.START_TAG) {
628                return new PersistableBundle((Map<String, Object>)
629                        XmlUtils.readThisMapXml(in, startTag, tagName, new MyReadMapCallback()));
630            }
631        }
632        return EMPTY;
633    }
634
635    @Override
636    synchronized public String toString() {
637        if (mParcelledData != null) {
638            if (mParcelledData == EMPTY_PARCEL) {
639                return "PersistableBundle[EMPTY_PARCEL]";
640            } else {
641                return "PersistableBundle[mParcelledData.dataSize=" +
642                        mParcelledData.dataSize() + "]";
643            }
644        }
645        return "PersistableBundle[" + mMap.toString() + "]";
646    }
647
648}
649