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