[go: nahoru, domu]

Add compat methods for new Bundle and Intent APIs

We have introduced new, safer, APIs for Parcel, Bundle and Intent in T
that we want to encourage developers to use. However the APIs have a bug
(b/232589966) which could hinder adoption. As such, we are adding
previously planned Compat methods that developers can use in all Andorid
versios, that will not use the T Apis until U where the bug is addressed.

There are subtle differences in the implementations of the platform
APIs, and so the behaviour of the compat methods:

1. Get Parcelable
public static <T> T getParcelable(@NonNull Bundle in, @Nullable String
key, @NonNull Class<T> clazz)

public static <T> T getParcelableExtra(@NonNull Intent in, @Nullable
String name, @NonNull Class<T> clazz)

These methods match the method signature and behaviour of the post-T,
type-safe APIs - return the value if it exists and is of the `clazz`
type, otherwise return null.

2. Get Collection
public static  <T> ArrayList<T> getParcelableArrayList(@NonNull Bundle
in, @Nullable String key, @NonNull Class<? extends T> clazz)

public static <T> SparseArray<T> getSparseParcelableArray(@NonNull
Bundle in, @Nullable String key, @NonNull Class<? extends T> clazz)

public static <T> ArrayList<T> getParcelableArrayListExtra(@NonNull
Intent in, @Nullable String name, @NonNull Class<? extends T>clazz)

On these methods, the method signature matches the post-T APIs, but type
checking is only done on U+ devices, on first deserialisation. Checking
outside of this scenario requires a linear traversal of the array on each
call, and so is not done for performance  (see b/222087511). Initial
benchmarking shows a 400x difference for large arrays.

3. Get Array

public static Parcelable[] getParcelableArray(@NonNull Bundle in,
@Nullable String key,  @NonNull Class<?> clazz)

public static Parcelable[] getParcelableArrayExtra(@NonNull Intent in,
@Nullable String name, @NonNull Class<?> clazz)

Due to Java generics erasure at runtime, the post-T method signature can
not be safely used on pre-U devices, as the pre-T platform APIs return a
Parcelable[] rather than T[], which can not be implicitely cast to a
subtype even if all items conform. The cost of manually casting is large
(see above). Therefore, we always return Parcelable[] on all device
versions, and we only type-check on U+ devices.

The ParcelCompat methods have been updated to match this behaviour. For
readArray, this is a source breaking API change. But
the current method signature would fail on any current devices if
assigned to anything other than an Object[]  - succeeding
at compile time, but failing with a ClassCastException at runtime.

For readParcelableArray, it has been deprecated and replaced with
readParcelableArrayTyped, as updating the type signature would be a ABI
breaking change for libraries.

Bug: 242048899
Test: atest ParcelCompat BundleCompat IntentCompat on T and U devices
Test: Check binary compatibility manually with a test library.
Relnote: "Adds compatibility methods for new APIs introduced in Android
  13 for Parcels, Bundles, and Intents.
  Note: Some ParcelCompat method signatures have been updated, and may
  require a source change on upgrade to confirm to the new signature."

Change-Id: I57e94c6efcc674173d201205fb175cef495bcf82
diff --git a/core/core/api/current.ignore b/core/core/api/current.ignore
new file mode 100644
index 0000000..3486258
--- /dev/null
+++ b/core/core/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.core.os.ParcelCompat#readArray(android.os.Parcel, ClassLoader, Class<T>):
+    Method androidx.core.os.ParcelCompat.readArray has changed return type from T[] to Object[]
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index cce42f2..87c7c28 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1052,6 +1052,9 @@
 
   public final class IntentCompat {
     method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+    method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+    method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
     method public static android.content.Intent makeMainSelectorActivity(String, String);
     field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
     field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
@@ -1773,6 +1776,13 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
   }
 
