Merge "Adding typed Parcel read/write APIs."

This commit is contained in:
Hao Ke
2021-09-30 21:13:54 +00:00
committed by Gerrit Code Review
2 changed files with 218 additions and 82 deletions

View File

@@ -29998,12 +29998,15 @@ package android.os {
method public int readInt(); method public int readInt();
method public void readIntArray(@NonNull int[]); method public void readIntArray(@NonNull int[]);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader); method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong(); method public long readLong();
method public void readLongArray(@NonNull long[]); method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader); method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader); method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);

View File

@@ -2766,7 +2766,20 @@ public final class Parcel {
*/ */
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) { public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
int N = readInt(); int N = readInt();
readListInternal(outVal, N, loader); readListInternal(outVal, N, loader, /* clazz */ null);
}
/**
* Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
* the type required for each item. If the item to be deserialized is not an instance
* of that class or any of its children class
* a {@link BadParcelableException} will be thrown.
*/
public <T> void readList(@NonNull List<? super T> outVal,
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
Objects.requireNonNull(clazz);
int n = readInt();
readListInternal(outVal, n, loader, clazz);
} }
/** /**
@@ -2965,7 +2978,7 @@ public final class Parcel {
return null; return null;
} }
ArrayList l = new ArrayList(N); ArrayList l = new ArrayList(N);
readListInternal(l, N, loader); readListInternal(l, N, loader, /* clazz */ null);
return l; return l;
} }
@@ -3365,12 +3378,21 @@ public final class Parcel {
*/ */
@Nullable @Nullable
public final Object readValue(@Nullable ClassLoader loader) { public final Object readValue(@Nullable ClassLoader loader) {
return readValue(loader, /* clazz */ null);
}
/**
* @param clazz The type of the object expected or {@code null} for performing no checks.
*/
@Nullable
private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
int type = readInt(); int type = readInt();
final Object object; final T object;
if (isLengthPrefixed(type)) { if (isLengthPrefixed(type)) {
int length = readInt(); int length = readInt();
int start = dataPosition(); int start = dataPosition();
object = readValue(type, loader); object = readValue(type, loader, clazz);
int actual = dataPosition() - start; int actual = dataPosition() - start;
if (actual != length) { if (actual != length) {
Slog.wtfStack(TAG, Slog.wtfStack(TAG,
@@ -3378,7 +3400,7 @@ public final class Parcel {
+ " consumed " + actual + " bytes, but " + length + " expected."); + " consumed " + actual + " bytes, but " + length + " expected.");
} }
} else { } else {
object = readValue(type, loader); object = readValue(type, loader, clazz);
} }
return object; return object;
} }
@@ -3415,7 +3437,7 @@ public final class Parcel {
setDataPosition(MathUtils.addOrThrow(dataPosition(), length)); setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
return new LazyValue(this, start, length, type, loader); return new LazyValue(this, start, length, type, loader);
} else { } else {
return readValue(type, loader); return readValue(type, loader, /* clazz */ null);
} }
} }
@@ -3547,105 +3569,145 @@ public final class Parcel {
/** /**
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
* type first. * type first.
* @param clazz The type of the object expected or {@code null} for performing no checks.
*/ */
@SuppressWarnings("unchecked")
@Nullable @Nullable
private Object readValue(int type, @Nullable ClassLoader loader) { private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
final Object object;
switch (type) { switch (type) {
case VAL_NULL: case VAL_NULL:
return null; object = null;
break;
case VAL_STRING: case VAL_STRING:
return readString(); object = readString();
break;
case VAL_INTEGER: case VAL_INTEGER:
return readInt(); object = readInt();
break;
case VAL_MAP: case VAL_MAP:
return readHashMap(loader); object = readHashMap(loader);
break;
case VAL_PARCELABLE: case VAL_PARCELABLE:
return readParcelable(loader); object = readParcelableInternal(loader, clazz);
break;
case VAL_SHORT: case VAL_SHORT:
return (short) readInt(); object = (short) readInt();
break;
case VAL_LONG: case VAL_LONG:
return readLong(); object = readLong();
break;
case VAL_FLOAT: case VAL_FLOAT:
return readFloat(); object = readFloat();
break;
case VAL_DOUBLE: case VAL_DOUBLE:
return readDouble(); object = readDouble();
break;
case VAL_BOOLEAN: case VAL_BOOLEAN:
return readInt() == 1; object = readInt() == 1;
break;
case VAL_CHARSEQUENCE: case VAL_CHARSEQUENCE:
return readCharSequence(); object = readCharSequence();
break;
case VAL_LIST: case VAL_LIST:
return readArrayList(loader); object = readArrayList(loader);
break;
case VAL_BOOLEANARRAY: case VAL_BOOLEANARRAY:
return createBooleanArray(); object = createBooleanArray();
break;
case VAL_BYTEARRAY: case VAL_BYTEARRAY:
return createByteArray(); object = createByteArray();
break;
case VAL_STRINGARRAY: case VAL_STRINGARRAY:
return readStringArray(); object = readStringArray();
break;
case VAL_CHARSEQUENCEARRAY: case VAL_CHARSEQUENCEARRAY:
return readCharSequenceArray(); object = readCharSequenceArray();
break;
case VAL_IBINDER: case VAL_IBINDER:
return readStrongBinder(); object = readStrongBinder();
break;
case VAL_OBJECTARRAY: case VAL_OBJECTARRAY:
return readArray(loader); object = readArray(loader);
break;
case VAL_INTARRAY: case VAL_INTARRAY:
return createIntArray(); object = createIntArray();
break;
case VAL_LONGARRAY: case VAL_LONGARRAY:
return createLongArray(); object = createLongArray();
break;
case VAL_BYTE: case VAL_BYTE:
return readByte(); object = readByte();
break;
case VAL_SERIALIZABLE: case VAL_SERIALIZABLE:
return readSerializable(loader); object = readSerializable(loader);
break;
case VAL_PARCELABLEARRAY: case VAL_PARCELABLEARRAY:
return readParcelableArray(loader); object = readParcelableArray(loader);
break;
case VAL_SPARSEARRAY: case VAL_SPARSEARRAY:
return readSparseArray(loader); object = readSparseArray(loader);
break;
case VAL_SPARSEBOOLEANARRAY: case VAL_SPARSEBOOLEANARRAY:
return readSparseBooleanArray(); object = readSparseBooleanArray();
break;
case VAL_BUNDLE: case VAL_BUNDLE:
return readBundle(loader); // loading will be deferred object = readBundle(loader); // loading will be deferred
break;
case VAL_PERSISTABLEBUNDLE: case VAL_PERSISTABLEBUNDLE:
return readPersistableBundle(loader); object = readPersistableBundle(loader);
break;
case VAL_SIZE: case VAL_SIZE:
return readSize(); object = readSize();
break;
case VAL_SIZEF: case VAL_SIZEF:
return readSizeF(); object = readSizeF();
break;
case VAL_DOUBLEARRAY: case VAL_DOUBLEARRAY:
return createDoubleArray(); object = createDoubleArray();
break;
default: default:
int off = dataPosition() - 4; int off = dataPosition() - 4;
throw new RuntimeException( throw new RuntimeException(
"Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off); "Parcel " + this + ": Unmarshalling unknown type code " + type
+ " at offset " + off);
} }
if (clazz != null && !clazz.isInstance(object)) {
throw new BadParcelableException("Unparcelled object " + object
+ " is not an instance of required class " + clazz.getName()
+ " provided in the parameter");
}
return (T) object;
} }
private boolean isLengthPrefixed(int type) { private boolean isLengthPrefixed(int type) {
@@ -3673,10 +3735,35 @@ public final class Parcel {
* @throws BadParcelableException Throws BadParcelableException if there * @throws BadParcelableException Throws BadParcelableException if there
* was an error trying to instantiate the Parcelable. * was an error trying to instantiate the Parcelable.
*/ */
@SuppressWarnings("unchecked")
@Nullable @Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) { public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
Parcelable.Creator<?> creator = readParcelableCreator(loader); return readParcelableInternal(loader, /* clazz */ null);
}
/**
* Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
* required for each item. If the item to be deserialized is not an instance of that class or
* any of its children classes a {@link BadParcelableException} will be thrown.
*/
@Nullable
public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
@NonNull Class<T> clazz) {
Objects.requireNonNull(clazz);
return readParcelableInternal(loader, clazz);
}
/**
*
* @param clazz The type of the parcelable expected or {@code null} for performing no checks.
*/
@SuppressWarnings("unchecked")
@Nullable
private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
throw new BadParcelableException("About to unparcel a parcelable object "
+ " but class required " + clazz.getName() + " is not Parcelable");
}
Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
if (creator == null) { if (creator == null) {
return null; return null;
} }
@@ -3717,6 +3804,28 @@ public final class Parcel {
*/ */
@Nullable @Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) { public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
return readParcelableCreatorInternal(loader, /* clazz */ null);
}
/**
* Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
* as the required type. If the item to be deserialized is not an instance of that class
* or any of its children classes a {@link BadParcelableException} will be thrown.
*/
@Nullable
public <T> Parcelable.Creator<T> readParcelableCreator(
@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
Objects.requireNonNull(clazz);
return readParcelableCreatorInternal(loader, clazz);
}
/**
* @param clazz The type of the parcelable expected or {@code null} for performing no checks.
*/
@SuppressWarnings("unchecked")
@Nullable
private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
String name = readString(); String name = readString();
if (name == null) { if (name == null) {
return null; return null;
@@ -3732,7 +3841,15 @@ public final class Parcel {
creator = map.get(name); creator = map.get(name);
} }
if (creator != null) { if (creator != null) {
return creator; if (clazz != null) {
Class<?> parcelableClass = creator.getClass().getEnclosingClass();
if (!clazz.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
}
return (Parcelable.Creator<T>) creator;
} }
try { try {
@@ -3748,6 +3865,14 @@ public final class Parcel {
throw new BadParcelableException("Parcelable protocol requires subclassing " throw new BadParcelableException("Parcelable protocol requires subclassing "
+ "from Parcelable on class " + name); + "from Parcelable on class " + name);
} }
if (clazz != null) {
if (!clazz.isAssignableFrom(parcelableClass)) {
throw new BadParcelableException("Parcelable creator " + name + " is not "
+ "a subclass of required class " + clazz.getName()
+ " provided in the parameter");
}
}
Field f = parcelableClass.getField("CREATOR"); Field f = parcelableClass.getField("CREATOR");
if ((f.getModifiers() & Modifier.STATIC) == 0) { if ((f.getModifiers() & Modifier.STATIC) == 0) {
throw new BadParcelableException("Parcelable protocol requires " throw new BadParcelableException("Parcelable protocol requires "
@@ -3785,7 +3910,7 @@ public final class Parcel {
map.put(name, creator); map.put(name, creator);
} }
return creator; return (Parcelable.Creator<T>) creator;
} }
/** /**
@@ -4031,13 +4156,21 @@ public final class Parcel {
return result; return result;
} }
private void readListInternal(@NonNull List outVal, int N, private void readListInternal(@NonNull List outVal, int n,
@Nullable ClassLoader loader) { @Nullable ClassLoader loader) {
while (N > 0) { readListInternal(outVal, n, loader, null);
Object value = readValue(loader); }
/**
* @param clazz The type of the object expected or {@code null} for performing no checks.
*/
private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
while (n > 0) {
T value = readValue(loader, clazz);
//Log.d(TAG, "Unmarshalling value=" + value); //Log.d(TAG, "Unmarshalling value=" + value);
outVal.add(value); outVal.add(value);
N--; n--;
} }
} }