Merge "Lazy bundle" am: 93b4b55ee7 am: 872f7a88e5
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1787847 Change-Id: If624941e8a84c8ec9bc1fa98fe0f3e27a5b0f03c
This commit is contained in:
@@ -31,6 +31,7 @@ import com.android.internal.util.IndentingPrintWriter;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping from String keys to values of various types. In most cases, you
|
* 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.
|
* {@link PersistableBundle} subclass.
|
||||||
*/
|
*/
|
||||||
public class BaseBundle {
|
public class BaseBundle {
|
||||||
private static final String TAG = "Bundle";
|
/** @hide */
|
||||||
|
protected static final String TAG = "Bundle";
|
||||||
static final boolean DEBUG = false;
|
static final boolean DEBUG = false;
|
||||||
|
|
||||||
// Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
|
// Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
|
||||||
@@ -95,7 +97,7 @@ public class BaseBundle {
|
|||||||
Parcel mParcelledData = null;
|
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;
|
private boolean mParcelledByNative;
|
||||||
|
|
||||||
@@ -198,7 +200,7 @@ public class BaseBundle {
|
|||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Object o = mMap.valueAt(0);
|
Object o = getValueAt(0);
|
||||||
try {
|
try {
|
||||||
return (String) o;
|
return (String) o;
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
@@ -229,7 +231,12 @@ public class BaseBundle {
|
|||||||
* using the currently assigned class loader.
|
* using the currently assigned class loader.
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@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) {
|
synchronized (this) {
|
||||||
final Parcel source = mParcelledData;
|
final Parcel source = mParcelledData;
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
@@ -241,9 +248,42 @@ public class BaseBundle {
|
|||||||
+ ": no parcelled data");
|
+ ": 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,
|
private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
|
||||||
boolean parcelledByNative) {
|
boolean parcelledByNative) {
|
||||||
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
|
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
|
||||||
@@ -282,15 +322,8 @@ public class BaseBundle {
|
|||||||
map.ensureCapacity(count);
|
map.ensureCapacity(count);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (parcelledByNative) {
|
recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative,
|
||||||
// If it was parcelled by native code, then the array map keys aren't sorted
|
/* lazy */ true, mClassLoader);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
} catch (BadParcelableException e) {
|
} catch (BadParcelableException e) {
|
||||||
if (sShouldDefuse) {
|
if (sShouldDefuse) {
|
||||||
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
|
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
|
||||||
@@ -342,7 +375,7 @@ public class BaseBundle {
|
|||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
ArrayMap<String, Object> getMap() {
|
ArrayMap<String, Object> getMap() {
|
||||||
unparcel();
|
unparcel(/* itemwise */ true);
|
||||||
return mMap;
|
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) {
|
public boolean kindofEquals(BaseBundle other) {
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
@@ -415,6 +453,12 @@ public class BaseBundle {
|
|||||||
} else if (isParcelled()) {
|
} else if (isParcelled()) {
|
||||||
return mParcelledData.compareData(other.mParcelledData) == 0;
|
return mParcelledData.compareData(other.mParcelledData) == 0;
|
||||||
} else {
|
} 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);
|
return mMap.equals(other.mMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,7 +497,7 @@ public class BaseBundle {
|
|||||||
final int N = fromMap.size();
|
final int N = fromMap.size();
|
||||||
mMap = new ArrayMap<>(N);
|
mMap = new ArrayMap<>(N);
|
||||||
for (int i = 0; i < N; i++) {
|
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 {
|
} else {
|
||||||
@@ -526,7 +570,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Object get(String key) {
|
public Object get(String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
return mMap.get(key);
|
return getValue(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1001,7 +1045,7 @@ public class BaseBundle {
|
|||||||
*/
|
*/
|
||||||
char getChar(String key, char defaultValue) {
|
char getChar(String key, char defaultValue) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@@ -1266,7 +1310,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
Serializable getSerializable(@Nullable String key) {
|
Serializable getSerializable(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1289,7 +1333,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
|
ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1312,7 +1356,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
ArrayList<String> getStringArrayList(@Nullable String key) {
|
ArrayList<String> getStringArrayList(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1335,7 +1379,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
|
ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1404,7 +1448,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
short[] getShortArray(@Nullable String key) {
|
short[] getShortArray(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1427,7 +1471,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
char[] getCharArray(@Nullable String key) {
|
char[] getCharArray(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1496,7 +1540,7 @@ public class BaseBundle {
|
|||||||
@Nullable
|
@Nullable
|
||||||
float[] getFloatArray(@Nullable String key) {
|
float[] getFloatArray(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1585,7 +1629,7 @@ public class BaseBundle {
|
|||||||
void writeToParcelInner(Parcel parcel, int flags) {
|
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 the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
|
||||||
if (parcel.hasReadWriteHelper()) {
|
if (parcel.hasReadWriteHelper()) {
|
||||||
unparcel();
|
unparcel(/* itemwise */ true);
|
||||||
}
|
}
|
||||||
// Keep implementation in sync with writeToParcel() in
|
// Keep implementation in sync with writeToParcel() in
|
||||||
// frameworks/native/libs/binder/PersistableBundle.cpp.
|
// frameworks/native/libs/binder/PersistableBundle.cpp.
|
||||||
@@ -1660,10 +1704,13 @@ public class BaseBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parcel.hasReadWriteHelper()) {
|
if (parcel.hasReadWriteHelper()) {
|
||||||
// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
|
// If the parcel has a read-write helper, it's better to deserialize immediately
|
||||||
// unparcel right away.
|
// 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) {
|
synchronized (this) {
|
||||||
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
|
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
|
||||||
|
unparcel(/* itemwise */ true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,47 +331,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
// It's been unparcelled, so we need to walk the map
|
// It's been unparcelled, so we need to walk the map
|
||||||
for (int i=mMap.size()-1; i>=0; i--) {
|
for (int i=mMap.size()-1; i>=0; i--) {
|
||||||
Object obj = mMap.valueAt(i);
|
Object obj = mMap.valueAt(i);
|
||||||
if (obj instanceof Parcelable) {
|
if (Parcel.hasFileDescriptors(obj)) {
|
||||||
if ((((Parcelable)obj).describeContents()
|
fdFound = true;
|
||||||
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -392,7 +354,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||||
public Bundle filterValues() {
|
public Bundle filterValues() {
|
||||||
unparcel();
|
unparcel(/* itemwise */ true);
|
||||||
Bundle bundle = this;
|
Bundle bundle = this;
|
||||||
if (mMap != null) {
|
if (mMap != null) {
|
||||||
ArrayMap<String, Object> map = mMap;
|
ArrayMap<String, Object> map = mMap;
|
||||||
@@ -973,7 +935,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Bundle getBundle(@Nullable String key) {
|
public Bundle getBundle(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1000,7 +962,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public <T extends Parcelable> T getParcelable(@Nullable String key) {
|
public <T extends Parcelable> T getParcelable(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1027,7 +989,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Parcelable[] getParcelableArray(@Nullable String key) {
|
public Parcelable[] getParcelableArray(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1054,7 +1016,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
|
public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1078,7 +1040,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
|
public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
|
||||||
unparcel();
|
unparcel();
|
||||||
Object o = mMap.get(key);
|
Object o = getValue(key);
|
||||||
if (o == null) {
|
if (o == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1301,7 +1263,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
public void writeToParcel(Parcel parcel, int flags) {
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
|
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
|
||||||
try {
|
try {
|
||||||
super.writeToParcelInner(parcel, flags);
|
writeToParcelInner(parcel, flags);
|
||||||
} finally {
|
} finally {
|
||||||
parcel.restoreAllowFds(oldAllowFds);
|
parcel.restoreAllowFds(oldAllowFds);
|
||||||
}
|
}
|
||||||
@@ -1313,7 +1275,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
|
|||||||
* @param parcel The parcel to overwrite this bundle from.
|
* @param parcel The parcel to overwrite this bundle from.
|
||||||
*/
|
*/
|
||||||
public void readFromParcel(Parcel parcel) {
|
public void readFromParcel(Parcel parcel) {
|
||||||
super.readFromParcelInner(parcel);
|
readFromParcelInner(parcel);
|
||||||
mFlags = FLAG_ALLOW_FDS;
|
mFlags = FLAG_ALLOW_FDS;
|
||||||
maybePrefillHasFds();
|
maybePrefillHasFds();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.MathUtils;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import android.util.SizeF;
|
import android.util.SizeF;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
@@ -56,7 +57,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container for a message (data and object references) that can
|
* Container for a message (data and object references) that can
|
||||||
@@ -678,6 +681,65 @@ public final class Parcel {
|
|||||||
return nativeHasFileDescriptors(mNativePtr);
|
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
|
* Store or read an IBinder interface token in the parcel at the current
|
||||||
* {@link #dataPosition}. This is used to validate that the marshalled
|
* {@link #dataPosition}. This is used to validate that the marshalled
|
||||||
@@ -1832,108 +1894,206 @@ public final class Parcel {
|
|||||||
* should be used).</p>
|
* should be used).</p>
|
||||||
*/
|
*/
|
||||||
public final void writeValue(@Nullable Object v) {
|
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) {
|
if (v == null) {
|
||||||
writeInt(VAL_NULL);
|
return VAL_NULL;
|
||||||
} else if (v instanceof String) {
|
} else if (v instanceof String) {
|
||||||
writeInt(VAL_STRING);
|
return VAL_STRING;
|
||||||
writeString((String) v);
|
|
||||||
} else if (v instanceof Integer) {
|
} else if (v instanceof Integer) {
|
||||||
writeInt(VAL_INTEGER);
|
return VAL_INTEGER;
|
||||||
writeInt((Integer) v);
|
|
||||||
} else if (v instanceof Map) {
|
} else if (v instanceof Map) {
|
||||||
writeInt(VAL_MAP);
|
return VAL_MAP;
|
||||||
writeMap((Map) v);
|
|
||||||
} else if (v instanceof Bundle) {
|
} else if (v instanceof Bundle) {
|
||||||
// Must be before Parcelable
|
// Must be before Parcelable
|
||||||
writeInt(VAL_BUNDLE);
|
return VAL_BUNDLE;
|
||||||
writeBundle((Bundle) v);
|
|
||||||
} else if (v instanceof PersistableBundle) {
|
} else if (v instanceof PersistableBundle) {
|
||||||
writeInt(VAL_PERSISTABLEBUNDLE);
|
// Must be before Parcelable
|
||||||
writePersistableBundle((PersistableBundle) v);
|
return VAL_PERSISTABLEBUNDLE;
|
||||||
|
} else if (v instanceof SizeF) {
|
||||||
|
// Must be before Parcelable
|
||||||
|
return VAL_SIZEF;
|
||||||
} else if (v instanceof Parcelable) {
|
} else if (v instanceof Parcelable) {
|
||||||
// IMPOTANT: cases for classes that implement Parcelable must
|
// 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.
|
// types will be written.
|
||||||
writeInt(VAL_PARCELABLE);
|
return VAL_PARCELABLE;
|
||||||
writeParcelable((Parcelable) v, 0);
|
|
||||||
} else if (v instanceof Short) {
|
} else if (v instanceof Short) {
|
||||||
writeInt(VAL_SHORT);
|
return VAL_SHORT;
|
||||||
writeInt(((Short) v).intValue());
|
|
||||||
} else if (v instanceof Long) {
|
} else if (v instanceof Long) {
|
||||||
writeInt(VAL_LONG);
|
return VAL_LONG;
|
||||||
writeLong((Long) v);
|
|
||||||
} else if (v instanceof Float) {
|
} else if (v instanceof Float) {
|
||||||
writeInt(VAL_FLOAT);
|
return VAL_FLOAT;
|
||||||
writeFloat((Float) v);
|
|
||||||
} else if (v instanceof Double) {
|
} else if (v instanceof Double) {
|
||||||
writeInt(VAL_DOUBLE);
|
return VAL_DOUBLE;
|
||||||
writeDouble((Double) v);
|
|
||||||
} else if (v instanceof Boolean) {
|
} else if (v instanceof Boolean) {
|
||||||
writeInt(VAL_BOOLEAN);
|
return VAL_BOOLEAN;
|
||||||
writeInt((Boolean) v ? 1 : 0);
|
|
||||||
} else if (v instanceof CharSequence) {
|
} else if (v instanceof CharSequence) {
|
||||||
// Must be after String
|
// Must be after String
|
||||||
writeInt(VAL_CHARSEQUENCE);
|
return VAL_CHARSEQUENCE;
|
||||||
writeCharSequence((CharSequence) v);
|
|
||||||
} else if (v instanceof List) {
|
} else if (v instanceof List) {
|
||||||
writeInt(VAL_LIST);
|
return VAL_LIST;
|
||||||
writeList((List) v);
|
|
||||||
} else if (v instanceof SparseArray) {
|
} else if (v instanceof SparseArray) {
|
||||||
writeInt(VAL_SPARSEARRAY);
|
return VAL_SPARSEARRAY;
|
||||||
writeSparseArray((SparseArray) v);
|
|
||||||
} else if (v instanceof boolean[]) {
|
} else if (v instanceof boolean[]) {
|
||||||
writeInt(VAL_BOOLEANARRAY);
|
return VAL_BOOLEANARRAY;
|
||||||
writeBooleanArray((boolean[]) v);
|
|
||||||
} else if (v instanceof byte[]) {
|
} else if (v instanceof byte[]) {
|
||||||
writeInt(VAL_BYTEARRAY);
|
return VAL_BYTEARRAY;
|
||||||
writeByteArray((byte[]) v);
|
|
||||||
} else if (v instanceof String[]) {
|
} else if (v instanceof String[]) {
|
||||||
writeInt(VAL_STRINGARRAY);
|
return VAL_STRINGARRAY;
|
||||||
writeStringArray((String[]) v);
|
|
||||||
} else if (v instanceof CharSequence[]) {
|
} else if (v instanceof CharSequence[]) {
|
||||||
// Must be after String[] and before Object[]
|
// Must be after String[] and before Object[]
|
||||||
writeInt(VAL_CHARSEQUENCEARRAY);
|
return VAL_CHARSEQUENCEARRAY;
|
||||||
writeCharSequenceArray((CharSequence[]) v);
|
|
||||||
} else if (v instanceof IBinder) {
|
} else if (v instanceof IBinder) {
|
||||||
writeInt(VAL_IBINDER);
|
return VAL_IBINDER;
|
||||||
writeStrongBinder((IBinder) v);
|
|
||||||
} else if (v instanceof Parcelable[]) {
|
} else if (v instanceof Parcelable[]) {
|
||||||
writeInt(VAL_PARCELABLEARRAY);
|
return VAL_PARCELABLEARRAY;
|
||||||
writeParcelableArray((Parcelable[]) v, 0);
|
|
||||||
} else if (v instanceof int[]) {
|
} else if (v instanceof int[]) {
|
||||||
writeInt(VAL_INTARRAY);
|
return VAL_INTARRAY;
|
||||||
writeIntArray((int[]) v);
|
|
||||||
} else if (v instanceof long[]) {
|
} else if (v instanceof long[]) {
|
||||||
writeInt(VAL_LONGARRAY);
|
return VAL_LONGARRAY;
|
||||||
writeLongArray((long[]) v);
|
|
||||||
} else if (v instanceof Byte) {
|
} else if (v instanceof Byte) {
|
||||||
writeInt(VAL_BYTE);
|
return VAL_BYTE;
|
||||||
writeInt((Byte) v);
|
|
||||||
} else if (v instanceof Size) {
|
} else if (v instanceof Size) {
|
||||||
writeInt(VAL_SIZE);
|
return VAL_SIZE;
|
||||||
writeSize((Size) v);
|
|
||||||
} else if (v instanceof SizeF) {
|
|
||||||
writeInt(VAL_SIZEF);
|
|
||||||
writeSizeF((SizeF) v);
|
|
||||||
} else if (v instanceof double[]) {
|
} else if (v instanceof double[]) {
|
||||||
writeInt(VAL_DOUBLEARRAY);
|
return VAL_DOUBLEARRAY;
|
||||||
writeDoubleArray((double[]) v);
|
|
||||||
} else {
|
} else {
|
||||||
Class<?> clazz = v.getClass();
|
Class<?> clazz = v.getClass();
|
||||||
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
|
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
|
||||||
// Only pure Object[] are written here, Other arrays of non-primitive types are
|
// Only pure Object[] are written here, Other arrays of non-primitive types are
|
||||||
// handled by serialization as this does not record the component type.
|
// handled by serialization as this does not record the component type.
|
||||||
writeInt(VAL_OBJECTARRAY);
|
return VAL_OBJECTARRAY;
|
||||||
writeArray((Object[]) v);
|
|
||||||
} else if (v instanceof Serializable) {
|
} else if (v instanceof Serializable) {
|
||||||
// Must be last
|
// Must be last
|
||||||
writeInt(VAL_SERIALIZABLE);
|
return VAL_SERIALIZABLE;
|
||||||
writeSerializable((Serializable) v);
|
|
||||||
} else {
|
} 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
|
* Flatten the name of the class of the Parcelable and its contents
|
||||||
@@ -3208,7 +3368,180 @@ public final class Parcel {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public final Object readValue(@Nullable ClassLoader loader) {
|
public final Object readValue(@Nullable ClassLoader loader) {
|
||||||
int type = readInt();
|
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) {
|
switch (type) {
|
||||||
case VAL_NULL:
|
case VAL_NULL:
|
||||||
return null;
|
return null;
|
||||||
@@ -3307,6 +3640,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
|
* 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
|
* will be used to load any enclosed Parcelables. If it is null, the default
|
||||||
@@ -3609,49 +3956,49 @@ public final class Parcel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
|
/* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
|
||||||
@Nullable ClassLoader loader) {
|
int size, @Nullable ClassLoader loader) {
|
||||||
if (DEBUG_ARRAY_MAP) {
|
readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
|
||||||
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 readArrayMapSafelyInternal(@NonNull ArrayMap outVal, int N,
|
/**
|
||||||
@Nullable ClassLoader loader) {
|
* Reads a map into {@code map}.
|
||||||
if (DEBUG_ARRAY_MAP) {
|
*
|
||||||
RuntimeException here = new RuntimeException("here");
|
* @param sorted Whether the keys are sorted by their hashes, if so we use an optimized path.
|
||||||
here.fillInStackTrace();
|
* @param lazy Whether to populate the map with lazy {@link Supplier} objects for
|
||||||
Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here);
|
* length-prefixed values. See {@link Parcel#readLazyValue(ClassLoader)} for more
|
||||||
}
|
* details.
|
||||||
while (N > 0) {
|
* @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();
|
String key = readString();
|
||||||
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x"
|
Object value = (lazy) ? readLazyValue(loader) : readValue(loader);
|
||||||
+ (key != null ? key.hashCode() : 0) + " " + key);
|
if (value instanceof LazyValue) {
|
||||||
Object value = readValue(loader);
|
recycle = false;
|
||||||
outVal.put(key, value);
|
}
|
||||||
N--;
|
if (sorted) {
|
||||||
|
map.append(key, value);
|
||||||
|
} else {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
size--;
|
||||||
}
|
}
|
||||||
|
if (sorted) {
|
||||||
|
map.validate();
|
||||||
|
}
|
||||||
|
return recycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide For testing only.
|
* @hide For testing only.
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
@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();
|
final int N = readInt();
|
||||||
if (N < 0) {
|
if (N < 0) {
|
||||||
return;
|
return;
|
||||||
@@ -3736,4 +4083,38 @@ public final class Parcel {
|
|||||||
public long getBlobAshmemSize() {
|
public long getBlobAshmemSize() {
|
||||||
return nativeGetBlobAshmemSize(mNativePtr);
|
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