Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94e177c7 authored by Jooyung Han's avatar Jooyung Han
Browse files

Add Parcel APIs for fixed-size array

* createFixedArray()
* readFixedArray()
* writeFixedArray()

Bug: 207087196
Test: android.os.cts.ParcelTest
Change-Id: Ie1b742dccba26b9c473d46f8d6b9edfda3ee5eeb
parent 63c3c1f3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -31584,6 +31584,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>);
@@ -31624,6 +31627,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);
@@ -31682,6 +31688,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);
+407 −0
Original line number Diff line number Diff line
@@ -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