From 95ace87925e635afe1f9e3ccf6cc86e220afd2d3 Mon Sep 17 00:00:00 2001 From: Bernardo Rufino Date: Mon, 27 Sep 2021 09:46:18 +0100 Subject: Log.wtf in mismatch + tests Log mismatches as wtf to be able track those. Also added unit tests around kindofEquals() and r/w mismatches. Test: atest -d android.os.cts.ParcelTest android.os.cts.BundleTest android.os.BundleTest android.os.ParcelTest Bug: 195622897 Change-Id: I361dd144b23975eedc8b19ed65457a1c5405936e --- core/java/android/os/BaseBundle.java | 2 +- core/java/android/os/Parcel.java | 2 +- .../tests/coretests/src/android/os/BundleTest.java | 205 +++++++++++++++++++++ 3 files changed, 207 insertions(+), 2 deletions(-) diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index fb21ce30415f..64d54b820a92 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -431,7 +431,7 @@ public class BaseBundle { * * @hide */ - public static boolean kindofEquals(BaseBundle a, BaseBundle b) { + public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) { return (a == b) || (a != null && a.kindofEquals(b)); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 7f2e2b115986..e809912f15df 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -3363,7 +3363,7 @@ public final class Parcel { object = readValue(type, loader); int actual = dataPosition() - start; if (actual != length) { - Log.w(TAG, + Slog.wtfStack(TAG, "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type) + " consumed " + actual + " bytes, but " + length + " expected."); } diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index 4cc70ba6448d..9d2cab3f7026 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -16,16 +16,24 @@ package android.os; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import android.util.Log; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Objects; + /** * Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with * public APIs should go in the CTS counterpart. @@ -35,6 +43,14 @@ import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class BundleTest { + private Log.TerribleFailureHandler mWtfHandler; + + @After + public void tearDown() throws Exception { + if (mWtfHandler != null) { + Log.setWtfHandler(mWtfHandler); + } + } /** * Take a bundle, write it to a parcel and return the parcel. @@ -217,4 +233,193 @@ public class BundleTest { // return true assertTrue(BaseBundle.kindofEquals(bundle1, bundle2)); } + + @Test + public void kindofEquals_lazyValues() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + + // 2 maps with live objects + Bundle a = new Bundle(); + a.putParcelable("key1", p1); + Bundle b = new Bundle(); + b.putParcelable("key1", p2); + assertTrue(Bundle.kindofEquals(a, b)); + + // 2 identical parcels + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + assertTrue(Bundle.kindofEquals(a, b)); + + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + assertTrue(Bundle.kindofEquals(a, b)); + + // 1 lazy value vs 1 live object + a.getParcelable("key1"); + assertFalse(Bundle.kindofEquals(a, b)); + + // 2 live objects + b.getParcelable("key1"); + assertTrue(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + + assertTrue(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(Bundle.class.getClassLoader()); // BCP + // 2 lazy values with identical parcels inside + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesOfDifferentTypes_returnsFalse() { + Parcelable p = new CustomParcelable(13, "Tiramisu"); + Parcelable[] ps = {p}; + Bundle a = new Bundle(); + a.putParcelable("key", p); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelableArray("key", ps); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void kindofEquals_lazyValuesWithDifferentLengths_returnsFalse() { + Parcelable p1 = new CustomParcelable(13, "Tiramisu"); + Parcelable p2 = new CustomParcelable(13, "Tiramisuuuuuuuu"); + Bundle a = new Bundle(); + a.putParcelable("key", p1); + a.readFromParcel(getParcelledBundle(a)); + a.setClassLoader(getClass().getClassLoader()); + Bundle b = new Bundle(); + b.putParcelable("key", p2); + b.readFromParcel(getParcelledBundle(b)); + b.setClassLoader(getClass().getClassLoader()); + a.isEmpty(); + b.isEmpty(); + + assertFalse(Bundle.kindofEquals(a, b)); + } + + @Test + public void readWriteLengthMismatch_logsWtf() throws Exception { + mWtfHandler = Log.setWtfHandler((tag, e, system) -> { + throw new RuntimeException(e); + }); + Parcelable parcelable = new CustomParcelable(13, "Tiramisu").setHasLengthMismatch(true); + Bundle bundle = new Bundle(); + bundle.putParcelable("p", parcelable); + bundle.readFromParcel(getParcelledBundle(bundle)); + bundle.setClassLoader(getClass().getClassLoader()); + RuntimeException e = assertThrows(RuntimeException.class, () -> bundle.getParcelable("p")); + assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class); + } + + private static class CustomParcelable implements Parcelable { + public final int integer; + public final String string; + public boolean hasLengthMismatch; + + CustomParcelable(int integer, String string) { + this.integer = integer; + this.string = string; + } + + protected CustomParcelable(Parcel in) { + integer = in.readInt(); + string = in.readString(); + hasLengthMismatch = in.readBoolean(); + } + + public CustomParcelable setHasLengthMismatch(boolean hasLengthMismatch) { + this.hasLengthMismatch = hasLengthMismatch; + return this; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(integer); + out.writeString(string); + out.writeBoolean(hasLengthMismatch); + if (hasLengthMismatch) { + out.writeString("extra-write"); + } + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof CustomParcelable)) { + return false; + } + CustomParcelable + that = (CustomParcelable) other; + return integer == that.integer + && hasLengthMismatch == that.hasLengthMismatch + && string.equals(that.string); + } + + @Override + public int hashCode() { + return Objects.hash(integer, string, hasLengthMismatch); + } + + public static final Creator CREATOR = new Creator() { + @Override + public CustomParcelable createFromParcel(Parcel in) { + return new CustomParcelable(in); + } + @Override + public CustomParcelable[] newArray(int size) { + return new CustomParcelable[size]; + } + }; + } } -- cgit v1.2.3-59-g8ed1b