Skip to content
Commit 9ca6a5e2 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Lazy bundle

Implement lazy deserialization for custom types in bundle:
* Parcelable (VAL_PARCELABLE)
* Serializable (VAL_SERIALIZABLE)
* Parcelable array (VAL_PARCELABLEARRAY)
* Lists (VAL_LIST)
* Sparse array (VAL_SPARSEARRAY)
* Bundle (VAL_BUNDLE)

This enhances security, makes bundles more robust to deserialization
errors and avoid deserializing unneeded objects in some cases*, for more
details check go/lazy-bundle.

To do that, we prefix those types with their length when writing them on
the wire. Map serialization and deserialization now happens inside
Bundle (instead of calling Parcel's readArrayMapInternal() /
writeArrayMapInternal()) and we use an intermediary object - LazyValue -
that holds information about the position and length of the value we
will deserialize when queried.

So, there are basically 3 states:

1. We received the bundle but haven't queried anything about it (not
   even isEmpty()): in this case the original parcel is held inside and
   we haven't attempted any deserialization (except for the metadata at
   the beginning such as the magic, etc)

2. We queried something on it (eg. isEmpty()): Now we deserialize the
   bundle skipping the custom values above (we're able to do this now
   with the length written on the wire) and instead placing LazyValue
   objects for them in the map.

3. We query one of the lazy values: Now, we deserialize the object
   represented by LazyValue and replace it on the map.

Since after (2) LazyValue objects are the only ones holding references
to the original Parcel, when all LazyValues are deserialized, the
original Parcel is available for GC.

Inside bundle now we differentiate between unparcel(itemwise = true) and
unparcel(itemwise = false) where the first also deserializes each item
(such that there are no LazyValues in the map). This is because some
operations such as kindofEquals() need all items deserialized.

I had to break a few methods in parcel into multiple methods in parcel
to be able to control the format in bundle. They are all @hide.

* In quick local experiments, counting the bytes that didn't need to be
deserialized after the change. Roughly 10% of bytes from custom-type
items in Bundle are not deserialized in the testing scenario (if I
haven't messed up the stats :). That's on a sdk_gphone_x86_64_arm64 a
few minutes after boot. Check
https://screenshot.googleplex.com/53uXrrqDMYahzg3, stats collection is
on ag/15403076.

Test: atest -d android.os.cts.ParcelTest android.os.cts.BundleTest android.os.BundleTest android.os.ParcelTest
Test: Boot device
Bug: 195622897
Change-Id: Icfe8880cad00c3cd2afcbe4b92400ad4579e680e
parent 6f1a606d
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment