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.annotation.Nullable;
20import android.util.ArrayMap;
21import android.util.Log;
22import android.util.MathUtils;
23import android.util.Slog;
24
25import java.io.Serializable;
26import java.util.ArrayList;
27import java.util.Set;
28
29/**
30 * A mapping from String keys to values of various types. In most cases, you
31 * should work directly with either the {@link Bundle} or
32 * {@link PersistableBundle} subclass.
33 */
34public class BaseBundle {
35    private static final String TAG = "Bundle";
36    static final boolean DEBUG = false;
37
38    // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
39    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
40
41    /**
42     * Flag indicating that this Bundle is okay to "defuse." That is, it's okay
43     * for system processes to ignore any {@link BadParcelableException}
44     * encountered when unparceling it, leaving an empty bundle in its place.
45     * <p>
46     * This should <em>only</em> be set when the Bundle reaches its final
47     * destination, otherwise a system process may clobber contents that were
48     * destined for an app that could have unparceled them.
49     */
50    static final int FLAG_DEFUSABLE = 1 << 0;
51
52    private static final boolean LOG_DEFUSABLE = false;
53
54    private static volatile boolean sShouldDefuse = false;
55
56    /**
57     * Set global variable indicating that any Bundles parsed in this process
58     * should be "defused." That is, any {@link BadParcelableException}
59     * encountered will be suppressed and logged, leaving an empty Bundle
60     * instead of crashing.
61     *
62     * @hide
63     */
64    public static void setShouldDefuse(boolean shouldDefuse) {
65        sShouldDefuse = shouldDefuse;
66    }
67
68    // A parcel cannot be obtained during compile-time initialization. Put the
69    // empty parcel into an inner class that can be initialized separately. This
70    // allows to initialize BaseBundle, and classes depending on it.
71    /** {@hide} */
72    static final class NoImagePreloadHolder {
73        public static final Parcel EMPTY_PARCEL = Parcel.obtain();
74    }
75
76    // Invariant - exactly one of mMap / mParcelledData will be null
77    // (except inside a call to unparcel)
78
79    ArrayMap<String, Object> mMap = null;
80
81    /*
82     * If mParcelledData is non-null, then mMap will be null and the
83     * data are stored as a Parcel containing a Bundle.  When the data
84     * are unparcelled, mParcelledData willbe set to null.
85     */
86    Parcel mParcelledData = null;
87
88    /**
89     * The ClassLoader used when unparcelling data from mParcelledData.
90     */
91    private ClassLoader mClassLoader;
92
93    /** {@hide} */
94    int mFlags;
95
96    /**
97     * Constructs a new, empty Bundle that uses a specific ClassLoader for
98     * instantiating Parcelable and Serializable objects.
99     *
100     * @param loader An explicit ClassLoader to use when instantiating objects
101     * inside of the Bundle.
102     * @param capacity Initial size of the ArrayMap.
103     */
104    BaseBundle(@Nullable ClassLoader loader, int capacity) {
105        mMap = capacity > 0 ?
106                new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
107        mClassLoader = loader == null ? getClass().getClassLoader() : loader;
108    }
109
110    /**
111     * Constructs a new, empty Bundle.
112     */
113    BaseBundle() {
114        this((ClassLoader) null, 0);
115    }
116
117    /**
118     * Constructs a Bundle whose data is stored as a Parcel.  The data
119     * will be unparcelled on first contact, using the assigned ClassLoader.
120     *
121     * @param parcelledData a Parcel containing a Bundle
122     */
123    BaseBundle(Parcel parcelledData) {
124        readFromParcelInner(parcelledData);
125    }
126
127    BaseBundle(Parcel parcelledData, int length) {
128        readFromParcelInner(parcelledData, length);
129    }
130
131    /**
132     * Constructs a new, empty Bundle that uses a specific ClassLoader for
133     * instantiating Parcelable and Serializable objects.
134     *
135     * @param loader An explicit ClassLoader to use when instantiating objects
136     * inside of the Bundle.
137     */
138    BaseBundle(ClassLoader loader) {
139        this(loader, 0);
140    }
141
142    /**
143     * Constructs a new, empty Bundle sized to hold the given number of
144     * elements. The Bundle will grow as needed.
145     *
146     * @param capacity the initial capacity of the Bundle
147     */
148    BaseBundle(int capacity) {
149        this((ClassLoader) null, capacity);
150    }
151
152    /**
153     * Constructs a Bundle containing a copy of the mappings from the given
154     * Bundle.
155     *
156     * @param b a Bundle to be copied.
157     */
158    BaseBundle(BaseBundle b) {
159        copyInternal(b, false);
160    }
161
162    /**
163     * Special constructor that does not initialize the bundle.
164     */
165    BaseBundle(boolean doInit) {
166    }
167
168    /**
169     * TODO: optimize this later (getting just the value part of a Bundle
170     * with a single pair) once Bundle.forPair() above is implemented
171     * with a special single-value Map implementation/serialization.
172     *
173     * Note: value in single-pair Bundle may be null.
174     *
175     * @hide
176     */
177    public String getPairValue() {
178        unparcel();
179        int size = mMap.size();
180        if (size > 1) {
181            Log.w(TAG, "getPairValue() used on Bundle with multiple pairs.");
182        }
183        if (size == 0) {
184            return null;
185        }
186        Object o = mMap.valueAt(0);
187        try {
188            return (String) o;
189        } catch (ClassCastException e) {
190            typeWarning("getPairValue()", o, "String", e);
191            return null;
192        }
193    }
194
195    /**
196     * Changes the ClassLoader this Bundle uses when instantiating objects.
197     *
198     * @param loader An explicit ClassLoader to use when instantiating objects
199     * inside of the Bundle.
200     */
201    void setClassLoader(ClassLoader loader) {
202        mClassLoader = loader;
203    }
204
205    /**
206     * Return the ClassLoader currently associated with this Bundle.
207     */
208    ClassLoader getClassLoader() {
209        return mClassLoader;
210    }
211
212    /**
213     * If the underlying data are stored as a Parcel, unparcel them
214     * using the currently assigned class loader.
215     */
216    /* package */ void unparcel() {
217        synchronized (this) {
218            final Parcel parcelledData = mParcelledData;
219            if (parcelledData == null) {
220                if (DEBUG) Log.d(TAG, "unparcel "
221                        + Integer.toHexString(System.identityHashCode(this))
222                        + ": no parcelled data");
223                return;
224            }
225
226            if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
227                Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
228                        + "clobber all data inside!", new Throwable());
229            }
230
231            if (isEmptyParcel()) {
232                if (DEBUG) Log.d(TAG, "unparcel "
233                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
234                if (mMap == null) {
235                    mMap = new ArrayMap<>(1);
236                } else {
237                    mMap.erase();
238                }
239                mParcelledData = null;
240                return;
241            }
242
243            int N = parcelledData.readInt();
244            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
245                    + ": reading " + N + " maps");
246            if (N < 0) {
247                return;
248            }
249            ArrayMap<String, Object> map = mMap;
250            if (map == null) {
251                map = new ArrayMap<>(N);
252            } else {
253                map.erase();
254                map.ensureCapacity(N);
255            }
256            try {
257                parcelledData.readArrayMapInternal(map, N, mClassLoader);
258            } catch (BadParcelableException e) {
259                if (sShouldDefuse) {
260                    Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
261                    map.erase();
262                } else {
263                    throw e;
264                }
265            } finally {
266                mMap = map;
267                parcelledData.recycle();
268                mParcelledData = null;
269            }
270            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
271                    + " final map: " + mMap);
272        }
273    }
274
275    /**
276     * @hide
277     */
278    public boolean isParcelled() {
279        return mParcelledData != null;
280    }
281
282    /**
283     * @hide
284     */
285    public boolean isEmptyParcel() {
286        return mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL;
287    }
288
289    /** @hide */
290    ArrayMap<String, Object> getMap() {
291        unparcel();
292        return mMap;
293    }
294
295    /**
296     * Returns the number of mappings contained in this Bundle.
297     *
298     * @return the number of mappings as an int.
299     */
300    public int size() {
301        unparcel();
302        return mMap.size();
303    }
304
305    /**
306     * Returns true if the mapping of this Bundle is empty, false otherwise.
307     */
308    public boolean isEmpty() {
309        unparcel();
310        return mMap.isEmpty();
311    }
312
313    /**
314     * @hide this should probably be the implementation of isEmpty().  To do that we
315     * need to ensure we always use the special empty parcel form when the bundle is
316     * empty.  (This may already be the case, but to be safe we'll do this later when
317     * we aren't trying to stabilize.)
318     */
319    public boolean maybeIsEmpty() {
320        if (isParcelled()) {
321            return isEmptyParcel();
322        } else {
323            return isEmpty();
324        }
325    }
326
327    /**
328     * @hide This kind-of does an equality comparison.  Kind-of.
329     */
330    public boolean kindofEquals(BaseBundle other) {
331        if (other == null) {
332            return false;
333        }
334        if (isParcelled() != other.isParcelled()) {
335            // Big kind-of here!
336            return false;
337        } else if (isParcelled()) {
338            return mParcelledData.compareData(other.mParcelledData) == 0;
339        } else {
340            return mMap.equals(other.mMap);
341        }
342    }
343
344    /**
345     * Removes all elements from the mapping of this Bundle.
346     */
347    public void clear() {
348        unparcel();
349        mMap.clear();
350    }
351
352    void copyInternal(BaseBundle from, boolean deep) {
353        synchronized (from) {
354            if (from.mParcelledData != null) {
355                if (from.isEmptyParcel()) {
356                    mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
357                } else {
358                    mParcelledData = Parcel.obtain();
359                    mParcelledData.appendFrom(from.mParcelledData, 0,
360                            from.mParcelledData.dataSize());
361                    mParcelledData.setDataPosition(0);
362                }
363            } else {
364                mParcelledData = null;
365            }
366
367            if (from.mMap != null) {
368                if (!deep) {
369                    mMap = new ArrayMap<>(from.mMap);
370                } else {
371                    final ArrayMap<String, Object> fromMap = from.mMap;
372                    final int N = fromMap.size();
373                    mMap = new ArrayMap<>(N);
374                    for (int i = 0; i < N; i++) {
375                        mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
376                    }
377                }
378            } else {
379                mMap = null;
380            }
381
382            mClassLoader = from.mClassLoader;
383        }
384    }
385
386    Object deepCopyValue(Object value) {
387        if (value == null) {
388            return null;
389        }
390        if (value instanceof Bundle) {
391            return ((Bundle)value).deepCopy();
392        } else if (value instanceof PersistableBundle) {
393            return ((PersistableBundle)value).deepCopy();
394        } else if (value instanceof ArrayList) {
395            return deepcopyArrayList((ArrayList) value);
396        } else if (value.getClass().isArray()) {
397            if (value instanceof int[]) {
398                return ((int[])value).clone();
399            } else if (value instanceof long[]) {
400                return ((long[])value).clone();
401            } else if (value instanceof float[]) {
402                return ((float[])value).clone();
403            } else if (value instanceof double[]) {
404                return ((double[])value).clone();
405            } else if (value instanceof Object[]) {
406                return ((Object[])value).clone();
407            } else if (value instanceof byte[]) {
408                return ((byte[])value).clone();
409            } else if (value instanceof short[]) {
410                return ((short[])value).clone();
411            } else if (value instanceof char[]) {
412                return ((char[]) value).clone();
413            }
414        }
415        return value;
416    }
417
418    ArrayList deepcopyArrayList(ArrayList from) {
419        final int N = from.size();
420        ArrayList out = new ArrayList(N);
421        for (int i=0; i<N; i++) {
422            out.add(deepCopyValue(from.get(i)));
423        }
424        return out;
425    }
426
427    /**
428     * Returns true if the given key is contained in the mapping
429     * of this Bundle.
430     *
431     * @param key a String key
432     * @return true if the key is part of the mapping, false otherwise
433     */
434    public boolean containsKey(String key) {
435        unparcel();
436        return mMap.containsKey(key);
437    }
438
439    /**
440     * Returns the entry with the given key as an object.
441     *
442     * @param key a String key
443     * @return an Object, or null
444     */
445    @Nullable
446    public Object get(String key) {
447        unparcel();
448        return mMap.get(key);
449    }
450
451    /**
452     * Removes any entry with the given key from the mapping of this Bundle.
453     *
454     * @param key a String key
455     */
456    public void remove(String key) {
457        unparcel();
458        mMap.remove(key);
459    }
460
461    /**
462     * Inserts all mappings from the given PersistableBundle into this BaseBundle.
463     *
464     * @param bundle a PersistableBundle
465     */
466    public void putAll(PersistableBundle bundle) {
467        unparcel();
468        bundle.unparcel();
469        mMap.putAll(bundle.mMap);
470    }
471
472    /**
473     * Inserts all mappings from the given Map into this BaseBundle.
474     *
475     * @param map a Map
476     */
477    void putAll(ArrayMap map) {
478        unparcel();
479        mMap.putAll(map);
480    }
481
482    /**
483     * Returns a Set containing the Strings used as keys in this Bundle.
484     *
485     * @return a Set of String keys
486     */
487    public Set<String> keySet() {
488        unparcel();
489        return mMap.keySet();
490    }
491
492    /**
493     * Inserts a Boolean value into the mapping of this Bundle, replacing
494     * any existing value for the given key.  Either key or value may be null.
495     *
496     * @param key a String, or null
497     * @param value a boolean
498     */
499    public void putBoolean(@Nullable String key, boolean value) {
500        unparcel();
501        mMap.put(key, value);
502    }
503
504    /**
505     * Inserts a byte value into the mapping of this Bundle, replacing
506     * any existing value for the given key.
507     *
508     * @param key a String, or null
509     * @param value a byte
510     */
511    void putByte(@Nullable String key, byte value) {
512        unparcel();
513        mMap.put(key, value);
514    }
515
516    /**
517     * Inserts a char value into the mapping of this Bundle, replacing
518     * any existing value for the given key.
519     *
520     * @param key a String, or null
521     * @param value a char
522     */
523    void putChar(@Nullable String key, char value) {
524        unparcel();
525        mMap.put(key, value);
526    }
527
528    /**
529     * Inserts a short value into the mapping of this Bundle, replacing
530     * any existing value for the given key.
531     *
532     * @param key a String, or null
533     * @param value a short
534     */
535    void putShort(@Nullable String key, short value) {
536        unparcel();
537        mMap.put(key, value);
538    }
539
540    /**
541     * Inserts an int value into the mapping of this Bundle, replacing
542     * any existing value for the given key.
543     *
544     * @param key a String, or null
545     * @param value an int
546     */
547    public void putInt(@Nullable String key, int value) {
548        unparcel();
549        mMap.put(key, value);
550    }
551
552    /**
553     * Inserts a long value into the mapping of this Bundle, replacing
554     * any existing value for the given key.
555     *
556     * @param key a String, or null
557     * @param value a long
558     */
559    public void putLong(@Nullable String key, long value) {
560        unparcel();
561        mMap.put(key, value);
562    }
563
564    /**
565     * Inserts a float value into the mapping of this Bundle, replacing
566     * any existing value for the given key.
567     *
568     * @param key a String, or null
569     * @param value a float
570     */
571    void putFloat(@Nullable String key, float value) {
572        unparcel();
573        mMap.put(key, value);
574    }
575
576    /**
577     * Inserts a double value into the mapping of this Bundle, replacing
578     * any existing value for the given key.
579     *
580     * @param key a String, or null
581     * @param value a double
582     */
583    public void putDouble(@Nullable String key, double value) {
584        unparcel();
585        mMap.put(key, value);
586    }
587
588    /**
589     * Inserts a String value into the mapping of this Bundle, replacing
590     * any existing value for the given key.  Either key or value may be null.
591     *
592     * @param key a String, or null
593     * @param value a String, or null
594     */
595    public void putString(@Nullable String key, @Nullable String value) {
596        unparcel();
597        mMap.put(key, value);
598    }
599
600    /**
601     * Inserts a CharSequence value into the mapping of this Bundle, replacing
602     * any existing value for the given key.  Either key or value may be null.
603     *
604     * @param key a String, or null
605     * @param value a CharSequence, or null
606     */
607    void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
608        unparcel();
609        mMap.put(key, value);
610    }
611
612    /**
613     * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
614     * any existing value for the given key.  Either key or value may be null.
615     *
616     * @param key a String, or null
617     * @param value an ArrayList<Integer> object, or null
618     */
619    void putIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
620        unparcel();
621        mMap.put(key, value);
622    }
623
624    /**
625     * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
626     * any existing value for the given key.  Either key or value may be null.
627     *
628     * @param key a String, or null
629     * @param value an ArrayList<String> object, or null
630     */
631    void putStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
632        unparcel();
633        mMap.put(key, value);
634    }
635
636    /**
637     * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
638     * any existing value for the given key.  Either key or value may be null.
639     *
640     * @param key a String, or null
641     * @param value an ArrayList<CharSequence> object, or null
642     */
643    void putCharSequenceArrayList(@Nullable String key, @Nullable ArrayList<CharSequence> value) {
644        unparcel();
645        mMap.put(key, value);
646    }
647
648    /**
649     * Inserts a Serializable value into the mapping of this Bundle, replacing
650     * any existing value for the given key.  Either key or value may be null.
651     *
652     * @param key a String, or null
653     * @param value a Serializable object, or null
654     */
655    void putSerializable(@Nullable String key, @Nullable Serializable value) {
656        unparcel();
657        mMap.put(key, value);
658    }
659
660    /**
661     * Inserts a boolean array value into the mapping of this Bundle, replacing
662     * any existing value for the given key.  Either key or value may be null.
663     *
664     * @param key a String, or null
665     * @param value a boolean array object, or null
666     */
667    public void putBooleanArray(@Nullable String key, @Nullable boolean[] value) {
668        unparcel();
669        mMap.put(key, value);
670    }
671
672    /**
673     * Inserts a byte array value into the mapping of this Bundle, replacing
674     * any existing value for the given key.  Either key or value may be null.
675     *
676     * @param key a String, or null
677     * @param value a byte array object, or null
678     */
679    void putByteArray(@Nullable String key, @Nullable byte[] value) {
680        unparcel();
681        mMap.put(key, value);
682    }
683
684    /**
685     * Inserts a short array value into the mapping of this Bundle, replacing
686     * any existing value for the given key.  Either key or value may be null.
687     *
688     * @param key a String, or null
689     * @param value a short array object, or null
690     */
691    void putShortArray(@Nullable String key, @Nullable short[] value) {
692        unparcel();
693        mMap.put(key, value);
694    }
695
696    /**
697     * Inserts a char array value into the mapping of this Bundle, replacing
698     * any existing value for the given key.  Either key or value may be null.
699     *
700     * @param key a String, or null
701     * @param value a char array object, or null
702     */
703    void putCharArray(@Nullable String key, @Nullable char[] value) {
704        unparcel();
705        mMap.put(key, value);
706    }
707
708    /**
709     * Inserts an int array value into the mapping of this Bundle, replacing
710     * any existing value for the given key.  Either key or value may be null.
711     *
712     * @param key a String, or null
713     * @param value an int array object, or null
714     */
715    public void putIntArray(@Nullable String key, @Nullable int[] value) {
716        unparcel();
717        mMap.put(key, value);
718    }
719
720    /**
721     * Inserts a long array value into the mapping of this Bundle, replacing
722     * any existing value for the given key.  Either key or value may be null.
723     *
724     * @param key a String, or null
725     * @param value a long array object, or null
726     */
727    public void putLongArray(@Nullable String key, @Nullable long[] value) {
728        unparcel();
729        mMap.put(key, value);
730    }
731
732    /**
733     * Inserts a float array value into the mapping of this Bundle, replacing
734     * any existing value for the given key.  Either key or value may be null.
735     *
736     * @param key a String, or null
737     * @param value a float array object, or null
738     */
739    void putFloatArray(@Nullable String key, @Nullable float[] value) {
740        unparcel();
741        mMap.put(key, value);
742    }
743
744    /**
745     * Inserts a double array value into the mapping of this Bundle, replacing
746     * any existing value for the given key.  Either key or value may be null.
747     *
748     * @param key a String, or null
749     * @param value a double array object, or null
750     */
751    public void putDoubleArray(@Nullable String key, @Nullable double[] value) {
752        unparcel();
753        mMap.put(key, value);
754    }
755
756    /**
757     * Inserts a String array value into the mapping of this Bundle, replacing
758     * any existing value for the given key.  Either key or value may be null.
759     *
760     * @param key a String, or null
761     * @param value a String array object, or null
762     */
763    public void putStringArray(@Nullable String key, @Nullable String[] value) {
764        unparcel();
765        mMap.put(key, value);
766    }
767
768    /**
769     * Inserts a CharSequence array value into the mapping of this Bundle, replacing
770     * any existing value for the given key.  Either key or value may be null.
771     *
772     * @param key a String, or null
773     * @param value a CharSequence array object, or null
774     */
775    void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
776        unparcel();
777        mMap.put(key, value);
778    }
779
780    /**
781     * Returns the value associated with the given key, or false if
782     * no mapping of the desired type exists for the given key.
783     *
784     * @param key a String
785     * @return a boolean value
786     */
787    public boolean getBoolean(String key) {
788        unparcel();
789        if (DEBUG) Log.d(TAG, "Getting boolean in "
790                + Integer.toHexString(System.identityHashCode(this)));
791        return getBoolean(key, false);
792    }
793
794    // Log a message if the value was non-null but not of the expected type
795    void typeWarning(String key, Object value, String className,
796            Object defaultValue, ClassCastException e) {
797        StringBuilder sb = new StringBuilder();
798        sb.append("Key ");
799        sb.append(key);
800        sb.append(" expected ");
801        sb.append(className);
802        sb.append(" but value was a ");
803        sb.append(value.getClass().getName());
804        sb.append(".  The default value ");
805        sb.append(defaultValue);
806        sb.append(" was returned.");
807        Log.w(TAG, sb.toString());
808        Log.w(TAG, "Attempt to cast generated internal exception:", e);
809    }
810
811    void typeWarning(String key, Object value, String className,
812            ClassCastException e) {
813        typeWarning(key, value, className, "<null>", e);
814    }
815
816    /**
817     * Returns the value associated with the given key, or defaultValue if
818     * no mapping of the desired type exists for the given key.
819     *
820     * @param key a String
821     * @param defaultValue Value to return if key does not exist
822     * @return a boolean value
823     */
824    public boolean getBoolean(String key, boolean defaultValue) {
825        unparcel();
826        Object o = mMap.get(key);
827        if (o == null) {
828            return defaultValue;
829        }
830        try {
831            return (Boolean) o;
832        } catch (ClassCastException e) {
833            typeWarning(key, o, "Boolean", defaultValue, e);
834            return defaultValue;
835        }
836    }
837
838    /**
839     * Returns the value associated with the given key, or (byte) 0 if
840     * no mapping of the desired type exists for the given key.
841     *
842     * @param key a String
843     * @return a byte value
844     */
845    byte getByte(String key) {
846        unparcel();
847        return getByte(key, (byte) 0);
848    }
849
850    /**
851     * Returns the value associated with the given key, or defaultValue if
852     * no mapping of the desired type exists for the given key.
853     *
854     * @param key a String
855     * @param defaultValue Value to return if key does not exist
856     * @return a byte value
857     */
858    Byte getByte(String key, byte defaultValue) {
859        unparcel();
860        Object o = mMap.get(key);
861        if (o == null) {
862            return defaultValue;
863        }
864        try {
865            return (Byte) o;
866        } catch (ClassCastException e) {
867            typeWarning(key, o, "Byte", defaultValue, e);
868            return defaultValue;
869        }
870    }
871
872    /**
873     * Returns the value associated with the given key, or (char) 0 if
874     * no mapping of the desired type exists for the given key.
875     *
876     * @param key a String
877     * @return a char value
878     */
879    char getChar(String key) {
880        unparcel();
881        return getChar(key, (char) 0);
882    }
883
884    /**
885     * Returns the value associated with the given key, or defaultValue if
886     * no mapping of the desired type exists for the given key.
887     *
888     * @param key a String
889     * @param defaultValue Value to return if key does not exist
890     * @return a char value
891     */
892    char getChar(String key, char defaultValue) {
893        unparcel();
894        Object o = mMap.get(key);
895        if (o == null) {
896            return defaultValue;
897        }
898        try {
899            return (Character) o;
900        } catch (ClassCastException e) {
901            typeWarning(key, o, "Character", defaultValue, e);
902            return defaultValue;
903        }
904    }
905
906    /**
907     * Returns the value associated with the given key, or (short) 0 if
908     * no mapping of the desired type exists for the given key.
909     *
910     * @param key a String
911     * @return a short value
912     */
913    short getShort(String key) {
914        unparcel();
915        return getShort(key, (short) 0);
916    }
917
918    /**
919     * Returns the value associated with the given key, or defaultValue if
920     * no mapping of the desired type exists for the given key.
921     *
922     * @param key a String
923     * @param defaultValue Value to return if key does not exist
924     * @return a short value
925     */
926    short getShort(String key, short defaultValue) {
927        unparcel();
928        Object o = mMap.get(key);
929        if (o == null) {
930            return defaultValue;
931        }
932        try {
933            return (Short) o;
934        } catch (ClassCastException e) {
935            typeWarning(key, o, "Short", defaultValue, e);
936            return defaultValue;
937        }
938    }
939
940    /**
941     * Returns the value associated with the given key, or 0 if
942     * no mapping of the desired type exists for the given key.
943     *
944     * @param key a String
945     * @return an int value
946     */
947    public int getInt(String key) {
948        unparcel();
949        return getInt(key, 0);
950    }
951
952    /**
953     * Returns the value associated with the given key, or defaultValue if
954     * no mapping of the desired type exists for the given key.
955     *
956     * @param key a String
957     * @param defaultValue Value to return if key does not exist
958     * @return an int value
959     */
960   public int getInt(String key, int defaultValue) {
961        unparcel();
962        Object o = mMap.get(key);
963        if (o == null) {
964            return defaultValue;
965        }
966        try {
967            return (Integer) o;
968        } catch (ClassCastException e) {
969            typeWarning(key, o, "Integer", defaultValue, e);
970            return defaultValue;
971        }
972    }
973
974    /**
975     * Returns the value associated with the given key, or 0L if
976     * no mapping of the desired type exists for the given key.
977     *
978     * @param key a String
979     * @return a long value
980     */
981    public long getLong(String key) {
982        unparcel();
983        return getLong(key, 0L);
984    }
985
986    /**
987     * Returns the value associated with the given key, or defaultValue if
988     * no mapping of the desired type exists for the given key.
989     *
990     * @param key a String
991     * @param defaultValue Value to return if key does not exist
992     * @return a long value
993     */
994    public long getLong(String key, long defaultValue) {
995        unparcel();
996        Object o = mMap.get(key);
997        if (o == null) {
998            return defaultValue;
999        }
1000        try {
1001            return (Long) o;
1002        } catch (ClassCastException e) {
1003            typeWarning(key, o, "Long", defaultValue, e);
1004            return defaultValue;
1005        }
1006    }
1007
1008    /**
1009     * Returns the value associated with the given key, or 0.0f if
1010     * no mapping of the desired type exists for the given key.
1011     *
1012     * @param key a String
1013     * @return a float value
1014     */
1015    float getFloat(String key) {
1016        unparcel();
1017        return getFloat(key, 0.0f);
1018    }
1019
1020    /**
1021     * Returns the value associated with the given key, or defaultValue if
1022     * no mapping of the desired type exists for the given key.
1023     *
1024     * @param key a String
1025     * @param defaultValue Value to return if key does not exist
1026     * @return a float value
1027     */
1028    float getFloat(String key, float defaultValue) {
1029        unparcel();
1030        Object o = mMap.get(key);
1031        if (o == null) {
1032            return defaultValue;
1033        }
1034        try {
1035            return (Float) o;
1036        } catch (ClassCastException e) {
1037            typeWarning(key, o, "Float", defaultValue, e);
1038            return defaultValue;
1039        }
1040    }
1041
1042    /**
1043     * Returns the value associated with the given key, or 0.0 if
1044     * no mapping of the desired type exists for the given key.
1045     *
1046     * @param key a String
1047     * @return a double value
1048     */
1049    public double getDouble(String key) {
1050        unparcel();
1051        return getDouble(key, 0.0);
1052    }
1053
1054    /**
1055     * Returns the value associated with the given key, or defaultValue if
1056     * no mapping of the desired type exists for the given key.
1057     *
1058     * @param key a String
1059     * @param defaultValue Value to return if key does not exist
1060     * @return a double value
1061     */
1062    public double getDouble(String key, double defaultValue) {
1063        unparcel();
1064        Object o = mMap.get(key);
1065        if (o == null) {
1066            return defaultValue;
1067        }
1068        try {
1069            return (Double) o;
1070        } catch (ClassCastException e) {
1071            typeWarning(key, o, "Double", defaultValue, e);
1072            return defaultValue;
1073        }
1074    }
1075
1076    /**
1077     * Returns the value associated with the given key, or null if
1078     * no mapping of the desired type exists for the given key or a null
1079     * value is explicitly associated with the key.
1080     *
1081     * @param key a String, or null
1082     * @return a String value, or null
1083     */
1084    @Nullable
1085    public String getString(@Nullable String key) {
1086        unparcel();
1087        final Object o = mMap.get(key);
1088        try {
1089            return (String) o;
1090        } catch (ClassCastException e) {
1091            typeWarning(key, o, "String", e);
1092            return null;
1093        }
1094    }
1095
1096    /**
1097     * Returns the value associated with the given key, or defaultValue if
1098     * no mapping of the desired type exists for the given key or if a null
1099     * value is explicitly associated with the given key.
1100     *
1101     * @param key a String, or null
1102     * @param defaultValue Value to return if key does not exist or if a null
1103     *     value is associated with the given key.
1104     * @return the String value associated with the given key, or defaultValue
1105     *     if no valid String object is currently mapped to that key.
1106     */
1107    public String getString(@Nullable String key, String defaultValue) {
1108        final String s = getString(key);
1109        return (s == null) ? defaultValue : s;
1110    }
1111
1112    /**
1113     * Returns the value associated with the given key, or null if
1114     * no mapping of the desired type exists for the given key or a null
1115     * value is explicitly associated with the key.
1116     *
1117     * @param key a String, or null
1118     * @return a CharSequence value, or null
1119     */
1120    @Nullable
1121    CharSequence getCharSequence(@Nullable String key) {
1122        unparcel();
1123        final Object o = mMap.get(key);
1124        try {
1125            return (CharSequence) o;
1126        } catch (ClassCastException e) {
1127            typeWarning(key, o, "CharSequence", e);
1128            return null;
1129        }
1130    }
1131
1132    /**
1133     * Returns the value associated with the given key, or defaultValue if
1134     * no mapping of the desired type exists for the given key or if a null
1135     * value is explicitly associated with the given key.
1136     *
1137     * @param key a String, or null
1138     * @param defaultValue Value to return if key does not exist or if a null
1139     *     value is associated with the given key.
1140     * @return the CharSequence value associated with the given key, or defaultValue
1141     *     if no valid CharSequence object is currently mapped to that key.
1142     */
1143    CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
1144        final CharSequence cs = getCharSequence(key);
1145        return (cs == null) ? defaultValue : cs;
1146    }
1147
1148    /**
1149     * Returns the value associated with the given key, or null if
1150     * no mapping of the desired type exists for the given key or a null
1151     * value is explicitly associated with the key.
1152     *
1153     * @param key a String, or null
1154     * @return a Serializable value, or null
1155     */
1156    @Nullable
1157    Serializable getSerializable(@Nullable String key) {
1158        unparcel();
1159        Object o = mMap.get(key);
1160        if (o == null) {
1161            return null;
1162        }
1163        try {
1164            return (Serializable) o;
1165        } catch (ClassCastException e) {
1166            typeWarning(key, o, "Serializable", e);
1167            return null;
1168        }
1169    }
1170
1171    /**
1172     * Returns the value associated with the given key, or null if
1173     * no mapping of the desired type exists for the given key or a null
1174     * value is explicitly associated with the key.
1175     *
1176     * @param key a String, or null
1177     * @return an ArrayList<String> value, or null
1178     */
1179    @Nullable
1180    ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
1181        unparcel();
1182        Object o = mMap.get(key);
1183        if (o == null) {
1184            return null;
1185        }
1186        try {
1187            return (ArrayList<Integer>) o;
1188        } catch (ClassCastException e) {
1189            typeWarning(key, o, "ArrayList<Integer>", e);
1190            return null;
1191        }
1192    }
1193
1194    /**
1195     * Returns the value associated with the given key, or null if
1196     * no mapping of the desired type exists for the given key or a null
1197     * value is explicitly associated with the key.
1198     *
1199     * @param key a String, or null
1200     * @return an ArrayList<String> value, or null
1201     */
1202    @Nullable
1203    ArrayList<String> getStringArrayList(@Nullable String key) {
1204        unparcel();
1205        Object o = mMap.get(key);
1206        if (o == null) {
1207            return null;
1208        }
1209        try {
1210            return (ArrayList<String>) o;
1211        } catch (ClassCastException e) {
1212            typeWarning(key, o, "ArrayList<String>", e);
1213            return null;
1214        }
1215    }
1216
1217    /**
1218     * Returns the value associated with the given key, or null if
1219     * no mapping of the desired type exists for the given key or a null
1220     * value is explicitly associated with the key.
1221     *
1222     * @param key a String, or null
1223     * @return an ArrayList<CharSequence> value, or null
1224     */
1225    @Nullable
1226    ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
1227        unparcel();
1228        Object o = mMap.get(key);
1229        if (o == null) {
1230            return null;
1231        }
1232        try {
1233            return (ArrayList<CharSequence>) o;
1234        } catch (ClassCastException e) {
1235            typeWarning(key, o, "ArrayList<CharSequence>", e);
1236            return null;
1237        }
1238    }
1239
1240    /**
1241     * Returns the value associated with the given key, or null if
1242     * no mapping of the desired type exists for the given key or a null
1243     * value is explicitly associated with the key.
1244     *
1245     * @param key a String, or null
1246     * @return a boolean[] value, or null
1247     */
1248    @Nullable
1249    public boolean[] getBooleanArray(@Nullable String key) {
1250        unparcel();
1251        Object o = mMap.get(key);
1252        if (o == null) {
1253            return null;
1254        }
1255        try {
1256            return (boolean[]) o;
1257        } catch (ClassCastException e) {
1258            typeWarning(key, o, "byte[]", e);
1259            return null;
1260        }
1261    }
1262
1263    /**
1264     * Returns the value associated with the given key, or null if
1265     * no mapping of the desired type exists for the given key or a null
1266     * value is explicitly associated with the key.
1267     *
1268     * @param key a String, or null
1269     * @return a byte[] value, or null
1270     */
1271    @Nullable
1272    byte[] getByteArray(@Nullable String key) {
1273        unparcel();
1274        Object o = mMap.get(key);
1275        if (o == null) {
1276            return null;
1277        }
1278        try {
1279            return (byte[]) o;
1280        } catch (ClassCastException e) {
1281            typeWarning(key, o, "byte[]", e);
1282            return null;
1283        }
1284    }
1285
1286    /**
1287     * Returns the value associated with the given key, or null if
1288     * no mapping of the desired type exists for the given key or a null
1289     * value is explicitly associated with the key.
1290     *
1291     * @param key a String, or null
1292     * @return a short[] value, or null
1293     */
1294    @Nullable
1295    short[] getShortArray(@Nullable String key) {
1296        unparcel();
1297        Object o = mMap.get(key);
1298        if (o == null) {
1299            return null;
1300        }
1301        try {
1302            return (short[]) o;
1303        } catch (ClassCastException e) {
1304            typeWarning(key, o, "short[]", e);
1305            return null;
1306        }
1307    }
1308
1309    /**
1310     * Returns the value associated with the given key, or null if
1311     * no mapping of the desired type exists for the given key or a null
1312     * value is explicitly associated with the key.
1313     *
1314     * @param key a String, or null
1315     * @return a char[] value, or null
1316     */
1317    @Nullable
1318    char[] getCharArray(@Nullable String key) {
1319        unparcel();
1320        Object o = mMap.get(key);
1321        if (o == null) {
1322            return null;
1323        }
1324        try {
1325            return (char[]) o;
1326        } catch (ClassCastException e) {
1327            typeWarning(key, o, "char[]", e);
1328            return null;
1329        }
1330    }
1331
1332    /**
1333     * Returns the value associated with the given key, or null if
1334     * no mapping of the desired type exists for the given key or a null
1335     * value is explicitly associated with the key.
1336     *
1337     * @param key a String, or null
1338     * @return an int[] value, or null
1339     */
1340    @Nullable
1341    public int[] getIntArray(@Nullable String key) {
1342        unparcel();
1343        Object o = mMap.get(key);
1344        if (o == null) {
1345            return null;
1346        }
1347        try {
1348            return (int[]) o;
1349        } catch (ClassCastException e) {
1350            typeWarning(key, o, "int[]", e);
1351            return null;
1352        }
1353    }
1354
1355    /**
1356     * Returns the value associated with the given key, or null if
1357     * no mapping of the desired type exists for the given key or a null
1358     * value is explicitly associated with the key.
1359     *
1360     * @param key a String, or null
1361     * @return a long[] value, or null
1362     */
1363    @Nullable
1364    public long[] getLongArray(@Nullable String key) {
1365        unparcel();
1366        Object o = mMap.get(key);
1367        if (o == null) {
1368            return null;
1369        }
1370        try {
1371            return (long[]) o;
1372        } catch (ClassCastException e) {
1373            typeWarning(key, o, "long[]", e);
1374            return null;
1375        }
1376    }
1377
1378    /**
1379     * Returns the value associated with the given key, or null if
1380     * no mapping of the desired type exists for the given key or a null
1381     * value is explicitly associated with the key.
1382     *
1383     * @param key a String, or null
1384     * @return a float[] value, or null
1385     */
1386    @Nullable
1387    float[] getFloatArray(@Nullable String key) {
1388        unparcel();
1389        Object o = mMap.get(key);
1390        if (o == null) {
1391            return null;
1392        }
1393        try {
1394            return (float[]) o;
1395        } catch (ClassCastException e) {
1396            typeWarning(key, o, "float[]", e);
1397            return null;
1398        }
1399    }
1400
1401    /**
1402     * Returns the value associated with the given key, or null if
1403     * no mapping of the desired type exists for the given key or a null
1404     * value is explicitly associated with the key.
1405     *
1406     * @param key a String, or null
1407     * @return a double[] value, or null
1408     */
1409    @Nullable
1410    public double[] getDoubleArray(@Nullable String key) {
1411        unparcel();
1412        Object o = mMap.get(key);
1413        if (o == null) {
1414            return null;
1415        }
1416        try {
1417            return (double[]) o;
1418        } catch (ClassCastException e) {
1419            typeWarning(key, o, "double[]", e);
1420            return null;
1421        }
1422    }
1423
1424    /**
1425     * Returns the value associated with the given key, or null if
1426     * no mapping of the desired type exists for the given key or a null
1427     * value is explicitly associated with the key.
1428     *
1429     * @param key a String, or null
1430     * @return a String[] value, or null
1431     */
1432    @Nullable
1433    public String[] getStringArray(@Nullable String key) {
1434        unparcel();
1435        Object o = mMap.get(key);
1436        if (o == null) {
1437            return null;
1438        }
1439        try {
1440            return (String[]) o;
1441        } catch (ClassCastException e) {
1442            typeWarning(key, o, "String[]", e);
1443            return null;
1444        }
1445    }
1446
1447    /**
1448     * Returns the value associated with the given key, or null if
1449     * no mapping of the desired type exists for the given key or a null
1450     * value is explicitly associated with the key.
1451     *
1452     * @param key a String, or null
1453     * @return a CharSequence[] value, or null
1454     */
1455    @Nullable
1456    CharSequence[] getCharSequenceArray(@Nullable String key) {
1457        unparcel();
1458        Object o = mMap.get(key);
1459        if (o == null) {
1460            return null;
1461        }
1462        try {
1463            return (CharSequence[]) o;
1464        } catch (ClassCastException e) {
1465            typeWarning(key, o, "CharSequence[]", e);
1466            return null;
1467        }
1468    }
1469
1470    /**
1471     * Writes the Bundle contents to a Parcel, typically in order for
1472     * it to be passed through an IBinder connection.
1473     * @param parcel The parcel to copy this bundle to.
1474     */
1475    void writeToParcelInner(Parcel parcel, int flags) {
1476        // Keep implementation in sync with writeToParcel() in
1477        // frameworks/native/libs/binder/PersistableBundle.cpp.
1478        final ArrayMap<String, Object> map;
1479        synchronized (this) {
1480            // unparcel() can race with this method and cause the parcel to recycle
1481            // at the wrong time. So synchronize access the mParcelledData's content.
1482            if (mParcelledData != null) {
1483                if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
1484                    parcel.writeInt(0);
1485                } else {
1486                    int length = mParcelledData.dataSize();
1487                    parcel.writeInt(length);
1488                    parcel.writeInt(BUNDLE_MAGIC);
1489                    parcel.appendFrom(mParcelledData, 0, length);
1490                }
1491                return;
1492            }
1493            map = mMap;
1494        }
1495
1496        // Special case for empty bundles.
1497        if (map == null || map.size() <= 0) {
1498            parcel.writeInt(0);
1499            return;
1500        }
1501        int lengthPos = parcel.dataPosition();
1502        parcel.writeInt(-1); // dummy, will hold length
1503        parcel.writeInt(BUNDLE_MAGIC);
1504
1505        int startPos = parcel.dataPosition();
1506        parcel.writeArrayMapInternal(map);
1507        int endPos = parcel.dataPosition();
1508
1509        // Backpatch length
1510        parcel.setDataPosition(lengthPos);
1511        int length = endPos - startPos;
1512        parcel.writeInt(length);
1513        parcel.setDataPosition(endPos);
1514    }
1515
1516    /**
1517     * Reads the Parcel contents into this Bundle, typically in order for
1518     * it to be passed through an IBinder connection.
1519     * @param parcel The parcel to overwrite this bundle from.
1520     */
1521    void readFromParcelInner(Parcel parcel) {
1522        // Keep implementation in sync with readFromParcel() in
1523        // frameworks/native/libs/binder/PersistableBundle.cpp.
1524        int length = parcel.readInt();
1525        readFromParcelInner(parcel, length);
1526    }
1527
1528    private void readFromParcelInner(Parcel parcel, int length) {
1529        if (length < 0) {
1530            throw new RuntimeException("Bad length in parcel: " + length);
1531
1532        } else if (length == 0) {
1533            // Empty Bundle or end of data.
1534            mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
1535            return;
1536        }
1537
1538        final int magic = parcel.readInt();
1539        if (magic != BUNDLE_MAGIC) {
1540            throw new IllegalStateException("Bad magic number for Bundle: 0x"
1541                    + Integer.toHexString(magic));
1542        }
1543
1544        // Advance within this Parcel
1545        int offset = parcel.dataPosition();
1546        parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
1547
1548        Parcel p = Parcel.obtain();
1549        p.setDataPosition(0);
1550        p.appendFrom(parcel, offset, length);
1551        p.adoptClassCookies(parcel);
1552        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
1553                + ": " + length + " bundle bytes starting at " + offset);
1554        p.setDataPosition(0);
1555
1556        mParcelledData = p;
1557    }
1558}
1559