diff options
| -rw-r--r-- | core/api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/os/Parcel.java | 83 |
2 files changed, 65 insertions, 19 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 19d5b1b7da0d..1c0144546239 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -31473,6 +31473,7 @@ package android.os { method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); method @Nullable public java.io.Serializable readSerializable(); + method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>); method @NonNull public android.util.Size readSize(); method @NonNull public android.util.SizeF readSizeF(); method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 61882ffcee85..dd0cb8cc62ae 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2448,9 +2448,9 @@ public final class Parcel { writeByteArray(baos.toByteArray()); } catch (IOException ioe) { - throw new RuntimeException("Parcelable encountered " + - "IOException writing serializable object (name = " + name + - ")", ioe); + throw new BadParcelableException("Parcelable encountered " + + "IOException writing serializable object (name = " + + name + ")", ioe); } } @@ -3818,7 +3818,7 @@ public final class Parcel { break; case VAL_SERIALIZABLE: - object = readSerializable(loader); + object = readSerializableInternal(loader, clazz); break; case VAL_PARCELABLEARRAY: @@ -4144,12 +4144,37 @@ public final class Parcel { * wasn't found in the parcel. */ @Nullable - public final Serializable readSerializable() { - return readSerializable(null); + public Serializable readSerializable() { + return readSerializableInternal(/* loader */ null, /* clazz */ null); } + /** + * Same as {@link #readSerializable()} but accepts {@code loader} parameter + * as the primary classLoader for resolving the Serializable class; and {@code clazz} parameter + * as the required type. + * + * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized + * is not an instance of that class or any of its children class or there there was an error + * deserializing the object. + */ @Nullable - private final Serializable readSerializable(@Nullable final ClassLoader loader) { + public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader, + @NonNull Class<T> clazz) { + Objects.requireNonNull(clazz); + return readSerializableInternal(loader, clazz); + } + + /** + * @param clazz The type of the serializable expected or {@code null} for performing no checks + */ + @Nullable + private <T> T readSerializableInternal(@Nullable final ClassLoader loader, + @Nullable Class<T> clazz) { + if (clazz != null && !Serializable.class.isAssignableFrom(clazz)) { + throw new BadParcelableException("About to unparcel a serializable object " + + " but class required " + clazz.getName() + " is not Serializable"); + } + String name = readString(); if (name == null) { // For some reason we were unable to read the name of the Serializable (either there @@ -4158,9 +4183,20 @@ public final class Parcel { return null; } - byte[] serializedData = createByteArray(); - ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); try { + if (clazz != null && loader != null) { + // If custom classloader is provided, resolve the type of serializable using the + // name, then check the type before deserialization. As in this case we can resolve + // the class the same way as ObjectInputStream, using the provided classloader. + Class<?> cl = Class.forName(name, false, loader); + if (!clazz.isAssignableFrom(cl)) { + throw new BadParcelableException("Serializable object " + + cl.getName() + " is not a subclass of required class " + + clazz.getName() + " provided in the parameter"); + } + } + byte[] serializedData = createByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); ObjectInputStream ois = new ObjectInputStream(bais) { @Override protected Class<?> resolveClass(ObjectStreamClass osClass) @@ -4168,22 +4204,31 @@ public final class Parcel { // try the custom classloader if provided if (loader != null) { Class<?> c = Class.forName(osClass.getName(), false, loader); - if (c != null) { - return c; - } + return Objects.requireNonNull(c); } return super.resolveClass(osClass); } }; - return (Serializable) ois.readObject(); + T object = (T) ois.readObject(); + if (clazz != null && loader == null) { + // If custom classloader is not provided, check the type of the serializable using + // the deserialized object, as we cannot resolve the class the same way as + // ObjectInputStream. + if (!clazz.isAssignableFrom(object.getClass())) { + throw new BadParcelableException("Serializable object " + + object.getClass().getName() + " is not a subclass of required class " + + clazz.getName() + " provided in the parameter"); + } + } + return object; } catch (IOException ioe) { - throw new RuntimeException("Parcelable encountered " + - "IOException reading a Serializable object (name = " + name + - ")", ioe); + throw new BadParcelableException("Parcelable encountered " + + "IOException reading a Serializable object (name = " + + name + ")", ioe); } catch (ClassNotFoundException cnfe) { - throw new RuntimeException("Parcelable encountered " + - "ClassNotFoundException reading a Serializable object (name = " - + name + ")", cnfe); + throw new BadParcelableException("Parcelable encountered " + + "ClassNotFoundException reading a Serializable object (name = " + + name + ")", cnfe); } } |