diff --git a/core/java/com/android/internal/inputmethod/InputConnectionCommand.java b/core/java/com/android/internal/inputmethod/InputConnectionCommand.java index 4116b028fbbf9..acc1e40fc6bab 100644 --- a/core/java/com/android/internal/inputmethod/InputConnectionCommand.java +++ b/core/java/com/android/internal/inputmethod/InputConnectionCommand.java @@ -23,12 +23,12 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.BadParcelableException; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -356,14 +356,24 @@ public final class InputConnectionCommand implements Parcelable { | (mResultCallbackType != ResultCallbackType.NULL ? FieldMask.CALLBACK : 0); } + + /** + * A utility method to unparcel {@link InputConnectionCommand} from the given {@link Parcel}. + * + *

When this method throws any {@link RuntimeException} or its derived class, notably + * {@link BadParcelableException}, {@code source} is considered to be in an unexpected state and + * unsafe to continue reading any subsequent data.

+ * + * @param source {@link Parcel} to read the data from. + * @return {@link InputConnectionCommand} that is parcelled from {@code source}. + */ @AnyThread - @Nullable + @NonNull private static InputConnectionCommand createFromParcel(@NonNull Parcel source) { final int type = source.readInt(); if (type < InputConnectionCommandType.FIRST_COMMAND || InputConnectionCommandType.LAST_COMMAND < type) { - Log.e(TAG, "Invalid InputConnectionCommand type=" + type); - return null; + throw new BadParcelableException("Invalid InputConnectionCommandType=" + type); } @FieldMask final int fieldMask = source.readInt(); @@ -380,9 +390,6 @@ public final class InputConnectionCommand implements Parcelable { if ((fieldMask & FieldMask.PARCELABLE) != 0) { parcelableType = source.readInt(); switch (parcelableType) { - case ParcelableType.NULL: - Log.e(TAG, "Unexpected ParcelableType=NULL"); - return null; case ParcelableType.EXTRACTED_TEXT_REQUEST: parcelable = source.readTypedObject(ExtractedTextRequest.CREATOR); break; @@ -399,8 +406,8 @@ public final class InputConnectionCommand implements Parcelable { parcelable = source.readTypedObject(InputContentInfo.CREATOR); break; default: - Log.e(TAG, "Unknown ParcelableType=" + parcelableType); - return null; + throw new BadParcelableException( + "Invalid InputConnectionCommand.ParcelableType=" + parcelableType); } } else { parcelableType = ParcelableType.NULL; @@ -411,9 +418,6 @@ public final class InputConnectionCommand implements Parcelable { if ((fieldMask & FieldMask.CALLBACK) != 0) { resultCallbackType = source.readInt(); switch (resultCallbackType) { - case ResultCallbackType.NULL: - Log.e(TAG, "Unexpected ResultCallbackType=NULL"); - return null; case ResultCallbackType.BOOLEAN: case ResultCallbackType.INT: case ResultCallbackType.CHAR_SEQUENCE: @@ -422,8 +426,9 @@ public final class InputConnectionCommand implements Parcelable { resultCallback = source.readStrongBinder(); break; default: - Log.e(TAG, "Unknown ResultCallbackType=" + resultCallbackType); - return null; + throw new BadParcelableException( + "Invalid InputConnectionCommand.ResultCallbackType=" + + resultCallbackType); } } else { resultCallbackType = ResultCallbackType.NULL; @@ -439,15 +444,10 @@ public final class InputConnectionCommand implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @AnyThread - @Nullable + @NonNull @Override public InputConnectionCommand createFromParcel(Parcel source) { - try { - return InputConnectionCommand.createFromParcel(source); - } catch (Exception e) { - Log.e(TAG, "Returning null due to exception.", e); - return null; - } + return InputConnectionCommand.createFromParcel(source); } @AnyThread diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputConnectionCommandTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputConnectionCommandTest.java new file mode 100644 index 0000000000000..efe6cec24458e --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputConnectionCommandTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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 com.android.internal.inputmethod; + +import static org.junit.Assert.assertThrows; + +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class InputConnectionCommandTest { + @Test + public void testCreateFromParcelDoesNotSwallowExceptions() { + final Parcel parcel = Parcel.obtain(); + try { + parcel.writeInt(InputConnectionCommandType.FIRST_COMMAND); + parcel.writeInt(InputConnectionCommand.FieldMask.PARCELABLE); + parcel.writeInt(InputConnectionCommand.ParcelableType.NULL); // invalid + assertThrows(RuntimeException.class, + () -> InputConnectionCommand.CREATOR.createFromParcel(parcel)); + } finally { + parcel.recycle(); + } + } +}