+  public final class BundleCompat {
+    method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+    method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+    method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+  }
+
   public final class CancellationSignal {
     ctor public CancellationSignal();
     method public void cancel();
@@ -1835,14 +1845,15 @@
   }
 
   public final class ParcelCompat {
-    method public static <T> T![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
     method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
     method public static boolean readBoolean(android.os.Parcel);
     method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
     method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
     method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
     method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
-    method public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
     method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 381de1d..2920156 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1052,6 +1052,9 @@
 
   public final class IntentCompat {
     method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+    method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+    method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
     method public static android.content.Intent makeMainSelectorActivity(String, String);
     field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
     field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
@@ -1779,6 +1782,13 @@
   @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface BuildCompat.PrereleaseSdkCheck {
   }
 
+  public final class BundleCompat {
+    method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+    method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+    method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+  }
+
   public final class CancellationSignal {
     ctor public CancellationSignal();
     method public void cancel();
@@ -1841,14 +1851,15 @@
   }
 
   public final class ParcelCompat {
-    method public static <T> T![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
     method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
     method public static boolean readBoolean(android.os.Parcel);
     method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
     method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
     method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
     method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
-    method public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
     method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
diff --git a/core/core/api/restricted_current.ignore b/core/core/api/restricted_current.ignore
new file mode 100644
index 0000000..3486258
--- /dev/null
+++ b/core/core/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ChangedType: androidx.core.os.ParcelCompat#readArray(android.os.Parcel, ClassLoader, Class<T>):
+    Method androidx.core.os.ParcelCompat.readArray has changed return type from T[] to Object[]
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index ba0259a..6ba0ed7e 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -1161,6 +1161,9 @@
 
   public final class IntentCompat {
     method public static android.content.Intent createManageUnusedAppRestrictionsIntent(android.content.Context, String);
+    method public static android.os.Parcelable![]? getParcelableArrayExtra(android.content.Intent, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayListExtra(android.content.Intent, String?, Class<? extends T>);
+    method public static <T> T? getParcelableExtra(android.content.Intent, String?, Class<T!>);
     method public static android.content.Intent makeMainSelectorActivity(String, String);
     field public static final String ACTION_CREATE_REMINDER = "android.intent.action.CREATE_REMINDER";
     field public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
@@ -2108,6 +2111,13 @@
     method @Deprecated @ChecksSdkIntAtLeast(api=31, codename="S") public static boolean isAtLeastS();
   }
 
+  public final class BundleCompat {
+    method public static <T> T? getParcelable(android.os.Bundle, String?, Class<T!>);
+    method public static android.os.Parcelable![]? getParcelableArray(android.os.Bundle, String?, Class<? extends android.os.Parcelable>);
+    method public static <T> java.util.ArrayList<T!>? getParcelableArrayList(android.os.Bundle, String?, Class<? extends T>);
+    method public static <T> android.util.SparseArray<T!>? getSparseParcelableArray(android.os.Bundle, String?, Class<? extends T>);
+  }
+
   public final class CancellationSignal {
     ctor public CancellationSignal();
     method public void cancel();
@@ -2170,14 +2180,15 @@
   }
 
   public final class ParcelCompat {
-    method public static <T> T![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> Object![]? readArray(android.os.Parcel, ClassLoader?, Class<T!>);
     method public static <T> java.util.ArrayList<T!>? readArrayList(android.os.Parcel, ClassLoader?, Class<? extends T>);
     method public static boolean readBoolean(android.os.Parcel);
     method public static <K, V> java.util.HashMap<K!,V!>? readHashMap(android.os.Parcel, ClassLoader?, Class<? extends K>, Class<? extends V>);
     method public static <T> void readList(android.os.Parcel, java.util.List<? super T>, ClassLoader?, Class<T!>);
     method public static <K, V> void readMap(android.os.Parcel, java.util.Map<? super K,? super V>, ClassLoader?, Class<K!>, Class<V!>);
     method public static <T extends android.os.Parcelable> T? readParcelable(android.os.Parcel, ClassLoader?, Class<T!>);
-    method public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method @Deprecated public static <T> T![]? readParcelableArray(android.os.Parcel, ClassLoader?, Class<T!>);
+    method public static <T> android.os.Parcelable![]? readParcelableArrayTyped(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(30) public static <T> android.os.Parcelable.Creator<T!>? readParcelableCreator(android.os.Parcel, ClassLoader?, Class<T!>);
     method @RequiresApi(api=android.os.Build.VERSION_CODES.Q) public static <T> java.util.List<T!> readParcelableList(android.os.Parcel, java.util.List<T!>, ClassLoader?, Class<T!>);
     method public static <T extends java.io.Serializable> T? readSerializable(android.os.Parcel, ClassLoader?, Class<T!>);
diff --git a/core/core/src/androidTest/java/androidx/core/content/IntentCompatTest.java b/core/core/src/androidTest/java/androidx/core/content/IntentCompatTest.java
index 2f43fb4..ec062a6 100644
--- a/core/core/src/androidTest/java/androidx/core/content/IntentCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/content/IntentCompatTest.java
@@ -30,6 +30,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -38,18 +41,26 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 
+import androidx.core.os.BuildCompat;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 
+import com.google.common.collect.Lists;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Objects;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -188,4 +199,117 @@
                 () -> IntentCompat.createManageUnusedAppRestrictionsIntent(
                         mContext, PACKAGE_NAME));
     }
+
+    @Test
+    public void getParcelableExtra() {
+        Intent intent = new Intent();
+        Signature signature = new Signature("");
+        intent.putExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertEquals(Signature.class, Objects.requireNonNull(
+                        IntentCompat.getParcelableExtra(intent, "extra", Signature.class))
+                .getClass());
+    }
+
+    @Test
+    public void getParcelableExtra_returnsNullOnClassMismatch() {
+        Intent intent = new Intent();
+        Signature signature = new Signature("");
+        intent.putExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertNull(IntentCompat.getParcelableExtra(intent, "extra", Intent.class));
+    }
+
+    @Test
+    public void getParcelableArrayExtra_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Intent intent = new Intent();
+        Signature[] signature = new Signature[] { new Signature("") };
+        intent.putExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertEquals(Signature[].class, Objects.requireNonNull(
+                IntentCompat.getParcelableArrayExtra(intent, "extra",
+                        Signature.class)).getClass());
+    }
+
+    @Test
+    public void getParcelableArrayExtra_returnsNullOnClassMismatch_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Intent intent = new Intent();
+        Signature[] signature = new Signature[] { new Signature("") };
+        intent.putExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertNull(IntentCompat.getParcelableArrayExtra(intent, "extra", Intent.class));
+    }
+
+    @Test
+    public void getParcelableArrayExtra_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Intent intent = new Intent();
+        Signature[] signature = new Signature[] { new Signature("") };
+        intent.putExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertEquals(Parcelable[].class, Objects.requireNonNull(
+                IntentCompat.getParcelableArrayExtra(intent, "extra",
+                        Signature.class)).getClass());
+
+        assertNotEquals(Signature[].class, Objects.requireNonNull(
+                IntentCompat.getParcelableArrayExtra(intent, "extra",
+                        Signature.class)).getClass());
+
+        // We do not check clazz Pre-U
+        assertEquals(Parcelable[].class, Objects.requireNonNull(
+                IntentCompat.getParcelableArrayExtra(intent, "extra",
+                        Intent.class)).getClass());
+    }
+
+    @Test
+    public void getParcelableArrayListExtra() {
+        Intent intent = new Intent();
+        ArrayList<Signature> signature = Lists.newArrayList(new Signature(""));
+        intent.putParcelableArrayListExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertEquals(Signature.class, Objects.requireNonNull(
+                IntentCompat.getParcelableArrayListExtra(intent, "extra",
+                        Signature.class)).get(0).getClass());
+    }
+
+    @Test
+    public void getParcelableArrayListExtra_returnsNullOnClassMismatch_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Intent intent = new Intent();
+        ArrayList<Signature> signature = Lists.newArrayList(new Signature(""));
+        intent.putParcelableArrayListExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        assertNull(IntentCompat.getParcelableArrayListExtra(intent, "extra", Intent.class));
+    }
+
+    @Test
+    public void getParcelableArrayListExtra_noTypeCheck_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Intent intent = new Intent();
+        ArrayList<Signature> signature = Lists.newArrayList(new Signature(""));
+        intent.putParcelableArrayListExtra("extra", signature);
+        parcelAndUnparcel(intent);
+
+        Object extra = Objects.requireNonNull(
+                IntentCompat.getParcelableArrayListExtra(intent, "extra",
+                        Intent.class)).get(0);
+        assertEquals(Signature.class, extra.getClass());
+    }
+
+    private void parcelAndUnparcel(Intent intent) {
+        Parcel p = Parcel.obtain();
+        intent.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        intent.readFromParcel(p);
+        p.recycle();
+    }
 }
diff --git a/core/core/src/androidTest/java/androidx/core/os/BundleCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/BundleCompatTest.java
new file mode 100644
index 0000000..d8cd469
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/os/BundleCompatTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 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 androidx.core.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.Intent;
+import android.content.pm.Signature;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.collect.Lists;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BundleCompatTest {
+
+    @Test
+    public void getParcelable() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable("parcelable", new Intent());
+        parcelAndUnparcel(bundle);
+
+        assertEquals(Intent.class, Objects.requireNonNull(
+                BundleCompat.getParcelable(bundle, "parcelable", Intent.class)).getClass());
+    }
+
+    @Test
+    public void getParcelable_returnsNullOnClassMismatch() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable("parcelable", new Intent());
+        parcelAndUnparcel(bundle);
+
+        assertNull(BundleCompat.getParcelable(bundle, "parcelable", Signature.class));
+    }
+
+    @Test
+    public void getParcelableArray_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArray("array", new Intent[] { new Intent() });
+        parcelAndUnparcel(bundle);
+
+        assertEquals(Intent[].class, Objects.requireNonNull(
+                BundleCompat.getParcelableArray(bundle, "array", Intent.class)).getClass());
+    }
+
+    @Test
+    public void getParcelableArray_returnsNullOnClassMismatch_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArray("array", new Intent[] { new Intent() });
+        parcelAndUnparcel(bundle);
+
+        assertNull(BundleCompat.getParcelableArray(bundle, "array", Signature.class));
+    }
+
+    @Test
+    public void getParcelableArray_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArray("array", new Intent[] { new Intent() });
+        parcelAndUnparcel(bundle);
+
+        assertEquals(Parcelable[].class, Objects.requireNonNull(
+                BundleCompat.getParcelableArray(bundle, "array", Intent.class)).getClass());
+
+        assertNotEquals(Intent[].class, Objects.requireNonNull(
+                BundleCompat.getParcelableArray(bundle, "array", Intent.class)).getClass());
+
+        // We do not check clazz Pre-U
+        assertEquals(Parcelable[].class, Objects.requireNonNull(
+                BundleCompat.getParcelableArray(bundle, "array", Signature.class)).getClass());
+    }
+
+    @Test
+    public void getParcelableArrayList() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList("array", Lists.newArrayList(new Intent()));
+        parcelAndUnparcel(bundle);
+
+        assertEquals(Intent.class, Objects.requireNonNull(
+                        BundleCompat.getParcelableArrayList(bundle, "array", Intent.class))
+                .get(0).getClass());
+    }
+
+    @Test
+    public void getParcelableArrayList_returnsNullOnClassMismatch_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList("array", Lists.newArrayList(new Intent()));
+        parcelAndUnparcel(bundle);
+
+        assertNull(BundleCompat.getParcelableArrayList(bundle, "array", Signature.class));
+    }
+
+    @Test
+    public void getParcelableArrayList_noTypeCheck_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        bundle.putParcelableArrayList("array", Lists.newArrayList(new Intent()));
+        parcelAndUnparcel(bundle);
+
+        Object intent = Objects.requireNonNull(
+                        BundleCompat.getParcelableArrayList(bundle, "array", Signature.class))
+                .get(0);
+        assertEquals(Intent.class, intent.getClass());
+    }
+
+    @Test
+    public void getSparseParcelableArray() {
+        Bundle bundle = new Bundle();
+        SparseArray<Intent> array = new SparseArray<>();
+        array.put(0, new Intent());
+        bundle.putSparseParcelableArray("array", array);
+        parcelAndUnparcel(bundle);
+
+        assertEquals(Intent.class, Objects.requireNonNull(
+                        BundleCompat.getSparseParcelableArray(bundle, "array", Intent.class))
+                .get(0).getClass());
+    }
+
+    @Test
+    @SdkSuppress(codeName = "UpsideDownCake")
+    public void getSparseParcelableArray_returnsNullOnClassMismatch_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        SparseArray<Intent> array = new SparseArray<>();
+        array.put(0, new Intent());
+        bundle.putSparseParcelableArray("array", array);
+        parcelAndUnparcel(bundle);
+
+        assertNull(BundleCompat.getSparseParcelableArray(bundle, "array", Signature.class));
+    }
+
+    @Test
+    public void getSparseParcelableArray_noTypeCheck_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Bundle bundle = new Bundle();
+        SparseArray<Intent> array = new SparseArray<>();
+        array.put(0, new Intent());
+        bundle.putSparseParcelableArray("array", array);
+        parcelAndUnparcel(bundle);
+
+        Object intent = Objects.requireNonNull(
+                        BundleCompat.getSparseParcelableArray(bundle, "array", Signature.class))
+                .get(0);
+        assertEquals(Intent.class, intent.getClass());
+    }
+
+    private void parcelAndUnparcel(Bundle bundle) {
+        Parcel p = Parcel.obtain();
+        bundle.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        bundle.readFromParcel(p);
+    }
+}
diff --git a/core/core/src/androidTest/java/androidx/core/os/ParcelCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/ParcelCompatTest.java
index 873a9c2..de31f9f 100644
--- a/core/core/src/androidTest/java/androidx/core/os/ParcelCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/os/ParcelCompatTest.java
@@ -16,13 +16,18 @@
 
 package androidx.core.os;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Intent;
 import android.content.pm.Signature;
 import android.graphics.Rect;
