InputMethodSubtypeArray: prevent negative count injection

Fixes an issue where negative counts could be injected
via the Parcel constructor. The writeToParcel method
in that case would write data that a subsequent read would
not consume.

Fixes: 277916797
Fixes: 354682735
Test: atest InputMethodSubtypeArrayTest
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:23933354757ddd81202fed5b3b9fe7402c6c1164)
Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e
Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e
diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
index 50e95c8..ee36dc7 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.BadParcelableException;
 import android.os.Parcel;
 import android.util.Slog;
 
@@ -69,6 +70,9 @@
      */
     public InputMethodSubtypeArray(final Parcel source) {
         mCount = source.readInt();
+        if (mCount < 0) {
+            throw new BadParcelableException("mCount must be non-negative.");
+        }
         if (mCount > 0) {
             mDecompressedSize = source.readInt();
             mCompressedData = source.createByteArray();
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
index e2fb46a..5af8558 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java
@@ -16,9 +16,14 @@
 
 package android.view.inputmethod;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertEquals;
 
+import android.os.BadParcelableException;
 import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
 import androidx.test.filters.SmallTest;
@@ -31,6 +36,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@Presubmit
 public class InputMethodSubtypeArrayTest {
 
     @Test
@@ -59,6 +65,36 @@
         assertEquals(clonedArray.get(2), clonedClonedArray.get(2));
     }
 
+    @Test
+    public void testNegativeCount() throws Exception {
+        InputMethodSubtypeArray negativeCountArray;
+        try {
+            // Construct a InputMethodSubtypeArray with: mCount = -1
+            Parcel p = Parcel.obtain();
+            p.writeInt(-1);
+            p.setDataPosition(0);
+            negativeCountArray = new InputMethodSubtypeArray(p);
+        } catch (BadParcelableException e) {
+            // Expected with fix: Prevent negative mCount
+            assertThat(e).hasMessageThat().contains("mCount");
+            return;
+        }
+        assertWithMessage("Test set-up failed")
+                .that(negativeCountArray.getCount()).isEqualTo(-1);
+
+        Parcel p = Parcel.obtain();
+        // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData)
+        negativeCountArray.writeToParcel(p);
+        p.setDataPosition(0);
+        // Reads: int (mCount)
+        // Leaves: int (mDecompressedSize), byte[] (mCompressedData)
+        new InputMethodSubtypeArray(p);
+
+        assertWithMessage("Didn't read all data that was previously written")
+                .that(p.dataPosition())
+                .isEqualTo(p.dataSize());
+    }
+
     InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) {
         Parcel parcel = null;
         try {