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