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