Add safer Bundle APIs and deprecated old ones
Add safer Bundle APIs that take an extra Class<T> argument that checks that the type about to be deserialized is a child of the type passed in parameter *before* actually deserializing it, while also deprecating old APIs. This allows use to reap the benefits of the new typed Parcel APIs and enhances security. Only the APIs that could involve custom object injection are modified. So, besides the obvious ones that have that design (eg. readParcelableList()), subtler cases such as readIntegerArrayList() could result in custom object deserialization, and since it's all generics, even the casting inside Bundle wouldn't fail, only after the client unpacked the list items would it blow up. Now those are checked beforehand. Since Bundle always calls Parcel.readValue() under the hood (instead of specialized APIs such as readParcelable() etc), we had to augment that method (that's used by LazyValue when retrieving the item) to accept item types now for containers, which I implemented as a vararg of Class<?> parameters (this is all private/@hide). This way we could retrieve a list of intents like readValue(.., List.class, Intent.class), or a map of string to intents like readValue(.., Map.class, String.class, Intent.class). For non-container items, we can just pass no arguments for the vararg. This is explained in internal javadocs. Inside readValue() now, we also check the container types before calling the internal methods for deserialization. So, if the thing on the wire is a VAL_MAP and we know the method we're about to call will return a HashMap, we verify that the type passed in parameter is a super type of that (if it's non-null, if it's null it means "perform no check"). Now, LazyValue became a BiFunction<Class<?>, Class<?>[], Object> to receive those extra "item types" for containers. The reason for separating the first from the rest is that the first defines the return type in the new APIs and inside Parcel, so we need the T from Class<T> to ensure type-safety. (I was torn here between using BiFunction or just exposing LazyValue as @hide for Bundle since it feels like we're missing meaning/abstraction, but end up leaving this way, advise if you'd prefer the other way) There was a bit of a refactor in Parcel so readValue() could call internal methods that accepted nullable Class<?> parameters with the meaning that null = "no verification" and non-null = "check against type provided" (because the external APIs all require non-null parameters). Now we can return null in all cases when there is a type mismatch. Note that the Bundle APIs catch ClassCastException to return null, but that only works for non-generic types (eg. getSizeF()). For generic types wrapping "return (T) o" with try-catch doesn't work because the type gets erased to its bound at runtime, so the type mismatch escapes that try-catch to the caller, potentially causing a crash. Now they happen inside the getters, as the non-generic ones. Test: Boots for now Test: Working on CTS Test: atest -d android.os.cts.ParcelTest android.os.cts.BundleTest android.os.BundleTest android.os.ParcelTest CTS-Coverage-Bug: 219980813 Change-Id: Ifcbeb34b4684d7de105756b9d414162a9205ffaa
Loading
Please register or sign in to comment