diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/os/Parcel.java | 297 |
1 files changed, 215 insertions, 82 deletions
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 47c7f2e40e00..7c6697da6334 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2807,7 +2807,20 @@ public final class Parcel { */ public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) { 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); } /** @@ -3006,7 +3019,7 @@ public final class Parcel { return null; } ArrayList l = new ArrayList(N); - readListInternal(l, N, loader); + readListInternal(l, N, loader, /* clazz */ null); return l; } @@ -3406,12 +3419,21 @@ public final class Parcel { */ @Nullable 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(); - final Object object; + final T object; if (isLengthPrefixed(type)) { int length = readInt(); int start = dataPosition(); - object = readValue(type, loader); + object = readValue(type, loader, clazz); int actual = dataPosition() - start; if (actual != length) { Slog.wtfStack(TAG, @@ -3419,7 +3441,7 @@ public final class Parcel { + " consumed " + actual + " bytes, but " + length + " expected."); } } else { - object = readValue(type, loader); + object = readValue(type, loader, clazz); } return object; } @@ -3456,7 +3478,7 @@ public final class Parcel { setDataPosition(MathUtils.addOrThrow(dataPosition(), length)); return new LazyValue(this, start, length, type, loader); } else { - return readValue(type, loader); + return readValue(type, loader, /* clazz */ null); } } @@ -3588,105 +3610,145 @@ public final class Parcel { /** * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the * type first. + * @param clazz The type of the object expected or {@code null} for performing no checks. */ + @SuppressWarnings("unchecked") @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) { - case VAL_NULL: - return null; + case VAL_NULL: + object = null; + break; - case VAL_STRING: - return readString(); + case VAL_STRING: + object = readString(); + break; - case VAL_INTEGER: - return readInt(); + case VAL_INTEGER: + object = readInt(); + break; - case VAL_MAP: - return readHashMap(loader); + case VAL_MAP: + object = readHashMap(loader); + break; - case VAL_PARCELABLE: - return readParcelable(loader); + case VAL_PARCELABLE: + object = readParcelableInternal(loader, clazz); + break; - case VAL_SHORT: - return (short) readInt(); + case VAL_SHORT: + object = (short) readInt(); + break; - case VAL_LONG: - return readLong(); + case VAL_LONG: + object = readLong(); + break; - case VAL_FLOAT: - return readFloat(); + case VAL_FLOAT: + object = readFloat(); + break; - case VAL_DOUBLE: - return readDouble(); + case VAL_DOUBLE: + object = readDouble(); + break; - case VAL_BOOLEAN: - return readInt() == 1; + case VAL_BOOLEAN: + object = readInt() == 1; + break; - case VAL_CHARSEQUENCE: - return readCharSequence(); + case VAL_CHARSEQUENCE: + object = readCharSequence(); + break; - case VAL_LIST: - return readArrayList(loader); + case VAL_LIST: + object = readArrayList(loader); + break; - case VAL_BOOLEANARRAY: - return createBooleanArray(); + case VAL_BOOLEANARRAY: + object = createBooleanArray(); + break; - case VAL_BYTEARRAY: - return createByteArray(); + case VAL_BYTEARRAY: + object = createByteArray(); + break; - case VAL_STRINGARRAY: - return readStringArray(); + case VAL_STRINGARRAY: + object = readStringArray(); + break; - case VAL_CHARSEQUENCEARRAY: - return readCharSequenceArray(); + case VAL_CHARSEQUENCEARRAY: + object = readCharSequenceArray(); + break; - case VAL_IBINDER: - return readStrongBinder(); + case VAL_IBINDER: + object = readStrongBinder(); + break; - case VAL_OBJECTARRAY: - return readArray(loader); + case VAL_OBJECTARRAY: + object = readArray(loader); + break; - case VAL_INTARRAY: - return createIntArray(); + case VAL_INTARRAY: + object = createIntArray(); + break; - case VAL_LONGARRAY: - return createLongArray(); + case VAL_LONGARRAY: + object = createLongArray(); + break; - case VAL_BYTE: - return readByte(); + case VAL_BYTE: + object = readByte(); + break; - case VAL_SERIALIZABLE: - return readSerializable(loader); + case VAL_SERIALIZABLE: + object = readSerializable(loader); + break; - case VAL_PARCELABLEARRAY: - return readParcelableArray(loader); + case VAL_PARCELABLEARRAY: + object = readParcelableArray(loader); + break; - case VAL_SPARSEARRAY: - return readSparseArray(loader); + case VAL_SPARSEARRAY: + object = readSparseArray(loader); + break; - case VAL_SPARSEBOOLEANARRAY: - return readSparseBooleanArray(); + case VAL_SPARSEBOOLEANARRAY: + object = readSparseBooleanArray(); + break; - case VAL_BUNDLE: - return readBundle(loader); // loading will be deferred + case VAL_BUNDLE: + object = readBundle(loader); // loading will be deferred + break; - case VAL_PERSISTABLEBUNDLE: - return readPersistableBundle(loader); + case VAL_PERSISTABLEBUNDLE: + object = readPersistableBundle(loader); + break; - case VAL_SIZE: - return readSize(); + case VAL_SIZE: + object = readSize(); + break; - case VAL_SIZEF: - return readSizeF(); + case VAL_SIZEF: + object = readSizeF(); + break; - case VAL_DOUBLEARRAY: - return createDoubleArray(); + case VAL_DOUBLEARRAY: + object = createDoubleArray(); + break; - default: - int off = dataPosition() - 4; - throw new RuntimeException( - "Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off); + default: + int off = dataPosition() - 4; + throw new RuntimeException( + "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) { @@ -3714,17 +3776,42 @@ public final class Parcel { * @throws BadParcelableException Throws BadParcelableException if there * was an error trying to instantiate the Parcelable. */ - @SuppressWarnings("unchecked") @Nullable 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) { return null; } if (creator instanceof Parcelable.ClassLoaderCreator<?>) { - Parcelable.ClassLoaderCreator<?> classLoaderCreator = - (Parcelable.ClassLoaderCreator<?>) creator; - return (T) classLoaderCreator.createFromParcel(this, loader); + Parcelable.ClassLoaderCreator<?> classLoaderCreator = + (Parcelable.ClassLoaderCreator<?>) creator; + return (T) classLoaderCreator.createFromParcel(this, loader); } return (T) creator.createFromParcel(this); } @@ -3758,6 +3845,28 @@ public final class Parcel { */ @Nullable 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(); if (name == null) { return null; @@ -3773,7 +3882,15 @@ public final class Parcel { creator = map.get(name); } 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 { @@ -3789,6 +3906,14 @@ public final class Parcel { throw new BadParcelableException("Parcelable protocol requires subclassing " + "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"); if ((f.getModifiers() & Modifier.STATIC) == 0) { throw new BadParcelableException("Parcelable protocol requires " @@ -3826,7 +3951,7 @@ public final class Parcel { map.put(name, creator); } - return creator; + return (Parcelable.Creator<T>) creator; } /** @@ -4076,13 +4201,21 @@ public final class Parcel { return result; } - private void readListInternal(@NonNull List outVal, int N, + private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) { - while (N > 0) { - Object value = readValue(loader); + readListInternal(outVal, n, loader, null); + } + + /** + * @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); outVal.add(value); - N--; + n--; } } |