+import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,9 +42,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Objects;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -56,7 +59,7 @@
     }
 
     @Test
-    public void readParcelable2Arg() {
+    public void readParcelable() {
         Rect r = new Rect(0, 0, 10, 10);
         Parcel p = Parcel.obtain();
         p.writeParcelable(r, 0);
@@ -64,10 +67,14 @@
         p.setDataPosition(0);
         Rect r2 = ParcelCompat.readParcelable(p, Rect.class.getClassLoader(), Rect.class);
         assertEquals(r, r2);
+
+        p.setDataPosition(0);
+        assertThrows(BadParcelableException.class, () -> ParcelCompat.readParcelable(p,
+                Rect.class.getClassLoader(), Intent.class));
     }
 
     @Test
-    public void readArrayInT() {
+    public void readArray() {
         Parcel p = Parcel.obtain();
 
         Signature[] s = {new Signature("1234"),
@@ -78,7 +85,7 @@
         p.setDataPosition(0);
         Object[] objects = ParcelCompat.readArray(p, Signature.class.getClassLoader(),
                 Signature.class);
-        assertTrue(Arrays.equals(s, objects));
+        assertArrayEquals(s, objects);
         p.setDataPosition(0);
 
         p.recycle();
@@ -86,7 +93,7 @@
 
     @RequiresApi(api = Build.VERSION_CODES.S)
     @Test
-    public void readSparseArrayInT() {
+    public void readSparseArray() {
         Parcel p = Parcel.obtain();
 
         SparseArray<Signature> s = new SparseArray<>();
@@ -101,18 +108,17 @@
         assertEquals(s.size(), s1.size());
         for (int index = 0; index < s.size(); index++) {
             int key = s.keyAt(index);
-            assertTrue(Objects.equals(s.valueAt(index), s1.get(key)));
+            assertEquals(s.valueAt(index), s1.get(key));
         }
 
         p.recycle();
     }
 
     @Test
-    @SuppressWarnings("unchecked")
-    public void readListInT() {
+    public void readList() {
         Parcel p = Parcel.obtain();
-        ArrayList<Signature> s = new ArrayList();
-        ArrayList<Signature> s2 = new ArrayList();
+        ArrayList<Signature> s = new ArrayList<>();
+        ArrayList<Signature> s2 = new ArrayList<>();
         s.add(new Signature("1234567890abcdef"));
         s.add(new Signature("abcdef1234567890"));
 
@@ -127,7 +133,7 @@
     }
 
     @Test
-    public void readArrayListInT() {
+    public void readArrayList() {
         Parcel p = Parcel.obtain();
 
         ArrayList<Signature> s = new ArrayList<>();
@@ -145,7 +151,7 @@
     }
 
     @Test
-    public void readMapInT() {
+    public void readMap() {
         Parcel p = Parcel.obtain();
         ClassLoader loader = getClass().getClassLoader();
         HashMap<String, Signature> map = new HashMap<>();
@@ -163,7 +169,7 @@
     }
 
     @Test
-    public void readHashMapInT() {
+    public void readHashMap() {
         Parcel p = Parcel.obtain();
         ClassLoader loader = getClass().getClassLoader();
         HashMap<String, Signature> map = new HashMap<>();
@@ -181,7 +187,7 @@
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
     @Test
-    public void readParcelableCreatorInT() {
+    public void readParcelableCreator() {
         final String signatureString  = "1234567890abcdef";
         Signature s = new Signature(signatureString);
 
@@ -196,7 +202,7 @@
     }
 
     @Test
-    public void readParcelableArrayInT() {
+    public void readParcelableArray() {
         Parcel p = Parcel.obtain();
         Signature[] s = {new Signature("1234"),
                 null,
@@ -206,13 +212,71 @@
         p.setDataPosition(0);
         Parcelable[] s1 = ParcelCompat.readParcelableArray(p, Signature.class.getClassLoader(),
                 Signature.class);
-        assertTrue(Arrays.equals(s, s1));
+        assertArrayEquals(s, s1);
+        assertEquals(Signature[].class, s1.getClass());
+
+        p.setDataPosition(0);
+        Parcelable[] s2 = ParcelCompat.readParcelableArray(p, Parcelable.class.getClassLoader(),
+                Parcelable.class);
+        assertArrayEquals(s, s2);
+        assertEquals(Parcelable[].class, s2.getClass());
+
+        p.setDataPosition(0);
+        assertThrows(BadParcelableException.class, () -> ParcelCompat.readParcelableArray(p,
+                Signature.class.getClassLoader(), Intent.class));
+
+        p.recycle();
+    }
+
+    @Test
+    public void readParcelableArrayTyped_postU() {
+        if (!BuildCompat.isAtLeastU()) return;
+        Parcel p = Parcel.obtain();
+        Signature[] s = {new Signature("1234"),
+                null,
+                new Signature("abcd")
+        };
+        p.writeParcelableArray(s, 0);
+        p.setDataPosition(0);
+        Parcelable[] s1 = ParcelCompat.readParcelableArrayTyped(p, Signature.class.getClassLoader(),
+                Signature.class);
+        assertArrayEquals(s, s1);
+        assertEquals(Signature[].class, s1.getClass());
+
+        p.setDataPosition(0);
+        assertThrows(BadParcelableException.class, () -> ParcelCompat.readParcelableArrayTyped(p,
+                Signature.class.getClassLoader(), Intent.class));
+
+        p.recycle();
+    }
+
+    @Test
+    public void readParcelableArrayTyped_preU() {
+        if (BuildCompat.isAtLeastU()) return;
+        Parcel p = Parcel.obtain();
+        Signature[] s = {new Signature("1234"),
+                null,
+                new Signature("abcd")
+        };
+        p.writeParcelableArray(s, 0);
+        p.setDataPosition(0);
+        Parcelable[] s1 = ParcelCompat.readParcelableArrayTyped(p, Signature.class.getClassLoader(),
+                Signature.class);
+        assertArrayEquals(s, s1);
+        assertNotEquals(Signature[].class, s1.getClass());
+
+        // Type not checked pre-U
+        p.setDataPosition(0);
+        s1 = ParcelCompat.readParcelableArrayTyped(p, Signature.class.getClassLoader(),
+                Intent.class);
+        assertArrayEquals(s, s1);
+
         p.recycle();
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
-    public void readParcelableListInT() {
+    public void readParcelableList() {
         final Parcel p = Parcel.obtain();
         ArrayList<Signature> list = new ArrayList<>();
         ArrayList<Signature> list1 = new ArrayList<>();
diff --git a/core/core/src/main/java/androidx/core/content/IntentCompat.java b/core/core/src/main/java/androidx/core/content/IntentCompat.java
index ad7e5d57..8647f13 100644
--- a/core/core/src/main/java/androidx/core/content/IntentCompat.java
+++ b/core/core/src/main/java/androidx/core/content/IntentCompat.java
@@ -29,10 +29,16 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Parcelable;
 
 import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
 import androidx.annotation.RequiresApi;
+import androidx.core.os.BuildCompat;
+
+import java.util.ArrayList;
 
 /**
  * Helper for accessing features in {@link Intent}.
@@ -202,6 +208,104 @@
         }
     }
 
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, the object type is checked after deserialization.
+     * </ul>
+     *
+     * @param in The intent to retrieve from.
+     * @param name The name of the desired item.
+     * @param clazz The type of the object expected.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable value was found.
+     *
+     * @see Intent#putExtra(String, Parcelable)
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation", "unchecked"})
+    public static <T> T getParcelableExtra(@NonNull Intent in, @Nullable String name,
+            @NonNull Class<T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelableExtra(in, name, clazz);
+        } else {
+            T extra = in.getParcelableExtra(name);
+            return clazz.isInstance(extra) ? extra : null;
+        }
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
+     * @param in The intent to retrieve from.
+     * @param name The name of the desired item.
+     * @param clazz The type of the items inside the array. This is only verified when unparceling.
+     *
+     * @return the value of an item previously added with putExtra(),
+     * or null if no Parcelable[] value was found.
+     *
+     * @see Intent#putExtra(String, Parcelable[])
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation"})
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    public static Parcelable[] getParcelableArrayExtra(@NonNull Intent in, @Nullable String name,
+            @NonNull Class<? extends Parcelable> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelableArrayExtra(in, name, clazz);
+        } else {
+            return in.getParcelableArrayExtra(name);
+        }
+    }
+
+    /**
+     * Retrieve extended data from the intent.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
+     * @param in The intent to retrieve from.
+     * @param name The name of the desired item.
+     * @param clazz The type of the items inside the array list. This is only verified when
+     *     unparceling.
+     *
+     * @return the value of an item previously added with
+     * putParcelableArrayListExtra(), or null if no
+     * ArrayList<Parcelable> value was found.
+     *
+     * @see Intent#putParcelableArrayListExtra(String, ArrayList)
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation", "unchecked"})
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    public static <T> ArrayList<T> getParcelableArrayListExtra(
+            @NonNull Intent in, @Nullable String name, @NonNull Class<? extends T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelableArrayListExtra(in, name, clazz);
+        } else {
+            return (ArrayList<T>) in.getParcelableArrayListExtra(name);
+        }
+    }
+
     @RequiresApi(15)
     static class Api15Impl {
         private Api15Impl() {
@@ -213,4 +317,29 @@
             return Intent.makeMainSelectorActivity(selectorAction, selectorCategory);
         }
     }
+
+    @RequiresApi(33)
+    static class Api33Impl {
+        private Api33Impl() {
+            // This class is non-instantiable.
+        }
+
+        @DoNotInline
+        static <T> T getParcelableExtra(@NonNull Intent in, @Nullable String name,
+                @NonNull Class<T> clazz) {
+            return in.getParcelableExtra(name, clazz);
+        }
+
+        @DoNotInline
+        static <T> T[] getParcelableArrayExtra(@NonNull Intent in, @Nullable String name,
+                @NonNull Class<T> clazz) {
+            return in.getParcelableArrayExtra(name, clazz);
+        }
+
+        @DoNotInline
+        static <T> ArrayList<T> getParcelableArrayListExtra(@NonNull Intent in,
+                @Nullable String name, @NonNull Class<? extends T> clazz) {
+            return in.getParcelableArrayListExtra(name, clazz);
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/BundleCompat.java b/core/core/src/main/java/androidx/core/os/BundleCompat.java
new file mode 100644
index 0000000..15bb312
--- /dev/null
+++ b/core/core/src/main/java/androidx/core/os/BundleCompat.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 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 androidx.core.os;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.OptIn;
+import androidx.annotation.RequiresApi;
+
+import java.util.ArrayList;
+
+/**
+ * Helper for accessing features in {@link Bundle}.
+ */
+public final class BundleCompat {
+    private BundleCompat() {
+        /* Hide constructor */
+    }
+
+    /**
+     * Returns the value associated with the given key or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link Bundle#setClassLoader(ClassLoader)} with the proper {@link ClassLoader}
+     * first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, the object type is checked after deserialization.
+     * </ul>
+     *
+     *
+     * @param in The bundle to retrieve from.
+     * @param key a String, or {@code null}
+     * @param clazz The type of the object expected
+     * @return a Parcelable value, or {@code null}
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation", "unchecked"})
+    public static <T> T getParcelable(@NonNull Bundle in, @Nullable String key,
+            @NonNull Class<T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelable(in, key, clazz);
+        } else {
+            T parcelable = in.getParcelable(key);
+            return clazz.isInstance(parcelable) ? parcelable : null;
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link Bundle#setClassLoader(ClassLoader)} with the proper {@link ClassLoader}
+     * first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
+     * @param in The bundle to retrieve from.
+     * @param key a String, or {@code null}
+     * @param clazz The type of the items inside the array. This is only verified when unparceling.
+     * @return a Parcelable[] value, or {@code null}
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation"})
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    public static Parcelable[] getParcelableArray(@NonNull Bundle in, @Nullable String key,
+            @NonNull Class<? extends Parcelable> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelableArray(in, key, clazz);
+        } else {
+            return in.getParcelableArray(key);
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * <p><b>Note: </b> if the expected value is not a class provided by the Android platform,
+     * you must call {@link Bundle#setClassLoader(ClassLoader)} with the proper {@link ClassLoader}
+     * first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the list elements' types.
+     * </ul>
+     *
+     * @param in The bundle to retrieve from.
+     * @param key   a String, or {@code null}
+     * @param clazz The type of the items inside the array list. This is only verified when
+     *     unparceling.
+     * @return an ArrayList<T> value, or {@code null}
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation", "unchecked"})
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    public static  <T> ArrayList<T> getParcelableArrayList(@NonNull Bundle in, @Nullable String key,
+            @NonNull Class<? extends T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getParcelableArrayList(in, key, clazz);
+        } else {
+            return (ArrayList<T>) in.getParcelableArrayList(key);
+        }
+    }
+
+    /**
+     * Returns the value associated with the given key, or {@code null} if:
+     * <ul>
+     *     <li>No mapping of the desired type exists for the given key.
+     *     <li>A {@code null} value is explicitly associated with the key.
+     *     <li>The object is not of type {@code clazz}.
+     * </ul>
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
+     * @param in The bundle to retrieve from.
+     * @param key a String, or null
+     * @param clazz The type of the items inside the sparse array. This is only verified when
+     *     unparceling.
+     * @return a SparseArray of T values, or null
+     */
+    @Nullable
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation", "unchecked"})
+    public static <T> SparseArray<T> getSparseParcelableArray(@NonNull Bundle in,
+            @Nullable String key, @NonNull Class<? extends T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.getSparseParcelableArray(in, key, clazz);
+        } else {
+            return (SparseArray<T>) in.getSparseParcelableArray(key);
+        }
+    }
+
+    @RequiresApi(33)
+    static class Api33Impl {
+        private Api33Impl() {
+            // This class is non-instantiable.
+        }
+
+        @DoNotInline
+        static <T> T getParcelable(@NonNull Bundle in, @Nullable String key,
+                @NonNull Class<T> clazz) {
+            return in.getParcelable(key, clazz);
+        }
+
+        @DoNotInline
+        static <T> T[] getParcelableArray(@NonNull Bundle in, @Nullable String key,
+                @NonNull Class<T> clazz) {
+            return in.getParcelableArray(key, clazz);
+        }
+
+        @DoNotInline
+        static <T> ArrayList<T> getParcelableArrayList(@NonNull Bundle in, @Nullable String key,
+                @NonNull Class<? extends T> clazz) {
+            return in.getParcelableArrayList(key, clazz);
+        }
+
+        @DoNotInline
+        static <T> SparseArray<T> getSparseParcelableArray(@NonNull Bundle in, @Nullable String key,
+                @NonNull Class<? extends T> clazz) {
+            return in.getSparseParcelableArray(key, clazz);
+        }
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/os/ParcelCompat.java b/core/core/src/main/java/androidx/core/os/ParcelCompat.java
index 8fa56eb..7de5a60 100644
--- a/core/core/src/main/java/androidx/core/os/ParcelCompat.java
+++ b/core/core/src/main/java/androidx/core/os/ParcelCompat.java
@@ -17,6 +17,7 @@
 package androidx.core.os;
 
 import android.annotation.SuppressLint;
+import android.os.BadParcelableException;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,6 +30,7 @@
 import androidx.annotation.RequiresApi;
 
 import java.io.Serializable;
+import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -47,7 +49,7 @@
     }
 
     /**
-     * Write a boolean value into the parcel at the current {@link Parcel#dataPosition()},
+     * Write a boolean value into the parcel at the current f{@link Parcel#dataPosition()},
      * growing {@link Parcel#dataCapacity()} if needed.
      *
      * <p>Note: This method currently delegates to {@link Parcel#writeInt} with a value of 1 or 0
@@ -61,6 +63,13 @@
      * Same as {@link Parcel#readList(List, ClassLoader)} but accepts {@code clazz} parameter as
      * the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the list elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -69,8 +78,8 @@
     @SuppressWarnings("deprecation")
     public static <T> void readList(@NonNull Parcel in, @NonNull List<? super T> outVal,
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            TiramisuImpl.readList(in, outVal, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            Api33Impl.readList(in, outVal, loader, clazz);
         } else {
             in.readList(outVal, loader);
         }
@@ -80,6 +89,13 @@
      * Same as {@link Parcel#readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
      * the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the list elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -90,8 +106,8 @@
     @Nullable
     public static <T> ArrayList<T> readArrayList(@NonNull Parcel in, @Nullable ClassLoader loader,
             @NonNull Class<? extends T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readArrayList(in, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readArrayList(in, loader, clazz);
         } else {
             return in.readArrayList(loader);
         }
@@ -101,6 +117,13 @@
      * Same as {@link Parcel#readArray(ClassLoader)} but accepts {@code clazz} parameter as
      * the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -109,12 +132,12 @@
     @SuppressWarnings({"deprecation", "unchecked"})
     @SuppressLint({"ArrayReturn", "NullableCollection"})
     @Nullable
-    public static <T> T[] readArray(@NonNull Parcel in, @Nullable ClassLoader loader,
+    public static <T> Object[] readArray(@NonNull Parcel in, @Nullable ClassLoader loader,
             @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readArray(in, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readArray(in, loader, clazz);
         } else {
-            return (T[]) in.readArray(loader);
+            return in.readArray(loader);
         }
     }
 
@@ -122,6 +145,13 @@
      * Same as {@link Parcel#readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
      * the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -130,20 +160,25 @@
     @SuppressWarnings("deprecation")
     @Nullable
     public static <T> SparseArray<T> readSparseArray(@NonNull Parcel in,
-            @Nullable ClassLoader loader,
-            @NonNull Class<? extends T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readSparseArray(in, loader, clazz);
+            @Nullable ClassLoader loader, @NonNull Class<? extends T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readSparseArray(in, loader, clazz);
         } else {
             return in.readSparseArray(loader);
         }
     }
 
-
     /**
      * Same as {@link Parcel#readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
      * {@code clazzValue} parameter as the types required for each key and value pair.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the map entries' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException If the item to be deserialized is not an
      * instance of that class or any of its children class.
      */
@@ -152,8 +187,8 @@
     public static <K, V> void readMap(@NonNull Parcel in, @NonNull Map<? super K, ? super V> outVal,
             @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
             @NonNull Class<V> clazzValue) {
-        if (BuildCompat.isAtLeastT()) {
-            TiramisuImpl.readMap(in, outVal, loader, clazzKey, clazzValue);
+        if (BuildCompat.isAtLeastU()) {
+            Api33Impl.readMap(in, outVal, loader, clazzKey, clazzValue);
         } else {
             in.readMap(outVal, loader);
         }
@@ -163,6 +198,13 @@
      * Same as {@link Parcel#readHashMap(ClassLoader)} but accepts {@code clazzKey} and
      * {@code clazzValue} parameter as the types required for each key and value pair.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the map entries' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException if the item to be deserialized is not an
      * instance of that class or any of its children class.
      */
@@ -172,8 +214,8 @@
     @Nullable
     public static <K, V> HashMap<K, V> readHashMap(@NonNull Parcel in, @Nullable ClassLoader loader,
             @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readHashMap(in, loader, clazzKey, clazzValue);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readHashMap(in, loader, clazzKey, clazzValue);
         } else {
             return in.readHashMap(loader);
         }
@@ -183,6 +225,13 @@
      * Same as {@link Parcel#readParcelable(ClassLoader)} but accepts {@code clazz} parameter as
      * the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, the object type is checked after deserialization.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -192,10 +241,16 @@
     @Nullable
     public static <T extends Parcelable> T readParcelable(@NonNull Parcel in,
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readParcelable(in, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readParcelable(in, loader, clazz);
         } else {
-            return in.readParcelable(loader);
+            T parcelable = in.readParcelable(loader);
+            if (!clazz.isInstance(parcelable)) {
+                throw new BadParcelableException("Parcelable " + parcelable.getClass() + " is not "
+                        + "a subclass of required class " + clazz.getName()
+                        + " provided in the parameter");
+            }
+            return parcelable;
         }
     }
 
@@ -203,6 +258,13 @@
      * Same as {@link Parcel#readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
      * as the required type.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the creator's type.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there
      * there was an error trying to read the {@link Parcelable.Creator}.
@@ -213,8 +275,8 @@
     @RequiresApi(30)
     public static <T> Parcelable.Creator<T> readParcelableCreator(@NonNull Parcel in,
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readParcelableCreator(in, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readParcelableCreator(in, loader, clazz);
         } else {
             return (Parcelable.Creator<T>) Api30Impl.readParcelableCreator(in, loader);
         }
@@ -224,20 +286,76 @@
      * Same as {@link Parcel#readParcelableArray(ClassLoader)}  but accepts {@code clazz} parameter
      * as the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
+     *
+     * @deprecated This method incurs a performance penalty on SDK 33 and below. Use
+     * {@link #readParcelableArrayTyped} instead.
      */
     @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
     @SuppressWarnings({"deprecation", "unchecked"})
     @SuppressLint({"ArrayReturn", "NullableCollection"})
     @Nullable
+    @Deprecated
     public static <T> T[] readParcelableArray(@NonNull Parcel in, @Nullable ClassLoader loader,
             @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readParcelableArray(in, loader, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readParcelableArray(in, loader, clazz);
         } else {
-            return (T[]) in.readParcelableArray(loader);
+            // The array type is always Parcelable[]. Cast to clazz[] for compatibility if needed.
+            Parcelable[] parcelables = in.readParcelableArray(loader);
+            if (clazz.isAssignableFrom(Parcelable.class)) {
+                return (T[]) parcelables;
+            }
+
+            T[] typedParcelables = (T[]) Array.newInstance(clazz, parcelables.length);
+            for (int i = 0; i < parcelables.length; i++) {
+                try {
+                    typedParcelables[i] = clazz.cast(parcelables[i]);
+                } catch (ClassCastException e) {
+                    throw new BadParcelableException("Parcelable at index " + i + " is not "
+                            + "a subclass of required class " + clazz.getName()
+                            + " provided in the parameter");
+                }
+            }
+
+            return typedParcelables;
+        }
+    }
+
+    /**
+     * Same as {@link Parcel#readParcelableArray(ClassLoader)}  but accepts {@code clazz} parameter
+     * as the type required for each item.
+     *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the array elements' types.
+     * </ul>
+     *
+     * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
+     * deserialized is not an instance of that class or any of its children classes or there was
+     * an error trying to instantiate an element.
+     */
+    @OptIn(markerClass = BuildCompat.PrereleaseSdkCheck.class)
+    @SuppressWarnings({"deprecation"})
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @Nullable
+    public static <T> Parcelable[] readParcelableArrayTyped(@NonNull Parcel in,
+            @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        if (BuildCompat.isAtLeastU()) {
+            return (Parcelable[]) Api33Impl.readParcelableArray(in, loader, clazz);
+        } else {
+            return in.readParcelableArray(loader);
         }
     }
 
@@ -245,6 +363,13 @@
      * Same as {@link Parcel#readParcelableList(List, ClassLoader)} but accepts {@code clazz}
      * parameter as the type required for each item.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>{@link BuildCompat#isAtLeastU() Android U and later}, this method matches platform
+     *     behavior.
+     *     <li>SDK 33 and below, this method will not check the list elements' types.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children classes or there was
      * an error trying to instantiate an element.
@@ -255,8 +380,8 @@
     @RequiresApi(api = Build.VERSION_CODES.Q)
     public static <T> List<T> readParcelableList(@NonNull Parcel in, @NonNull List<T> list,
             @Nullable ClassLoader cl, @NonNull Class<T> clazz) {
-        if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readParcelableList(in, list, cl, clazz);
+        if (BuildCompat.isAtLeastU()) {
+            return Api33Impl.readParcelableList(in, list, cl, clazz);
         } else {
             return Api29Impl.readParcelableList(in, (List) list, cl);
         }
@@ -267,6 +392,13 @@
      * as the primary classLoader for resolving the Serializable class; and {@code clazz} parameter
      * as the required type.
      *
+     * Compatibility behavior:
+     * <ul>
+     *     <li>SDK 33 and later, this method matches platform
+     *     behavior.
+     *     <li>SDK 32 and below, this method will not check the item's type.
+     * </ul>
+     *
      * @throws android.os.BadParcelableException Throws BadParcelableException if the item to be
      * deserialized is not an instance of that class or any of its children class or there there
      * was an error deserializing the object.
@@ -277,7 +409,7 @@
     public static <T extends Serializable> T readSerializable(@NonNull Parcel in,
             @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
         if (BuildCompat.isAtLeastT()) {
-            return TiramisuImpl.readSerializable(in, loader, clazz);
+            return Api33Impl.readSerializable(in, loader, clazz);
         } else {
             return (T) in.readSerializable();
         }
@@ -292,7 +424,7 @@
         }
 
         @DoNotInline
-        static final <T extends Parcelable> List<T> readParcelableList(@NonNull Parcel in,
+        static <T extends Parcelable> List<T> readParcelableList(@NonNull Parcel in,
                 @NonNull List<T> list, @Nullable ClassLoader cl) {
             return in.readParcelableList(list, cl);
         }
@@ -305,15 +437,15 @@
         }
 
         @DoNotInline
-        static final Parcelable.Creator<?> readParcelableCreator(@NonNull Parcel in,
+        static Parcelable.Creator<?> readParcelableCreator(@NonNull Parcel in,
                 @Nullable ClassLoader loader) {
             return in.readParcelableCreator(loader);
         }
     }
 
     @RequiresApi(33)
-    static class TiramisuImpl {
-        private TiramisuImpl() {
+    static class Api33Impl {
+        private Api33Impl() {
             // This class is non-instantiable.
         }
 
@@ -330,7 +462,7 @@
         }
 
         @DoNotInline
-        public static <T> Parcelable.Creator<T> readParcelableCreator(Parcel in, ClassLoader loader,
+        static <T> Parcelable.Creator<T> readParcelableCreator(Parcel in, ClassLoader loader,
                 Class<T> clazz) {
             return in.readParcelableCreator(loader, clazz);
         }
@@ -348,36 +480,36 @@
         }
 
         @DoNotInline
-        public static <T> void readList(@NonNull Parcel in, @NonNull List<? super T> outVal,
+        static <T> void readList(@NonNull Parcel in, @NonNull List<? super T> outVal,
                 @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
             in.readList(outVal, loader, clazz);
         }
 
         @DoNotInline
-        public static <T> ArrayList<T> readArrayList(Parcel in, ClassLoader loader,
+        static <T> ArrayList<T> readArrayList(Parcel in, ClassLoader loader,
                 Class<? extends T> clazz) {
             return in.readArrayList(loader, clazz);
         }
 
         @DoNotInline
-        public static <T> T[] readArray(Parcel in, ClassLoader loader, Class<T> clazz) {
+        static <T> T[] readArray(Parcel in, ClassLoader loader, Class<T> clazz) {
             return in.readArray(loader, clazz);
         }
 
         @DoNotInline
-        public static <T> SparseArray<T> readSparseArray(Parcel in, ClassLoader loader,
+        static <T> SparseArray<T> readSparseArray(Parcel in, ClassLoader loader,
                 Class<? extends T> clazz) {
             return in.readSparseArray(loader, clazz);
         }
 
         @DoNotInline
-        public static <K, V> void readMap(Parcel in, Map<? super K, ? super V> outVal,
+        static <K, V> void readMap(Parcel in, Map<? super K, ? super V> outVal,
                 ClassLoader loader, Class<K> clazzKey, Class<V> clazzValue) {
             in.readMap(outVal, loader, clazzKey, clazzValue);
         }
 
         @DoNotInline
-        public static <V, K> HashMap<K, V> readHashMap(Parcel in, ClassLoader loader,
+        static <V, K> HashMap<K, V> readHashMap(Parcel in, ClassLoader loader,
                 Class<? extends K> clazzKey, Class<? extends V> clazzValue) {
             return in.readHashMap(loader, clazzKey, clazzValue);
         }