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