Merge "Lazy bundle"
This commit is contained in:
@@ -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,9 +248,42 @@ 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,
|
||||
boolean parcelledByNative) {
|
||||
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -330,47 +330,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
// It's been unparcelled, so we need to walk the map
|
||||
for (int i=mMap.size()-1; i>=0; i--) {
|
||||
Object obj = mMap.valueAt(i);
|
||||
if (obj instanceof Parcelable) {
|
||||
if ((((Parcelable)obj).describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
|
||||
fdFound = true;
|
||||
break;
|
||||
}
|
||||
} else if (obj instanceof Parcelable[]) {
|
||||
Parcelable[] array = (Parcelable[]) obj;
|
||||
for (int n = array.length - 1; n >= 0; n--) {
|
||||
Parcelable p = array[n];
|
||||
if (p != null && ((p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
|
||||
fdFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (obj instanceof SparseArray) {
|
||||
SparseArray<? extends Parcelable> array =
|
||||
(SparseArray<? extends Parcelable>) obj;
|
||||
for (int n = array.size() - 1; n >= 0; n--) {
|
||||
Parcelable p = array.valueAt(n);
|
||||
if (p != null && (p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
|
||||
fdFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (obj instanceof ArrayList) {
|
||||
ArrayList array = (ArrayList) obj;
|
||||
// an ArrayList here might contain either Strings or
|
||||
// Parcelables; only look inside for Parcelables
|
||||
if (!array.isEmpty() && (array.get(0) instanceof Parcelable)) {
|
||||
for (int n = array.size() - 1; n >= 0; n--) {
|
||||
Parcelable p = (Parcelable) array.get(n);
|
||||
if (p != null && ((p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
|
||||
fdFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Parcel.hasFileDescriptors(obj)) {
|
||||
fdFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,7 +353,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
*/
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public Bundle filterValues() {
|
||||
unparcel();
|
||||
unparcel(/* itemwise */ true);
|
||||
Bundle bundle = this;
|
||||
if (mMap != null) {
|
||||
ArrayMap<String, Object> map = mMap;
|
||||
@@ -972,7 +934,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
@Nullable
|
||||
public Bundle getBundle(@Nullable String key) {
|
||||
unparcel();
|
||||
Object o = mMap.get(key);
|
||||
Object o = getValue(key);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -999,7 +961,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
@Nullable
|
||||
public <T extends Parcelable> T getParcelable(@Nullable String key) {
|
||||
unparcel();
|
||||
Object o = mMap.get(key);
|
||||
Object o = getValue(key);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1026,7 +988,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
@Nullable
|
||||
public Parcelable[] getParcelableArray(@Nullable String key) {
|
||||
unparcel();
|
||||
Object o = mMap.get(key);
|
||||
Object o = getValue(key);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1053,7 +1015,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
@Nullable
|
||||
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
|
||||
unparcel();
|
||||
Object o = mMap.get(key);
|
||||
Object o = getValue(key);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1077,7 +1039,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
@Nullable
|
||||
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
|
||||
unparcel();
|
||||
Object o = mMap.get(key);
|
||||
Object o = getValue(key);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1300,7 +1262,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
|
||||
try {
|
||||
super.writeToParcelInner(parcel, flags);
|
||||
writeToParcelInner(parcel, flags);
|
||||
} finally {
|
||||
parcel.restoreAllowFds(oldAllowFds);
|
||||
}
|
||||
@@ -1312,7 +1274,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
||||
* @param parcel The parcel to overwrite this bundle from.
|
||||
*/
|
||||
public void readFromParcel(Parcel parcel) {
|
||||
super.readFromParcelInner(parcel);
|
||||
readFromParcelInner(parcel);
|
||||
mFlags = FLAG_ALLOW_FDS;
|
||||
maybePrefillHasFds();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.ExceptionUtils;
|
||||
import android.util.Log;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Size;
|
||||
import android.util.SizeF;
|
||||
import android.util.Slog;
|
||||
@@ -56,7 +57,9 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Container for a message (data and object references) that can
|
||||
@@ -648,6 +651,65 @@ public final class Parcel {
|
||||
return nativeHasFileDescriptors(mNativePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the object used in {@link #readValue(ClassLoader)} / {@link #writeValue(Object)}
|
||||
* has file descriptors.
|
||||
*
|
||||
* <p>For most cases, it will use the self-reported {@link Parcelable#describeContents()} method
|
||||
* for that.
|
||||
*
|
||||
* @throws IllegalArgumentException if you provide any object not supported by above methods.
|
||||
* Most notably, if you pass {@link Parcel}, this method will throw, for that check
|
||||
* {@link Parcel#hasFileDescriptors()}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static boolean hasFileDescriptors(Object value) {
|
||||
getValueType(value); // Will throw if value is not supported
|
||||
if (value instanceof LazyValue) {
|
||||
return ((LazyValue) value).hasFileDescriptors();
|
||||
} else if (value instanceof Parcelable) {
|
||||
if ((((Parcelable) value).describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (value instanceof Parcelable[]) {
|
||||
Parcelable[] array = (Parcelable[]) value;
|
||||
for (int n = array.length - 1; n >= 0; n--) {
|
||||
Parcelable p = array[n];
|
||||
if (p != null && ((p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (value instanceof SparseArray<?>) {
|
||||
SparseArray<?> array = (SparseArray<?>) value;
|
||||
for (int n = array.size() - 1; n >= 0; n--) {
|
||||
Object object = array.valueAt(n);
|
||||
if (object instanceof Parcelable) {
|
||||
Parcelable p = (Parcelable) object;
|
||||
if (p != null && (p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value instanceof ArrayList<?>) {
|
||||
ArrayList<?> array = (ArrayList<?>) value;
|
||||
for (int n = array.size() - 1; n >= 0; n--) {
|
||||
Object object = array.get(n);
|
||||
if (object instanceof Parcelable) {
|
||||
Parcelable p = (Parcelable) object;
|
||||
if (p != null && ((p.describeContents()
|
||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store or read an IBinder interface token in the parcel at the current
|
||||
* {@link #dataPosition}. This is used to validate that the marshalled
|
||||
@@ -1795,108 +1857,206 @@ public final class Parcel {
|
||||
* should be used).</p>
|
||||
*/
|
||||
public final void writeValue(@Nullable Object v) {
|
||||
if (v instanceof LazyValue) {
|
||||
LazyValue value = (LazyValue) v;
|
||||
value.writeToParcel(this);
|
||||
return;
|
||||
}
|
||||
int type = getValueType(v);
|
||||
writeInt(type);
|
||||
if (isLengthPrefixed(type)) {
|
||||
// Length
|
||||
int length = dataPosition();
|
||||
writeInt(-1); // Placeholder
|
||||
// Object
|
||||
int start = dataPosition();
|
||||
writeValue(type, v);
|
||||
int end = dataPosition();
|
||||
// Backpatch length
|
||||
setDataPosition(length);
|
||||
writeInt(end - start);
|
||||
setDataPosition(end);
|
||||
} else {
|
||||
writeValue(type, v);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static int getValueType(@Nullable Object v) {
|
||||
if (v == null) {
|
||||
writeInt(VAL_NULL);
|
||||
return VAL_NULL;
|
||||
} else if (v instanceof String) {
|
||||
writeInt(VAL_STRING);
|
||||
writeString((String) v);
|
||||
return VAL_STRING;
|
||||
} else if (v instanceof Integer) {
|
||||
writeInt(VAL_INTEGER);
|
||||
writeInt((Integer) v);
|
||||
return VAL_INTEGER;
|
||||
} else if (v instanceof Map) {
|
||||
writeInt(VAL_MAP);
|
||||
writeMap((Map) v);
|
||||
return VAL_MAP;
|
||||
} else if (v instanceof Bundle) {
|
||||
// Must be before Parcelable
|
||||
writeInt(VAL_BUNDLE);
|
||||
writeBundle((Bundle) v);
|
||||
return VAL_BUNDLE;
|
||||
} else if (v instanceof PersistableBundle) {
|
||||
writeInt(VAL_PERSISTABLEBUNDLE);
|
||||
writePersistableBundle((PersistableBundle) v);
|
||||
// Must be before Parcelable
|
||||
return VAL_PERSISTABLEBUNDLE;
|
||||
} else if (v instanceof SizeF) {
|
||||
// Must be before Parcelable
|
||||
return VAL_SIZEF;
|
||||
} else if (v instanceof Parcelable) {
|
||||
// IMPOTANT: cases for classes that implement Parcelable must
|
||||
// come before the Parcelable case, so that their specific VAL_*
|
||||
// come before the Parcelable case, so that their speci fic VAL_*
|
||||
// types will be written.
|
||||
writeInt(VAL_PARCELABLE);
|
||||
writeParcelable((Parcelable) v, 0);
|
||||
return VAL_PARCELABLE;
|
||||
} else if (v instanceof Short) {
|
||||
writeInt(VAL_SHORT);
|
||||
writeInt(((Short) v).intValue());
|
||||
return VAL_SHORT;
|
||||
} else if (v instanceof Long) {
|
||||
writeInt(VAL_LONG);
|
||||
writeLong((Long) v);
|
||||
return VAL_LONG;
|
||||
} else if (v instanceof Float) {
|
||||
writeInt(VAL_FLOAT);
|
||||
writeFloat((Float) v);
|
||||
return VAL_FLOAT;
|
||||
} else if (v instanceof Double) {
|
||||
writeInt(VAL_DOUBLE);
|
||||
writeDouble((Double) v);
|
||||
return VAL_DOUBLE;
|
||||
} else if (v instanceof Boolean) {
|
||||
writeInt(VAL_BOOLEAN);
|
||||
writeInt((Boolean) v ? 1 : 0);
|
||||
return VAL_BOOLEAN;
|
||||
} else if (v instanceof CharSequence) {
|
||||
// Must be after String
|
||||
writeInt(VAL_CHARSEQUENCE);
|
||||
writeCharSequence((CharSequence) v);
|
||||
return VAL_CHARSEQUENCE;
|
||||
} else if (v instanceof List) {
|
||||
writeInt(VAL_LIST);
|
||||
writeList((List) v);
|
||||
return VAL_LIST;
|
||||
} else if (v instanceof SparseArray) {
|
||||
writeInt(VAL_SPARSEARRAY);
|
||||
writeSparseArray((SparseArray) v);
|
||||
return VAL_SPARSEARRAY;
|
||||
} else if (v instanceof boolean[]) {
|
||||
writeInt(VAL_BOOLEANARRAY);
|
||||
writeBooleanArray((boolean[]) v);
|
||||
return VAL_BOOLEANARRAY;
|
||||
} else if (v instanceof byte[]) {
|
||||
writeInt(VAL_BYTEARRAY);
|
||||
writeByteArray((byte[]) v);
|
||||
return VAL_BYTEARRAY;
|
||||
} else if (v instanceof String[]) {
|
||||
writeInt(VAL_STRINGARRAY);
|
||||
writeStringArray((String[]) v);
|
||||
return VAL_STRINGARRAY;
|
||||
} else if (v instanceof CharSequence[]) {
|
||||
// Must be after String[] and before Object[]
|
||||
writeInt(VAL_CHARSEQUENCEARRAY);
|
||||
writeCharSequenceArray((CharSequence[]) v);
|
||||
return VAL_CHARSEQUENCEARRAY;
|
||||
} else if (v instanceof IBinder) {
|
||||
writeInt(VAL_IBINDER);
|
||||
writeStrongBinder((IBinder) v);
|
||||
return VAL_IBINDER;
|
||||
} else if (v instanceof Parcelable[]) {
|
||||
writeInt(VAL_PARCELABLEARRAY);
|
||||
writeParcelableArray((Parcelable[]) v, 0);
|
||||
return VAL_PARCELABLEARRAY;
|
||||
} else if (v instanceof int[]) {
|
||||
writeInt(VAL_INTARRAY);
|
||||
writeIntArray((int[]) v);
|
||||
return VAL_INTARRAY;
|
||||
} else if (v instanceof long[]) {
|
||||
writeInt(VAL_LONGARRAY);
|
||||
writeLongArray((long[]) v);
|
||||
return VAL_LONGARRAY;
|
||||
} else if (v instanceof Byte) {
|
||||
writeInt(VAL_BYTE);
|
||||
writeInt((Byte) v);
|
||||
return VAL_BYTE;
|
||||
} else if (v instanceof Size) {
|
||||
writeInt(VAL_SIZE);
|
||||
writeSize((Size) v);
|
||||
} else if (v instanceof SizeF) {
|
||||
writeInt(VAL_SIZEF);
|
||||
writeSizeF((SizeF) v);
|
||||
return VAL_SIZE;
|
||||
} else if (v instanceof double[]) {
|
||||
writeInt(VAL_DOUBLEARRAY);
|
||||
writeDoubleArray((double[]) v);
|
||||
return VAL_DOUBLEARRAY;
|
||||
} else {
|
||||
Class<?> clazz = v.getClass();
|
||||
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
|
||||
// Only pure Object[] are written here, Other arrays of non-primitive types are
|
||||
// handled by serialization as this does not record the component type.
|
||||
writeInt(VAL_OBJECTARRAY);
|
||||
writeArray((Object[]) v);
|
||||
return VAL_OBJECTARRAY;
|
||||
} else if (v instanceof Serializable) {
|
||||
// Must be last
|
||||
writeInt(VAL_SERIALIZABLE);
|
||||
writeSerializable((Serializable) v);
|
||||
return VAL_SERIALIZABLE;
|
||||
} else {
|
||||
throw new RuntimeException("Parcel: unable to marshal value " + v);
|
||||
throw new IllegalArgumentException("Parcel: unknown type for value " + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Writes value {@code v} in the parcel. This does NOT write the int representing the type
|
||||
* first.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void writeValue(int type, @Nullable Object v) {
|
||||
switch (type) {
|
||||
case VAL_NULL:
|
||||
break;
|
||||
case VAL_STRING:
|
||||
writeString((String) v);
|
||||
break;
|
||||
case VAL_INTEGER:
|
||||
writeInt((Integer) v);
|
||||
break;
|
||||
case VAL_MAP:
|
||||
writeMap((Map) v);
|
||||
break;
|
||||
case VAL_BUNDLE:
|
||||
writeBundle((Bundle) v);
|
||||
break;
|
||||
case VAL_PERSISTABLEBUNDLE:
|
||||
writePersistableBundle((PersistableBundle) v);
|
||||
break;
|
||||
case VAL_PARCELABLE:
|
||||
writeParcelable((Parcelable) v, 0);
|
||||
break;
|
||||
case VAL_SHORT:
|
||||
writeInt(((Short) v).intValue());
|
||||
break;
|
||||
case VAL_LONG:
|
||||
writeLong((Long) v);
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
writeFloat((Float) v);
|
||||
break;
|
||||
case VAL_DOUBLE:
|
||||
writeDouble((Double) v);
|
||||
break;
|
||||
case VAL_BOOLEAN:
|
||||
writeInt((Boolean) v ? 1 : 0);
|
||||
break;
|
||||
case VAL_CHARSEQUENCE:
|
||||
writeCharSequence((CharSequence) v);
|
||||
break;
|
||||
case VAL_LIST:
|
||||
writeList((List) v);
|
||||
break;
|
||||
case VAL_SPARSEARRAY:
|
||||
writeSparseArray((SparseArray) v);
|
||||
break;
|
||||
case VAL_BOOLEANARRAY:
|
||||
writeBooleanArray((boolean[]) v);
|
||||
break;
|
||||
case VAL_BYTEARRAY:
|
||||
writeByteArray((byte[]) v);
|
||||
break;
|
||||
case VAL_STRINGARRAY:
|
||||
writeStringArray((String[]) v);
|
||||
break;
|
||||
case VAL_CHARSEQUENCEARRAY:
|
||||
writeCharSequenceArray((CharSequence[]) v);
|
||||
break;
|
||||
case VAL_IBINDER:
|
||||
writeStrongBinder((IBinder) v);
|
||||
break;
|
||||
case VAL_PARCELABLEARRAY:
|
||||
writeParcelableArray((Parcelable[]) v, 0);
|
||||
break;
|
||||
case VAL_INTARRAY:
|
||||
writeIntArray((int[]) v);
|
||||
break;
|
||||
case VAL_LONGARRAY:
|
||||
writeLongArray((long[]) v);
|
||||
break;
|
||||
case VAL_BYTE:
|
||||
writeInt((Byte) v);
|
||||
break;
|
||||
case VAL_SIZE:
|
||||
writeSize((Size) v);
|
||||
break;
|
||||
case VAL_SIZEF:
|
||||
writeSizeF((SizeF) v);
|
||||
break;
|
||||
case VAL_DOUBLEARRAY:
|
||||
writeDoubleArray((double[]) v);
|
||||
break;
|
||||
case VAL_OBJECTARRAY:
|
||||
writeArray((Object[]) v);
|
||||
break;
|
||||
case VAL_SERIALIZABLE:
|
||||
writeSerializable((Serializable) v);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Parcel: unable to marshal value " + v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten the name of the class of the Parcelable and its contents
|
||||
@@ -3167,7 +3327,180 @@ public final class Parcel {
|
||||
@Nullable
|
||||
public final Object readValue(@Nullable ClassLoader loader) {
|
||||
int type = readInt();
|
||||
final Object object;
|
||||
if (isLengthPrefixed(type)) {
|
||||
int length = readInt();
|
||||
int start = dataPosition();
|
||||
object = readValue(type, loader);
|
||||
int actual = dataPosition() - start;
|
||||
if (actual != length) {
|
||||
Log.w(TAG,
|
||||
"Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
|
||||
+ " consumed " + actual + " bytes, but " + length + " expected.");
|
||||
}
|
||||
} else {
|
||||
object = readValue(type, loader);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return a {@link Supplier} for length-prefixed types that deserializes the object
|
||||
* when {@link Supplier#get()} is called, for other types it will return the object itself.
|
||||
*
|
||||
* <p>After calling {@link Supplier#get()} the parcel cursor will not change. Note that you
|
||||
* shouldn't recycle the parcel, not at least until all objects have been retrieved. No
|
||||
* synchronization attempts are made.
|
||||
*
|
||||
* </p>The supplier returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
|
||||
* suppliers are equal if either of the following is true:
|
||||
* <ul>
|
||||
* <li>{@link Supplier#get()} has been called on both and both objects returned are equal.
|
||||
* <li>{@link Supplier#get()} hasn't been called on either one and everything below is true:
|
||||
* <ul>
|
||||
* <li>The {@code loader} parameters used to retrieve each are equal.
|
||||
* <li>They both have the same type.
|
||||
* <li>They have the same payload length.
|
||||
* <li>Their binary content is the same.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public Object readLazyValue(@Nullable ClassLoader loader) {
|
||||
int start = dataPosition();
|
||||
int type = readInt();
|
||||
if (isLengthPrefixed(type)) {
|
||||
int length = readInt();
|
||||
setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
|
||||
return new LazyValue(this, start, length, type, loader);
|
||||
} else {
|
||||
return readValue(type, loader);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LazyValue implements Supplier<Object> {
|
||||
private final int mPosition;
|
||||
private final int mLength;
|
||||
private final int mType;
|
||||
@Nullable private final ClassLoader mLoader;
|
||||
@Nullable private Parcel mSource;
|
||||
@Nullable private Object mObject;
|
||||
@Nullable private Parcel mValueParcel;
|
||||
|
||||
LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
|
||||
mSource = source;
|
||||
mPosition = position;
|
||||
mLength = length;
|
||||
mType = type;
|
||||
mLoader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
if (mObject == null) {
|
||||
int restore = mSource.dataPosition();
|
||||
try {
|
||||
mSource.setDataPosition(mPosition);
|
||||
mObject = mSource.readValue(mLoader);
|
||||
} finally {
|
||||
mSource.setDataPosition(restore);
|
||||
}
|
||||
mSource = null;
|
||||
if (mValueParcel != null) {
|
||||
mValueParcel.recycle();
|
||||
mValueParcel = null;
|
||||
}
|
||||
}
|
||||
return mObject;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out) {
|
||||
if (mObject == null) {
|
||||
int restore = mSource.dataPosition();
|
||||
try {
|
||||
mSource.setDataPosition(mPosition);
|
||||
out.writeInt(mSource.readInt()); // Type
|
||||
out.writeInt(mSource.readInt()); // Length
|
||||
out.appendFrom(mSource, mSource.dataPosition(), mLength);
|
||||
} finally {
|
||||
mSource.setDataPosition(restore);
|
||||
}
|
||||
} else {
|
||||
out.writeValue(mObject);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasFileDescriptors() {
|
||||
return getValueParcel().hasFileDescriptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mObject == null
|
||||
? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}'
|
||||
: "Supplier{" + mObject + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* We're checking if the *lazy value* is equal to another one, not if the *object*
|
||||
* represented by the lazy value is equal to the other one. So, if there are two lazy values
|
||||
* and one of them has been deserialized but the other hasn't this will always return false.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof LazyValue)) {
|
||||
return false;
|
||||
}
|
||||
LazyValue value = (LazyValue) other;
|
||||
// Check if they are either both serialized or both deserialized
|
||||
if ((mObject == null) != (value.mObject == null)) {
|
||||
return false;
|
||||
}
|
||||
// If both are deserialized, compare the live objects
|
||||
if (mObject != null) {
|
||||
return mObject.equals(value.mObject);
|
||||
}
|
||||
// Better safely fail here since this could mean we get different objects
|
||||
if (!Objects.equals(mLoader, value.mLoader)) {
|
||||
return false;
|
||||
}
|
||||
// Otherwise compare metadata prior to comparing payload
|
||||
if (mType != value.mType || mLength != value.mLength) {
|
||||
return false;
|
||||
}
|
||||
// Finally we compare the payload
|
||||
return getValueParcel().compareData(value.getValueParcel()) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mObject, mLoader, mType, mLength);
|
||||
}
|
||||
|
||||
/** This extracts the parcel section responsible for the object and returns it. */
|
||||
private Parcel getValueParcel() {
|
||||
if (mValueParcel == null) {
|
||||
mValueParcel = Parcel.obtain();
|
||||
// mLength is the length of object representation, excluding the type and length.
|
||||
// mPosition is the position of the entire value container, right before the type.
|
||||
// So, we add 4 bytes for the type + 4 bytes for the length written.
|
||||
mValueParcel.appendFrom(mSource, mPosition, mLength + 8);
|
||||
}
|
||||
return mValueParcel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
|
||||
* type first.
|
||||
*/
|
||||
@Nullable
|
||||
private Object readValue(int type, @Nullable ClassLoader loader) {
|
||||
switch (type) {
|
||||
case VAL_NULL:
|
||||
return null;
|
||||
@@ -3266,6 +3599,20 @@ public final class Parcel {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLengthPrefixed(int type) {
|
||||
switch (type) {
|
||||
case VAL_PARCELABLE:
|
||||
case VAL_PARCELABLEARRAY:
|
||||
case VAL_LIST:
|
||||
case VAL_SPARSEARRAY:
|
||||
case VAL_BUNDLE:
|
||||
case VAL_SERIALIZABLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a new Parcelable from the parcel. The given class loader
|
||||
* will be used to load any enclosed Parcelables. If it is null, the default
|
||||
@@ -3564,49 +3911,49 @@ public final class Parcel {
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
|
||||
@Nullable ClassLoader loader) {
|
||||
if (DEBUG_ARRAY_MAP) {
|
||||
RuntimeException here = new RuntimeException("here");
|
||||
here.fillInStackTrace();
|
||||
Log.d(TAG, "Reading " + N + " ArrayMap entries", here);
|
||||
}
|
||||
int startPos;
|
||||
while (N > 0) {
|
||||
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
|
||||
String key = readString();
|
||||
Object value = readValue(loader);
|
||||
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " "
|
||||
+ (dataPosition()-startPos) + " bytes: key=0x"
|
||||
+ Integer.toHexString((key != null ? key.hashCode() : 0)) + " " + key);
|
||||
outVal.append(key, value);
|
||||
N--;
|
||||
}
|
||||
outVal.validate();
|
||||
/* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
|
||||
int size, @Nullable ClassLoader loader) {
|
||||
readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
|
||||
}
|
||||
|
||||
/* package */ void readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N,
|
||||
@Nullable ClassLoader loader) {
|
||||
if (DEBUG_ARRAY_MAP) {
|
||||
RuntimeException here = new RuntimeException("here");
|
||||
here.fillInStackTrace();
|
||||
Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here);
|
||||
}
|
||||
while (N > 0) {
|
||||
/**
|
||||
* Reads a map into {@code map}.
|
||||
*
|
||||
* @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
|
||||
* @param lazy Whether to populate the map with lazy {@link Supplier} objects for
|
||||
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
|
||||
* details.
|
||||
* @return whether the parcel can be recycled or not.
|
||||
* @hide
|
||||
*/
|
||||
boolean readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
|
||||
boolean lazy, @Nullable ClassLoader loader) {
|
||||
boolean recycle = true;
|
||||
while (size > 0) {
|
||||
String key = readString();
|
||||
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x"
|
||||
+ (key != null ? key.hashCode() : 0) + " " + key);
|
||||
Object value = readValue(loader);
|
||||
outVal.put(key, value);
|
||||
N--;
|
||||
Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
|
||||
if (value instanceof LazyValue) {
|
||||
recycle = false;
|
||||
}
|
||||
if (sorted) {
|
||||
map.append(key, value);
|
||||
} else {
|
||||
map.put(key, value);
|
||||
}
|
||||
size--;
|
||||
}
|
||||
if (sorted) {
|
||||
map.validate();
|
||||
}
|
||||
return recycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide For testing only.
|
||||
*/
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||
public void readArrayMap(@NonNull ArrayMap outVal, @Nullable ClassLoader loader) {
|
||||
public void readArrayMap(@NonNull ArrayMap<? super String, Object> outVal,
|
||||
@Nullable ClassLoader loader) {
|
||||
final int N = readInt();
|
||||
if (N < 0) {
|
||||
return;
|
||||
@@ -3691,4 +4038,38 @@ public final class Parcel {
|
||||
public long getBlobAshmemSize() {
|
||||
return nativeGetBlobAshmemSize(mNativePtr);
|
||||
}
|
||||
|
||||
private static String valueTypeToString(int type) {
|
||||
switch (type) {
|
||||
case VAL_NULL: return "VAL_NULL";
|
||||
case VAL_INTEGER: return "VAL_INTEGER";
|
||||
case VAL_MAP: return "VAL_MAP";
|
||||
case VAL_BUNDLE: return "VAL_BUNDLE";
|
||||
case VAL_PERSISTABLEBUNDLE: return "VAL_PERSISTABLEBUNDLE";
|
||||
case VAL_PARCELABLE: return "VAL_PARCELABLE";
|
||||
case VAL_SHORT: return "VAL_SHORT";
|
||||
case VAL_LONG: return "VAL_LONG";
|
||||
case VAL_FLOAT: return "VAL_FLOAT";
|
||||
case VAL_DOUBLE: return "VAL_DOUBLE";
|
||||
case VAL_BOOLEAN: return "VAL_BOOLEAN";
|
||||
case VAL_CHARSEQUENCE: return "VAL_CHARSEQUENCE";
|
||||
case VAL_LIST: return "VAL_LIST";
|
||||
case VAL_SPARSEARRAY: return "VAL_SPARSEARRAY";
|
||||
case VAL_BOOLEANARRAY: return "VAL_BOOLEANARRAY";
|
||||
case VAL_BYTEARRAY: return "VAL_BYTEARRAY";
|
||||
case VAL_STRINGARRAY: return "VAL_STRINGARRAY";
|
||||
case VAL_CHARSEQUENCEARRAY: return "VAL_CHARSEQUENCEARRAY";
|
||||
case VAL_IBINDER: return "VAL_IBINDER";
|
||||
case VAL_PARCELABLEARRAY: return "VAL_PARCELABLEARRAY";
|
||||
case VAL_INTARRAY: return "VAL_INTARRAY";
|
||||
case VAL_LONGARRAY: return "VAL_LONGARRAY";
|
||||
case VAL_BYTE: return "VAL_BYTE";
|
||||
case VAL_SIZE: return "VAL_SIZE";
|
||||
case VAL_SIZEF: return "VAL_SIZEF";
|
||||
case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
|
||||
case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
|
||||
case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
|
||||
default: return "UNKNOWN(" + type + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user