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