summaryrefslogtreecommitdiff
path: root/core/java/android/os/BaseBundle.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/os/BaseBundle.java')
-rw-r--r--core/java/android/os/BaseBundle.java103
1 files changed, 75 insertions, 28 deletions
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 1692921f4a8f..6da02f5c9ff5 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -31,6 +31,7 @@ import com.android.internal.util.IndentingPrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Set;
+import java.util.function.Supplier;
/**
* A mapping from String keys to values of various types. In most cases, you
@@ -38,7 +39,8 @@ import java.util.Set;
* {@link PersistableBundle} subclass.
*/
public class BaseBundle {
- private static final String TAG = "Bundle";
+ /** @hide */
+ protected static final String TAG = "Bundle";
static final boolean DEBUG = false;
// Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
@@ -95,7 +97,7 @@ public class BaseBundle {
Parcel mParcelledData = null;
/**
- * Whether {@link #mParcelledData} was generated by native coed or not.
+ * Whether {@link #mParcelledData} was generated by native code or not.
*/
private boolean mParcelledByNative;
@@ -198,7 +200,7 @@ public class BaseBundle {
if (size == 0) {
return null;
}
- Object o = mMap.valueAt(0);
+ Object o = getValueAt(0);
try {
return (String) o;
} catch (ClassCastException e) {
@@ -229,7 +231,12 @@ public class BaseBundle {
* using the currently assigned class loader.
*/
@UnsupportedAppUsage
- /* package */ void unparcel() {
+ final void unparcel() {
+ unparcel(/* itemwise */ false);
+ }
+
+ /** Deserializes the underlying data and each item if {@code itemwise} is true. */
+ final void unparcel(boolean itemwise) {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
@@ -241,7 +248,40 @@ public class BaseBundle {
+ ": no parcelled data");
}
}
+ if (itemwise) {
+ for (int i = 0, n = mMap.size(); i < n; i++) {
+ // Triggers deserialization of i-th item, if needed
+ getValueAt(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the value for key {@code key}.
+ *
+ * @hide
+ */
+ final Object getValue(String key) {
+ int i = mMap.indexOfKey(key);
+ return (i >= 0) ? getValueAt(i) : null;
+ }
+
+ /**
+ * Returns the value for a certain position in the array map.
+ *
+ * @hide
+ */
+ final Object getValueAt(int i) {
+ Object object = mMap.valueAt(i);
+ if (object instanceof Supplier<?>) {
+ Supplier<?> supplier = (Supplier<?>) object;
+ synchronized (this) {
+ object = supplier.get();
+ }
+ mMap.setValueAt(i, object);
}
+ return object;
}
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
@@ -282,15 +322,8 @@ public class BaseBundle {
map.ensureCapacity(count);
}
try {
- if (parcelledByNative) {
- // If it was parcelled by native code, then the array map keys aren't sorted
- // by their hash codes, so use the safe (slow) one.
- parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
- } else {
- // If parcelled by Java, we know the contents are sorted properly,
- // so we can use ArrayMap.append().
- parcelledData.readArrayMapInternal(map, count, mClassLoader);
- }
+ recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
+ /* lazy */ true, mClassLoader);
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -342,7 +375,7 @@ public class BaseBundle {
/** @hide */
ArrayMap<String, Object> getMap() {
- unparcel();
+ unparcel(/* itemwise */ true);
return mMap;
}
@@ -400,7 +433,12 @@ public class BaseBundle {
}
/**
- * @hide This kind-of does an equality comparison. Kind-of.
+ * Performs a loose equality check, which means there can be false negatives but if the method
+ * returns true than both objects are guaranteed to be equal.
+ *
+ * The point is that this method is a light-weight check in performance terms.
+ *
+ * @hide
*/
public boolean kindofEquals(BaseBundle other) {
if (other == null) {
@@ -415,6 +453,12 @@ public class BaseBundle {
} else if (isParcelled()) {
return mParcelledData.compareData(other.mParcelledData) == 0;
} else {
+ // Following semantic above of failing in case we get a serialized value vs a
+ // deserialized one, we'll compare the map. If a certain element hasn't been
+ // deserialized yet, it's a Supplier (or more specifically a LazyValue, but let's
+ // pretend we don't know that here :P), we'll use that element's equality comparison as
+ // map naturally does. That will takes care of comparing the payload if needed (see
+ // Parcel.readLazyValue() for details).
return mMap.equals(other.mMap);
}
}
@@ -453,7 +497,7 @@ public class BaseBundle {
final int N = fromMap.size();
mMap = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
- mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
+ mMap.append(fromMap.keyAt(i), deepCopyValue(from.getValueAt(i)));
}
}
} else {
@@ -526,7 +570,7 @@ public class BaseBundle {
@Nullable
public Object get(String key) {
unparcel();
- return mMap.get(key);
+ return getValue(key);
}
/**
@@ -1001,7 +1045,7 @@ public class BaseBundle {
*/
char getChar(String key, char defaultValue) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return defaultValue;
}
@@ -1266,7 +1310,7 @@ public class BaseBundle {
@Nullable
Serializable getSerializable(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1289,7 +1333,7 @@ public class BaseBundle {
@Nullable
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1312,7 +1356,7 @@ public class BaseBundle {
@Nullable
ArrayList<String> getStringArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1335,7 +1379,7 @@ public class BaseBundle {
@Nullable
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1404,7 +1448,7 @@ public class BaseBundle {
@Nullable
short[] getShortArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1427,7 +1471,7 @@ public class BaseBundle {
@Nullable
char[] getCharArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1496,7 +1540,7 @@ public class BaseBundle {
@Nullable
float[] getFloatArray(@Nullable String key) {
unparcel();
- Object o = mMap.get(key);
+ Object o = getValue(key);
if (o == null) {
return null;
}
@@ -1585,7 +1629,7 @@ public class BaseBundle {
void writeToParcelInner(Parcel parcel, int flags) {
// If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
if (parcel.hasReadWriteHelper()) {
- unparcel();
+ unparcel(/* itemwise */ true);
}
// Keep implementation in sync with writeToParcel() in
// frameworks/native/libs/binder/PersistableBundle.cpp.
@@ -1660,10 +1704,13 @@ public class BaseBundle {
}
if (parcel.hasReadWriteHelper()) {
- // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
- // unparcel right away.
+ // If the parcel has a read-write helper, it's better to deserialize immediately
+ // otherwise the helper would have to either maintain valid state long after the bundle
+ // had been constructed with parcel or to make sure they trigger deserialization of the
+ // bundle immediately; neither of which is obvious.
synchronized (this) {
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
+ unparcel(/* itemwise */ true);
}
return;
}