Merge "Add range-based Parcel.compareData()" am: 98a4a1b8b9 am: e9eb2ecff1 am: dcfad1498f am: bddf602aca
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1854184
Change-Id: Ia79e802b1efb2068bc00cb7266dd5b6ff5433d92
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1468d9f..61882ff 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -381,6 +381,8 @@
private static native void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
private static native int nativeCompareData(long thisNativePtr, long otherNativePtr);
+ private static native boolean nativeCompareDataInRange(
+ long ptrA, int offsetA, long ptrB, int offsetB, int length);
private static native void nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
@CriticalNative
@@ -678,11 +680,16 @@
}
/** @hide */
- public final int compareData(Parcel other) {
+ public int compareData(Parcel other) {
return nativeCompareData(mNativePtr, other.mNativePtr);
}
/** @hide */
+ public static boolean compareData(Parcel a, int offsetA, Parcel b, int offsetB, int length) {
+ return nativeCompareDataInRange(a.mNativePtr, offsetA, b.mNativePtr, offsetB, length);
+ }
+
+ /** @hide */
public final void setClassCookie(Class clz, Object cookie) {
if (mClassCookies == null) {
mClassCookies = new ArrayMap<>();
@@ -3609,7 +3616,6 @@
private final int mType;
@Nullable private final ClassLoader mLoader;
@Nullable private Object mObject;
- @Nullable private volatile Parcel mValueParcel;
/**
* This goes from non-null to null once. Always check the nullability of this object before
@@ -3707,7 +3713,7 @@
return false;
}
// Finally we compare the payload.
- return getValueParcel(source).compareData(value.getValueParcel(otherSource)) == 0;
+ return Parcel.compareData(source, mPosition, otherSource, value.mPosition, mLength);
}
@Override
@@ -3715,17 +3721,6 @@
// Accessing mSource first to provide memory barrier for mObject
return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
}
-
- /** This extracts the parcel section responsible for the object and returns it. */
- private Parcel getValueParcel(Parcel source) {
- Parcel parcel = mValueParcel;
- if (parcel == null) {
- parcel = Parcel.obtain();
- parcel.appendFrom(source, mPosition, mLength);
- mValueParcel = parcel;
- }
- return parcel;
- }
}
/**
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index be9aaaf..0d530f6 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -604,6 +604,25 @@
return thisParcel->compareData(*otherParcel);
}
+static jboolean android_os_Parcel_compareDataInRange(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+ jint thisOffset, jlong otherNativePtr,
+ jint otherOffset, jint length) {
+ Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
+ LOG_ALWAYS_FATAL_IF(thisParcel == nullptr, "Should not be null");
+
+ Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
+ LOG_ALWAYS_FATAL_IF(otherParcel == nullptr, "Should not be null");
+
+ int result;
+ status_t err =
+ thisParcel->compareDataInRange(thisOffset, *otherParcel, otherOffset, length, &result);
+ if (err != NO_ERROR) {
+ signalExceptionForError(env, clazz, err);
+ return JNI_FALSE;
+ }
+ return (result == 0) ? JNI_TRUE : JNI_FALSE;
+}
+
static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
jlong otherNativePtr, jint offset, jint length)
{
@@ -841,6 +860,7 @@
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
{"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
{"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData},
+ {"nativeCompareDataInRange", "(JIJII)Z", (void*)android_os_Parcel_compareDataInRange},
{"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
// @CriticalNative
{"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index ddd0070..09f4840 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -273,16 +273,21 @@
Parcelable p1 = new CustomParcelable(13, "Tiramisu");
Parcelable p2 = new CustomParcelable(13, "Tiramisu");
Bundle a = new Bundle();
- a.putParcelable("key", p1);
+ a.putParcelable("key1", p1);
a.readFromParcel(getParcelledBundle(a));
a.setClassLoader(getClass().getClassLoader());
Bundle b = new Bundle();
- b.putParcelable("key", p2);
+ // Adding extra element so that the position of the elements of interest in their respective
+ // source parcels are different so we can cover that case of Parcel.compareData(). We'll
+ // remove the element later so the map is equal.
+ b.putString("key0", "string");
+ b.putParcelable("key1", p2);
b.readFromParcel(getParcelledBundle(b));
b.setClassLoader(getClass().getClassLoader());
- // 2 lazy values with identical parcels inside
a.isEmpty();
b.isEmpty();
+ b.remove("key0");
+ // 2 lazy values with identical parcels inside
assertTrue(Bundle.kindofEquals(a, b));
}
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index dcb3e2f..fdd278b 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -17,6 +17,9 @@
package android.os;
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.platform.test.annotations.Presubmit;
@@ -110,4 +113,130 @@
assertEquals(string, p.readString16());
}
}
+
+ @Test
+ public void testCompareDataInRange_whenSameData() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(13);
+ pA.writeString("Tiramisu");
+ int length = pA.dataPosition() - iA;
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+
+ assertTrue(Parcel.compareData(pA, iA, pB, iB, length));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenSameDataWithBinder() {
+ Binder binder = new Binder();
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(13);
+ pA.writeStrongBinder(binder);
+ pA.writeString("Tiramisu");
+ int length = pA.dataPosition() - iA;
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeStrongBinder(binder);
+ pB.writeString("Tiramisu");
+
+ assertTrue(Parcel.compareData(pA, iA, pB, iB, length));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenDifferentData() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(13);
+ pA.writeString("Tiramisu");
+ int length = pA.dataPosition() - iA;
+ Parcel pB = Parcel.obtain();
+ int iB = pB.dataPosition();
+ pB.writeString("Prefix");
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+
+ assertFalse(Parcel.compareData(pA, iA, pB, iB, length));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenLimitOutOfBounds_throws() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(12);
+ pA.writeString("Tiramisu");
+ int length = pA.dataPosition() - iA;
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+ pB.writeInt(-1);
+
+ assertThrows(IllegalArgumentException.class,
+ () -> Parcel.compareData(pA, iA + length, pB, iB, 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> Parcel.compareData(pA, iA, pB, pB.dataSize(), 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> Parcel.compareData(pA, iA, pB, iB, length + 1));
+ assertThrows(IllegalArgumentException.class,
+ () -> Parcel.compareData(pA, iA + length + 1, pB, iB, 0));
+ assertThrows(IllegalArgumentException.class,
+ () -> Parcel.compareData(pA, iA, pB, iB + pB.dataSize() + 1, 0));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenLengthZero() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(12);
+ pA.writeString("Tiramisu");
+ int length = pA.dataPosition() - iA;
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+
+ assertTrue(Parcel.compareData(pA, 0, pB, iB, 0));
+ assertTrue(Parcel.compareData(pA, iA + length, pB, iB, 0));
+ assertTrue(Parcel.compareData(pA, iA, pB, pB.dataSize(), 0));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenNegativeLength_throws() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(12);
+ pA.writeString("Tiramisu");
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+
+ assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, iA, pB, iB, -1));
+ }
+
+ @Test
+ public void testCompareDataInRange_whenNegativeOffset_throws() {
+ Parcel pA = Parcel.obtain();
+ int iA = pA.dataPosition();
+ pA.writeInt(12);
+ pA.writeString("Tiramisu");
+ Parcel pB = Parcel.obtain();
+ pB.writeString("Prefix");
+ int iB = pB.dataPosition();
+ pB.writeInt(13);
+ pB.writeString("Tiramisu");
+
+ assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, -1, pB, iB, 0));
+ assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, 0, pB, -1, 0));
+ }
}