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