Merge "Add Parcel APIs for fixed-size array" am: aa16747474

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1958141

Change-Id: I7b7824d2cd0f500945f6123395f355988e9698c9
This commit is contained in:
Jooyung Han
2022-02-02 00:29:04 +00:00
committed by Automerger Merge Worker
2 changed files with 414 additions and 0 deletions

View File

@@ -30269,6 +30269,9 @@ package android.os {
method @Nullable public byte[] createByteArray();
method @Nullable public char[] createCharArray();
method @Nullable public double[] createDoubleArray();
method @Nullable public <T> T createFixedArray(@NonNull Class<T>, @NonNull int...);
method @Nullable public <T, S extends android.os.IInterface> T createFixedArray(@NonNull Class<T>, @NonNull java.util.function.Function<android.os.IBinder,S>, @NonNull int...);
method @Nullable public <T, S extends android.os.Parcelable> T createFixedArray(@NonNull Class<T>, @NonNull android.os.Parcelable.Creator<S>, @NonNull int...);
method @Nullable public float[] createFloatArray();
method @Nullable public int[] createIntArray();
method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -30309,6 +30312,9 @@ package android.os {
method public void readException();
method public void readException(int, String);
method public android.os.ParcelFileDescriptor readFileDescriptor();
method public <T> void readFixedArray(@NonNull T);
method public <T, S extends android.os.IInterface> void readFixedArray(@NonNull T, @NonNull java.util.function.Function<android.os.IBinder,S>);
method public <T, S extends android.os.Parcelable> void readFixedArray(@NonNull T, @NonNull android.os.Parcelable.Creator<S>);
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
@@ -30367,6 +30373,7 @@ package android.os {
method public void writeDoubleArray(@Nullable double[]);
method public void writeException(@NonNull Exception);
method public void writeFileDescriptor(@NonNull java.io.FileDescriptor);
method public <T> void writeFixedArray(@Nullable T, int, @NonNull int...);
method public void writeFloat(float);
method public void writeFloatArray(@Nullable float[]);
method public void writeInt(int);

View File

@@ -2078,6 +2078,102 @@ public final class Parcel {
}
}
/**
* Flatten a homogeneous multi-dimensional array with fixed-size. This delegates to other
* APIs to write a one-dimensional array. Use {@link #readFixedArray(Object)} or
* {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal.
*
* @param val The array to be written.
* @param parcelableFlags Contextual flags as per
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
* Used only if val is an array of Parcelable objects.
* @param dimensions an array of int representing length of each dimension. The array should be
* sized with the exact size of dimensions.
*
* @see #readFixedArray
* @see #createFixedArray
* @see #writeBooleanArray
* @see #writeByteArray
* @see #writeCharArray
* @see #writeIntArray
* @see #writeLongArray
* @see #writeFloatArray
* @see #writeDoubleArray
* @see #writeBinderArray
* @see #writeInterfaceArray
* @see #writeTypedArray
* @throws BadParcelableException If the array's component type is not supported or if its
* size doesn't match with the given dimensions.
*/
public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
@NonNull int... dimensions) {
if (val == null) {
writeInt(-1);
return;
}
writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions);
}
private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index,
int[] dimensions) {
if (index >= dimensions.length) {
throw new BadParcelableException("Array has more dimensions than expected: "
+ dimensions.length);
}
int length = dimensions[index];
// val should be an array of length N
if (val == null) {
throw new BadParcelableException("Non-null array shouldn't have a null array.");
}
if (!val.getClass().isArray()) {
throw new BadParcelableException("Not an array: " + val);
}
if (Array.getLength(val) != length) {
throw new BadParcelableException("bad length: expected " + length + ", but got "
+ Array.getLength(val));
}
// Delegates to other writers if this is a one-dimensional array.
// Otherwise, write component arrays with recursive calls.
final Class<?> componentType = val.getClass().getComponentType();
if (!componentType.isArray() && index + 1 != dimensions.length) {
throw new BadParcelableException("Array has fewer dimensions than expected: "
+ dimensions.length);
}
if (componentType == boolean.class) {
writeBooleanArray((boolean[]) val);
} else if (componentType == byte.class) {
writeByteArray((byte[]) val);
} else if (componentType == char.class) {
writeCharArray((char[]) val);
} else if (componentType == int.class) {
writeIntArray((int[]) val);
} else if (componentType == long.class) {
writeLongArray((long[]) val);
} else if (componentType == float.class) {
writeFloatArray((float[]) val);
} else if (componentType == double.class) {
writeDoubleArray((double[]) val);
} else if (componentType == IBinder.class) {
writeBinderArray((IBinder[]) val);
} else if (IInterface.class.isAssignableFrom(componentType)) {
writeInterfaceArray((IInterface[]) val);
} else if (Parcelable.class.isAssignableFrom(componentType)) {
writeTypedArray((Parcelable[]) val, parcelableFlags);
} else if (componentType.isArray()) {
writeInt(length);
for (int i = 0; i < length; i++) {
writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1,
dimensions);
}
} else {
throw new BadParcelableException("unknown type for fixed-size array: " + componentType);
}
}
/**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
@@ -3780,6 +3876,317 @@ public final class Parcel {
}
}
/**
* Read a new multi-dimensional array from a parcel. If you want to read Parcelable or
* IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or
* {@link #readFixedArray(Object, Function)}.
* @param val the destination array to hold the read values.
*
* @see #writeTypedArray
* @see #readBooleanArray
* @see #readByteArray
* @see #readCharArray
* @see #readIntArray
* @see #readLongArray
* @see #readFloatArray
* @see #readDoubleArray
* @see #readBinderArray
* @see #readInterfaceArray
* @see #readTypedArray
*/
public <T> void readFixedArray(@NonNull T val) {
Class<?> componentType = val.getClass().getComponentType();
if (componentType == boolean.class) {
readBooleanArray((boolean[]) val);
} else if (componentType == byte.class) {
readByteArray((byte[]) val);
} else if (componentType == char.class) {
readCharArray((char[]) val);
} else if (componentType == int.class) {
readIntArray((int[]) val);
} else if (componentType == long.class) {
readLongArray((long[]) val);
} else if (componentType == float.class) {
readFloatArray((float[]) val);
} else if (componentType == double.class) {
readDoubleArray((double[]) val);
} else if (componentType == IBinder.class) {
readBinderArray((IBinder[]) val);
} else if (componentType.isArray()) {
int length = readInt();
if (length != Array.getLength(val)) {
throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ ", but got " + length);
}
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i));
}
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
}
/**
* Read a new multi-dimensional array of typed interfaces from a parcel.
* If you want to read Parcelable values, use
* {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use
* {@link #readFixedArray(Object)}.
* @param val the destination array to hold the read values.
*/
public <T, S extends IInterface> void readFixedArray(@NonNull T val,
@NonNull Function<IBinder, S> asInterface) {
Class<?> componentType = val.getClass().getComponentType();
if (IInterface.class.isAssignableFrom(componentType)) {
readInterfaceArray((S[]) val, asInterface);
} else if (componentType.isArray()) {
int length = readInt();
if (length != Array.getLength(val)) {
throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ ", but got " + length);
}
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), asInterface);
}
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
}
/**
* Read a new multi-dimensional array of typed parcelables from a parcel.
* If you want to read IInterface values, use
* {@link #readFixedArray(Object, Function)}. For values of other types, use
* {@link #readFixedArray(Object)}.
* @param val the destination array to hold the read values.
*/
public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
@NonNull Parcelable.Creator<S> c) {
Class<?> componentType = val.getClass().getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
readTypedArray((S[]) val, c);
} else if (componentType.isArray()) {
int length = readInt();
if (length != Array.getLength(val)) {
throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ ", but got " + length);
}
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), c);
}
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
}
private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) {
if (numDimension <= 0) {
throw new BadParcelableException("Fixed-size array should have dimensions.");
}
for (int i = 0; i < numDimension; i++) {
if (!cls.isArray()) {
throw new BadParcelableException("Array has fewer dimensions than expected: "
+ numDimension);
}
cls = cls.getComponentType();
}
if (cls.isArray()) {
throw new BadParcelableException("Array has more dimensions than expected: "
+ numDimension);
}
}
/**
* Read and return a new multi-dimensional array from a parcel. Returns null if the
* previously written array object is null. If you want to read Parcelable or
* IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or
* {@link #createFixedArray(Class, Function, int[])}.
* @param cls the Class object for the target array type. (e.g. int[][].class)
* @param dimensions an array of int representing length of each dimension.
*
* @see #writeTypedArray
* @see #createBooleanArray
* @see #createByteArray
* @see #createCharArray
* @see #createIntArray
* @see #createLongArray
* @see #createFloatArray
* @see #createDoubleArray
* @see #createBinderArray
* @see #createInterfaceArray
* @see #createTypedArray
*/
@Nullable
public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
ensureClassHasExpectedDimensions(cls, dimensions.length);
T val = null;
final Class<?> componentType = cls.getComponentType();
if (componentType == boolean.class) {
val = (T) createBooleanArray();
} else if (componentType == byte.class) {
val = (T) createByteArray();
} else if (componentType == char.class) {
val = (T) createCharArray();
} else if (componentType == int.class) {
val = (T) createIntArray();
} else if (componentType == long.class) {
val = (T) createLongArray();
} else if (componentType == float.class) {
val = (T) createFloatArray();
} else if (componentType == double.class) {
val = (T) createDoubleArray();
} else if (componentType == IBinder.class) {
val = (T) createBinderArray();
} else if (componentType.isArray()) {
int length = readInt();
if (length < 0) {
return null;
}
if (length != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ ", but got " + length);
}
// Create a multi-dimensional array with an innermost component type and dimensions
Class<?> innermost = componentType.getComponentType();
while (innermost.isArray()) {
innermost = innermost.getComponentType();
}
val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i));
}
return val;
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
// Check if val is null (which is OK) or has the expected size.
// This check doesn't have to be multi-dimensional because multi-dimensional arrays
// are created with expected dimensions.
if (val != null && Array.getLength(val) != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ Array.getLength(val));
}
return val;
}
/**
* Read and return a new multi-dimensional array of typed interfaces from a parcel.
* Returns null if the previously written array object is null. If you want to read
* Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}.
* For values of other types use {@link #createFixedArray(Class, int[])}.
* @param cls the Class object for the target array type. (e.g. IFoo[][].class)
* @param dimensions an array of int representing length of each dimension.
*/
@Nullable
public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
@NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
ensureClassHasExpectedDimensions(cls, dimensions.length);
T val = null;
final Class<?> componentType = cls.getComponentType();
if (IInterface.class.isAssignableFrom(componentType)) {
val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n),
asInterface);
} else if (componentType.isArray()) {
int length = readInt();
if (length < 0) {
return null;
}
if (length != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ ", but got " + length);
}
// Create a multi-dimensional array with an innermost component type and dimensions
Class<?> innermost = componentType.getComponentType();
while (innermost.isArray()) {
innermost = innermost.getComponentType();
}
val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), asInterface);
}
return val;
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
// Check if val is null (which is OK) or has the expected size.
// This check doesn't have to be multi-dimensional because multi-dimensional arrays
// are created with expected dimensions.
if (val != null && Array.getLength(val) != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ Array.getLength(val));
}
return val;
}
/**
* Read and return a new multi-dimensional array of typed parcelables from a parcel.
* Returns null if the previously written array object is null. If you want to read
* IInterface values, use {@link #createFixedArray(Class, Function, int[])}.
* For values of other types use {@link #createFixedArray(Class, int[])}.
* @param cls the Class object for the target array type. (e.g. Foo[][].class)
* @param dimensions an array of int representing length of each dimension.
*/
@Nullable
public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
@NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
// Check if type matches with dimensions
// If type is one-dimensional array, delegate to other creators
// Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
ensureClassHasExpectedDimensions(cls, dimensions.length);
T val = null;
final Class<?> componentType = cls.getComponentType();
if (Parcelable.class.isAssignableFrom(componentType)) {
val = (T) createTypedArray(c);
} else if (componentType.isArray()) {
int length = readInt();
if (length < 0) {
return null;
}
if (length != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ ", but got " + length);
}
// Create a multi-dimensional array with an innermost component type and dimensions
Class<?> innermost = componentType.getComponentType();
while (innermost.isArray()) {
innermost = innermost.getComponentType();
}
val = (T) Array.newInstance(innermost, dimensions);
for (int i = 0; i < length; i++) {
readFixedArray(Array.get(val, i), c);
}
return val;
} else {
throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
}
// Check if val is null (which is OK) or has the expected size.
// This check doesn't have to be multi-dimensional because multi-dimensional arrays
// are created with expected dimensions.
if (val != null && Array.getLength(val) != dimensions[0]) {
throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ Array.getLength(val));
}
return val;
}
/**
* Write a heterogeneous array of Parcelable objects into the Parcel.
* Each object in the array is written along with its class name, so