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

Commit fdd1771a authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Gerrit Code Review
Browse files

Merge "Change defusing for lazy bundles"

parents 6a0afe18 0c5074ed
Loading
Loading
Loading
Loading
+37 −18
Original line number Original line Diff line number Diff line
@@ -43,18 +43,22 @@ public class BaseBundle {
    protected static final String TAG = "Bundle";
    protected static final String TAG = "Bundle";
    static final boolean DEBUG = false;
    static final boolean DEBUG = false;


    // Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
    /**
    private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
     * Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
     *
     * @hide
     */
    @VisibleForTesting
    static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
    private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
    private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'


    /**
    /**
     * Flag indicating that this Bundle is okay to "defuse." That is, it's okay
     * Flag indicating that this Bundle is okay to "defuse", see {@link #setShouldDefuse(boolean)}
     * for system processes to ignore any {@link BadParcelableException}
     * for more details.
     * encountered when unparceling it, leaving an empty bundle in its place.
     * <p>
     * <p>
     * This should <em>only</em> be set when the Bundle reaches its final
     * This should <em>only</em> be set when the Bundle reaches its final destination, otherwise a
     * destination, otherwise a system process may clobber contents that were
     * system process may clobber contents that were destined for an app that could have unparceled
     * destined for an app that could have unparceled them.
     * them.
     */
     */
    static final int FLAG_DEFUSABLE = 1 << 0;
    static final int FLAG_DEFUSABLE = 1 << 0;


@@ -63,10 +67,15 @@ public class BaseBundle {
    private static volatile boolean sShouldDefuse = false;
    private static volatile boolean sShouldDefuse = false;


    /**
    /**
     * Set global variable indicating that any Bundles parsed in this process
     * Set global variable indicating that any Bundles parsed in this process should be "defused".
     * should be "defused." That is, any {@link BadParcelableException}
     * That is, any {@link BadParcelableException} encountered will be suppressed and logged. Also:
     * encountered will be suppressed and logged, leaving an empty Bundle
     * <ul>
     * instead of crashing.
     *   <li>If it was the deserialization of a custom item (eg. {@link Parcelable}) that caused the
     *   exception, {@code null} will be returned but the item will be held in the map in its
     *   serialized form (lazy value).
     *   <li>If the exception happened during partial deserialization, that is, during the read of
     *   the map and its basic types (while skipping custom types), the map will be left empty.
     * </ul>
     *
     *
     * @hide
     * @hide
     */
     */
@@ -249,6 +258,12 @@ public class BaseBundle {
                }
                }
            }
            }
            if (itemwise) {
            if (itemwise) {
                if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
                    Slog.wtf(TAG,
                            "Attempting to unparcel all items in a Bundle while in transit; this "
                                    + "may remove elements intended for the final desitination.",
                            new Throwable());
                }
                for (int i = 0, n = mMap.size(); i < n; i++) {
                for (int i = 0, n = mMap.size(); i < n; i++) {
                    // Triggers deserialization of i-th item, if needed
                    // Triggers deserialization of i-th item, if needed
                    getValueAt(i);
                    getValueAt(i);
@@ -281,7 +296,16 @@ public class BaseBundle {
    final Object getValueAt(int i) {
    final Object getValueAt(int i) {
        Object object = mMap.valueAt(i);
        Object object = mMap.valueAt(i);
        if (object instanceof Supplier<?>) {
        if (object instanceof Supplier<?>) {
            try {
                object = ((Supplier<?>) object).get();
                object = ((Supplier<?>) object).get();
            } catch (BadParcelableException e) {
                if (sShouldDefuse) {
                    Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
                    return null;
                } else {
                    throw e;
                }
            }
            mMap.setValueAt(i, object);
            mMap.setValueAt(i, object);
        }
        }
        return object;
        return object;
@@ -289,11 +313,6 @@ public class BaseBundle {


    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
            boolean parcelledByNative) {
            boolean parcelledByNative) {
        if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
            Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
                    + "clobber all data inside!", new Throwable());
        }

        if (isEmptyParcel(parcelledData)) {
        if (isEmptyParcel(parcelledData)) {
            if (DEBUG) {
            if (DEBUG) {
                Log.d(TAG, "unparcel "
                Log.d(TAG, "unparcel "
+1 −1
Original line number Original line Diff line number Diff line
@@ -3776,7 +3776,7 @@ public final class Parcel {


            default:
            default:
                int off = dataPosition() - 4;
                int off = dataPosition() - 4;
                throw new RuntimeException(
                throw new BadParcelableException(
                    "Parcel " + this + ": Unmarshalling unknown type code " + type
                    "Parcel " + this + ": Unmarshalling unknown type code " + type
                            + " at offset " + off);
                            + " at offset " + off);
        }
        }
+76 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ public class BundleTest {


    @After
    @After
    public void tearDown() throws Exception {
    public void tearDown() throws Exception {
        BaseBundle.setShouldDefuse(false);
        if (mWtfHandler != null) {
        if (mWtfHandler != null) {
            Log.setWtfHandler(mWtfHandler);
            Log.setWtfHandler(mWtfHandler);
        }
        }
@@ -355,6 +356,81 @@ public class BundleTest {
        assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class);
        assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class);
    }
    }


    @Test
    public void getParcelable_whenThrowingAndNotDefusing_throws() throws Exception {
        Bundle.setShouldDefuse(false);
        Bundle bundle = new Bundle();
        bundle.putParcelable("key", new CustomParcelable(13, "Tiramisu"));
        bundle.readFromParcel(getParcelledBundle(bundle));

        // Default class-loader is the bootpath class-loader, which doesn't contain
        // CustomParcelable, so trying to read it will throw BadParcelableException.
        assertThrows(BadParcelableException.class, () -> bundle.getParcelable("key"));
    }

    @Test
    public void getParcelable_whenThrowingAndDefusing_returnsNull() throws Exception {
        Bundle.setShouldDefuse(true);
        Bundle bundle = new Bundle();
        bundle.putParcelable("key", new CustomParcelable(13, "Tiramisu"));
        bundle.putString("string", "value");
        bundle.readFromParcel(getParcelledBundle(bundle));

        // Default class-loader is the bootpath class-loader, which doesn't contain
        // CustomParcelable, so trying to read it will throw BadParcelableException.
        assertThat(bundle.<Parcelable>getParcelable("key")).isNull();
        // Doesn't affect other items
        assertThat(bundle.getString("string")).isEqualTo("value");
    }

    @Test
    public void getParcelable_whenThrowingAndDefusing_leavesElement() throws Exception {
        Bundle.setShouldDefuse(true);
        Bundle bundle = new Bundle();
        Parcelable parcelable = new CustomParcelable(13, "Tiramisu");
        bundle.putParcelable("key", parcelable);
        bundle.putString("string", "value");
        bundle.readFromParcel(getParcelledBundle(bundle));
        assertThat(bundle.<Parcelable>getParcelable("key")).isNull();

        // Now, we simulate reserializing and assign the proper class loader to not throw anymore
        bundle.readFromParcel(getParcelledBundle(bundle));
        bundle.setClassLoader(getClass().getClassLoader());

        // We're able to retrieve it even though we failed before
        assertThat(bundle.<Parcelable>getParcelable("key")).isEqualTo(parcelable);
    }

    @Test
    public void partialDeserialization_whenNotDefusing_throws() throws Exception {
        Bundle.setShouldDefuse(false);
        Bundle bundle = getMalformedBundle();
        assertThrows(BadParcelableException.class, bundle::isEmpty);
    }

    @Test
    public void partialDeserialization_whenDefusing_emptiesMap() throws Exception {
        Bundle.setShouldDefuse(true);
        Bundle bundle = getMalformedBundle();
        bundle.isEmpty();
        // Nothing thrown
        assertThat(bundle.size()).isEqualTo(0);
    }

    private Bundle getMalformedBundle() {
        Parcel p = Parcel.obtain();
        p.writeInt(BaseBundle.BUNDLE_MAGIC);
        int start = p.dataPosition();
        p.writeInt(1); // Number of items
        p.writeString("key");
        p.writeInt(131313); // Invalid type
        p.writeInt(0); // Anything, really
        int end = p.dataPosition();
        p.setDataPosition(0);
        return new Bundle(p, end - start);
    }


    private static class CustomParcelable implements Parcelable {
    private static class CustomParcelable implements Parcelable {
        public final int integer;
        public final int integer;
        public final String string;
        public final String string;