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
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     /**
574      * Inserts a Boolean value into the mapping of this Bundle, replacing
575      * any existing value for the given key.  Either key or value may be null.
576      *
577      * @param key a String, or null
578      * @param value a boolean
579      */
putBoolean(@ullable String key, boolean value)580     public void putBoolean(@Nullable String key, boolean value) {
581         unparcel();
582         mMap.put(key, value);
583     }
584 
585     /**
586      * Inserts a byte value into the mapping of this Bundle, replacing
587      * any existing value for the given key.
588      *
589      * @param key a String, or null
590      * @param value a byte
591      */
putByte(@ullable String key, byte value)592     void putByte(@Nullable String key, byte value) {
593         unparcel();
594         mMap.put(key, value);
595     }
596 
597     /**
598      * Inserts a char value into the mapping of this Bundle, replacing
599      * any existing value for the given key.
600      *
601      * @param key a String, or null
602      * @param value a char
603      */
putChar(@ullable String key, char value)604     void putChar(@Nullable String key, char value) {
605         unparcel();
606         mMap.put(key, value);
607     }
608 
609     /**
610      * Inserts a short value into the mapping of this Bundle, replacing
611      * any existing value for the given key.
612      *
613      * @param key a String, or null
614      * @param value a short
615      */
putShort(@ullable String key, short value)616     void putShort(@Nullable String key, short value) {
617         unparcel();
618         mMap.put(key, value);
619     }
620 
621     /**
622      * Inserts an int value into the mapping of this Bundle, replacing
623      * any existing value for the given key.
624      *
625      * @param key a String, or null
626      * @param value an int
627      */
putInt(@ullable String key, int value)628     public void putInt(@Nullable String key, int value) {
629         unparcel();
630         mMap.put(key, value);
631     }
632 
633     /**
634      * Inserts a long value into the mapping of this Bundle, replacing
635      * any existing value for the given key.
636      *
637      * @param key a String, or null
638      * @param value a long
639      */
putLong(@ullable String key, long value)640     public void putLong(@Nullable String key, long value) {
641         unparcel();
642         mMap.put(key, value);
643     }
644 
645     /**
646      * Inserts a float value into the mapping of this Bundle, replacing
647      * any existing value for the given key.
648      *
649      * @param key a String, or null
650      * @param value a float
651      */
putFloat(@ullable String key, float value)652     void putFloat(@Nullable String key, float value) {
653         unparcel();
654         mMap.put(key, value);
655     }
656 
657     /**
658      * Inserts a double value into the mapping of this Bundle, replacing
659      * any existing value for the given key.
660      *
661      * @param key a String, or null
662      * @param value a double
663      */
putDouble(@ullable String key, double value)664     public void putDouble(@Nullable String key, double value) {
665         unparcel();
666         mMap.put(key, value);
667     }
668 
669     /**
670      * Inserts a String value into the mapping of this Bundle, replacing
671      * any existing value for the given key.  Either key or value may be null.
672      *
673      * @param key a String, or null
674      * @param value a String, or null
675      */
putString(@ullable String key, @Nullable String value)676     public void putString(@Nullable String key, @Nullable String value) {
677         unparcel();
678         mMap.put(key, value);
679     }
680 
681     /**
682      * Inserts a CharSequence value into the mapping of this Bundle, replacing
683      * any existing value for the given key.  Either key or value may be null.
684      *
685      * @param key a String, or null
686      * @param value a CharSequence, or null
687      */
putCharSequence(@ullable String key, @Nullable CharSequence value)688     void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
689         unparcel();
690         mMap.put(key, value);
691     }
692 
693     /**
694      * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
695      * any existing value for the given key.  Either key or value may be null.
696      *
697      * @param key a String, or null
698      * @param value an ArrayList<Integer> object, or null
699      */
putIntegerArrayList(@ullable String key, @Nullable ArrayList<Integer> value)700     void putIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
701         unparcel();
702         mMap.put(key, value);
703     }
704 
705     /**
706      * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
707      * any existing value for the given key.  Either key or value may be null.
708      *
709      * @param key a String, or null
710      * @param value an ArrayList<String> object, or null
711      */
putStringArrayList(@ullable String key, @Nullable ArrayList<String> value)712     void putStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
713         unparcel();
714         mMap.put(key, value);
715     }
716 
717     /**
718      * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
719      * any existing value for the given key.  Either key or value may be null.
720      *
721      * @param key a String, or null
722      * @param value an ArrayList<CharSequence> object, or null
723      */
putCharSequenceArrayList(@ullable String key, @Nullable ArrayList<CharSequence> value)724     void putCharSequenceArrayList(@Nullable String key, @Nullable ArrayList<CharSequence> value) {
725         unparcel();
726         mMap.put(key, value);
727     }
728 
729     /**
730      * Inserts a Serializable value into the mapping of this Bundle, replacing
731      * any existing value for the given key.  Either key or value may be null.
732      *
733      * @param key a String, or null
734      * @param value a Serializable object, or null
735      */
putSerializable(@ullable String key, @Nullable Serializable value)736     void putSerializable(@Nullable String key, @Nullable Serializable value) {
737         unparcel();
738         mMap.put(key, value);
739     }
740 
741     /**
742      * Inserts a boolean array value into the mapping of this Bundle, replacing
743      * any existing value for the given key.  Either key or value may be null.
744      *
745      * @param key a String, or null
746      * @param value a boolean array object, or null
747      */
putBooleanArray(@ullable String key, @Nullable boolean[] value)748     public void putBooleanArray(@Nullable String key, @Nullable boolean[] value) {
749         unparcel();
750         mMap.put(key, value);
751     }
752 
753     /**
754      * Inserts a byte array value into the mapping of this Bundle, replacing
755      * any existing value for the given key.  Either key or value may be null.
756      *
757      * @param key a String, or null
758      * @param value a byte array object, or null
759      */
putByteArray(@ullable String key, @Nullable byte[] value)760     void putByteArray(@Nullable String key, @Nullable byte[] value) {
761         unparcel();
762         mMap.put(key, value);
763     }
764 
765     /**
766      * Inserts a short array value into the mapping of this Bundle, replacing
767      * any existing value for the given key.  Either key or value may be null.
768      *
769      * @param key a String, or null
770      * @param value a short array object, or null
771      */
putShortArray(@ullable String key, @Nullable short[] value)772     void putShortArray(@Nullable String key, @Nullable short[] value) {
773         unparcel();
774         mMap.put(key, value);
775     }
776 
777     /**
778      * Inserts a char array value into the mapping of this Bundle, replacing
779      * any existing value for the given key.  Either key or value may be null.
780      *
781      * @param key a String, or null
782      * @param value a char array object, or null
783      */
putCharArray(@ullable String key, @Nullable char[] value)784     void putCharArray(@Nullable String key, @Nullable char[] value) {
785         unparcel();
786         mMap.put(key, value);
787     }
788 
789     /**
790      * Inserts an int array value into the mapping of this Bundle, replacing
791      * any existing value for the given key.  Either key or value may be null.
792      *
793      * @param key a String, or null
794      * @param value an int array object, or null
795      */
putIntArray(@ullable String key, @Nullable int[] value)796     public void putIntArray(@Nullable String key, @Nullable int[] value) {
797         unparcel();
798         mMap.put(key, value);
799     }
800 
801     /**
802      * Inserts a long array value into the mapping of this Bundle, replacing
803      * any existing value for the given key.  Either key or value may be null.
804      *
805      * @param key a String, or null
806      * @param value a long array object, or null
807      */
putLongArray(@ullable String key, @Nullable long[] value)808     public void putLongArray(@Nullable String key, @Nullable long[] value) {
809         unparcel();
810         mMap.put(key, value);
811     }
812 
813     /**
814      * Inserts a float array value into the mapping of this Bundle, replacing
815      * any existing value for the given key.  Either key or value may be null.
816      *
817      * @param key a String, or null
818      * @param value a float array object, or null
819      */
putFloatArray(@ullable String key, @Nullable float[] value)820     void putFloatArray(@Nullable String key, @Nullable float[] value) {
821         unparcel();
822         mMap.put(key, value);
823     }
824 
825     /**
826      * Inserts a double array value into the mapping of this Bundle, replacing
827      * any existing value for the given key.  Either key or value may be null.
828      *
829      * @param key a String, or null
830      * @param value a double array object, or null
831      */
putDoubleArray(@ullable String key, @Nullable double[] value)832     public void putDoubleArray(@Nullable String key, @Nullable double[] value) {
833         unparcel();
834         mMap.put(key, value);
835     }
836 
837     /**
838      * Inserts a String array value into the mapping of this Bundle, replacing
839      * any existing value for the given key.  Either key or value may be null.
840      *
841      * @param key a String, or null
842      * @param value a String array object, or null
843      */
putStringArray(@ullable String key, @Nullable String[] value)844     public void putStringArray(@Nullable String key, @Nullable String[] value) {
845         unparcel();
846         mMap.put(key, value);
847     }
848 
849     /**
850      * Inserts a CharSequence array value into the mapping of this Bundle, replacing
851      * any existing value for the given key.  Either key or value may be null.
852      *
853      * @param key a String, or null
854      * @param value a CharSequence array object, or null
855      */
putCharSequenceArray(@ullable String key, @Nullable CharSequence[] value)856     void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
857         unparcel();
858         mMap.put(key, value);
859     }
860 
861     /**
862      * Returns the value associated with the given key, or false if
863      * no mapping of the desired type exists for the given key.
864      *
865      * @param key a String
866      * @return a boolean value
867      */
getBoolean(String key)868     public boolean getBoolean(String key) {
869         unparcel();
870         if (DEBUG) Log.d(TAG, "Getting boolean in "
871                 + Integer.toHexString(System.identityHashCode(this)));
872         return getBoolean(key, false);
873     }
874 
875     // 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)876     void typeWarning(String key, Object value, String className,
877             Object defaultValue, ClassCastException e) {
878         StringBuilder sb = new StringBuilder();
879         sb.append("Key ");
880         sb.append(key);
881         sb.append(" expected ");
882         sb.append(className);
883         sb.append(" but value was a ");
884         sb.append(value.getClass().getName());
885         sb.append(".  The default value ");
886         sb.append(defaultValue);
887         sb.append(" was returned.");
888         Log.w(TAG, sb.toString());
889         Log.w(TAG, "Attempt to cast generated internal exception:", e);
890     }
891 
typeWarning(String key, Object value, String className, ClassCastException e)892     void typeWarning(String key, Object value, String className,
893             ClassCastException e) {
894         typeWarning(key, value, className, "<null>", e);
895     }
896 
897     /**
898      * Returns the value associated with the given key, or defaultValue if
899      * no mapping of the desired type exists for the given key.
900      *
901      * @param key a String
902      * @param defaultValue Value to return if key does not exist
903      * @return a boolean value
904      */
getBoolean(String key, boolean defaultValue)905     public boolean getBoolean(String key, boolean defaultValue) {
906         unparcel();
907         Object o = mMap.get(key);
908         if (o == null) {
909             return defaultValue;
910         }
911         try {
912             return (Boolean) o;
913         } catch (ClassCastException e) {
914             typeWarning(key, o, "Boolean", defaultValue, e);
915             return defaultValue;
916         }
917     }
918 
919     /**
920      * Returns the value associated with the given key, or (byte) 0 if
921      * no mapping of the desired type exists for the given key.
922      *
923      * @param key a String
924      * @return a byte value
925      */
getByte(String key)926     byte getByte(String key) {
927         unparcel();
928         return getByte(key, (byte) 0);
929     }
930 
931     /**
932      * Returns the value associated with the given key, or defaultValue if
933      * no mapping of the desired type exists for the given key.
934      *
935      * @param key a String
936      * @param defaultValue Value to return if key does not exist
937      * @return a byte value
938      */
getByte(String key, byte defaultValue)939     Byte getByte(String key, byte defaultValue) {
940         unparcel();
941         Object o = mMap.get(key);
942         if (o == null) {
943             return defaultValue;
944         }
945         try {
946             return (Byte) o;
947         } catch (ClassCastException e) {
948             typeWarning(key, o, "Byte", defaultValue, e);
949             return defaultValue;
950         }
951     }
952 
953     /**
954      * Returns the value associated with the given key, or (char) 0 if
955      * no mapping of the desired type exists for the given key.
956      *
957      * @param key a String
958      * @return a char value
959      */
getChar(String key)960     char getChar(String key) {
961         unparcel();
962         return getChar(key, (char) 0);
963     }
964 
965     /**
966      * Returns the value associated with the given key, or defaultValue if
967      * no mapping of the desired type exists for the given key.
968      *
969      * @param key a String
970      * @param defaultValue Value to return if key does not exist
971      * @return a char value
972      */
getChar(String key, char defaultValue)973     char getChar(String key, char defaultValue) {
974         unparcel();
975         Object o = mMap.get(key);
976         if (o == null) {
977             return defaultValue;
978         }
979         try {
980             return (Character) o;
981         } catch (ClassCastException e) {
982             typeWarning(key, o, "Character", defaultValue, e);
983             return defaultValue;
984         }
985     }
986 
987     /**
988      * Returns the value associated with the given key, or (short) 0 if
989      * no mapping of the desired type exists for the given key.
990      *
991      * @param key a String
992      * @return a short value
993      */
getShort(String key)994     short getShort(String key) {
995         unparcel();
996         return getShort(key, (short) 0);
997     }
998 
999     /**
1000      * Returns the value associated with the given key, or defaultValue if
1001      * no mapping of the desired type exists for the given key.
1002      *
1003      * @param key a String
1004      * @param defaultValue Value to return if key does not exist
1005      * @return a short value
1006      */
getShort(String key, short defaultValue)1007     short getShort(String key, short defaultValue) {
1008         unparcel();
1009         Object o = mMap.get(key);
1010         if (o == null) {
1011             return defaultValue;
1012         }
1013         try {
1014             return (Short) o;
1015         } catch (ClassCastException e) {
1016             typeWarning(key, o, "Short", defaultValue, e);
1017             return defaultValue;
1018         }
1019     }
1020 
1021     /**
1022      * Returns the value associated with the given key, or 0 if
1023      * no mapping of the desired type exists for the given key.
1024      *
1025      * @param key a String
1026      * @return an int value
1027      */
getInt(String key)1028     public int getInt(String key) {
1029         unparcel();
1030         return getInt(key, 0);
1031     }
1032 
1033     /**
1034      * Returns the value associated with the given key, or defaultValue if
1035      * no mapping of the desired type exists for the given key.
1036      *
1037      * @param key a String
1038      * @param defaultValue Value to return if key does not exist
1039      * @return an int value
1040      */
getInt(String key, int defaultValue)1041    public int getInt(String key, int defaultValue) {
1042         unparcel();
1043         Object o = mMap.get(key);
1044         if (o == null) {
1045             return defaultValue;
1046         }
1047         try {
1048             return (Integer) o;
1049         } catch (ClassCastException e) {
1050             typeWarning(key, o, "Integer", defaultValue, e);
1051             return defaultValue;
1052         }
1053     }
1054 
1055     /**
1056      * Returns the value associated with the given key, or 0L if
1057      * no mapping of the desired type exists for the given key.
1058      *
1059      * @param key a String
1060      * @return a long value
1061      */
getLong(String key)1062     public long getLong(String key) {
1063         unparcel();
1064         return getLong(key, 0L);
1065     }
1066 
1067     /**
1068      * Returns the value associated with the given key, or defaultValue if
1069      * no mapping of the desired type exists for the given key.
1070      *
1071      * @param key a String
1072      * @param defaultValue Value to return if key does not exist
1073      * @return a long value
1074      */
getLong(String key, long defaultValue)1075     public long getLong(String key, long defaultValue) {
1076         unparcel();
1077         Object o = mMap.get(key);
1078         if (o == null) {
1079             return defaultValue;
1080         }
1081         try {
1082             return (Long) o;
1083         } catch (ClassCastException e) {
1084             typeWarning(key, o, "Long", defaultValue, e);
1085             return defaultValue;
1086         }
1087     }
1088 
1089     /**
1090      * Returns the value associated with the given key, or 0.0f if
1091      * no mapping of the desired type exists for the given key.
1092      *
1093      * @param key a String
1094      * @return a float value
1095      */
getFloat(String key)1096     float getFloat(String key) {
1097         unparcel();
1098         return getFloat(key, 0.0f);
1099     }
1100 
1101     /**
1102      * Returns the value associated with the given key, or defaultValue if
1103      * no mapping of the desired type exists for the given key.
1104      *
1105      * @param key a String
1106      * @param defaultValue Value to return if key does not exist
1107      * @return a float value
1108      */
getFloat(String key, float defaultValue)1109     float getFloat(String key, float defaultValue) {
1110         unparcel();
1111         Object o = mMap.get(key);
1112         if (o == null) {
1113             return defaultValue;
1114         }
1115         try {
1116             return (Float) o;
1117         } catch (ClassCastException e) {
1118             typeWarning(key, o, "Float", defaultValue, e);
1119             return defaultValue;
1120         }
1121     }
1122 
1123     /**
1124      * Returns the value associated with the given key, or 0.0 if
1125      * no mapping of the desired type exists for the given key.
1126      *
1127      * @param key a String
1128      * @return a double value
1129      */
getDouble(String key)1130     public double getDouble(String key) {
1131         unparcel();
1132         return getDouble(key, 0.0);
1133     }
1134 
1135     /**
1136      * Returns the value associated with the given key, or defaultValue if
1137      * no mapping of the desired type exists for the given key.
1138      *
1139      * @param key a String
1140      * @param defaultValue Value to return if key does not exist
1141      * @return a double value
1142      */
getDouble(String key, double defaultValue)1143     public double getDouble(String key, double defaultValue) {
1144         unparcel();
1145         Object o = mMap.get(key);
1146         if (o == null) {
1147             return defaultValue;
1148         }
1149         try {
1150             return (Double) o;
1151         } catch (ClassCastException e) {
1152             typeWarning(key, o, "Double", defaultValue, e);
1153             return defaultValue;
1154         }
1155     }
1156 
1157     /**
1158      * Returns the value associated with the given key, or null if
1159      * no mapping of the desired type exists for the given key or a null
1160      * value is explicitly associated with the key.
1161      *
1162      * @param key a String, or null
1163      * @return a String value, or null
1164      */
1165     @Nullable
getString(@ullable String key)1166     public String getString(@Nullable String key) {
1167         unparcel();
1168         final Object o = mMap.get(key);
1169         try {
1170             return (String) o;
1171         } catch (ClassCastException e) {
1172             typeWarning(key, o, "String", e);
1173             return null;
1174         }
1175     }
1176 
1177     /**
1178      * Returns the value associated with the given key, or defaultValue if
1179      * no mapping of the desired type exists for the given key or if a null
1180      * value is explicitly associated with the given key.
1181      *
1182      * @param key a String, or null
1183      * @param defaultValue Value to return if key does not exist or if a null
1184      *     value is associated with the given key.
1185      * @return the String value associated with the given key, or defaultValue
1186      *     if no valid String object is currently mapped to that key.
1187      */
getString(@ullable String key, String defaultValue)1188     public String getString(@Nullable String key, String defaultValue) {
1189         final String s = getString(key);
1190         return (s == null) ? defaultValue : s;
1191     }
1192 
1193     /**
1194      * Returns the value associated with the given key, or null if
1195      * no mapping of the desired type exists for the given key or a null
1196      * value is explicitly associated with the key.
1197      *
1198      * @param key a String, or null
1199      * @return a CharSequence value, or null
1200      */
1201     @Nullable
getCharSequence(@ullable String key)1202     CharSequence getCharSequence(@Nullable String key) {
1203         unparcel();
1204         final Object o = mMap.get(key);
1205         try {
1206             return (CharSequence) o;
1207         } catch (ClassCastException e) {
1208             typeWarning(key, o, "CharSequence", e);
1209             return null;
1210         }
1211     }
1212 
1213     /**
1214      * Returns the value associated with the given key, or defaultValue if
1215      * no mapping of the desired type exists for the given key or if a null
1216      * value is explicitly associated with the given key.
1217      *
1218      * @param key a String, or null
1219      * @param defaultValue Value to return if key does not exist or if a null
1220      *     value is associated with the given key.
1221      * @return the CharSequence value associated with the given key, or defaultValue
1222      *     if no valid CharSequence object is currently mapped to that key.
1223      */
getCharSequence(@ullable String key, CharSequence defaultValue)1224     CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
1225         final CharSequence cs = getCharSequence(key);
1226         return (cs == null) ? defaultValue : cs;
1227     }
1228 
1229     /**
1230      * Returns the value associated with the given key, or null if
1231      * no mapping of the desired type exists for the given key or a null
1232      * value is explicitly associated with the key.
1233      *
1234      * @param key a String, or null
1235      * @return a Serializable value, or null
1236      */
1237     @Nullable
getSerializable(@ullable String key)1238     Serializable getSerializable(@Nullable String key) {
1239         unparcel();
1240         Object o = mMap.get(key);
1241         if (o == null) {
1242             return null;
1243         }
1244         try {
1245             return (Serializable) o;
1246         } catch (ClassCastException e) {
1247             typeWarning(key, o, "Serializable", e);
1248             return null;
1249         }
1250     }
1251 
1252     /**
1253      * Returns the value associated with the given key, or null if
1254      * no mapping of the desired type exists for the given key or a null
1255      * value is explicitly associated with the key.
1256      *
1257      * @param key a String, or null
1258      * @return an ArrayList<String> value, or null
1259      */
1260     @Nullable
getIntegerArrayList(@ullable String key)1261     ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
1262         unparcel();
1263         Object o = mMap.get(key);
1264         if (o == null) {
1265             return null;
1266         }
1267         try {
1268             return (ArrayList<Integer>) o;
1269         } catch (ClassCastException e) {
1270             typeWarning(key, o, "ArrayList<Integer>", e);
1271             return null;
1272         }
1273     }
1274 
1275     /**
1276      * Returns the value associated with the given key, or null if
1277      * no mapping of the desired type exists for the given key or a null
1278      * value is explicitly associated with the key.
1279      *
1280      * @param key a String, or null
1281      * @return an ArrayList<String> value, or null
1282      */
1283     @Nullable
getStringArrayList(@ullable String key)1284     ArrayList<String> getStringArrayList(@Nullable String key) {
1285         unparcel();
1286         Object o = mMap.get(key);
1287         if (o == null) {
1288             return null;
1289         }
1290         try {
1291             return (ArrayList<String>) o;
1292         } catch (ClassCastException e) {
1293             typeWarning(key, o, "ArrayList<String>", e);
1294             return null;
1295         }
1296     }
1297 
1298     /**
1299      * Returns the value associated with the given key, or null if
1300      * no mapping of the desired type exists for the given key or a null
1301      * value is explicitly associated with the key.
1302      *
1303      * @param key a String, or null
1304      * @return an ArrayList<CharSequence> value, or null
1305      */
1306     @Nullable
getCharSequenceArrayList(@ullable String key)1307     ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
1308         unparcel();
1309         Object o = mMap.get(key);
1310         if (o == null) {
1311             return null;
1312         }
1313         try {
1314             return (ArrayList<CharSequence>) o;
1315         } catch (ClassCastException e) {
1316             typeWarning(key, o, "ArrayList<CharSequence>", e);
1317             return null;
1318         }
1319     }
1320 
1321     /**
1322      * Returns the value associated with the given key, or null if
1323      * no mapping of the desired type exists for the given key or a null
1324      * value is explicitly associated with the key.
1325      *
1326      * @param key a String, or null
1327      * @return a boolean[] value, or null
1328      */
1329     @Nullable
getBooleanArray(@ullable String key)1330     public boolean[] getBooleanArray(@Nullable String key) {
1331         unparcel();
1332         Object o = mMap.get(key);
1333         if (o == null) {
1334             return null;
1335         }
1336         try {
1337             return (boolean[]) o;
1338         } catch (ClassCastException e) {
1339             typeWarning(key, o, "byte[]", e);
1340             return null;
1341         }
1342     }
1343 
1344     /**
1345      * Returns the value associated with the given key, or null if
1346      * no mapping of the desired type exists for the given key or a null
1347      * value is explicitly associated with the key.
1348      *
1349      * @param key a String, or null
1350      * @return a byte[] value, or null
1351      */
1352     @Nullable
getByteArray(@ullable String key)1353     byte[] getByteArray(@Nullable String key) {
1354         unparcel();
1355         Object o = mMap.get(key);
1356         if (o == null) {
1357             return null;
1358         }
1359         try {
1360             return (byte[]) o;
1361         } catch (ClassCastException e) {
1362             typeWarning(key, o, "byte[]", e);
1363             return null;
1364         }
1365     }
1366 
1367     /**
1368      * Returns the value associated with the given key, or null if
1369      * no mapping of the desired type exists for the given key or a null
1370      * value is explicitly associated with the key.
1371      *
1372      * @param key a String, or null
1373      * @return a short[] value, or null
1374      */
1375     @Nullable
getShortArray(@ullable String key)1376     short[] getShortArray(@Nullable String key) {
1377         unparcel();
1378         Object o = mMap.get(key);
1379         if (o == null) {
1380             return null;
1381         }
1382         try {
1383             return (short[]) o;
1384         } catch (ClassCastException e) {
1385             typeWarning(key, o, "short[]", e);
1386             return null;
1387         }
1388     }
1389 
1390     /**
1391      * Returns the value associated with the given key, or null if
1392      * no mapping of the desired type exists for the given key or a null
1393      * value is explicitly associated with the key.
1394      *
1395      * @param key a String, or null
1396      * @return a char[] value, or null
1397      */
1398     @Nullable
getCharArray(@ullable String key)1399     char[] getCharArray(@Nullable String key) {
1400         unparcel();
1401         Object o = mMap.get(key);
1402         if (o == null) {
1403             return null;
1404         }
1405         try {
1406             return (char[]) o;
1407         } catch (ClassCastException e) {
1408             typeWarning(key, o, "char[]", e);
1409             return null;
1410         }
1411     }
1412 
1413     /**
1414      * Returns the value associated with the given key, or null if
1415      * no mapping of the desired type exists for the given key or a null
1416      * value is explicitly associated with the key.
1417      *
1418      * @param key a String, or null
1419      * @return an int[] value, or null
1420      */
1421     @Nullable
getIntArray(@ullable String key)1422     public int[] getIntArray(@Nullable String key) {
1423         unparcel();
1424         Object o = mMap.get(key);
1425         if (o == null) {
1426             return null;
1427         }
1428         try {
1429             return (int[]) o;
1430         } catch (ClassCastException e) {
1431             typeWarning(key, o, "int[]", e);
1432             return null;
1433         }
1434     }
1435 
1436     /**
1437      * Returns the value associated with the given key, or null if
1438      * no mapping of the desired type exists for the given key or a null
1439      * value is explicitly associated with the key.
1440      *
1441      * @param key a String, or null
1442      * @return a long[] value, or null
1443      */
1444     @Nullable
getLongArray(@ullable String key)1445     public long[] getLongArray(@Nullable String key) {
1446         unparcel();
1447         Object o = mMap.get(key);
1448         if (o == null) {
1449             return null;
1450         }
1451         try {
1452             return (long[]) o;
1453         } catch (ClassCastException e) {
1454             typeWarning(key, o, "long[]", e);
1455             return null;
1456         }
1457     }
1458 
1459     /**
1460      * Returns the value associated with the given key, or null if
1461      * no mapping of the desired type exists for the given key or a null
1462      * value is explicitly associated with the key.
1463      *
1464      * @param key a String, or null
1465      * @return a float[] value, or null
1466      */
1467     @Nullable
getFloatArray(@ullable String key)1468     float[] getFloatArray(@Nullable String key) {
1469         unparcel();
1470         Object o = mMap.get(key);
1471         if (o == null) {
1472             return null;
1473         }
1474         try {
1475             return (float[]) o;
1476         } catch (ClassCastException e) {
1477             typeWarning(key, o, "float[]", e);
1478             return null;
1479         }
1480     }
1481 
1482     /**
1483      * Returns the value associated with the given key, or null if
1484      * no mapping of the desired type exists for the given key or a null
1485      * value is explicitly associated with the key.
1486      *
1487      * @param key a String, or null
1488      * @return a double[] value, or null
1489      */
1490     @Nullable
getDoubleArray(@ullable String key)1491     public double[] getDoubleArray(@Nullable String key) {
1492         unparcel();
1493         Object o = mMap.get(key);
1494         if (o == null) {
1495             return null;
1496         }
1497         try {
1498             return (double[]) o;
1499         } catch (ClassCastException e) {
1500             typeWarning(key, o, "double[]", e);
1501             return null;
1502         }
1503     }
1504 
1505     /**
1506      * Returns the value associated with the given key, or null if
1507      * no mapping of the desired type exists for the given key or a null
1508      * value is explicitly associated with the key.
1509      *
1510      * @param key a String, or null
1511      * @return a String[] value, or null
1512      */
1513     @Nullable
getStringArray(@ullable String key)1514     public String[] getStringArray(@Nullable String key) {
1515         unparcel();
1516         Object o = mMap.get(key);
1517         if (o == null) {
1518             return null;
1519         }
1520         try {
1521             return (String[]) o;
1522         } catch (ClassCastException e) {
1523             typeWarning(key, o, "String[]", e);
1524             return null;
1525         }
1526     }
1527 
1528     /**
1529      * Returns the value associated with the given key, or null if
1530      * no mapping of the desired type exists for the given key or a null
1531      * value is explicitly associated with the key.
1532      *
1533      * @param key a String, or null
1534      * @return a CharSequence[] value, or null
1535      */
1536     @Nullable
getCharSequenceArray(@ullable String key)1537     CharSequence[] getCharSequenceArray(@Nullable String key) {
1538         unparcel();
1539         Object o = mMap.get(key);
1540         if (o == null) {
1541             return null;
1542         }
1543         try {
1544             return (CharSequence[]) o;
1545         } catch (ClassCastException e) {
1546             typeWarning(key, o, "CharSequence[]", e);
1547             return null;
1548         }
1549     }
1550 
1551     /**
1552      * Writes the Bundle contents to a Parcel, typically in order for
1553      * it to be passed through an IBinder connection.
1554      * @param parcel The parcel to copy this bundle to.
1555      */
writeToParcelInner(Parcel parcel, int flags)1556     void writeToParcelInner(Parcel parcel, int flags) {
1557         // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
1558         if (parcel.hasReadWriteHelper()) {
1559             unparcel();
1560         }
1561         // Keep implementation in sync with writeToParcel() in
1562         // frameworks/native/libs/binder/PersistableBundle.cpp.
1563         final ArrayMap<String, Object> map;
1564         synchronized (this) {
1565             // unparcel() can race with this method and cause the parcel to recycle
1566             // at the wrong time. So synchronize access the mParcelledData's content.
1567             if (mParcelledData != null) {
1568                 if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
1569                     parcel.writeInt(0);
1570                 } else {
1571                     int length = mParcelledData.dataSize();
1572                     parcel.writeInt(length);
1573                     parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
1574                     parcel.appendFrom(mParcelledData, 0, length);
1575                 }
1576                 return;
1577             }
1578             map = mMap;
1579         }
1580 
1581         // Special case for empty bundles.
1582         if (map == null || map.size() <= 0) {
1583             parcel.writeInt(0);
1584             return;
1585         }
1586         int lengthPos = parcel.dataPosition();
1587         parcel.writeInt(-1); // dummy, will hold length
1588         parcel.writeInt(BUNDLE_MAGIC);
1589 
1590         int startPos = parcel.dataPosition();
1591         parcel.writeArrayMapInternal(map);
1592         int endPos = parcel.dataPosition();
1593 
1594         // Backpatch length
1595         parcel.setDataPosition(lengthPos);
1596         int length = endPos - startPos;
1597         parcel.writeInt(length);
1598         parcel.setDataPosition(endPos);
1599     }
1600 
1601     /**
1602      * Reads the Parcel contents into this Bundle, typically in order for
1603      * it to be passed through an IBinder connection.
1604      * @param parcel The parcel to overwrite this bundle from.
1605      */
readFromParcelInner(Parcel parcel)1606     void readFromParcelInner(Parcel parcel) {
1607         // Keep implementation in sync with readFromParcel() in
1608         // frameworks/native/libs/binder/PersistableBundle.cpp.
1609         int length = parcel.readInt();
1610         readFromParcelInner(parcel, length);
1611     }
1612 
readFromParcelInner(Parcel parcel, int length)1613     private void readFromParcelInner(Parcel parcel, int length) {
1614         if (length < 0) {
1615             throw new RuntimeException("Bad length in parcel: " + length);
1616         } else if (length == 0) {
1617             // Empty Bundle or end of data.
1618             mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
1619             mParcelledByNative = false;
1620             return;
1621         } else if (length % 4 != 0) {
1622             throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
1623         }
1624 
1625         final int magic = parcel.readInt();
1626         final boolean isJavaBundle = magic == BUNDLE_MAGIC;
1627         final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
1628         if (!isJavaBundle && !isNativeBundle) {
1629             throw new IllegalStateException("Bad magic number for Bundle: 0x"
1630                     + Integer.toHexString(magic));
1631         }
1632 
1633         if (parcel.hasReadWriteHelper()) {
1634             // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
1635             // unparcel right away.
1636             synchronized (this) {
1637                 initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
1638             }
1639             return;
1640         }
1641 
1642         // Advance within this Parcel
1643         int offset = parcel.dataPosition();
1644         parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
1645 
1646         Parcel p = Parcel.obtain();
1647         p.setDataPosition(0);
1648         p.appendFrom(parcel, offset, length);
1649         p.adoptClassCookies(parcel);
1650         if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
1651                 + ": " + length + " bundle bytes starting at " + offset);
1652         p.setDataPosition(0);
1653 
1654         mParcelledData = p;
1655         mParcelledByNative = isNativeBundle;
1656     }
1657 
1658     /** {@hide} */
dumpStats(IndentingPrintWriter pw, String key, Object value)1659     public static void dumpStats(IndentingPrintWriter pw, String key, Object value) {
1660         final Parcel tmp = Parcel.obtain();
1661         tmp.writeValue(value);
1662         final int size = tmp.dataPosition();
1663         tmp.recycle();
1664 
1665         // We only really care about logging large values
1666         if (size > 1024) {
1667             pw.println(key + " [size=" + size + "]");
1668             if (value instanceof BaseBundle) {
1669                 dumpStats(pw, (BaseBundle) value);
1670             } else if (value instanceof SparseArray) {
1671                 dumpStats(pw, (SparseArray) value);
1672             }
1673         }
1674     }
1675 
1676     /** {@hide} */
dumpStats(IndentingPrintWriter pw, SparseArray array)1677     public static void dumpStats(IndentingPrintWriter pw, SparseArray array) {
1678         pw.increaseIndent();
1679         if (array == null) {
1680             pw.println("[null]");
1681             return;
1682         }
1683         for (int i = 0; i < array.size(); i++) {
1684             dumpStats(pw, "0x" + Integer.toHexString(array.keyAt(i)), array.valueAt(i));
1685         }
1686         pw.decreaseIndent();
1687     }
1688 
1689     /** {@hide} */
dumpStats(IndentingPrintWriter pw, BaseBundle bundle)1690     public static void dumpStats(IndentingPrintWriter pw, BaseBundle bundle) {
1691         pw.increaseIndent();
1692         if (bundle == null) {
1693             pw.println("[null]");
1694             return;
1695         }
1696         final ArrayMap<String, Object> map = bundle.getMap();
1697         for (int i = 0; i < map.size(); i++) {
1698             dumpStats(pw, map.keyAt(i), map.valueAt(i));
1699         }
1700         pw.decreaseIndent();
1701     }
1702 }
1703