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