Add safer Bundle APIs and deprecated old ones

Add safer Bundle APIs that take an extra Class<T> argument that checks
that the type about to be deserialized is a child of the type passed in
parameter *before* actually deserializing it, while also deprecating old
APIs.

This allows use to reap the benefits of the new typed Parcel APIs and
enhances security.

Only the APIs that could involve custom object injection are modified.
So, besides the obvious ones that have that design (eg.
readParcelableList()), subtler cases such as readIntegerArrayList()
could result in custom object deserialization, and since it's all
generics, even the casting inside Bundle wouldn't fail, only after the
client unpacked the list items would it blow up. Now those are checked
beforehand.

Since Bundle always calls Parcel.readValue() under the hood (instead of
specialized APIs such as readParcelable() etc), we had to augment that
method (that's used by LazyValue when retrieving the item) to accept
item types now for containers, which I implemented as a vararg of
Class<?> parameters (this is all private/@hide). This way we could
retrieve a list of intents like readValue(.., List.class, Intent.class),
or a map of string to intents like readValue(.., Map.class,
String.class, Intent.class). For non-container items, we can just pass
no arguments for the vararg. This is explained in internal javadocs.

Inside readValue() now, we also check the container types before
calling the internal methods for deserialization. So, if the thing on
the wire is a VAL_MAP and we know the method we're about to call will
return a HashMap, we verify that the type passed in parameter is a super
type of that (if it's non-null, if it's null it means "perform no
check").

Now, LazyValue became a BiFunction<Class<?>, Class<?>[], Object> to
receive those extra "item types" for containers. The reason for
separating the first from the rest is that the first defines the return
type in the new APIs and inside Parcel, so we need the T from Class<T>
to ensure type-safety.

(I was torn here between using BiFunction or just exposing LazyValue as
@hide for Bundle since it feels like we're missing meaning/abstraction,
but end up leaving this way, advise if you'd prefer the other way)

There was a bit of a refactor in Parcel so readValue() could call
internal methods that accepted nullable Class<?> parameters with the
meaning that null = "no verification"  and non-null = "check against
type provided" (because the external APIs all require non-null
parameters).

Now we can return null in all cases when there is a type mismatch. Note
that the Bundle APIs catch ClassCastException to return null, but that
only works for non-generic types (eg. getSizeF()). For generic types
wrapping "return (T) o" with try-catch doesn't work because the type
gets erased to its bound at runtime, so the type mismatch escapes that
try-catch to the caller, potentially causing a crash. Now they happen
inside the getters, as the non-generic ones.

Test: Boots for now
Test: Working on CTS
Test: atest -d android.os.cts.ParcelTest android.os.cts.BundleTest android.os.BundleTest android.os.ParcelTest
CTS-Coverage-Bug: 219980813
Change-Id: Ifcbeb34b4684d7de105756b9d414162a9205ffaa
diff --git a/core/api/current.txt b/core/api/current.txt
index 4651747..ba430af 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29424,7 +29424,7 @@
   public class BaseBundle {
     method public void clear();
     method public boolean containsKey(String);
-    method @Nullable public Object get(String);
+    method @Deprecated @Nullable public Object get(String);
     method public boolean getBoolean(String);
     method public boolean getBoolean(String, boolean);
     method @Nullable public boolean[] getBooleanArray(@Nullable String);
@@ -29665,16 +29665,21 @@
     method public float getFloat(String, float);
     method @Nullable public float[] getFloatArray(@Nullable String);
     method @Nullable public java.util.ArrayList<java.lang.Integer> getIntegerArrayList(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
-    method @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
-    method @Nullable public java.io.Serializable getSerializable(@Nullable String);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelable(@Nullable String);
+    method @Nullable public <T> T getParcelable(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArray(@Nullable String);
+    method @Nullable public <T> T[] getParcelableArray(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayList(@Nullable String);
+    method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayList(@Nullable String, @NonNull Class<T>);
+    method @Deprecated @Nullable public java.io.Serializable getSerializable(@Nullable String);
+    method @Nullable public <T extends java.io.Serializable> T getSerializable(@Nullable String, @NonNull Class<T>);
     method public short getShort(String);
     method public short getShort(String, short);
     method @Nullable public short[] getShortArray(@Nullable String);
     method @Nullable public android.util.Size getSize(@Nullable String);
     method @Nullable public android.util.SizeF getSizeF(@Nullable String);
-    method @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+    method @Deprecated @Nullable public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String);
+    method @Nullable public <T> android.util.SparseArray<T> getSparseParcelableArray(@Nullable String, @NonNull Class<T>);
     method @Nullable public java.util.ArrayList<java.lang.String> getStringArrayList(@Nullable String);
     method public boolean hasFileDescriptors();
     method public void putAll(android.os.Bundle);
diff --git a/core/java/android/os/BadTypeParcelableException.java b/core/java/android/os/BadTypeParcelableException.java
new file mode 100644
index 0000000..2ca3bd2
--- /dev/null
+++ b/core/java/android/os/BadTypeParcelableException.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.os;
+
+/** Used by Parcel to signal that the type on the payload was not expected by the caller. */
+class BadTypeParcelableException extends BadParcelableException {
+    BadTypeParcelableException(String msg) {
+        super(msg);
+    }
+    BadTypeParcelableException(Exception cause) {
+        super(cause);
+    }
+    BadTypeParcelableException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 244335d..45812e5 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -31,7 +33,7 @@
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Set;
-import java.util.function.Function;
+import java.util.function.BiFunction;
 
 /**
  * A mapping from String keys to values of various types. In most cases, you
@@ -254,8 +256,8 @@
         }
         try {
             return getValueAt(0, String.class);
-        } catch (ClassCastException | BadParcelableException e) {
-            typeWarning("getPairValue()", /* value */ null, "String", e);
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning("getPairValue()", "String", e);
             return null;
         }
     }
@@ -320,28 +322,46 @@
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
      *
+     * @deprecated Use {@link #getValue(String, Class, Class[])}. This method should only be used in
+     *      other deprecated APIs.
+     *
      * @hide
      */
+    @Deprecated
+    @Nullable
     final Object getValue(String key) {
         return getValue(key, /* clazz */ null);
     }
 
+    /** Same as {@link #getValue(String, Class, Class[])} with no item types. */
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return getValue(key, clazz, (Class<?>[]) null);
+    }
+
     /**
-     * Returns the value for key {@code key} for expected return type {@param clazz} (or {@code
+     * Returns the value for key {@code key} for expected return type {@code clazz} (or pass {@code
      * null} for no type check).
      *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
+     *
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
      *
      * @hide
      */
-    final <T> T getValue(String key, @Nullable Class<T> clazz) {
+    @Nullable
+    final <T> T getValue(String key, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
         int i = mMap.indexOfKey(key);
-        return (i >= 0) ? getValueAt(i, clazz) : null;
+        return (i >= 0) ? getValueAt(i, clazz, itemTypes) : null;
     }
 
     /**
-     * Returns the value for a certain position in the array map.
+     * Returns the value for a certain position in the array map for expected return type {@code
+     * clazz} (or pass {@code null} for no type check).
+     *
+     * For {@code itemTypes}, see {@link Parcel#readValue(int, ClassLoader, Class, Class[])}.
      *
      * This call should always be made after {@link #unparcel()} or inside a lock after making sure
      * {@code mMap} is not null.
@@ -349,11 +369,12 @@
      * @hide
      */
     @SuppressWarnings("unchecked")
-    final <T> T getValueAt(int i, @Nullable Class<T> clazz) {
+    @Nullable
+    final <T> T getValueAt(int i, @Nullable Class<T> clazz, @Nullable Class<?>... itemTypes) {
         Object object = mMap.valueAt(i);
-        if (object instanceof Function<?, ?>) {
+        if (object instanceof BiFunction<?, ?, ?>) {
             try {
-                object = ((Function<Class<?>, ?>) object).apply(clazz);
+                object = ((BiFunction<Class<?>, Class<?>[], ?>) object).apply(clazz, itemTypes);
             } catch (BadParcelableException e) {
                 if (sShouldDefuse) {
                     Log.w(TAG, "Failed to parse item " + mMap.keyAt(i) + ", returning null.", e);
@@ -615,7 +636,11 @@
      *
      * @param key a String key
      * @return an Object, or null
+     *
+     * @deprecated Use the type-safe specific APIs depending on the type of the item to be
+     *      retrieved, eg. {@link #getString(String)}.
      */
+    @Deprecated
     @Nullable
     public Object get(String key) {
         unparcel();
@@ -623,6 +648,32 @@
     }
 
     /**
+     * Returns the object of type {@code clazz} for the given {@code 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>Use the more specific APIs where possible, especially in the case of containers such as
+     * lists, since those APIs allow you to specify the type of the items.
+     *
+     * @param key String key
+     * @param clazz The type of the object expected
+     * @return an Object, or null
+     */
+    @Nullable
+    <T> T get(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName(), e);
+            return null;
+        }
+    }
+
+    /**
      * Removes any entry with the given key from the mapping of this Bundle.
      *
      * @param key a String key
@@ -1006,7 +1057,7 @@
             sb.append(" but value was a ");
             sb.append(value.getClass().getName());
         } else {
-            sb.append(" but value was of a different type ");
+            sb.append(" but value was of a different type");
         }
         sb.append(".  The default value ");
         sb.append(defaultValue);
@@ -1019,6 +1070,10 @@
         typeWarning(key, value, className, "<null>", e);
     }
 
+    void typeWarning(String key, String className, RuntimeException e) {
+        typeWarning(key, /* value */ null, className, "<null>", e);
+    }
+
     /**
      * Returns the value associated with the given key, or defaultValue if
      * no mapping of the desired type exists for the given key.
@@ -1358,7 +1413,11 @@
      *
      * @param key a String, or null
      * @return a Serializable value, or null
+     *
+     * @deprecated Use {@link #getSerializable(String, Class)}. This method should only be used in
+     *      other deprecated APIs.
      */
+    @Deprecated
     @Nullable
     Serializable getSerializable(@Nullable String key) {
         unparcel();
@@ -1375,6 +1434,36 @@
     }
 
     /**
+     * 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>
+     *
+     * @param key a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    <T extends Serializable> T getSerializable(@Nullable String key, @NonNull Class<T> clazz) {
+        return get(key, clazz);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    <T> ArrayList<T> getArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+        unparcel();
+        try {
+            return getValue(key, ArrayList.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "ArrayList<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
@@ -1384,17 +1473,7 @@
      */
     @Nullable
     ArrayList<Integer> getIntegerArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<Integer>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<Integer>", e);
-            return null;
-        }
+        return getArrayList(key, Integer.class);
     }
 
     /**
@@ -1407,17 +1486,7 @@
      */
     @Nullable
     ArrayList<String> getStringArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<String>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<String>", e);
-            return null;
-        }
+        return getArrayList(key, String.class);
     }
 
     /**
@@ -1430,17 +1499,7 @@
      */
     @Nullable
     ArrayList<CharSequence> getCharSequenceArrayList(@Nullable String key) {
-        unparcel();
-        Object o = getValue(key);
-        if (o == null) {
-            return null;
-        }
-        try {
-            return (ArrayList<CharSequence>) o;
-        } catch (ClassCastException e) {
-            typeWarning(key, o, "ArrayList<CharSequence>", e);
-            return null;
-        }
+        return getArrayList(key, CharSequence.class);
     }
 
     /**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 2b13f20..edbbb59 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 import android.util.Size;
@@ -876,7 +877,7 @@
     @Nullable
     public Bundle getBundle(@Nullable String key) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return null;
         }
@@ -899,7 +900,11 @@
      *
      * @param key a String, or {@code null}
      * @return a Parcelable value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> T getParcelable(@Nullable String key) {
         unparcel();
@@ -916,30 +921,28 @@
     }
 
     /**
-     * Returns the value associated with the given key, or {@code null} if
-     * no mapping of the desired type exists for the given key or a {@code null}
-     * value is explicitly associated with the 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 #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
      * Otherwise, this method might throw an exception or return {@code null}.
      *
      * @param key a String, or {@code null}
-     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @param clazz The type of the object expected
      * @return a Parcelable value, or {@code null}
-     *
-     * @hide
      */
     @SuppressWarnings("unchecked")
     @Nullable
     public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) {
-        unparcel();
-        try {
-            return getValue(key, requireNonNull(clazz));
-        } catch (ClassCastException | BadParcelableException e) {
-            typeWarning(key, /* value */ null, "Parcelable", e);
-            return null;
-        }
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return get(key, clazz);
     }
 
     /**
@@ -953,7 +956,11 @@
      *
      * @param key a String, or {@code null}
      * @return a Parcelable[] value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public Parcelable[] getParcelableArray(@Nullable String key) {
         unparcel();
@@ -970,6 +977,39 @@
     }
 
     /**
+     * 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 #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key a String, or {@code null}
+     * @param clazz The type of the items inside the array
+     * @return a Parcelable[] value, or {@code null}
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> T[] getParcelableArray(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            // In Java 12, we can pass clazz.arrayType() instead of Parcelable[] and later casting.
+            return (T[]) getValue(key, Parcelable[].class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, clazz.getCanonicalName() + "[]", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or {@code null} if
      * no mapping of the desired type exists for the given key or a {@code null}
      * value is explicitly associated with the key.
@@ -980,7 +1020,11 @@
      *
      * @param key a String, or {@code null}
      * @return an ArrayList<T> value, or {@code null}
+     *
+     * @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> ArrayList<T> getParcelableArrayList(@Nullable String key) {
         unparcel();
@@ -997,14 +1041,43 @@
     }
 
     /**
+     * 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 #setClassLoader(ClassLoader)} with the proper {@link ClassLoader} first.
+     * Otherwise, this method might throw an exception or return {@code null}.
+     *
+     * @param key   a String, or {@code null}
+     * @param clazz The type of the items inside the array list
+     * @return an ArrayList<T> value, or {@code null}
+     */
+    @SuppressLint("NullableCollection")
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> ArrayList<T> getParcelableArrayList(@Nullable String key, @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        return getArrayList(key, clazz);
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
-     *
      * @return a SparseArray of T values, or null
+     *
+     * @deprecated Use the type-safer {@link #getSparseParcelableArray(String, Class)} starting from
+     *      Android {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Nullable
     public <T extends Parcelable> SparseArray<T> getSparseParcelableArray(@Nullable String key) {
         unparcel();
@@ -1021,13 +1094,44 @@
     }
 
     /**
+     * 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>
+     *
+     * @param key a String, or null
+     * @return a SparseArray of T values, or null
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T> SparseArray<T> getSparseParcelableArray(@Nullable String key,
+            @NonNull Class<T> clazz) {
+        // The reason for not using <T extends Parcelable> is because the caller could provide a
+        // super class to restrict the children that doesn't implement Parcelable itself while the
+        // children do, more details at b/210800751 (same reasoning applies here).
+        unparcel();
+        try {
+            return (SparseArray<T>) getValue(key, SparseArray.class, requireNonNull(clazz));
+        } catch (ClassCastException | BadTypeParcelableException e) {
+            typeWarning(key, "SparseArray<" + clazz.getCanonicalName() + ">", e);
+            return null;
+        }
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
      *
      * @param key a String, or null
      * @return a Serializable value, or null
+     *
+     * @deprecated Use the type-safer {@link #getSerializable(String, Class)} starting from Android
+     *      {@link Build.VERSION_CODES#TIRAMISU}.
      */
+    @Deprecated
     @Override
     @Nullable
     public Serializable getSerializable(@Nullable String key) {
@@ -1035,6 +1139,24 @@
     }
 
     /**
+     * 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>
+     *
+     * @param key   a String, or null
+     * @param clazz The expected class of the returned type
+     * @return a Serializable value, or null
+     */
+    @Nullable
+    public <T extends Serializable> T getSerializable(@Nullable String key,
+            @NonNull Class<T> clazz) {
+        return super.getSerializable(key, requireNonNull(clazz));
+    }
+
+    /**
      * Returns the value associated with the given key, or null if
      * no mapping of the desired type exists for the given key or a null
      * value is explicitly associated with the key.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 32cee94..d3b35a0 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.IntDef;
@@ -65,6 +67,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.function.Supplier;
@@ -287,26 +290,26 @@
     private static final int VAL_NULL = -1;
     private static final int VAL_STRING = 0;
     private static final int VAL_INTEGER = 1;
-    private static final int VAL_MAP = 2;
+    private static final int VAL_MAP = 2; // length-prefixed
     private static final int VAL_BUNDLE = 3;
-    private static final int VAL_PARCELABLE = 4;
+    private static final int VAL_PARCELABLE = 4; // length-prefixed
     private static final int VAL_SHORT = 5;
     private static final int VAL_LONG = 6;
     private static final int VAL_FLOAT = 7;
     private static final int VAL_DOUBLE = 8;
     private static final int VAL_BOOLEAN = 9;
     private static final int VAL_CHARSEQUENCE = 10;
-    private static final int VAL_LIST  = 11;
-    private static final int VAL_SPARSEARRAY = 12;
+    private static final int VAL_LIST  = 11; // length-prefixed
+    private static final int VAL_SPARSEARRAY = 12; // length-prefixed
     private static final int VAL_BYTEARRAY = 13;
     private static final int VAL_STRINGARRAY = 14;
     private static final int VAL_IBINDER = 15;
-    private static final int VAL_PARCELABLEARRAY = 16;
-    private static final int VAL_OBJECTARRAY = 17;
+    private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
+    private static final int VAL_OBJECTARRAY = 17; // length-prefixed
     private static final int VAL_INTARRAY = 18;
     private static final int VAL_LONGARRAY = 19;
     private static final int VAL_BYTE = 20;
-    private static final int VAL_SERIALIZABLE = 21;
+    private static final int VAL_SERIALIZABLE = 21; // length-prefixed
     private static final int VAL_SPARSEBOOLEANARRAY = 22;
     private static final int VAL_BOOLEANARRAY = 23;
     private static final int VAL_CHARSEQUENCEARRAY = 24;
@@ -3170,8 +3173,7 @@
      */
     @Deprecated
     public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
-        int n = readInt();
-        readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+        readMapInternal(outVal, loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
     /**
@@ -3186,8 +3188,7 @@
             @NonNull Class<V> clazzValue) {
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
-        int n = readInt();
-        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+        readMapInternal(outVal, loader, clazzKey, clazzValue);
     }
 
     /**
@@ -3236,13 +3237,7 @@
     @Deprecated
     @Nullable
     public HashMap readHashMap(@Nullable ClassLoader loader) {
-        int n = readInt();
-        if (n < 0) {
-            return null;
-        }
-        HashMap m = new HashMap(n);
-        readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
-        return m;
+        return readHashMapInternal(loader, /* clazzKey */ null, /* clazzValue */ null);
     }
 
     /**
@@ -3258,13 +3253,7 @@
             @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
         Objects.requireNonNull(clazzKey);
         Objects.requireNonNull(clazzValue);
-        int n = readInt();
-        if (n < 0) {
-            return null;
-        }
-        HashMap<K, V> map = new HashMap<>(n);
-        readMapInternal(map, n, loader, clazzKey, clazzValue);
-        return map;
+        return readHashMapInternal(loader, clazzKey, clazzValue);
     }
 
     /**
@@ -4289,16 +4278,17 @@
 
 
     /**
-     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @see #readValue(int, ClassLoader, Class, Class[])
      */
     @Nullable
-    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
         int type = readInt();
         final T object;
         if (isLengthPrefixed(type)) {
             int length = readInt();
             int start = dataPosition();
-            object = readValue(type, loader, clazz);
+            object = readValue(type, loader, clazz, itemTypes);
             int actual = dataPosition() - start;
             if (actual != length) {
                 Slog.wtfStack(TAG,
@@ -4306,25 +4296,26 @@
                                 + "  consumed " + actual + " bytes, but " + length + " expected.");
             }
         } else {
-            object = readValue(type, loader, clazz);
+            object = readValue(type, loader, clazz, itemTypes);
         }
         return object;
     }
 
     /**
-     * This will return a {@link Function} for length-prefixed types that deserializes the object
-     * when {@link Function#apply} is called with the expected class of the return object (or {@code
-     * null} for no type check), for other types it will return the object itself.
+     * This will return a {@link BiFunction} for length-prefixed types that deserializes the object
+     * when {@link BiFunction#apply} is called (the arguments correspond to the ones of {@link
+     * #readValue(int, ClassLoader, Class, Class[])} after the class loader), for other types it
+     * will return the object itself.
      *
-     * <p>After calling {@link Function#apply(Object)} the parcel cursor will not change. Note that
-     * you shouldn't recycle the parcel, not at least until all objects have been retrieved. No
+     * <p>After calling {@link BiFunction#apply} the parcel cursor will not change. Note that you
+     * shouldn't recycle the parcel, not at least until all objects have been retrieved. No
      * synchronization attempts are made.
      *
      * </p>The function returned implements {@link #equals(Object)} and {@link #hashCode()}. Two
      * function objects are equal if either of the following is true:
      * <ul>
-     *   <li>{@link Function#apply} has been called on both and both objects returned are equal.
-     *   <li>{@link Function#apply} hasn't been called on either one and everything below is true:
+     *   <li>{@link BiFunction#apply} has been called on both and both objects returned are equal.
+     *   <li>{@link BiFunction#apply} hasn't been called on either one and everything below is true:
      *   <ul>
      *       <li>The {@code loader} parameters used to retrieve each are equal.
      *       <li>They both have the same type.
@@ -4351,7 +4342,7 @@
     }
 
 
-    private static final class LazyValue implements Function<Class<?>, Object> {
+    private static final class LazyValue implements BiFunction<Class<?>, Class<?>[], Object> {
         /**
          *                      |   4B   |   4B   |
          * mSource = Parcel{... |  type  | length | object | ...}
@@ -4383,7 +4374,7 @@
         }
 
         @Override
-        public Object apply(@Nullable Class<?> clazz) {
+        public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
             Parcel source = mSource;
             if (source != null) {
                 synchronized (source) {
@@ -4392,7 +4383,7 @@
                         int restore = source.dataPosition();
                         try {
                             source.setDataPosition(mPosition);
-                            mObject = source.readValue(mLoader, clazz);
+                            mObject = source.readValue(mLoader, clazz, itemTypes);
                         } finally {
                             source.setDataPosition(restore);
                         }
@@ -4472,14 +4463,25 @@
         }
     }
 
+    /** Same as {@link #readValue(ClassLoader, Class, Class[])} without any item types. */
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        // Avoids allocating Class[0] array
+        return readValue(type, loader, clazz, (Class<?>[]) null);
+    }
+
     /**
      * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
      * type first.
+     *
      * @param clazz The type of the object expected or {@code null} for performing no checks.
+     * @param itemTypes If the value is a container, these represent the item types (eg. for a list
+     *                  it's the item type, for a map, it's the key type, followed by the value
+     *                  type).
      */
     @SuppressWarnings("unchecked")
     @Nullable
-    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz,
+            @Nullable Class<?>... itemTypes) {
         final Object object;
         switch (type) {
             case VAL_NULL:
@@ -4495,7 +4497,11 @@
                 break;
 
             case VAL_MAP:
-                object = readHashMap(loader);
+                checkTypeToUnparcel(clazz, HashMap.class);
+                Class<?> keyType = ArrayUtils.getOrNull(itemTypes, 0);
+                Class<?> valueType = ArrayUtils.getOrNull(itemTypes, 1);
+                checkArgument((keyType == null) == (valueType == null));
+                object = readHashMapInternal(loader, keyType, valueType);
                 break;
 
             case VAL_PARCELABLE:
@@ -4526,10 +4532,12 @@
                 object = readCharSequence();
                 break;
 
-            case VAL_LIST:
-                object = readArrayList(loader);
+            case VAL_LIST: {
+                checkTypeToUnparcel(clazz, ArrayList.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readArrayListInternal(loader, itemType);
                 break;
-
+            }
             case VAL_BOOLEANARRAY:
                 object = createBooleanArray();
                 break;
@@ -4550,10 +4558,12 @@
                 object = readStrongBinder();
                 break;
 
-            case VAL_OBJECTARRAY:
-                object = readArray(loader);
+            case VAL_OBJECTARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Object.class);
+                object = readArrayInternal(loader, itemType);
                 break;
-
+            }
             case VAL_INTARRAY:
                 object = createIntArray();
                 break;
@@ -4570,14 +4580,18 @@
                 object = readSerializableInternal(loader, clazz);
                 break;
 
-            case VAL_PARCELABLEARRAY:
-                object = readParcelableArray(loader);
+            case VAL_PARCELABLEARRAY: {
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                checkArrayTypeToUnparcel(clazz, (itemType != null) ? itemType : Parcelable.class);
+                object = readParcelableArrayInternal(loader, itemType);
                 break;
-
-            case VAL_SPARSEARRAY:
-                object = readSparseArray(loader);
+            }
+            case VAL_SPARSEARRAY: {
+                checkTypeToUnparcel(clazz, SparseArray.class);
+                Class<?> itemType = ArrayUtils.getOrNull(itemTypes, 0);
+                object = readSparseArrayInternal(loader, itemType);
                 break;
-
+            }
             case VAL_SPARSEBOOLEANARRAY:
                 object = readSparseBooleanArray();
                 break;
@@ -4625,7 +4639,7 @@
                             + " at offset " + off);
         }
         if (object != null && clazz != null && !clazz.isInstance(object)) {
-            throw new BadParcelableException("Unparcelled object " + object
+            throw new BadTypeParcelableException("Unparcelled object " + object
                     + " is not an instance of required class " + clazz.getName()
                     + " provided in the parameter");
         }
@@ -4652,6 +4666,38 @@
     }
 
     /**
+     * Checks that an array of type T[], where T is {@code componentTypeToUnparcel}, is a subtype of
+     * {@code requiredArrayType}.
+     */
+    private void checkArrayTypeToUnparcel(@Nullable Class<?> requiredArrayType,
+            Class<?> componentTypeToUnparcel) {
+        if (requiredArrayType != null) {
+            // In Java 12, we could use componentTypeToUnparcel.arrayType() for the check
+            Class<?> requiredComponentType = requiredArrayType.getComponentType();
+            if (requiredComponentType == null) {
+                throw new BadTypeParcelableException(
+                        "About to unparcel an array but type "
+                                + requiredArrayType.getCanonicalName()
+                                + " required by caller is not an array.");
+            }
+            checkTypeToUnparcel(requiredComponentType, componentTypeToUnparcel);
+        }
+    }
+
+    /**
+     * Checks that {@code typeToUnparcel} is a subtype of {@code requiredType}, if {@code
+     * requiredType} is not {@code null}.
+     */
+    private void checkTypeToUnparcel(@Nullable Class<?> requiredType, Class<?> typeToUnparcel) {
+        if (requiredType != null && !requiredType.isAssignableFrom(typeToUnparcel)) {
+            throw new BadTypeParcelableException(
+                    "About to unparcel a " + typeToUnparcel.getCanonicalName()
+                            + ", which is not a subtype of type " + requiredType.getCanonicalName()
+                            + " required by caller.");
+        }
+    }
+
+    /**
      * Read and return a new Parcelable from the parcel.  The given class loader
      * will be used to load any enclosed Parcelables.  If it is null, the default
      * class loader will be used.
@@ -4781,7 +4827,7 @@
             if (clazz != null) {
                 Class<?> parcelableClass = creator.getClass().getEnclosingClass();
                 if (!clazz.isAssignableFrom(parcelableClass)) {
-                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
                             + "a subclass of required class " + clazz.getName()
                             + " provided in the parameter");
                 }
@@ -4804,7 +4850,7 @@
             }
             if (clazz != null) {
                 if (!clazz.isAssignableFrom(parcelableClass)) {
-                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                    throw new BadTypeParcelableException("Parcelable creator " + name + " is not "
                             + "a subclass of required class " + clazz.getName()
                             + " provided in the parameter");
                 }
@@ -4865,15 +4911,7 @@
     @Deprecated
     @Nullable
     public Parcelable[] readParcelableArray(@Nullable ClassLoader loader) {
-        int N = readInt();
-        if (N < 0) {
-            return null;
-        }
-        Parcelable[] p = new Parcelable[N];
-        for (int i = 0; i < N; i++) {
-            p[i] = readParcelable(loader);
-        }
-        return p;
+        return readParcelableArrayInternal(loader, /* clazz */ null);
     }
 
     /**
@@ -4885,14 +4923,20 @@
      * trying to instantiate an element.
      */
     @SuppressLint({"ArrayReturn", "NullableCollection"})
