diff options
| author | 2022-12-06 18:46:42 +0000 | |
|---|---|---|
| committer | 2022-12-06 18:46:42 +0000 | |
| commit | bd98943f2c47b1ab608148be88c743f4593833fe (patch) | |
| tree | 7e5e8a081b8e3931fcfa8cfd93f268f4600ff540 | |
| parent | 4dd9069804d2c7ba9bd1843bf69a838daed7620a (diff) | |
| parent | 8a09c9a244b2c135c0bdcdfbdad36b7b226cb889 (diff) | |
Merge "Fuzz java parcel surfaces" am: 8a09c9a244
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2321414
Change-Id: Icf70bcaaf3c335a5e51413361188f2d1d3811046
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
| -rw-r--r-- | core/java/Android.bp | 1 | ||||
| -rw-r--r-- | core/tests/fuzzers/FuzzService/FuzzBinder.java | 8 | ||||
| -rw-r--r-- | core/tests/fuzzers/FuzzService/random_parcel_jni.cpp | 2 | ||||
| -rw-r--r-- | core/tests/fuzzers/FuzzService/random_parcel_jni.h | 2 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/Android.bp | 40 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/FuzzOperation.java | 22 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/FuzzUtils.java | 87 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/ParcelFuzzer.java | 42 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/ReadOperation.java | 24 | ||||
| -rw-r--r-- | core/tests/fuzzers/ParcelFuzzer/ReadUtils.java | 435 |
10 files changed, 657 insertions, 6 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp index 88ee39d913f3..83ffa2405688 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -432,6 +432,7 @@ filegroup { "android/os/IInterface.java", "android/os/Binder.java", "android/os/IBinder.java", + "android/os/Parcelable.java", ], } diff --git a/core/tests/fuzzers/FuzzService/FuzzBinder.java b/core/tests/fuzzers/FuzzService/FuzzBinder.java index 52aafeb4bc9d..7fd199afaf85 100644 --- a/core/tests/fuzzers/FuzzService/FuzzBinder.java +++ b/core/tests/fuzzers/FuzzService/FuzzBinder.java @@ -34,12 +34,12 @@ public class FuzzBinder { fuzzServiceInternal(binder, data); } - // This API creates random parcel object - public static void createRandomParcel(Parcel parcel, byte[] data) { - getRandomParcel(parcel, data); + // This API fills parcel object + public static void fillRandomParcel(Parcel parcel, byte[] data) { + fillParcelInternal(parcel, data); } private static native void fuzzServiceInternal(IBinder binder, byte[] data); - private static native void getRandomParcel(Parcel parcel, byte[] data); + private static native void fillParcelInternal(Parcel parcel, byte[] data); private static native int registerNatives(); } diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp index dbeae87f3279..264aa5f26bc1 100644 --- a/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp +++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.cpp @@ -38,7 +38,7 @@ JNIEXPORT jint JNICALL Java_randomparcel_FuzzBinder_registerNatives(JNIEnv* env) return registerFrameworkNatives(env); } -JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_getRandomParcel(JNIEnv *env, jobject thiz, jobject jparcel, jbyteArray fuzzData) { +JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fillParcelInternal(JNIEnv *env, jobject thiz, jobject jparcel, jbyteArray fuzzData) { size_t len = static_cast<size_t>(env->GetArrayLength(fuzzData)); uint8_t data[len]; env->GetByteArrayRegion(fuzzData, 0, len, reinterpret_cast<jbyte*>(data)); diff --git a/core/tests/fuzzers/FuzzService/random_parcel_jni.h b/core/tests/fuzzers/FuzzService/random_parcel_jni.h index bc18b2ff6fc7..c96354a2b35b 100644 --- a/core/tests/fuzzers/FuzzService/random_parcel_jni.h +++ b/core/tests/fuzzers/FuzzService/random_parcel_jni.h @@ -24,5 +24,5 @@ extern "C" { // Function from AndroidRuntime jint registerFrameworkNatives(JNIEnv* env); - JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_getRandomParcel(JNIEnv *env, jobject thiz, jobject parcel, jbyteArray fuzzData); + JNIEXPORT void JNICALL Java_randomparcel_FuzzBinder_fillParcelInternal(JNIEnv *env, jobject thiz, jobject parcel, jbyteArray fuzzData); } diff --git a/core/tests/fuzzers/ParcelFuzzer/Android.bp b/core/tests/fuzzers/ParcelFuzzer/Android.bp new file mode 100644 index 000000000000..b71a06e3572e --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/Android.bp @@ -0,0 +1,40 @@ +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +java_fuzz { + name: "java_binder_parcel_fuzzer", + srcs: [ + "ParcelFuzzer.java", + "ReadUtils.java", + "FuzzUtils.java", + "FuzzOperation.java", + "ReadOperation.java", + ":framework-core-sources-for-fuzzers", + ], + static_libs: [ + "jazzer", + "random_parcel_lib", + "binderReadParcelIface-java", + ], + jni_libs: [ + "librandom_parcel_jni", + "libc++", + "libandroid_runtime", + ], + libs: [ + "framework", + "unsupportedappusage", + "ext", + "framework-res", + ], + native_bridge_supported: true, + fuzz_config: { + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + // Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer + hotlists: ["4637097"], + }, +} diff --git a/core/tests/fuzzers/ParcelFuzzer/FuzzOperation.java b/core/tests/fuzzers/ParcelFuzzer/FuzzOperation.java new file mode 100644 index 000000000000..033231df28be --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/FuzzOperation.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelfuzzer; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +public interface FuzzOperation { + void doFuzz(FuzzedDataProvider data); +} diff --git a/core/tests/fuzzers/ParcelFuzzer/FuzzUtils.java b/core/tests/fuzzers/ParcelFuzzer/FuzzUtils.java new file mode 100644 index 000000000000..5e9e5ecac4dc --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/FuzzUtils.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelfuzzer; + +import android.os.Parcel; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +import randomparcel.FuzzBinder; + +public class FuzzUtils { + public static FuzzOperation[] FUZZ_OPERATIONS = + new FuzzOperation[] { + new FuzzOperation() { + @java.lang.Override + public void doFuzz(FuzzedDataProvider provider) { + // Fuzz Append + int start = provider.consumeInt(); + int len = provider.consumeInt(); + Parcel p1 = null; + Parcel p2 = null; + + try { + p1 = Parcel.obtain(); + p2 = Parcel.obtain(); + + byte[] data = + provider.consumeBytes( + provider.consumeInt(0, provider.remainingBytes())); + FuzzBinder.fillRandomParcel(p1, data); + FuzzBinder.fillRandomParcel(p2, provider.consumeRemainingAsBytes()); + + p1.appendFrom(p2, start, len); + + } catch (Exception e) { + // Rethrow exception as runtime exceptions are catched + // at highest level. + throw e; + } finally { + p1.recycle(); + p2.recycle(); + } + } + }, + new FuzzOperation() { + @java.lang.Override + public void doFuzz(FuzzedDataProvider provider) { + // Fuzz Read + // Use maximum bytes to generate read instructions and remaining for parcel + // creation + int maxParcelBytes = provider.remainingBytes() / 3; + byte[] data = provider.consumeBytes(maxParcelBytes); + Parcel randomParcel = null; + + try { + randomParcel = Parcel.obtain(); + FuzzBinder.fillRandomParcel(randomParcel, data); + + while (provider.remainingBytes() > 0) { + provider.pickValue(ReadUtils.READ_OPERATIONS) + .readParcel(randomParcel, provider); + } + + } catch (Exception e) { + // Rethrow exception as runtime exceptions are catched + // at highest level. + throw e; + } finally { + randomParcel.recycle(); + } + } + }, + }; +} diff --git a/core/tests/fuzzers/ParcelFuzzer/ParcelFuzzer.java b/core/tests/fuzzers/ParcelFuzzer/ParcelFuzzer.java new file mode 100644 index 000000000000..688c8126c99e --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/ParcelFuzzer.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelfuzzer; + +import android.util.Log; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +import randomparcel.FuzzBinder; + +public class ParcelFuzzer { + + static { + // Initialize JNI dependencies + FuzzBinder.init(); + } + + public static void fuzzerTestOneInput(FuzzedDataProvider provider) { + // Default behavior for Java APIs is to throw RuntimeException. + // We need to fuzz to detect other problems which are not handled explicitly. + // TODO(b/150808347): Change known API exceptions to subclass of + // RuntimeExceptions and catch those only. + try { + provider.pickValue(FuzzUtils.FUZZ_OPERATIONS).doFuzz(provider); + } catch (RuntimeException e) { + Log.e("ParcelFuzzer", "Exception occurred while fuzzing ", e); + } + } +} diff --git a/core/tests/fuzzers/ParcelFuzzer/ReadOperation.java b/core/tests/fuzzers/ParcelFuzzer/ReadOperation.java new file mode 100644 index 000000000000..5c227e3525fd --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/ReadOperation.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelfuzzer; + +import android.os.Parcel; + +import com.code_intelligence.jazzer.api.FuzzedDataProvider; + +public interface ReadOperation { + void readParcel(Parcel parcel, FuzzedDataProvider provider); +} diff --git a/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java new file mode 100644 index 000000000000..0eff5f24f7e0 --- /dev/null +++ b/core/tests/fuzzers/ParcelFuzzer/ReadUtils.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package parcelfuzzer; + +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.HashMap; + +import parcelables.EmptyParcelable; +import parcelables.GenericDataParcelable; +import parcelables.SingleDataParcelable; + +public class ReadUtils { + public static int MAX_LEN = 1000000; + public static int MIN_LEN = 0; + + private static class SomeParcelable implements Parcelable { + private final int mValue; + + private SomeParcelable(Parcel in) { + this.mValue = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mValue); + } + + public static Parcelable.Creator<SomeParcelable> CREATOR = + new Parcelable.Creator<SomeParcelable>() { + + @Override + public SomeParcelable createFromParcel(Parcel source) { + return new SomeParcelable(source); + } + + @Override + public SomeParcelable[] newArray(int size) { + return new SomeParcelable[size]; + } + }; + } + + private static class TestClassLoader extends ClassLoader { + TestClassLoader() { + super(); + } + } + + private static class TestInterface implements IInterface { + public Binder binder; + private static final String DESCRIPTOR = "TestInterface"; + + TestInterface() { + binder = new Binder(); + binder.attachInterface(this, DESCRIPTOR); + } + + public IBinder asBinder() { + return binder; + } + + public static TestInterface asInterface(IBinder binder) { + if (binder != null) { + IInterface iface = binder.queryLocalInterface(DESCRIPTOR); + if (iface != null && iface instanceof TestInterface) { + return (TestInterface) iface; + } + } + return null; + } + } + + public static ReadOperation[] READ_OPERATIONS = + new ReadOperation[] { + (parcel, provider) -> { + parcel.setDataPosition(provider.consumeInt()); + }, + (parcel, provider) -> { + parcel.setDataCapacity(provider.consumeInt()); + }, + (parcel, provider) -> { + parcel.setDataSize(provider.consumeInt()); + }, + (parcel, provider) -> { + parcel.dataSize(); + }, + (parcel, provider) -> { + parcel.dataPosition(); + }, + (parcel, provider) -> { + parcel.dataCapacity(); + }, + + // read basic types + (parcel, provider) -> { + parcel.readByte(); + }, + (parcel, provider) -> { + parcel.readBoolean(); + }, + (parcel, provider) -> { + parcel.readInt(); + }, + (parcel, provider) -> { + parcel.readLong(); + }, + (parcel, provider) -> { + parcel.readFloat(); + }, + (parcel, provider) -> { + parcel.readDouble(); + }, + (parcel, provider) -> { + parcel.readString(); + }, + (parcel, provider) -> { + parcel.readString8(); + }, + (parcel, provider) -> { + parcel.readString16(); + }, + (parcel, provider) -> { + parcel.readBlob(); + }, + (parcel, provider) -> { + parcel.readStrongBinder(); + }, + + // read arrays of random length + (parcel, provider) -> { + byte[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new byte[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new byte[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readByteArray(array); + }, + (parcel, provider) -> { + char[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new char[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new char[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readCharArray(array); + }, + (parcel, provider) -> { + int[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new int[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new int[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readIntArray(array); + }, + (parcel, provider) -> { + double[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new double[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new double[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readDoubleArray(array); + }, + (parcel, provider) -> { + float[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new float[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new float[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readFloatArray(array); + }, + (parcel, provider) -> { + boolean[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new boolean[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new boolean[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readBooleanArray(array); + }, + (parcel, provider) -> { + long[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new long[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new long[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readLongArray(array); + }, + (parcel, provider) -> { + IBinder[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new IBinder[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new IBinder[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readBinderArray(array); + }, + (parcel, provider) -> { + ArrayList<IBinder> arrayList = new ArrayList<IBinder>(); + parcel.readBinderList(arrayList); + }, + + // unmarshall from random parcel data and random bytes + (parcel, provider) -> { + byte[] data = parcel.marshall(); + Parcel p = Parcel.obtain(); + p.unmarshall(data, provider.consumeInt(), provider.consumeInt()); + p.recycle(); + }, + (parcel, provider) -> { + byte[] data = provider.consumeRemainingAsBytes(); + Parcel p = Parcel.obtain(); + p.unmarshall(data, provider.consumeInt(), provider.consumeInt()); + p.recycle(); + }, + (parcel, provider) -> { + parcel.hasFileDescriptors(provider.consumeInt(), provider.consumeInt()); + }, + + // read AIDL generated parcelables + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelable(loader, SingleDataParcelable.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader, SingleDataParcelable.class); + }, + (parcel, provider) -> { + SingleDataParcelable[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new SingleDataParcelable[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new SingleDataParcelable[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readTypedArray(array, SingleDataParcelable.CREATOR); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelable(loader, EmptyParcelable.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader, EmptyParcelable.class); + }, + (parcel, provider) -> { + EmptyParcelable[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new EmptyParcelable[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new EmptyParcelable[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readTypedArray(array, EmptyParcelable.CREATOR); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelable(loader, GenericDataParcelable.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader, GenericDataParcelable.class); + }, + (parcel, provider) -> { + GenericDataParcelable[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new GenericDataParcelable[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + int len = provider.consumeInt(MIN_LEN, MAX_LEN); + array = new GenericDataParcelable[len]; + } + parcel.readTypedArray(array, GenericDataParcelable.CREATOR); + }, + + // read parcelables + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelable(loader, SomeParcelable.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader, SomeParcelable.class); + }, + (parcel, provider) -> { + SomeParcelable[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new SomeParcelable[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new SomeParcelable[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readTypedArray(array, SomeParcelable.CREATOR); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader); + }, + (parcel, provider) -> { + parcel.hasFileDescriptors(provider.consumeInt(), provider.consumeInt()); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readParcelableArray(loader); + }, + + // read lists + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readArrayList(loader); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readArrayList(loader, Object.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readArrayList(loader, SomeParcelable.class); + }, + + // read sparse arrays + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readSparseArray(loader); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readSparseArray(loader, Object.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readSparseArray(loader, SomeParcelable.class); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readSerializable(loader, Object.class); + }, + + // read interface + (parcel, provider) -> { + TestInterface[] array; + if (provider.consumeBoolean()) { + int pos = parcel.dataPosition(); + array = new TestInterface[Math.min(MAX_LEN, parcel.readInt())]; + parcel.setDataPosition(pos); + } else { + array = new TestInterface[provider.consumeInt(MIN_LEN, MAX_LEN)]; + } + parcel.readInterfaceArray(array, TestInterface::asInterface); + }, + (parcel, provider) -> { + int w = provider.consumeInt(MIN_LEN, MAX_LEN); + int h = provider.consumeInt(MIN_LEN, MAX_LEN); + TestInterface[][] array = new TestInterface[w][h]; + parcel.readFixedArray(array, TestInterface::asInterface); + }, + (parcel, provider) -> { + ArrayList<TestInterface> array = new ArrayList<TestInterface>(); + parcel.readInterfaceList(array, TestInterface::asInterface); + }, + + // read bundle + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readBundle(loader); + }, + (parcel, provider) -> { + parcel.readBundle(); + }, + + // read HashMap + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readHashMap(loader); + }, + (parcel, provider) -> { + TestClassLoader loader = new TestClassLoader(); + parcel.readHashMap(loader, String.class, String.class); + }, + (parcel, provider) -> { + HashMap<String, String> hashMap = new HashMap<>(); + TestClassLoader loader = new TestClassLoader(); + parcel.readMap(hashMap, loader, String.class, String.class); + }, + }; +} |