BaseBundle.java revision 0a8e160eb56f3b8f504b37349a79ec4edb7e5039
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.os;
18
19import android.util.ArrayMap;
20import android.util.Log;
21
22import java.io.Serializable;
23import java.util.ArrayList;
24import java.util.Map;
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(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    public Object get(String key) {
280        unparcel();
281        return mMap.get(key);
282    }
283
284    /**
285     * Removes any entry with the given key from the mapping of this Bundle.
286     *
287     * @param key a String key
288     */
289    public void remove(String key) {
290        unparcel();
291        mMap.remove(key);
292    }
293
294    /**
295     * Inserts all mappings from the given PersistableBundle into this BaseBundle.
296     *
297     * @param bundle a PersistableBundle
298     */
299    public void putAll(PersistableBundle bundle) {
300        unparcel();
301        bundle.unparcel();
302        mMap.putAll(bundle.mMap);
303    }
304
305    /**
306     * Inserts all mappings from the given Map into this BaseBundle.
307     *
308     * @param map a Map
309     */
310    void putAll(Map map) {
311        unparcel();
312        mMap.putAll(map);
313    }
314
315    /**
316     * Returns a Set containing the Strings used as keys in this Bundle.
317     *
318     * @return a Set of String keys
319     */
320    public Set<String> keySet() {
321        unparcel();
322        return mMap.keySet();
323    }
324
325    /**
326     * Inserts a Boolean value into the mapping of this Bundle, replacing
327     * any existing value for the given key.  Either key or value may be null.
328     *
329     * @param key a String, or null
330     * @param value a Boolean, or null
331     */
332    void putBoolean(String key, boolean value) {
333        unparcel();
334        mMap.put(key, value);
335    }
336
337    /**
338     * Inserts a byte value into the mapping of this Bundle, replacing
339     * any existing value for the given key.
340     *
341     * @param key a String, or null
342     * @param value a byte
343     */
344    void putByte(String key, byte value) {
345        unparcel();
346        mMap.put(key, value);
347    }
348
349    /**
350     * Inserts a char value into the mapping of this Bundle, replacing
351     * any existing value for the given key.
352     *
353     * @param key a String, or null
354     * @param value a char, or null
355     */
356    void putChar(String key, char value) {
357        unparcel();
358        mMap.put(key, value);
359    }
360
361    /**
362     * Inserts a short value into the mapping of this Bundle, replacing
363     * any existing value for the given key.
364     *
365     * @param key a String, or null
366     * @param value a short
367     */
368    void putShort(String key, short value) {
369        unparcel();
370        mMap.put(key, value);
371    }
372
373    /**
374     * Inserts an int value into the mapping of this Bundle, replacing
375     * any existing value for the given key.
376     *
377     * @param key a String, or null
378     * @param value an int, or null
379     */
380    public void putInt(String key, int value) {
381        unparcel();
382        mMap.put(key, value);
383    }
384
385    /**
386     * Inserts a long value into the mapping of this Bundle, replacing
387     * any existing value for the given key.
388     *
389     * @param key a String, or null
390     * @param value a long
391     */
392    public void putLong(String key, long value) {
393        unparcel();
394        mMap.put(key, value);
395    }
396
397    /**
398     * Inserts a float value into the mapping of this Bundle, replacing
399     * any existing value for the given key.
400     *
401     * @param key a String, or null
402     * @param value a float
403     */
404    void putFloat(String key, float value) {
405        unparcel();
406        mMap.put(key, value);
407    }
408
409    /**
410     * Inserts a double value into the mapping of this Bundle, replacing
411     * any existing value for the given key.
412     *
413     * @param key a String, or null
414     * @param value a double
415     */
416    public void putDouble(String key, double value) {
417        unparcel();
418        mMap.put(key, value);
419    }
420
421    /**
422     * Inserts a String value into the mapping of this Bundle, replacing
423     * any existing value for the given key.  Either key or value may be null.
424     *
425     * @param key a String, or null
426     * @param value a String, or null
427     */
428    public void putString(String key, String value) {
429        unparcel();
430        mMap.put(key, value);
431    }
432
433    /**
434     * Inserts a CharSequence value into the mapping of this Bundle, replacing
435     * any existing value for the given key.  Either key or value may be null.
436     *
437     * @param key a String, or null
438     * @param value a CharSequence, or null
439     */
440    void putCharSequence(String key, CharSequence value) {
441        unparcel();
442        mMap.put(key, value);
443    }
444
445    /**
446     * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
447     * any existing value for the given key.  Either key or value may be null.
448     *
449     * @param key a String, or null
450     * @param value an ArrayList<Integer> object, or null
451     */
452    void putIntegerArrayList(String key, ArrayList<Integer> value) {
453        unparcel();
454        mMap.put(key, value);
455    }
456
457    /**
458     * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
459     * any existing value for the given key.  Either key or value may be null.
460     *
461     * @param key a String, or null
462     * @param value an ArrayList<String> object, or null
463     */
464    void putStringArrayList(String key, ArrayList<String> value) {
465        unparcel();
466        mMap.put(key, value);
467    }
468
469    /**
470     * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
471     * any existing value for the given key.  Either key or value may be null.
472     *
473     * @param key a String, or null
474     * @param value an ArrayList<CharSequence> object, or null
475     */
476    void putCharSequenceArrayList(String key, ArrayList<CharSequence> value) {
477        unparcel();
478        mMap.put(key, value);
479    }
480
481    /**
482     * Inserts a Serializable value into the mapping of this Bundle, replacing
483     * any existing value for the given key.  Either key or value may be null.
484     *
485     * @param key a String, or null
486     * @param value a Serializable object, or null
487     */
488    void putSerializable(String key, Serializable value) {
489        unparcel();
490        mMap.put(key, value);
491    }
492
493    /**
494     * Inserts a boolean array value into the mapping of this Bundle, replacing
495     * any existing value for the given key.  Either key or value may be null.
496     *
497     * @param key a String, or null
498     * @param value a boolean array object, or null
499     */
500    void putBooleanArray(String key, boolean[] value) {
501        unparcel();
502        mMap.put(key, value);
503    }
504
505    /**
506     * Inserts a byte array value into the mapping of this Bundle, replacing
507     * any existing value for the given key.  Either key or value may be null.
508     *
509     * @param key a String, or null
510     * @param value a byte array object, or null
511     */
512    void putByteArray(String key, byte[] value) {
513        unparcel();
514        mMap.put(key, value);
515    }
516
517    /**
518     * Inserts a short array value into the mapping of this Bundle, replacing
519     * any existing value for the given key.  Either key or value may be null.
520     *
521     * @param key a String, or null
522     * @param value a short array object, or null
523     */
524    void putShortArray(String key, short[] value) {
525        unparcel();
526        mMap.put(key, value);
527    }
528
529    /**
530     * Inserts a char array value into the mapping of this Bundle, replacing
531     * any existing value for the given key.  Either key or value may be null.
532     *
533     * @param key a String, or null
534     * @param value a char array object, or null
535     */
536    void putCharArray(String key, char[] value) {
537        unparcel();
538        mMap.put(key, value);
539    }
540
541    /**
542     * Inserts an int array value into the mapping of this Bundle, replacing
543     * any existing value for the given key.  Either key or value may be null.
544     *
545     * @param key a String, or null
546     * @param value an int array object, or null
547     */
548    public void putIntArray(String key, int[] value) {
549        unparcel();
550        mMap.put(key, value);
551    }
552
553    /**
554     * Inserts a long array value into the mapping of this Bundle, replacing
555     * any existing value for the given key.  Either key or value may be null.
556     *
557     * @param key a String, or null
558     * @param value a long array object, or null
559     */
560    public void putLongArray(String key, long[] value) {
561        unparcel();
562        mMap.put(key, value);
563    }
564
565    /**
566     * Inserts a float array value into the mapping of this Bundle, replacing
567     * any existing value for the given key.  Either key or value may be null.
568     *
569     * @param key a String, or null
570     * @param value a float array object, or null
571     */
572    void putFloatArray(String key, float[] value) {
573        unparcel();
574        mMap.put(key, value);
575    }
576
577    /**
578     * Inserts a double array value into the mapping of this Bundle, replacing
579     * any existing value for the given key.  Either key or value may be null.
580     *
581     * @param key a String, or null
582     * @param value a double array object, or null
583     */
584    public void putDoubleArray(String key, double[] value) {
585        unparcel();
586        mMap.put(key, value);
587    }
588
589    /**
590     * Inserts a String array value into the mapping of this Bundle, replacing
591     * any existing value for the given key.  Either key or value may be null.
592     *
593     * @param key a String, or null
594     * @param value a String array object, or null
595     */
596    public void putStringArray(String key, String[] value) {
597        unparcel();
598        mMap.put(key, value);
599    }
600
601    /**
602     * Inserts a CharSequence array value into the mapping of this Bundle, replacing
603     * any existing value for the given key.  Either key or value may be null.
604     *
605     * @param key a String, or null
606     * @param value a CharSequence array object, or null
607     */
608    void putCharSequenceArray(String key, CharSequence[] value) {
609        unparcel();
610        mMap.put(key, value);
611    }
612
613    /**
614     * Returns the value associated with the given key, or false if
615     * no mapping of the desired type exists for the given key.
616     *
617     * @param key a String
618     * @return a boolean value
619     */
620    boolean getBoolean(String key) {
621        unparcel();
622        if (DEBUG) Log.d(TAG, "Getting boolean in "
623                + Integer.toHexString(System.identityHashCode(this)));
624        return getBoolean(key, false);
625    }
626
627    // Log a message if the value was non-null but not of the expected type
628    void typeWarning(String key, Object value, String className,
629            Object defaultValue, ClassCastException e) {
630        StringBuilder sb = new StringBuilder();
631        sb.append("Key ");
632        sb.append(key);
633        sb.append(" expected ");
634        sb.append(className);
635        sb.append(" but value was a ");
636        sb.append(value.getClass().getName());
637        sb.append(".  The default value ");
638        sb.append(defaultValue);
639        sb.append(" was returned.");
640        Log.w(TAG, sb.toString());
641        Log.w(TAG, "Attempt to cast generated internal exception:", e);
642    }
643
644    void typeWarning(String key, Object value, String className,
645            ClassCastException e) {
646        typeWarning(key, value, className, "<null>", e);
647    }
648
649    /**
650     * Returns the value associated with the given key, or defaultValue if
651     * no mapping of the desired type exists for the given key.
652     *
653     * @param key a String
654     * @param defaultValue Value to return if key does not exist
655     * @return a boolean value
656     */
657    boolean getBoolean(String key, boolean defaultValue) {
658        unparcel();
659        Object o = mMap.get(key);
660        if (o == null) {
661            return defaultValue;
662        }
663        try {
664            return (Boolean) o;
665        } catch (ClassCastException e) {
666            typeWarning(key, o, "Boolean", defaultValue, e);
667            return defaultValue;
668        }
669    }
670
671    /**
672     * Returns the value associated with the given key, or (byte) 0 if
673     * no mapping of the desired type exists for the given key.
674     *
675     * @param key a String
676     * @return a byte value
677     */
678    byte getByte(String key) {
679        unparcel();
680        return getByte(key, (byte) 0);
681    }
682
683    /**
684     * Returns the value associated with the given key, or defaultValue if
685     * no mapping of the desired type exists for the given key.
686     *
687     * @param key a String
688     * @param defaultValue Value to return if key does not exist
689     * @return a byte value
690     */
691    Byte getByte(String key, byte defaultValue) {
692        unparcel();
693        Object o = mMap.get(key);
694        if (o == null) {
695            return defaultValue;
696        }
697        try {
698            return (Byte) o;
699        } catch (ClassCastException e) {
700            typeWarning(key, o, "Byte", defaultValue, e);
701            return defaultValue;
702        }
703    }
704
705    /**
706     * Returns the value associated with the given key, or (char) 0 if
707     * no mapping of the desired type exists for the given key.
708     *
709     * @param key a String
710     * @return a char value
711     */
712    char getChar(String key) {
713        unparcel();
714        return getChar(key, (char) 0);
715    }
716
717    /**
718     * Returns the value associated with the given key, or defaultValue if
719     * no mapping of the desired type exists for the given key.
720     *
721     * @param key a String
722     * @param defaultValue Value to return if key does not exist
723     * @return a char value
724     */
725    char getChar(String key, char defaultValue) {
726        unparcel();
727        Object o = mMap.get(key);
728        if (o == null) {
729            return defaultValue;
730        }
731        try {
732            return (Character) o;
733        } catch (ClassCastException e) {
734            typeWarning(key, o, "Character", defaultValue, e);
735            return defaultValue;
736        }
737    }
738
739    /**
740     * Returns the value associated with the given key, or (short) 0 if
741     * no mapping of the desired type exists for the given key.
742     *
743     * @param key a String
744     * @return a short value
745     */
746    short getShort(String key) {
747        unparcel();
748        return getShort(key, (short) 0);
749    }
750
751    /**
752     * Returns the value associated with the given key, or defaultValue if
753     * no mapping of the desired type exists for the given key.
754     *
755     * @param key a String
756     * @param defaultValue Value to return if key does not exist
757     * @return a short value
758     */
759    short getShort(String key, short defaultValue) {
760        unparcel();
761        Object o = mMap.get(key);
762        if (o == null) {
763            return defaultValue;
764        }
765        try {
766            return (Short) o;
767        } catch (ClassCastException e) {
768            typeWarning(key, o, "Short", defaultValue, e);
769            return defaultValue;
770        }
771    }
772
773    /**
774     * Returns the value associated with the given key, or 0 if
775     * no mapping of the desired type exists for the given key.
776     *
777     * @param key a String
778     * @return an int value
779     */
780    public int getInt(String key) {
781        unparcel();
782        return getInt(key, 0);
783    }
784
785    /**
786     * Returns the value associated with the given key, or defaultValue if
787     * no mapping of the desired type exists for the given key.
788     *
789     * @param key a String
790     * @param defaultValue Value to return if key does not exist
791     * @return an int value
792     */
793   public int getInt(String key, int defaultValue) {
794        unparcel();
795        Object o = mMap.get(key);
796        if (o == null) {
797            return defaultValue;
798        }
799        try {
800            return (Integer) o;
801        } catch (ClassCastException e) {
802            typeWarning(key, o, "Integer", defaultValue, e);
803            return defaultValue;
804        }
805    }
806
807    /**
808     * Returns the value associated with the given key, or 0L if
809     * no mapping of the desired type exists for the given key.
810     *
811     * @param key a String
812     * @return a long value
813     */
814    public long getLong(String key) {
815        unparcel();
816        return getLong(key, 0L);
817    }
818
819    /**
820     * Returns the value associated with the given key, or defaultValue if
821     * no mapping of the desired type exists for the given key.
822     *
823     * @param key a String
824     * @param defaultValue Value to return if key does not exist
825     * @return a long value
826     */
827    public long getLong(String key, long defaultValue) {
828        unparcel();
829        Object o = mMap.get(key);
830        if (o == null) {
831            return defaultValue;
832        }
833        try {
834            return (Long) o;
835        } catch (ClassCastException e) {
836            typeWarning(key, o, "Long", defaultValue, e);
837            return defaultValue;
838        }
839    }
840
841    /**
842     * Returns the value associated with the given key, or 0.0f if
843     * no mapping of the desired type exists for the given key.
844     *
845     * @param key a String
846     * @return a float value
847     */
848    float getFloat(String key) {
849        unparcel();
850        return getFloat(key, 0.0f);
851    }
852
853    /**
854     * Returns the value associated with the given key, or defaultValue if
855     * no mapping of the desired type exists for the given key.
856     *
857     * @param key a String
858     * @param defaultValue Value to return if key does not exist
859     * @return a float value
860     */
861    float getFloat(String key, float defaultValue) {
862        unparcel();
863        Object o = mMap.get(key);
864        if (o == null) {
865            return defaultValue;
866        }
867        try {
868            return (Float) o;
869        } catch (ClassCastException e) {
870            typeWarning(key, o, "Float", defaultValue, e);
871            return defaultValue;
872        }
873    }
874
875    /**
876     * Returns the value associated with the given key, or 0.0 if
877     * no mapping of the desired type exists for the given key.
878     *
879     * @param key a String
880     * @return a double value
881     */
882    public double getDouble(String key) {
883        unparcel();
884        return getDouble(key, 0.0);
885    }
886
887    /**
888     * Returns the value associated with the given key, or defaultValue if
889     * no mapping of the desired type exists for the given key.
890     *
891     * @param key a String
892     * @param defaultValue Value to return if key does not exist
893     * @return a double value
894     */
895    public double getDouble(String key, double defaultValue) {
896        unparcel();
897        Object o = mMap.get(key);
898        if (o == null) {
899            return defaultValue;
900        }
901        try {
902            return (Double) o;
903        } catch (ClassCastException e) {
904            typeWarning(key, o, "Double", defaultValue, e);
905            return defaultValue;
906        }
907    }
908
909    /**
910     * Returns the value associated with the given key, or null if
911     * no mapping of the desired type exists for the given key or a null
912     * value is explicitly associated with the key.
913     *
914     * @param key a String, or null
915     * @return a String value, or null
916     */
917    public String getString(String key) {
918        unparcel();
919        final Object o = mMap.get(key);
920        try {
921            return (String) o;
922        } catch (ClassCastException e) {
923            typeWarning(key, o, "String", e);
924            return null;
925        }
926    }
927
928    /**
929     * Returns the value associated with the given key, or defaultValue if
930     * no mapping of the desired type exists for the given key.
931     *
932     * @param key a String, or null
933     * @param defaultValue Value to return if key does not exist
934     * @return the String value associated with the given key, or defaultValue
935     *     if no valid String object is currently mapped to that key.
936     */
937    public String getString(String key, String defaultValue) {
938        final String s = getString(key);
939        return (s == null) ? defaultValue : s;
940    }
941
942    /**
943     * Returns the value associated with the given key, or null if
944     * no mapping of the desired type exists for the given key or a null
945     * value is explicitly associated with the key.
946     *
947     * @param key a String, or null
948     * @return a CharSequence value, or null
949     */
950    CharSequence getCharSequence(String key) {
951        unparcel();
952        final Object o = mMap.get(key);
953        try {
954            return (CharSequence) o;
955        } catch (ClassCastException e) {
956            typeWarning(key, o, "CharSequence", e);
957            return null;
958        }
959    }
960
961    /**
962     * Returns the value associated with the given key, or defaultValue if
963     * no mapping of the desired type exists for the given key.
964     *
965     * @param key a String, or null
966     * @param defaultValue Value to return if key does not exist
967     * @return the CharSequence value associated with the given key, or defaultValue
968     *     if no valid CharSequence object is currently mapped to that key.
969     */
970    CharSequence getCharSequence(String key, CharSequence defaultValue) {
971        final CharSequence cs = getCharSequence(key);
972        return (cs == null) ? defaultValue : cs;
973    }
974
975    /**
976     * Returns the value associated with the given key, or null if
977     * no mapping of the desired type exists for the given key or a null
978     * value is explicitly associated with the key.
979     *
980     * @param key a String, or null
981     * @return a Serializable value, or null
982     */
983    Serializable getSerializable(String key) {
984        unparcel();
985        Object o = mMap.get(key);
986        if (o == null) {
987            return null;
988        }
989        try {
990            return (Serializable) o;
991        } catch (ClassCastException e) {
992            typeWarning(key, o, "Serializable", e);
993            return null;
994        }
995    }
996
997    /**
998     * Returns the value associated with the given key, or null if
999     * no mapping of the desired type exists for the given key or a null
1000     * value is explicitly associated with the key.
1001     *
1002     * @param key a String, or null
1003     * @return an ArrayList<String> value, or null
1004     */
1005    ArrayList<Integer> getIntegerArrayList(String key) {
1006        unparcel();
1007        Object o = mMap.get(key);
1008        if (o == null) {
1009            return null;
1010        }
1011        try {
1012            return (ArrayList<Integer>) o;
1013        } catch (ClassCastException e) {
1014            typeWarning(key, o, "ArrayList<Integer>", e);
1015            return null;
1016        }
1017    }
1018
1019    /**
1020     * Returns the value associated with the given key, or null if
1021     * no mapping of the desired type exists for the given key or a null
1022     * value is explicitly associated with the key.
1023     *
1024     * @param key a String, or null
1025     * @return an ArrayList<String> value, or null
1026     */
1027    ArrayList<String> getStringArrayList(String key) {
1028        unparcel();
1029        Object o = mMap.get(key);
1030        if (o == null) {
1031            return null;
1032        }
1033        try {
1034            return (ArrayList<String>) o;
1035        } catch (ClassCastException e) {
1036            typeWarning(key, o, "ArrayList<String>", e);
1037            return null;
1038        }
1039    }
1040
1041    /**
1042     * Returns the value associated with the given key, or null if
1043     * no mapping of the desired type exists for the given key or a null
1044     * value is explicitly associated with the key.
1045     *
1046     * @param key a String, or null
1047     * @return an ArrayList<CharSequence> value, or null
1048     */
1049    ArrayList<CharSequence> getCharSequenceArrayList(String key) {
1050        unparcel();
1051        Object o = mMap.get(key);
1052        if (o == null) {
1053            return null;
1054        }
1055        try {
1056            return (ArrayList<CharSequence>) o;
1057        } catch (ClassCastException e) {
1058            typeWarning(key, o, "ArrayList<CharSequence>", e);
1059            return null;
1060        }
1061    }
1062
1063    /**
1064     * Returns the value associated with the given key, or null if
1065     * no mapping of the desired type exists for the given key or a null
1066     * value is explicitly associated with the key.
1067     *
1068     * @param key a String, or null
1069     * @return a boolean[] value, or null
1070     */
1071    boolean[] getBooleanArray(String key) {
1072        unparcel();
1073        Object o = mMap.get(key);
1074        if (o == null) {
1075            return null;
1076        }
1077        try {
1078            return (boolean[]) o;
1079        } catch (ClassCastException e) {
1080            typeWarning(key, o, "byte[]", e);
1081            return null;
1082        }
1083    }
1084
1085    /**
1086     * Returns the value associated with the given key, or null if
1087     * no mapping of the desired type exists for the given key or a null
1088     * value is explicitly associated with the key.
1089     *
1090     * @param key a String, or null
1091     * @return a byte[] value, or null
1092     */
1093    byte[] getByteArray(String key) {
1094        unparcel();
1095        Object o = mMap.get(key);
1096        if (o == null) {
1097            return null;
1098        }
1099        try {
1100            return (byte[]) o;
1101        } catch (ClassCastException e) {
1102            typeWarning(key, o, "byte[]", e);
1103            return null;
1104        }
1105    }
1106
1107    /**
1108     * Returns the value associated with the given key, or null if
1109     * no mapping of the desired type exists for the given key or a null
1110     * value is explicitly associated with the key.
1111     *
1112     * @param key a String, or null
1113     * @return a short[] value, or null
1114     */
1115    short[] getShortArray(String key) {
1116        unparcel();
1117        Object o = mMap.get(key);
1118        if (o == null) {
1119            return null;
1120        }
1121        try {
1122            return (short[]) o;
1123        } catch (ClassCastException e) {
1124            typeWarning(key, o, "short[]", e);
1125            return null;
1126        }
1127    }
1128
1129    /**
1130     * Returns the value associated with the given key, or null if
1131     * no mapping of the desired type exists for the given key or a null
1132     * value is explicitly associated with the key.
1133     *
1134     * @param key a String, or null
1135     * @return a char[] value, or null
1136     */
1137    char[] getCharArray(String key) {
1138        unparcel();
1139        Object o = mMap.get(key);
1140        if (o == null) {
1141            return null;
1142        }
1143        try {
1144            return (char[]) o;
1145        } catch (ClassCastException e) {
1146            typeWarning(key, o, "char[]", e);
1147            return null;
1148        }
1149    }
1150
1151    /**
1152     * Returns the value associated with the given key, or null if
1153     * no mapping of the desired type exists for the given key or a null
1154     * value is explicitly associated with the key.
1155     *
1156     * @param key a String, or null
1157     * @return an int[] value, or null
1158     */
1159    public int[] getIntArray(String key) {
1160        unparcel();
1161        Object o = mMap.get(key);
1162        if (o == null) {
1163            return null;
1164        }
1165        try {
1166            return (int[]) o;
1167        } catch (ClassCastException e) {
1168            typeWarning(key, o, "int[]", e);
1169            return null;
1170        }
1171    }
1172
1173    /**
1174     * Returns the value associated with the given key, or null if
1175     * no mapping of the desired type exists for the given key or a null
1176     * value is explicitly associated with the key.
1177     *
1178     * @param key a String, or null
1179     * @return a long[] value, or null
1180     */
1181    public long[] getLongArray(String key) {
1182        unparcel();
1183        Object o = mMap.get(key);
1184        if (o == null) {
1185            return null;
1186        }
1187        try {
1188            return (long[]) o;
1189        } catch (ClassCastException e) {
1190            typeWarning(key, o, "long[]", e);
1191            return null;
1192        }
1193    }
1194
1195    /**
1196     * Returns the value associated with the given key, or null if
1197     * no mapping of the desired type exists for the given key or a null
1198     * value is explicitly associated with the key.
1199     *
1200     * @param key a String, or null
1201     * @return a float[] value, or null
1202     */
1203    float[] getFloatArray(String key) {
1204        unparcel();
1205        Object o = mMap.get(key);
1206        if (o == null) {
1207            return null;
1208        }
1209        try {
1210            return (float[]) o;
1211        } catch (ClassCastException e) {
1212            typeWarning(key, o, "float[]", 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 a double[] value, or null
1224     */
1225    public double[] getDoubleArray(String key) {
1226        unparcel();
1227        Object o = mMap.get(key);
1228        if (o == null) {
1229            return null;
1230        }
1231        try {
1232            return (double[]) o;
1233        } catch (ClassCastException e) {
1234            typeWarning(key, o, "double[]", e);
1235            return null;
1236        }
1237    }
1238
1239    /**
1240     * Returns the value associated with the given key, or null if
1241     * no mapping of the desired type exists for the given key or a null
1242     * value is explicitly associated with the key.
1243     *
1244     * @param key a String, or null
1245     * @return a String[] value, or null
1246     */
1247    public String[] getStringArray(String key) {
1248        unparcel();
1249        Object o = mMap.get(key);
1250        if (o == null) {
1251            return null;
1252        }
1253        try {
1254            return (String[]) o;
1255        } catch (ClassCastException e) {
1256            typeWarning(key, o, "String[]", e);
1257            return null;
1258        }
1259    }
1260
1261    /**
1262     * Returns the value associated with the given key, or null if
1263     * no mapping of the desired type exists for the given key or a null
1264     * value is explicitly associated with the key.
1265     *
1266     * @param key a String, or null
1267     * @return a CharSequence[] value, or null
1268     */
1269    CharSequence[] getCharSequenceArray(String key) {
1270        unparcel();
1271        Object o = mMap.get(key);
1272        if (o == null) {
1273            return null;
1274        }
1275        try {
1276            return (CharSequence[]) o;
1277        } catch (ClassCastException e) {
1278            typeWarning(key, o, "CharSequence[]", e);
1279            return null;
1280        }
1281    }
1282
1283    /**
1284     * Writes the Bundle contents to a Parcel, typically in order for
1285     * it to be passed through an IBinder connection.
1286     * @param parcel The parcel to copy this bundle to.
1287     */
1288    void writeToParcelInner(Parcel parcel, int flags) {
1289        if (mParcelledData != null) {
1290            if (mParcelledData == EMPTY_PARCEL) {
1291                parcel.writeInt(0);
1292            } else {
1293                int length = mParcelledData.dataSize();
1294                parcel.writeInt(length);
1295                parcel.writeInt(BUNDLE_MAGIC);
1296                parcel.appendFrom(mParcelledData, 0, length);
1297            }
1298        } else {
1299            // Special case for empty bundles.
1300            if (mMap == null || mMap.size() <= 0) {
1301                parcel.writeInt(0);
1302                return;
1303            }
1304            int lengthPos = parcel.dataPosition();
1305            parcel.writeInt(-1); // dummy, will hold length
1306            parcel.writeInt(BUNDLE_MAGIC);
1307
1308            int startPos = parcel.dataPosition();
1309            parcel.writeArrayMapInternal(mMap);
1310            int endPos = parcel.dataPosition();
1311
1312            // Backpatch length
1313            parcel.setDataPosition(lengthPos);
1314            int length = endPos - startPos;
1315            parcel.writeInt(length);
1316            parcel.setDataPosition(endPos);
1317        }
1318    }
1319
1320    /**
1321     * Reads the Parcel contents into this Bundle, typically in order for
1322     * it to be passed through an IBinder connection.
1323     * @param parcel The parcel to overwrite this bundle from.
1324     */
1325    void readFromParcelInner(Parcel parcel) {
1326        int length = parcel.readInt();
1327        if (length < 0) {
1328            throw new RuntimeException("Bad length in parcel: " + length);
1329        }
1330        readFromParcelInner(parcel, length);
1331    }
1332
1333    private void readFromParcelInner(Parcel parcel, int length) {
1334        if (length == 0) {
1335            // Empty Bundle or end of data.
1336            mParcelledData = EMPTY_PARCEL;
1337            return;
1338        }
1339        int magic = parcel.readInt();
1340        if (magic != BUNDLE_MAGIC) {
1341            //noinspection ThrowableInstanceNeverThrown
1342            throw new IllegalStateException("Bad magic number for Bundle: 0x"
1343                    + Integer.toHexString(magic));
1344        }
1345
1346        // Advance within this Parcel
1347        int offset = parcel.dataPosition();
1348        parcel.setDataPosition(offset + length);
1349
1350        Parcel p = Parcel.obtain();
1351        p.setDataPosition(0);
1352        p.appendFrom(parcel, offset, length);
1353        if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
1354                + ": " + length + " bundle bytes starting at " + offset);
1355        p.setDataPosition(0);
1356
1357        mParcelledData = p;
1358    }
1359}
1360