-    @SuppressWarnings("unchecked")
     @Nullable
     public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        return readParcelableArrayInternal(loader, requireNonNull(clazz));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T[] readParcelableArrayInternal(@Nullable ClassLoader loader,
+            @Nullable Class<T> clazz) {
         int n = readInt();
         if (n < 0) {
             return null;
         }
-        T[] p = (T[]) Array.newInstance(clazz, n);
+        T[] p = (T[]) ((clazz == null) ? new Parcelable[n] : Array.newInstance(clazz, n));
         for (int i = 0; i < n; i++) {
             p[i] = readParcelableInternal(loader, clazz);
         }
@@ -4955,7 +4999,7 @@
                 // the class the same way as ObjectInputStream, using the provided classloader.
                 Class<?> cl = Class.forName(name, false, loader);
                 if (!clazz.isAssignableFrom(cl)) {
-                    throw new BadParcelableException("Serializable object "
+                    throw new BadTypeParcelableException("Serializable object "
                             + cl.getName() + " is not a subclass of required class "
                             + clazz.getName() + " provided in the parameter");
                 }
@@ -4980,7 +5024,7 @@
                 // the deserialized object, as we cannot resolve the class the same way as
                 // ObjectInputStream.
                 if (!clazz.isAssignableFrom(object.getClass())) {
-                    throw new BadParcelableException("Serializable object "
+                    throw new BadTypeParcelableException("Serializable object "
                             + object.getClass().getName() + " is not a subclass of required class "
                             + clazz.getName() + " provided in the parameter");
                 }
@@ -5090,7 +5134,26 @@
         readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
     }
 
-    /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+    @Nullable
+    private <K, V> HashMap<K, V> readHashMapInternal(@Nullable ClassLoader loader,
+            @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        HashMap<K, V> map = new HashMap<>(n);
+        readMapInternal(map, n, loader, clazzKey, clazzValue);
+        return map;
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal,
+            @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+            @Nullable Class<V> clazzValue) {
+        int n = readInt();
+        readMapInternal(outVal, n, loader, clazzKey, clazzValue);
+    }
+
+    private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
             @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
             @Nullable Class<V> clazzValue) {
         while (n > 0) {
@@ -5101,7 +5164,7 @@
         }
     }
 
-    /* package */ void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
+    private void readArrayMapInternal(@NonNull ArrayMap<? super String, Object> outVal,
             int size, @Nullable ClassLoader loader) {
         readArrayMap(outVal, size, /* sorted */ true, /* lazy */ false, loader);
     }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index c6fd6ee..55df09a 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -888,6 +888,15 @@
         }
     }
 
+    /**
+     * Returns the {@code i}-th item in {@code items}, if it exists and {@code items} is not {@code
+     * null}, otherwise returns {@code null}.
+     */
+    @Nullable
+    public static <T> T getOrNull(@Nullable T[] items, int i) {
+        return (items != null && items.length > i) ? items[i] : null;
+    }
+
     public static @Nullable <T> T firstOrNull(T[] items) {
         return items.length > 0 ? items[0] : null;
     }