blob: 6453af80262386cb28eed20ffc892b6228260cf8 [file] [log] [blame]
Craig Mautner719e6b12014-04-04 20:29:41 -07001/*
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
17package android.os;
18
Makoto Onuki4501c61d2017-07-27 15:56:40 -070019import android.annotation.NonNull;
Scott Kennedyc6a65dff2015-03-01 17:10:10 -080020import android.annotation.Nullable;
Artur Satayevafdb23a2019-12-10 17:47:53 +000021import android.compat.annotation.UnsupportedAppUsage;
Craig Mautner719e6b12014-04-04 20:29:41 -070022import android.util.ArrayMap;
23import android.util.Log;
Adam Lesinski1619ed42015-09-22 13:02:09 -070024import android.util.MathUtils;
Amith Yamasani23879322016-03-30 16:51:26 -070025import android.util.Slog;
Jeff Sharkeya6bfeae2017-07-05 16:50:24 -060026import android.util.SparseArray;
27
Makoto Onuki4501c61d2017-07-27 15:56:40 -070028import com.android.internal.annotations.VisibleForTesting;
Jeff Sharkeya6bfeae2017-07-05 16:50:24 -060029import com.android.internal.util.IndentingPrintWriter;
Craig Mautner719e6b12014-04-04 20:29:41 -070030
31import java.io.Serializable;
32import java.util.ArrayList;
Craig Mautner719e6b12014-04-04 20:29:41 -070033import java.util.Set;
34
35/**
Jeff Sharkeyd136e512016-03-09 22:30:56 -070036 * A mapping from String keys to values of various types. In most cases, you
37 * should work directly with either the {@link Bundle} or
38 * {@link PersistableBundle} subclass.
Craig Mautner719e6b12014-04-04 20:29:41 -070039 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -070040public class BaseBundle {
Craig Mautner719e6b12014-04-04 20:29:41 -070041 private static final String TAG = "Bundle";
42 static final boolean DEBUG = false;
43
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -070044 // Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
45 private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
46 private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
Samuel Tan3cefe6a2015-12-14 13:29:17 -080047
Jeff Sharkeyd136e512016-03-09 22:30:56 -070048 /**
49 * Flag indicating that this Bundle is okay to "defuse." That is, it's okay
50 * for system processes to ignore any {@link BadParcelableException}
51 * encountered when unparceling it, leaving an empty bundle in its place.
52 * <p>
53 * This should <em>only</em> be set when the Bundle reaches its final
54 * destination, otherwise a system process may clobber contents that were
55 * destined for an app that could have unparceled them.
56 */
57 static final int FLAG_DEFUSABLE = 1 << 0;
58
Jeff Sharkeycb459512016-04-18 13:53:53 -060059 private static final boolean LOG_DEFUSABLE = false;
60
Jeff Sharkeyd136e512016-03-09 22:30:56 -070061 private static volatile boolean sShouldDefuse = false;
62
63 /**
64 * Set global variable indicating that any Bundles parsed in this process
65 * should be "defused." That is, any {@link BadParcelableException}
66 * encountered will be suppressed and logged, leaving an empty Bundle
67 * instead of crashing.
68 *
69 * @hide
70 */
71 public static void setShouldDefuse(boolean shouldDefuse) {
72 sShouldDefuse = shouldDefuse;
73 }
74
Andreas Gampe52764cb2016-04-19 20:46:43 -070075 // A parcel cannot be obtained during compile-time initialization. Put the
76 // empty parcel into an inner class that can be initialized separately. This
77 // allows to initialize BaseBundle, and classes depending on it.
Jeff Sharkeyd136e512016-03-09 22:30:56 -070078 /** {@hide} */
Andreas Gampe52764cb2016-04-19 20:46:43 -070079 static final class NoImagePreloadHolder {
80 public static final Parcel EMPTY_PARCEL = Parcel.obtain();
Craig Mautner719e6b12014-04-04 20:29:41 -070081 }
82
83 // Invariant - exactly one of mMap / mParcelledData will be null
84 // (except inside a call to unparcel)
85
Andrei Onea24ec3212019-03-15 17:35:05 +000086 @UnsupportedAppUsage
Craig Mautner719e6b12014-04-04 20:29:41 -070087 ArrayMap<String, Object> mMap = null;
88
89 /*
90 * If mParcelledData is non-null, then mMap will be null and the
91 * data are stored as a Parcel containing a Bundle. When the data
92 * are unparcelled, mParcelledData willbe set to null.
93 */
Andrei Onea24ec3212019-03-15 17:35:05 +000094 @UnsupportedAppUsage
Craig Mautner719e6b12014-04-04 20:29:41 -070095 Parcel mParcelledData = null;
96
97 /**
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -070098 * Whether {@link #mParcelledData} was generated by native coed or not.
99 */
100 private boolean mParcelledByNative;
101
102 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700103 * The ClassLoader used when unparcelling data from mParcelledData.
104 */
105 private ClassLoader mClassLoader;
106
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700107 /** {@hide} */
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700108 @VisibleForTesting
109 public int mFlags;
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700110
Craig Mautner719e6b12014-04-04 20:29:41 -0700111 /**
112 * Constructs a new, empty Bundle that uses a specific ClassLoader for
113 * instantiating Parcelable and Serializable objects.
114 *
115 * @param loader An explicit ClassLoader to use when instantiating objects
116 * inside of the Bundle.
117 * @param capacity Initial size of the ArrayMap.
118 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800119 BaseBundle(@Nullable ClassLoader loader, int capacity) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700120 mMap = capacity > 0 ?
121 new ArrayMap<String, Object>(capacity) : new ArrayMap<String, Object>();
122 mClassLoader = loader == null ? getClass().getClassLoader() : loader;
123 }
124
125 /**
126 * Constructs a new, empty Bundle.
127 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700128 BaseBundle() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700129 this((ClassLoader) null, 0);
130 }
131
132 /**
133 * Constructs a Bundle whose data is stored as a Parcel. The data
134 * will be unparcelled on first contact, using the assigned ClassLoader.
135 *
136 * @param parcelledData a Parcel containing a Bundle
137 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700138 BaseBundle(Parcel parcelledData) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700139 readFromParcelInner(parcelledData);
140 }
141
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700142 BaseBundle(Parcel parcelledData, int length) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700143 readFromParcelInner(parcelledData, length);
144 }
145
146 /**
147 * Constructs a new, empty Bundle that uses a specific ClassLoader for
148 * instantiating Parcelable and Serializable objects.
149 *
150 * @param loader An explicit ClassLoader to use when instantiating objects
151 * inside of the Bundle.
152 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700153 BaseBundle(ClassLoader loader) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700154 this(loader, 0);
155 }
156
157 /**
158 * Constructs a new, empty Bundle sized to hold the given number of
159 * elements. The Bundle will grow as needed.
160 *
161 * @param capacity the initial capacity of the Bundle
162 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700163 BaseBundle(int capacity) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700164 this((ClassLoader) null, capacity);
165 }
166
167 /**
168 * Constructs a Bundle containing a copy of the mappings from the given
169 * Bundle.
170 *
171 * @param b a Bundle to be copied.
172 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700173 BaseBundle(BaseBundle b) {
Dianne Hackbornba604732016-02-10 17:05:10 -0800174 copyInternal(b, false);
175 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700176
Dianne Hackbornba604732016-02-10 17:05:10 -0800177 /**
178 * Special constructor that does not initialize the bundle.
179 */
180 BaseBundle(boolean doInit) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700181 }
182
183 /**
184 * TODO: optimize this later (getting just the value part of a Bundle
185 * with a single pair) once Bundle.forPair() above is implemented
186 * with a special single-value Map implementation/serialization.
187 *
188 * Note: value in single-pair Bundle may be null.
189 *
190 * @hide
191 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700192 public String getPairValue() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700193 unparcel();
194 int size = mMap.size();
195 if (size > 1) {
196 Log.w(TAG, "getPairValue() used on Bundle with multiple pairs.");
197 }
198 if (size == 0) {
199 return null;
200 }
201 Object o = mMap.valueAt(0);
202 try {
203 return (String) o;
204 } catch (ClassCastException e) {
205 typeWarning("getPairValue()", o, "String", e);
206 return null;
207 }
208 }
209
210 /**
211 * Changes the ClassLoader this Bundle uses when instantiating objects.
212 *
213 * @param loader An explicit ClassLoader to use when instantiating objects
214 * inside of the Bundle.
215 */
216 void setClassLoader(ClassLoader loader) {
217 mClassLoader = loader;
218 }
219
220 /**
221 * Return the ClassLoader currently associated with this Bundle.
222 */
223 ClassLoader getClassLoader() {
224 return mClassLoader;
225 }
226
227 /**
228 * If the underlying data are stored as a Parcel, unparcel them
229 * using the currently assigned class loader.
230 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000231 @UnsupportedAppUsage
Amith Yamasani69475342016-12-15 14:23:24 -0800232 /* package */ void unparcel() {
Dianne Hackbornd8e877d2016-05-03 16:57:54 -0700233 synchronized (this) {
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700234 final Parcel source = mParcelledData;
235 if (source != null) {
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700236 initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
Dianne Hackbornd8e877d2016-05-03 16:57:54 -0700237 } else {
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700238 if (DEBUG) {
239 Log.d(TAG, "unparcel "
240 + Integer.toHexString(System.identityHashCode(this))
241 + ": no parcelled data");
Dianne Hackbornd8e877d2016-05-03 16:57:54 -0700242 }
Dianne Hackbornd8e877d2016-05-03 16:57:54 -0700243 }
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700244 }
245 }
246
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700247 private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
248 boolean parcelledByNative) {
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700249 if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
250 Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
251 + "clobber all data inside!", new Throwable());
252 }
253
254 if (isEmptyParcel(parcelledData)) {
255 if (DEBUG) {
256 Log.d(TAG, "unparcel "
257 + Integer.toHexString(System.identityHashCode(this)) + ": empty");
258 }
259 if (mMap == null) {
260 mMap = new ArrayMap<>(1);
261 } else {
262 mMap.erase();
263 }
264 mParcelledData = null;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700265 mParcelledByNative = false;
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700266 return;
267 }
268
269 final int count = parcelledData.readInt();
270 if (DEBUG) {
271 Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
272 + ": reading " + count + " maps");
273 }
274 if (count < 0) {
275 return;
276 }
277 ArrayMap<String, Object> map = mMap;
278 if (map == null) {
279 map = new ArrayMap<>(count);
280 } else {
281 map.erase();
282 map.ensureCapacity(count);
283 }
284 try {
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700285 if (parcelledByNative) {
286 // If it was parcelled by native code, then the array map keys aren't sorted
287 // by their hash codes, so use the safe (slow) one.
288 parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
289 } else {
290 // If parcelled by Java, we know the contents are sorted properly,
291 // so we can use ArrayMap.append().
292 parcelledData.readArrayMapInternal(map, count, mClassLoader);
293 }
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700294 } catch (BadParcelableException e) {
295 if (sShouldDefuse) {
296 Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
297 map.erase();
298 } else {
299 throw e;
300 }
301 } finally {
302 mMap = map;
303 if (recycleParcel) {
304 recycleParcel(parcelledData);
305 }
306 mParcelledData = null;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700307 mParcelledByNative = false;
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700308 }
309 if (DEBUG) {
310 Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
Dianne Hackbornd8e877d2016-05-03 16:57:54 -0700311 + " final map: " + mMap);
Jeff Sharkeyd136e512016-03-09 22:30:56 -0700312 }
Craig Mautner719e6b12014-04-04 20:29:41 -0700313 }
314
315 /**
316 * @hide
317 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000318 @UnsupportedAppUsage
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700319 public boolean isParcelled() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700320 return mParcelledData != null;
321 }
322
Andreas Gampe52764cb2016-04-19 20:46:43 -0700323 /**
324 * @hide
325 */
326 public boolean isEmptyParcel() {
Makoto Onuki4501c61d2017-07-27 15:56:40 -0700327 return isEmptyParcel(mParcelledData);
328 }
329
330 /**
331 * @hide
332 */
333 private static boolean isEmptyParcel(Parcel p) {
334 return p == NoImagePreloadHolder.EMPTY_PARCEL;
335 }
336
337 private static void recycleParcel(Parcel p) {
338 if (p != null && !isEmptyParcel(p)) {
339 p.recycle();
340 }
Andreas Gampe52764cb2016-04-19 20:46:43 -0700341 }
342
Makoto Onuki6f7362d92016-03-04 13:39:41 -0800343 /** @hide */
344 ArrayMap<String, Object> getMap() {
345 unparcel();
346 return mMap;
347 }
348
Craig Mautner719e6b12014-04-04 20:29:41 -0700349 /**
350 * Returns the number of mappings contained in this Bundle.
351 *
352 * @return the number of mappings as an int.
353 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700354 public int size() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700355 unparcel();
356 return mMap.size();
357 }
358
359 /**
360 * Returns true if the mapping of this Bundle is empty, false otherwise.
361 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700362 public boolean isEmpty() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700363 unparcel();
364 return mMap.isEmpty();
365 }
366
367 /**
Dianne Hackborna47223f2017-03-30 13:49:13 -0700368 * @hide this should probably be the implementation of isEmpty(). To do that we
369 * need to ensure we always use the special empty parcel form when the bundle is
370 * empty. (This may already be the case, but to be safe we'll do this later when
371 * we aren't trying to stabilize.)
372 */
373 public boolean maybeIsEmpty() {
374 if (isParcelled()) {
375 return isEmptyParcel();
376 } else {
377 return isEmpty();
378 }
379 }
380
381 /**
Suprabh Shukla021b57a2018-03-08 18:21:50 -0800382 * Does a loose equality check between two given {@link BaseBundle} objects.
383 * Returns {@code true} if both are {@code null}, or if both are equal as per
384 * {@link #kindofEquals(BaseBundle)}
385 *
386 * @param a A {@link BaseBundle} object
387 * @param b Another {@link BaseBundle} to compare with a
388 * @return {@code true} if both are the same, {@code false} otherwise
389 *
390 * @see #kindofEquals(BaseBundle)
391 *
392 * @hide
393 */
394 public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
395 return (a == b) || (a != null && a.kindofEquals(b));
396 }
397
398 /**
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700399 * @hide This kind-of does an equality comparison. Kind-of.
400 */
401 public boolean kindofEquals(BaseBundle other) {
402 if (other == null) {
403 return false;
404 }
405 if (isParcelled() != other.isParcelled()) {
406 // Big kind-of here!
407 return false;
408 } else if (isParcelled()) {
409 return mParcelledData.compareData(other.mParcelledData) == 0;
410 } else {
411 return mMap.equals(other.mMap);
412 }
413 }
414
415 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700416 * Removes all elements from the mapping of this Bundle.
417 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700418 public void clear() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700419 unparcel();
420 mMap.clear();
421 }
422
Dianne Hackbornba604732016-02-10 17:05:10 -0800423 void copyInternal(BaseBundle from, boolean deep) {
Amith Yamasani69475342016-12-15 14:23:24 -0800424 synchronized (from) {
425 if (from.mParcelledData != null) {
426 if (from.isEmptyParcel()) {
427 mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700428 mParcelledByNative = false;
Amith Yamasani69475342016-12-15 14:23:24 -0800429 } else {
430 mParcelledData = Parcel.obtain();
431 mParcelledData.appendFrom(from.mParcelledData, 0,
432 from.mParcelledData.dataSize());
433 mParcelledData.setDataPosition(0);
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700434 mParcelledByNative = from.mParcelledByNative;
Dianne Hackbornba604732016-02-10 17:05:10 -0800435 }
Amith Yamasani69475342016-12-15 14:23:24 -0800436 } else {
437 mParcelledData = null;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -0700438 mParcelledByNative = false;
Dianne Hackbornba604732016-02-10 17:05:10 -0800439 }
Dianne Hackbornba604732016-02-10 17:05:10 -0800440
Amith Yamasani69475342016-12-15 14:23:24 -0800441 if (from.mMap != null) {
442 if (!deep) {
443 mMap = new ArrayMap<>(from.mMap);
444 } else {
445 final ArrayMap<String, Object> fromMap = from.mMap;
446 final int N = fromMap.size();
447 mMap = new ArrayMap<>(N);
448 for (int i = 0; i < N; i++) {
Dianne Hackborn2510b372017-03-03 17:01:38 -0800449 mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
Amith Yamasani69475342016-12-15 14:23:24 -0800450 }
451 }
452 } else {
453 mMap = null;
454 }
455
456 mClassLoader = from.mClassLoader;
457 }
Dianne Hackbornba604732016-02-10 17:05:10 -0800458 }
459
Dianne Hackborn2510b372017-03-03 17:01:38 -0800460 Object deepCopyValue(Object value) {
Dianne Hackbornba604732016-02-10 17:05:10 -0800461 if (value == null) {
462 return null;
463 }
464 if (value instanceof Bundle) {
Dianne Hackborn2510b372017-03-03 17:01:38 -0800465 return ((Bundle)value).deepCopy();
Dianne Hackbornba604732016-02-10 17:05:10 -0800466 } else if (value instanceof PersistableBundle) {
Dianne Hackborn2510b372017-03-03 17:01:38 -0800467 return ((PersistableBundle)value).deepCopy();
Dianne Hackbornba604732016-02-10 17:05:10 -0800468 } else if (value instanceof ArrayList) {
469 return deepcopyArrayList((ArrayList) value);
470 } else if (value.getClass().isArray()) {
471 if (value instanceof int[]) {
472 return ((int[])value).clone();
473 } else if (value instanceof long[]) {
474 return ((long[])value).clone();
475 } else if (value instanceof float[]) {
476 return ((float[])value).clone();
477 } else if (value instanceof double[]) {
478 return ((double[])value).clone();
479 } else if (value instanceof Object[]) {
480 return ((Object[])value).clone();
481 } else if (value instanceof byte[]) {
482 return ((byte[])value).clone();
483 } else if (value instanceof short[]) {
484 return ((short[])value).clone();
485 } else if (value instanceof char[]) {
486 return ((char[]) value).clone();
487 }
488 }
489 return value;
490 }
491
492 ArrayList deepcopyArrayList(ArrayList from) {
493 final int N = from.size();
494 ArrayList out = new ArrayList(N);
495 for (int i=0; i<N; i++) {
Dianne Hackborn2510b372017-03-03 17:01:38 -0800496 out.add(deepCopyValue(from.get(i)));
Dianne Hackbornba604732016-02-10 17:05:10 -0800497 }
498 return out;
499 }
500
Craig Mautner719e6b12014-04-04 20:29:41 -0700501 /**
502 * Returns true if the given key is contained in the mapping
503 * of this Bundle.
504 *
505 * @param key a String key
506 * @return true if the key is part of the mapping, false otherwise
507 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700508 public boolean containsKey(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700509 unparcel();
510 return mMap.containsKey(key);
511 }
512
513 /**
514 * Returns the entry with the given key as an object.
515 *
516 * @param key a String key
517 * @return an Object, or null
518 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800519 @Nullable
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700520 public Object get(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700521 unparcel();
522 return mMap.get(key);
523 }
524
525 /**
526 * Removes any entry with the given key from the mapping of this Bundle.
527 *
528 * @param key a String key
529 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700530 public void remove(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700531 unparcel();
532 mMap.remove(key);
533 }
534
535 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700536 * Inserts all mappings from the given PersistableBundle into this BaseBundle.
Craig Mautner719e6b12014-04-04 20:29:41 -0700537 *
538 * @param bundle a PersistableBundle
539 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700540 public void putAll(PersistableBundle bundle) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700541 unparcel();
542 bundle.unparcel();
543 mMap.putAll(bundle.mMap);
544 }
545
546 /**
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700547 * Inserts all mappings from the given Map into this BaseBundle.
Craig Mautner21d24a22014-04-23 11:45:37 -0700548 *
549 * @param map a Map
550 */
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700551 void putAll(ArrayMap map) {
Craig Mautner21d24a22014-04-23 11:45:37 -0700552 unparcel();
553 mMap.putAll(map);
554 }
555
556 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700557 * Returns a Set containing the Strings used as keys in this Bundle.
558 *
559 * @return a Set of String keys
560 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -0700561 public Set<String> keySet() {
Craig Mautner719e6b12014-04-04 20:29:41 -0700562 unparcel();
563 return mMap.keySet();
564 }
565
Jeff Sharkey7d6fd4b2019-08-07 18:12:00 -0600566 /** {@hide} */
567 public void putObject(@Nullable String key, @Nullable Object value) {
568 if (value == null) {
569 putString(key, null);
570 } else if (value instanceof Boolean) {
571 putBoolean(key, (Boolean) value);
572 } else if (value instanceof Integer) {
573 putInt(key, (Integer) value);
574 } else if (value instanceof Long) {
575 putLong(key, (Long) value);
576 } else if (value instanceof Double) {
577 putDouble(key, (Double) value);
578 } else if (value instanceof String) {
579 putString(key, (String) value);
580 } else if (value instanceof boolean[]) {
581 putBooleanArray(key, (boolean[]) value);
582 } else if (value instanceof int[]) {
583 putIntArray(key, (int[]) value);
584 } else if (value instanceof long[]) {
585 putLongArray(key, (long[]) value);
586 } else if (value instanceof double[]) {
587 putDoubleArray(key, (double[]) value);
588 } else if (value instanceof String[]) {
589 putStringArray(key, (String[]) value);
590 } else {
591 throw new IllegalArgumentException("Unsupported type " + value.getClass());
592 }
593 }
594
Craig Mautner719e6b12014-04-04 20:29:41 -0700595 /**
596 * Inserts a Boolean value into the mapping of this Bundle, replacing
597 * any existing value for the given key. Either key or value may be null.
598 *
599 * @param key a String, or null
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800600 * @param value a boolean
Craig Mautner719e6b12014-04-04 20:29:41 -0700601 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800602 public void putBoolean(@Nullable String key, boolean value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700603 unparcel();
604 mMap.put(key, value);
605 }
606
607 /**
608 * Inserts a byte value into the mapping of this Bundle, replacing
609 * any existing value for the given key.
610 *
611 * @param key a String, or null
612 * @param value a byte
613 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800614 void putByte(@Nullable String key, byte value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700615 unparcel();
616 mMap.put(key, value);
617 }
618
619 /**
620 * Inserts a char value into the mapping of this Bundle, replacing
621 * any existing value for the given key.
622 *
623 * @param key a String, or null
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800624 * @param value a char
Craig Mautner719e6b12014-04-04 20:29:41 -0700625 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800626 void putChar(@Nullable String key, char value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700627 unparcel();
628 mMap.put(key, value);
629 }
630
631 /**
632 * Inserts a short value into the mapping of this Bundle, replacing
633 * any existing value for the given key.
634 *
635 * @param key a String, or null
636 * @param value a short
637 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800638 void putShort(@Nullable String key, short value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700639 unparcel();
640 mMap.put(key, value);
641 }
642
643 /**
644 * Inserts an int value into the mapping of this Bundle, replacing
645 * any existing value for the given key.
646 *
647 * @param key a String, or null
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800648 * @param value an int
Craig Mautner719e6b12014-04-04 20:29:41 -0700649 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800650 public void putInt(@Nullable String key, int value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700651 unparcel();
652 mMap.put(key, value);
653 }
654
655 /**
656 * Inserts a long value into the mapping of this Bundle, replacing
657 * any existing value for the given key.
658 *
659 * @param key a String, or null
660 * @param value a long
661 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800662 public void putLong(@Nullable String key, long value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700663 unparcel();
664 mMap.put(key, value);
665 }
666
667 /**
668 * Inserts a float value into the mapping of this Bundle, replacing
669 * any existing value for the given key.
670 *
671 * @param key a String, or null
672 * @param value a float
673 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800674 void putFloat(@Nullable String key, float value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700675 unparcel();
676 mMap.put(key, value);
677 }
678
679 /**
680 * Inserts a double value into the mapping of this Bundle, replacing
681 * any existing value for the given key.
682 *
683 * @param key a String, or null
684 * @param value a double
685 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800686 public void putDouble(@Nullable String key, double value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700687 unparcel();
688 mMap.put(key, value);
689 }
690
691 /**
692 * Inserts a String value into the mapping of this Bundle, replacing
693 * any existing value for the given key. Either key or value may be null.
694 *
695 * @param key a String, or null
696 * @param value a String, or null
697 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800698 public void putString(@Nullable String key, @Nullable String value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700699 unparcel();
700 mMap.put(key, value);
701 }
702
703 /**
704 * Inserts a CharSequence value into the mapping of this Bundle, replacing
705 * any existing value for the given key. Either key or value may be null.
706 *
707 * @param key a String, or null
708 * @param value a CharSequence, or null
709 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800710 void putCharSequence(@Nullable String key, @Nullable CharSequence value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700711 unparcel();
712 mMap.put(key, value);
713 }
714
715 /**
716 * Inserts an ArrayList<Integer> value into the mapping of this Bundle, replacing
717 * any existing value for the given key. Either key or value may be null.
718 *
719 * @param key a String, or null
720 * @param value an ArrayList<Integer> object, or null
721 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800722 void putIntegerArrayList(@Nullable String key, @Nullable ArrayList<Integer> value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700723 unparcel();
724 mMap.put(key, value);
725 }
726
727 /**
728 * Inserts an ArrayList<String> value into the mapping of this Bundle, replacing
729 * any existing value for the given key. Either key or value may be null.
730 *
731 * @param key a String, or null
732 * @param value an ArrayList<String> object, or null
733 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800734 void putStringArrayList(@Nullable String key, @Nullable ArrayList<String> value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700735 unparcel();
736 mMap.put(key, value);
737 }
738
739 /**
740 * Inserts an ArrayList<CharSequence> value into the mapping of this Bundle, replacing
741 * any existing value for the given key. Either key or value may be null.
742 *
743 * @param key a String, or null
744 * @param value an ArrayList<CharSequence> object, or null
745 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800746 void putCharSequenceArrayList(@Nullable String key, @Nullable ArrayList<CharSequence> value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700747 unparcel();
748 mMap.put(key, value);
749 }
750
751 /**
752 * Inserts a Serializable value into the mapping of this Bundle, replacing
753 * any existing value for the given key. Either key or value may be null.
754 *
755 * @param key a String, or null
756 * @param value a Serializable object, or null
757 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800758 void putSerializable(@Nullable String key, @Nullable Serializable value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700759 unparcel();
760 mMap.put(key, value);
761 }
762
763 /**
764 * Inserts a boolean array value into the mapping of this Bundle, replacing
765 * any existing value for the given key. Either key or value may be null.
766 *
767 * @param key a String, or null
768 * @param value a boolean array object, or null
769 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800770 public void putBooleanArray(@Nullable String key, @Nullable boolean[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700771 unparcel();
772 mMap.put(key, value);
773 }
774
775 /**
776 * Inserts a byte array value into the mapping of this Bundle, replacing
777 * any existing value for the given key. Either key or value may be null.
778 *
779 * @param key a String, or null
780 * @param value a byte array object, or null
781 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800782 void putByteArray(@Nullable String key, @Nullable byte[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700783 unparcel();
784 mMap.put(key, value);
785 }
786
787 /**
788 * Inserts a short array value into the mapping of this Bundle, replacing
789 * any existing value for the given key. Either key or value may be null.
790 *
791 * @param key a String, or null
792 * @param value a short array object, or null
793 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800794 void putShortArray(@Nullable String key, @Nullable short[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700795 unparcel();
796 mMap.put(key, value);
797 }
798
799 /**
800 * Inserts a char array value into the mapping of this Bundle, replacing
801 * any existing value for the given key. Either key or value may be null.
802 *
803 * @param key a String, or null
804 * @param value a char array object, or null
805 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800806 void putCharArray(@Nullable String key, @Nullable char[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700807 unparcel();
808 mMap.put(key, value);
809 }
810
811 /**
812 * Inserts an int array value into the mapping of this Bundle, replacing
813 * any existing value for the given key. Either key or value may be null.
814 *
815 * @param key a String, or null
816 * @param value an int array object, or null
817 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800818 public void putIntArray(@Nullable String key, @Nullable int[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700819 unparcel();
820 mMap.put(key, value);
821 }
822
823 /**
824 * Inserts a long array value into the mapping of this Bundle, replacing
825 * any existing value for the given key. Either key or value may be null.
826 *
827 * @param key a String, or null
828 * @param value a long array object, or null
829 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800830 public void putLongArray(@Nullable String key, @Nullable long[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700831 unparcel();
832 mMap.put(key, value);
833 }
834
835 /**
836 * Inserts a float array value into the mapping of this Bundle, replacing
837 * any existing value for the given key. Either key or value may be null.
838 *
839 * @param key a String, or null
840 * @param value a float array object, or null
841 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800842 void putFloatArray(@Nullable String key, @Nullable float[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700843 unparcel();
844 mMap.put(key, value);
845 }
846
847 /**
848 * Inserts a double array value into the mapping of this Bundle, replacing
849 * any existing value for the given key. Either key or value may be null.
850 *
851 * @param key a String, or null
852 * @param value a double array object, or null
853 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800854 public void putDoubleArray(@Nullable String key, @Nullable double[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700855 unparcel();
856 mMap.put(key, value);
857 }
858
859 /**
860 * Inserts a String array value into the mapping of this Bundle, replacing
861 * any existing value for the given key. Either key or value may be null.
862 *
863 * @param key a String, or null
864 * @param value a String array object, or null
865 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800866 public void putStringArray(@Nullable String key, @Nullable String[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700867 unparcel();
868 mMap.put(key, value);
869 }
870
871 /**
872 * Inserts a CharSequence array value into the mapping of this Bundle, replacing
873 * any existing value for the given key. Either key or value may be null.
874 *
875 * @param key a String, or null
876 * @param value a CharSequence array object, or null
877 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -0800878 void putCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700879 unparcel();
880 mMap.put(key, value);
881 }
882
883 /**
Craig Mautner719e6b12014-04-04 20:29:41 -0700884 * Returns the value associated with the given key, or false if
885 * no mapping of the desired type exists for the given key.
886 *
887 * @param key a String
888 * @return a boolean value
889 */
Craig Mautner73bdf972014-12-09 18:10:20 -0800890 public boolean getBoolean(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700891 unparcel();
892 if (DEBUG) Log.d(TAG, "Getting boolean in "
893 + Integer.toHexString(System.identityHashCode(this)));
894 return getBoolean(key, false);
895 }
896
897 // Log a message if the value was non-null but not of the expected type
898 void typeWarning(String key, Object value, String className,
899 Object defaultValue, ClassCastException e) {
900 StringBuilder sb = new StringBuilder();
901 sb.append("Key ");
902 sb.append(key);
903 sb.append(" expected ");
904 sb.append(className);
905 sb.append(" but value was a ");
906 sb.append(value.getClass().getName());
907 sb.append(". The default value ");
908 sb.append(defaultValue);
909 sb.append(" was returned.");
910 Log.w(TAG, sb.toString());
911 Log.w(TAG, "Attempt to cast generated internal exception:", e);
912 }
913
914 void typeWarning(String key, Object value, String className,
915 ClassCastException e) {
916 typeWarning(key, value, className, "<null>", e);
917 }
918
919 /**
920 * Returns the value associated with the given key, or defaultValue if
921 * no mapping of the desired type exists for the given key.
922 *
923 * @param key a String
924 * @param defaultValue Value to return if key does not exist
925 * @return a boolean value
926 */
Craig Mautner73bdf972014-12-09 18:10:20 -0800927 public boolean getBoolean(String key, boolean defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -0700928 unparcel();
929 Object o = mMap.get(key);
930 if (o == null) {
931 return defaultValue;
932 }
933 try {
934 return (Boolean) o;
935 } catch (ClassCastException e) {
936 typeWarning(key, o, "Boolean", defaultValue, e);
937 return defaultValue;
938 }
939 }
940
941 /**
942 * Returns the value associated with the given key, or (byte) 0 if
943 * no mapping of the desired type exists for the given key.
944 *
945 * @param key a String
946 * @return a byte value
947 */
948 byte getByte(String key) {
949 unparcel();
950 return getByte(key, (byte) 0);
951 }
952
953 /**
954 * Returns the value associated with the given key, or defaultValue if
955 * no mapping of the desired type exists for the given key.
956 *
957 * @param key a String
958 * @param defaultValue Value to return if key does not exist
959 * @return a byte value
960 */
961 Byte getByte(String key, byte defaultValue) {
962 unparcel();
963 Object o = mMap.get(key);
964 if (o == null) {
965 return defaultValue;
966 }
967 try {
968 return (Byte) o;
969 } catch (ClassCastException e) {
970 typeWarning(key, o, "Byte", defaultValue, e);
971 return defaultValue;
972 }
973 }
974
975 /**
976 * Returns the value associated with the given key, or (char) 0 if
977 * no mapping of the desired type exists for the given key.
978 *
979 * @param key a String
980 * @return a char value
981 */
982 char getChar(String key) {
983 unparcel();
984 return getChar(key, (char) 0);
985 }
986
987 /**
988 * Returns the value associated with the given key, or defaultValue if
989 * no mapping of the desired type exists for the given key.
990 *
991 * @param key a String
992 * @param defaultValue Value to return if key does not exist
993 * @return a char value
994 */
995 char getChar(String key, char defaultValue) {
996 unparcel();
997 Object o = mMap.get(key);
998 if (o == null) {
999 return defaultValue;
1000 }
1001 try {
1002 return (Character) o;
1003 } catch (ClassCastException e) {
1004 typeWarning(key, o, "Character", defaultValue, e);
1005 return defaultValue;
1006 }
1007 }
1008
1009 /**
1010 * Returns the value associated with the given key, or (short) 0 if
1011 * no mapping of the desired type exists for the given key.
1012 *
1013 * @param key a String
1014 * @return a short value
1015 */
1016 short getShort(String key) {
1017 unparcel();
1018 return getShort(key, (short) 0);
1019 }
1020
1021 /**
1022 * Returns the value associated with the given key, or defaultValue if
1023 * no mapping of the desired type exists for the given key.
1024 *
1025 * @param key a String
1026 * @param defaultValue Value to return if key does not exist
1027 * @return a short value
1028 */
1029 short getShort(String key, short defaultValue) {
1030 unparcel();
1031 Object o = mMap.get(key);
1032 if (o == null) {
1033 return defaultValue;
1034 }
1035 try {
1036 return (Short) o;
1037 } catch (ClassCastException e) {
1038 typeWarning(key, o, "Short", defaultValue, e);
1039 return defaultValue;
1040 }
1041 }
1042
1043 /**
1044 * Returns the value associated with the given key, or 0 if
1045 * no mapping of the desired type exists for the given key.
1046 *
1047 * @param key a String
1048 * @return an int value
1049 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001050 public int getInt(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001051 unparcel();
1052 return getInt(key, 0);
1053 }
1054
1055 /**
1056 * Returns the value associated with the given key, or defaultValue if
1057 * no mapping of the desired type exists for the given key.
1058 *
1059 * @param key a String
1060 * @param defaultValue Value to return if key does not exist
1061 * @return an int value
1062 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001063 public int getInt(String key, int defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001064 unparcel();
1065 Object o = mMap.get(key);
1066 if (o == null) {
1067 return defaultValue;
1068 }
1069 try {
1070 return (Integer) o;
1071 } catch (ClassCastException e) {
1072 typeWarning(key, o, "Integer", defaultValue, e);
1073 return defaultValue;
1074 }
1075 }
1076
1077 /**
1078 * Returns the value associated with the given key, or 0L if
1079 * no mapping of the desired type exists for the given key.
1080 *
1081 * @param key a String
1082 * @return a long value
1083 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001084 public long getLong(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001085 unparcel();
1086 return getLong(key, 0L);
1087 }
1088
1089 /**
1090 * Returns the value associated with the given key, or defaultValue if
1091 * no mapping of the desired type exists for the given key.
1092 *
1093 * @param key a String
1094 * @param defaultValue Value to return if key does not exist
1095 * @return a long value
1096 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001097 public long getLong(String key, long defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001098 unparcel();
1099 Object o = mMap.get(key);
1100 if (o == null) {
1101 return defaultValue;
1102 }
1103 try {
1104 return (Long) o;
1105 } catch (ClassCastException e) {
1106 typeWarning(key, o, "Long", defaultValue, e);
1107 return defaultValue;
1108 }
1109 }
1110
1111 /**
1112 * Returns the value associated with the given key, or 0.0f if
1113 * no mapping of the desired type exists for the given key.
1114 *
1115 * @param key a String
1116 * @return a float value
1117 */
1118 float getFloat(String key) {
1119 unparcel();
1120 return getFloat(key, 0.0f);
1121 }
1122
1123 /**
1124 * Returns the value associated with the given key, or defaultValue if
1125 * no mapping of the desired type exists for the given key.
1126 *
1127 * @param key a String
1128 * @param defaultValue Value to return if key does not exist
1129 * @return a float value
1130 */
1131 float getFloat(String key, float defaultValue) {
1132 unparcel();
1133 Object o = mMap.get(key);
1134 if (o == null) {
1135 return defaultValue;
1136 }
1137 try {
1138 return (Float) o;
1139 } catch (ClassCastException e) {
1140 typeWarning(key, o, "Float", defaultValue, e);
1141 return defaultValue;
1142 }
1143 }
1144
1145 /**
1146 * Returns the value associated with the given key, or 0.0 if
1147 * no mapping of the desired type exists for the given key.
1148 *
1149 * @param key a String
1150 * @return a double value
1151 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001152 public double getDouble(String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001153 unparcel();
1154 return getDouble(key, 0.0);
1155 }
1156
1157 /**
1158 * Returns the value associated with the given key, or defaultValue if
1159 * no mapping of the desired type exists for the given key.
1160 *
1161 * @param key a String
1162 * @param defaultValue Value to return if key does not exist
1163 * @return a double value
1164 */
Craig Mautner0a8e160e2014-05-29 10:27:32 -07001165 public double getDouble(String key, double defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001166 unparcel();
1167 Object o = mMap.get(key);
1168 if (o == null) {
1169 return defaultValue;
1170 }
1171 try {
1172 return (Double) o;
1173 } catch (ClassCastException e) {
1174 typeWarning(key, o, "Double", defaultValue, e);
1175 return defaultValue;
1176 }
1177 }
1178
1179 /**
1180 * Returns the value associated with the given key, or null if
1181 * no mapping of the desired type exists for the given key or a null
1182 * value is explicitly associated with the key.
1183 *
1184 * @param key a String, or null
1185 * @return a String value, or null
1186 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001187 @Nullable
1188 public String getString(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001189 unparcel();
1190 final Object o = mMap.get(key);
1191 try {
1192 return (String) o;
1193 } catch (ClassCastException e) {
1194 typeWarning(key, o, "String", e);
1195 return null;
1196 }
1197 }
1198
1199 /**
1200 * Returns the value associated with the given key, or defaultValue if
Narayan Kamathaeaf87f2014-06-26 18:10:51 +01001201 * no mapping of the desired type exists for the given key or if a null
1202 * value is explicitly associated with the given key.
Craig Mautner719e6b12014-04-04 20:29:41 -07001203 *
1204 * @param key a String, or null
Narayan Kamathaeaf87f2014-06-26 18:10:51 +01001205 * @param defaultValue Value to return if key does not exist or if a null
1206 * value is associated with the given key.
Craig Mautner719e6b12014-04-04 20:29:41 -07001207 * @return the String value associated with the given key, or defaultValue
1208 * if no valid String object is currently mapped to that key.
1209 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001210 public String getString(@Nullable String key, String defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001211 final String s = getString(key);
1212 return (s == null) ? defaultValue : s;
1213 }
1214
1215 /**
1216 * Returns the value associated with the given key, or null if
1217 * no mapping of the desired type exists for the given key or a null
1218 * value is explicitly associated with the key.
1219 *
1220 * @param key a String, or null
1221 * @return a CharSequence value, or null
1222 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001223 @Nullable
1224 CharSequence getCharSequence(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001225 unparcel();
1226 final Object o = mMap.get(key);
1227 try {
1228 return (CharSequence) o;
1229 } catch (ClassCastException e) {
1230 typeWarning(key, o, "CharSequence", e);
1231 return null;
1232 }
1233 }
1234
1235 /**
1236 * Returns the value associated with the given key, or defaultValue if
Narayan Kamathaeaf87f2014-06-26 18:10:51 +01001237 * no mapping of the desired type exists for the given key or if a null
1238 * value is explicitly associated with the given key.
Craig Mautner719e6b12014-04-04 20:29:41 -07001239 *
1240 * @param key a String, or null
Narayan Kamathaeaf87f2014-06-26 18:10:51 +01001241 * @param defaultValue Value to return if key does not exist or if a null
1242 * value is associated with the given key.
Craig Mautner719e6b12014-04-04 20:29:41 -07001243 * @return the CharSequence value associated with the given key, or defaultValue
1244 * if no valid CharSequence object is currently mapped to that key.
1245 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001246 CharSequence getCharSequence(@Nullable String key, CharSequence defaultValue) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001247 final CharSequence cs = getCharSequence(key);
1248 return (cs == null) ? defaultValue : cs;
1249 }
1250
1251 /**
1252 * Returns the value associated with the given key, or null if
1253 * no mapping of the desired type exists for the given key or a null
1254 * value is explicitly associated with the key.
1255 *
1256 * @param key a String, or null
Craig Mautner719e6b12014-04-04 20:29:41 -07001257 * @return a Serializable value, or null
1258 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001259 @Nullable
1260 Serializable getSerializable(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001261 unparcel();
1262 Object o = mMap.get(key);
1263 if (o == null) {
1264 return null;
1265 }
1266 try {
1267 return (Serializable) o;
1268 } catch (ClassCastException e) {
1269 typeWarning(key, o, "Serializable", e);
1270 return null;
1271 }
1272 }
1273
1274 /**
1275 * Returns the value associated with the given key, or null if
1276 * no mapping of the desired type exists for the given key or a null
1277 * value is explicitly associated with the key.
1278 *
1279 * @param key a String, or null
1280 * @return an ArrayList<String> value, or null
1281 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001282 @Nullable
1283 ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001284 unparcel();
1285 Object o = mMap.get(key);
1286 if (o == null) {
1287 return null;
1288 }
1289 try {
1290 return (ArrayList<Integer>) o;
1291 } catch (ClassCastException e) {
1292 typeWarning(key, o, "ArrayList<Integer>", e);
1293 return null;
1294 }
1295 }
1296
1297 /**
1298 * Returns the value associated with the given key, or null if
1299 * no mapping of the desired type exists for the given key or a null
1300 * value is explicitly associated with the key.
1301 *
1302 * @param key a String, or null
1303 * @return an ArrayList<String> value, or null
1304 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001305 @Nullable
1306 ArrayList<String> getStringArrayList(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001307 unparcel();
1308 Object o = mMap.get(key);
1309 if (o == null) {
1310 return null;
1311 }
1312 try {
1313 return (ArrayList<String>) o;
1314 } catch (ClassCastException e) {
1315 typeWarning(key, o, "ArrayList<String>", e);
1316 return null;
1317 }
1318 }
1319
1320 /**
1321 * Returns the value associated with the given key, or null if
1322 * no mapping of the desired type exists for the given key or a null
1323 * value is explicitly associated with the key.
1324 *
1325 * @param key a String, or null
1326 * @return an ArrayList<CharSequence> value, or null
1327 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001328 @Nullable
1329 ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001330 unparcel();
1331 Object o = mMap.get(key);
1332 if (o == null) {
1333 return null;
1334 }
1335 try {
1336 return (ArrayList<CharSequence>) o;
1337 } catch (ClassCastException e) {
1338 typeWarning(key, o, "ArrayList<CharSequence>", e);
1339 return null;
1340 }
1341 }
1342
1343 /**
1344 * Returns the value associated with the given key, or null if
1345 * no mapping of the desired type exists for the given key or a null
1346 * value is explicitly associated with the key.
1347 *
1348 * @param key a String, or null
1349 * @return a boolean[] value, or null
1350 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001351 @Nullable
1352 public boolean[] getBooleanArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001353 unparcel();
1354 Object o = mMap.get(key);
1355 if (o == null) {
1356 return null;
1357 }
1358 try {
1359 return (boolean[]) o;
1360 } catch (ClassCastException e) {
1361 typeWarning(key, o, "byte[]", e);
1362 return null;
1363 }
1364 }
1365
1366 /**
1367 * Returns the value associated with the given key, or null if
1368 * no mapping of the desired type exists for the given key or a null
1369 * value is explicitly associated with the key.
1370 *
1371 * @param key a String, or null
1372 * @return a byte[] value, or null
1373 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001374 @Nullable
1375 byte[] getByteArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001376 unparcel();
1377 Object o = mMap.get(key);
1378 if (o == null) {
1379 return null;
1380 }
1381 try {
1382 return (byte[]) o;
1383 } catch (ClassCastException e) {
1384 typeWarning(key, o, "byte[]", e);
1385 return null;
1386 }
1387 }
1388
1389 /**
1390 * Returns the value associated with the given key, or null if
1391 * no mapping of the desired type exists for the given key or a null
1392 * value is explicitly associated with the key.
1393 *
1394 * @param key a String, or null
1395 * @return a short[] value, or null
1396 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001397 @Nullable
1398 short[] getShortArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001399 unparcel();
1400 Object o = mMap.get(key);
1401 if (o == null) {
1402 return null;
1403 }
1404 try {
1405 return (short[]) o;
1406 } catch (ClassCastException e) {
1407 typeWarning(key, o, "short[]", e);
1408 return null;
1409 }
1410 }
1411
1412 /**
1413 * Returns the value associated with the given key, or null if
1414 * no mapping of the desired type exists for the given key or a null
1415 * value is explicitly associated with the key.
1416 *
1417 * @param key a String, or null
1418 * @return a char[] value, or null
1419 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001420 @Nullable
1421 char[] getCharArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001422 unparcel();
1423 Object o = mMap.get(key);
1424 if (o == null) {
1425 return null;
1426 }
1427 try {
1428 return (char[]) o;
1429 } catch (ClassCastException e) {
1430 typeWarning(key, o, "char[]", e);
1431 return null;
1432 }
1433 }
1434
1435 /**
1436 * Returns the value associated with the given key, or null if
1437 * no mapping of the desired type exists for the given key or a null
1438 * value is explicitly associated with the key.
1439 *
1440 * @param key a String, or null
1441 * @return an int[] value, or null
1442 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001443 @Nullable
1444 public int[] getIntArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001445 unparcel();
1446 Object o = mMap.get(key);
1447 if (o == null) {
1448 return null;
1449 }
1450 try {
1451 return (int[]) o;
1452 } catch (ClassCastException e) {
1453 typeWarning(key, o, "int[]", e);
1454 return null;
1455 }
1456 }
1457
1458 /**
1459 * Returns the value associated with the given key, or null if
1460 * no mapping of the desired type exists for the given key or a null
1461 * value is explicitly associated with the key.
1462 *
1463 * @param key a String, or null
1464 * @return a long[] value, or null
1465 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001466 @Nullable
1467 public long[] getLongArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001468 unparcel();
1469 Object o = mMap.get(key);
1470 if (o == null) {
1471 return null;
1472 }
1473 try {
1474 return (long[]) o;
1475 } catch (ClassCastException e) {
1476 typeWarning(key, o, "long[]", e);
1477 return null;
1478 }
1479 }
1480
1481 /**
1482 * Returns the value associated with the given key, or null if
1483 * no mapping of the desired type exists for the given key or a null
1484 * value is explicitly associated with the key.
1485 *
1486 * @param key a String, or null
1487 * @return a float[] value, or null
1488 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001489 @Nullable
1490 float[] getFloatArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001491 unparcel();
1492 Object o = mMap.get(key);
1493 if (o == null) {
1494 return null;
1495 }
1496 try {
1497 return (float[]) o;
1498 } catch (ClassCastException e) {
1499 typeWarning(key, o, "float[]", e);
1500 return null;
1501 }
1502 }
1503
1504 /**
1505 * Returns the value associated with the given key, or null if
1506 * no mapping of the desired type exists for the given key or a null
1507 * value is explicitly associated with the key.
1508 *
1509 * @param key a String, or null
1510 * @return a double[] value, or null
1511 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001512 @Nullable
1513 public double[] getDoubleArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001514 unparcel();
1515 Object o = mMap.get(key);
1516 if (o == null) {
1517 return null;
1518 }
1519 try {
1520 return (double[]) o;
1521 } catch (ClassCastException e) {
1522 typeWarning(key, o, "double[]", e);
1523 return null;
1524 }
1525 }
1526
1527 /**
1528 * Returns the value associated with the given key, or null if
1529 * no mapping of the desired type exists for the given key or a null
1530 * value is explicitly associated with the key.
1531 *
1532 * @param key a String, or null
1533 * @return a String[] value, or null
1534 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001535 @Nullable
1536 public String[] getStringArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001537 unparcel();
1538 Object o = mMap.get(key);
1539 if (o == null) {
1540 return null;
1541 }
1542 try {
1543 return (String[]) o;
1544 } catch (ClassCastException e) {
1545 typeWarning(key, o, "String[]", e);
1546 return null;
1547 }
1548 }
1549
1550 /**
1551 * Returns the value associated with the given key, or null if
1552 * no mapping of the desired type exists for the given key or a null
1553 * value is explicitly associated with the key.
1554 *
1555 * @param key a String, or null
1556 * @return a CharSequence[] value, or null
1557 */
Scott Kennedyc6a65dff2015-03-01 17:10:10 -08001558 @Nullable
1559 CharSequence[] getCharSequenceArray(@Nullable String key) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001560 unparcel();
1561 Object o = mMap.get(key);
1562 if (o == null) {
1563 return null;
1564 }
1565 try {
1566 return (CharSequence[]) o;
1567 } catch (ClassCastException e) {
1568 typeWarning(key, o, "CharSequence[]", e);
1569 return null;
1570 }
1571 }
1572
1573 /**
1574 * Writes the Bundle contents to a Parcel, typically in order for
1575 * it to be passed through an IBinder connection.
1576 * @param parcel The parcel to copy this bundle to.
1577 */
1578 void writeToParcelInner(Parcel parcel, int flags) {
Makoto Onuki4501c61d2017-07-27 15:56:40 -07001579 // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
1580 if (parcel.hasReadWriteHelper()) {
1581 unparcel();
1582 }
Samuel Tan3cefe6a2015-12-14 13:29:17 -08001583 // Keep implementation in sync with writeToParcel() in
1584 // frameworks/native/libs/binder/PersistableBundle.cpp.
Amith Yamasani69475342016-12-15 14:23:24 -08001585 final ArrayMap<String, Object> map;
Dianne Hackbornd8e877d2016-05-03 16:57:54 -07001586 synchronized (this) {
Amith Yamasani69475342016-12-15 14:23:24 -08001587 // unparcel() can race with this method and cause the parcel to recycle
1588 // at the wrong time. So synchronize access the mParcelledData's content.
1589 if (mParcelledData != null) {
1590 if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
1591 parcel.writeInt(0);
1592 } else {
1593 int length = mParcelledData.dataSize();
1594 parcel.writeInt(length);
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -07001595 parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
Amith Yamasani69475342016-12-15 14:23:24 -08001596 parcel.appendFrom(mParcelledData, 0, length);
1597 }
Craig Mautner719e6b12014-04-04 20:29:41 -07001598 return;
1599 }
Amith Yamasani69475342016-12-15 14:23:24 -08001600 map = mMap;
Craig Mautner719e6b12014-04-04 20:29:41 -07001601 }
Amith Yamasani69475342016-12-15 14:23:24 -08001602
1603 // Special case for empty bundles.
1604 if (map == null || map.size() <= 0) {
1605 parcel.writeInt(0);
1606 return;
1607 }
1608 int lengthPos = parcel.dataPosition();
1609 parcel.writeInt(-1); // dummy, will hold length
1610 parcel.writeInt(BUNDLE_MAGIC);
1611
1612 int startPos = parcel.dataPosition();
1613 parcel.writeArrayMapInternal(map);
1614 int endPos = parcel.dataPosition();
1615
1616 // Backpatch length
1617 parcel.setDataPosition(lengthPos);
1618 int length = endPos - startPos;
1619 parcel.writeInt(length);
1620 parcel.setDataPosition(endPos);
Craig Mautner719e6b12014-04-04 20:29:41 -07001621 }
1622
1623 /**
1624 * Reads the Parcel contents into this Bundle, typically in order for
1625 * it to be passed through an IBinder connection.
1626 * @param parcel The parcel to overwrite this bundle from.
1627 */
1628 void readFromParcelInner(Parcel parcel) {
Samuel Tan3cefe6a2015-12-14 13:29:17 -08001629 // Keep implementation in sync with readFromParcel() in
1630 // frameworks/native/libs/binder/PersistableBundle.cpp.
Craig Mautner719e6b12014-04-04 20:29:41 -07001631 int length = parcel.readInt();
Craig Mautner719e6b12014-04-04 20:29:41 -07001632 readFromParcelInner(parcel, length);
1633 }
1634
1635 private void readFromParcelInner(Parcel parcel, int length) {
Adam Lesinski1619ed42015-09-22 13:02:09 -07001636 if (length < 0) {
1637 throw new RuntimeException("Bad length in parcel: " + length);
Adam Lesinski1619ed42015-09-22 13:02:09 -07001638 } else if (length == 0) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001639 // Empty Bundle or end of data.
Andreas Gampe52764cb2016-04-19 20:46:43 -07001640 mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -07001641 mParcelledByNative = false;
Craig Mautner719e6b12014-04-04 20:29:41 -07001642 return;
Hui Yufc451f42018-09-11 17:42:14 -07001643 } else if (length % 4 != 0) {
1644 throw new IllegalStateException("Bundle length is not aligned by 4: " + length);
Craig Mautner719e6b12014-04-04 20:29:41 -07001645 }
Adam Lesinski1619ed42015-09-22 13:02:09 -07001646
Jeff Sharkeyd136e512016-03-09 22:30:56 -07001647 final int magic = parcel.readInt();
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -07001648 final boolean isJavaBundle = magic == BUNDLE_MAGIC;
1649 final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
1650 if (!isJavaBundle && !isNativeBundle) {
Craig Mautner719e6b12014-04-04 20:29:41 -07001651 throw new IllegalStateException("Bad magic number for Bundle: 0x"
1652 + Integer.toHexString(magic));
1653 }
1654
Makoto Onuki4501c61d2017-07-27 15:56:40 -07001655 if (parcel.hasReadWriteHelper()) {
1656 // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
1657 // unparcel right away.
1658 synchronized (this) {
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -07001659 initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
Makoto Onuki4501c61d2017-07-27 15:56:40 -07001660 }
1661 return;
1662 }
1663
Craig Mautner719e6b12014-04-04 20:29:41 -07001664 // Advance within this Parcel
1665 int offset = parcel.dataPosition();
Adam Lesinski1619ed42015-09-22 13:02:09 -07001666 parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Craig Mautner719e6b12014-04-04 20:29:41 -07001667
1668 Parcel p = Parcel.obtain();
1669 p.setDataPosition(0);
1670 p.appendFrom(parcel, offset, length);
Dianne Hackborn98305522017-05-05 17:53:53 -07001671 p.adoptClassCookies(parcel);
Craig Mautner719e6b12014-04-04 20:29:41 -07001672 if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
1673 + ": " + length + " bundle bytes starting at " + offset);
1674 p.setDataPosition(0);
1675
1676 mParcelledData = p;
Makoto Onuki6bdd4ac2018-04-26 09:11:35 -07001677 mParcelledByNative = isNativeBundle;
Craig Mautner719e6b12014-04-04 20:29:41 -07001678 }
Jeff Sharkeya6bfeae2017-07-05 16:50:24 -06001679
1680 /** {@hide} */
1681 public static void dumpStats(IndentingPrintWriter pw, String key, Object value) {
1682 final Parcel tmp = Parcel.obtain();
1683 tmp.writeValue(value);
1684 final int size = tmp.dataPosition();
1685 tmp.recycle();
1686
1687 // We only really care about logging large values
1688 if (size > 1024) {
1689 pw.println(key + " [size=" + size + "]");
1690 if (value instanceof BaseBundle) {
1691 dumpStats(pw, (BaseBundle) value);
1692 } else if (value instanceof SparseArray) {
1693 dumpStats(pw, (SparseArray) value);
1694 }
1695 }
1696 }
1697
1698 /** {@hide} */
1699 public static void dumpStats(IndentingPrintWriter pw, SparseArray array) {
1700 pw.increaseIndent();
1701 if (array == null) {
1702 pw.println("[null]");
1703 return;
1704 }
1705 for (int i = 0; i < array.size(); i++) {
1706 dumpStats(pw, "0x" + Integer.toHexString(array.keyAt(i)), array.valueAt(i));
1707 }
1708 pw.decreaseIndent();
1709 }
1710
1711 /** {@hide} */
1712 public static void dumpStats(IndentingPrintWriter pw, BaseBundle bundle) {
1713 pw.increaseIndent();
1714 if (bundle == null) {
1715 pw.println("[null]");
1716 return;
1717 }
1718 final ArrayMap<String, Object> map = bundle.getMap();
1719 for (int i = 0; i < map.size(); i++) {
1720 dumpStats(pw, map.keyAt(i), map.valueAt(i));
1721 }
1722 pw.decreaseIndent();
1723 }
Craig Mautner719e6b12014-04-04 20:29:41 -07001724}