Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2757c248 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

A unified data class to represent all the InputConnection call

This CL introduces a new parcelable class InputConnectionCommand so
that any of existing and future InputConnection APIs can be marshalled
into a standard Java object, which itself can be a subject of other
useful operations.

Historically each InputConnection API has had a corresponding IPC
definition in IInputContext binder interface, which is indeed a widely
used design pattern in the Android OS [1].  Instead of continuing
doing so, this CL reimplements everything by a single unified IPC
method with a single unified Parcelable data object existing and
future InputConnection APIs.  Although doing so would introduce some
complexity, it also gives us huge flexibility and opportunities to
abstract out common data structure and operations across all the
InputConnection API calls.

For instance, here are code snippets we can now simply write.

  ArrayList<InputConnectionCommand> pendingTasks;

  void dumpToProto(@NonNull InputConnectionCommand command) {
     ...
  }

  void cancelPendingTasks(Predicate<InputConnectionCommand> cond) {
    ...
  }

to do more complex abstractions as needed.

Other than rewriting InputConnection remote execution in a different
manner, this CL changes nothing.  There should be no developer/user
visible behavior changed in this CL, which can also be verified with
CtsInputMethodTestCases:InputConnectionEndToEndTest.

 [1]: This is not a hard requiement though. In fact there is already
      android.os.Messenger that has been used in several places in the
      system.  What android.os.Messenger does is semantically the same
      as what IInputConnectionWrapper is going to do with this CL.

Fix: 194151409
Test: atest CtsInputMethodTestCases:InputConnectionEndToEndTest
Test: atest FrameworksCoreTests:InputMethodDebugTest
Change-Id: I86eba7185b4b0664c1b0b3da794dfc5eeddc725c
parent 7a3500fb
Loading
Loading
Loading
Loading
+143 −0
Original line number Diff line number Diff line
@@ -17,7 +17,11 @@
package com.android.internal.inputmethod;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import android.util.Log;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.SurroundingText;

import java.util.function.BooleanSupplier;
import java.util.function.IntSupplier;
@@ -137,4 +141,143 @@ public final class CallbackUtils {
            callback.onResult(result);
        } catch (RemoteException ignored) { }
    }

    /**
     * A utility method to reply associated with {@link InputConnectionCommand}.
     *
     * @param command {@link InputConnectionCommand} to be replied.
     * @param result a {@link String} value to be replied.
     * @param tag tag name to be used for debug output when the invocation fails.
     */
    public static void onResult(@NonNull InputConnectionCommand command, boolean result,
            @Nullable String tag) {
        if (command.mResultCallbackType != InputConnectionCommand.ResultCallbackType.BOOLEAN) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result + " due to callback type mismatch."
                        + " expected=String actual=" + command.mResultCallbackType);
            }
            return;
        }
        try {
            IBooleanResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
        } catch (Throwable e) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result, e);
            }
        }
    }

    /**
     * A utility method to reply associated with {@link InputConnectionCommand}.
     *
     * @param command {@link InputConnectionCommand} to be replied.
     * @param result an int result value to be replied.
     * @param tag tag name to be used for debug output when the invocation fails.
     */
    public static void onResult(@NonNull InputConnectionCommand command, int result,
            @Nullable String tag) {
        if (command.mResultCallbackType != InputConnectionCommand.ResultCallbackType.INT) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result + " due to callback type mismatch."
                        + " expected=int actual=" + command.mResultCallbackType);
            }
            return;
        }
        try {
            IIntResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
        } catch (Throwable e) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result, e);
            }
        }
    }

    /**
     * A utility method to reply associated with {@link InputConnectionCommand}.
     *
     * @param command {@link InputConnectionCommand} to be replied.
     * @param result a {@link CharSequence} result value to be replied.
     * @param tag tag name to be used for debug output when the invocation fails.
     */
    public static void onResult(@NonNull InputConnectionCommand command,
            @Nullable CharSequence result, @Nullable String tag) {
        if (command.mResultCallbackType
                != InputConnectionCommand.ResultCallbackType.CHAR_SEQUENCE) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result + " due to callback type mismatch."
                        + " expected=CharSequence actual=" + command.mResultCallbackType);
            }
            return;
        }
        try {
            ICharSequenceResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
        } catch (Throwable e) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result, e);
            }
        }
    }

    /**
     * A utility method to reply associated with {@link InputConnectionCommand}.
     *
     * @param command {@link InputConnectionCommand} to be replied.
     * @param result a {@link ExtractedText} result value to be replied.
     * @param tag tag name to be used for debug output when the invocation fails.
     */
    public static void onResult(@NonNull InputConnectionCommand command,
            @Nullable ExtractedText result, @Nullable String tag) {
        if (command.mResultCallbackType
                != InputConnectionCommand.ResultCallbackType.EXTRACTED_TEXT) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result + " due to callback type mismatch."
                        + " expected=ExtractedText actual=" + command.mResultCallbackType);
            }
            return;
        }
        try {
            IExtractedTextResultCallback.Stub.asInterface(command.mResultCallback).onResult(result);
        } catch (Throwable e) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result, e);
            }
        }
    }

    /**
     * A utility method to reply associated with {@link InputConnectionCommand}.
     *
     * @param command {@link InputConnectionCommand} to be replied.
     * @param result a {@link SurroundingText} result value to be replied.
     * @param tag tag name to be used for debug output when the invocation fails.
     */
    public static void onResult(@NonNull InputConnectionCommand command,
            @Nullable SurroundingText result, @Nullable String tag) {
        if (command.mResultCallbackType
                != InputConnectionCommand.ResultCallbackType.SURROUNDING_TEXT) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result + " due to callback type mismatch."
                        + " expected=SurroundingText actual=" + command.mResultCallbackType);
            }
            return;
        }
        try {
            ISurroundingTextResultCallback.Stub.asInterface(command.mResultCallback)
                    .onResult(result);
        } catch (Throwable e) {
            if (tag != null) {
                Log.e(tag, InputMethodDebug.inputConnectionCommandTypeToString(command.mCommandType)
                        + ": Failed to return result=" + result, e);
            }
        }
    }
}
+420 −58

File changed.

Preview size limit exceeded, changes collapsed.

+19 −0
Original line number Diff line number Diff line
/*
 * 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;

parcelable InputConnectionCommand;
+460 −0
Original line number Diff line number Diff line
/*
 * 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 java.lang.annotation.RetentionPolicy.SOURCE;

import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
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;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;

import java.lang.annotation.Retention;

/**
 * Defines the command message to be used for IMEs to remotely invoke
 * {@link android.view.inputmethod.InputConnection} APIs in the IME client process then receive
 * results.
 */
public final class InputConnectionCommand implements Parcelable {
    private static final String TAG = "InputConnectionCommand";

    @Retention(SOURCE)
    @IntDef(value = {
            ResultCallbackType.NULL,
            ResultCallbackType.BOOLEAN,
            ResultCallbackType.INT,
            ResultCallbackType.CHAR_SEQUENCE,
            ResultCallbackType.EXTRACTED_TEXT,
            ResultCallbackType.SURROUNDING_TEXT,
    })
    @interface ResultCallbackType {
        int NULL = 0;
        int BOOLEAN = 1;
        int INT = 2;
        int CHAR_SEQUENCE = 3;
        int EXTRACTED_TEXT = 4;
        int SURROUNDING_TEXT = 5;
    }

    @Retention(SOURCE)
    @IntDef(value = {
            ParcelableType.NULL,
            ParcelableType.EXTRACTED_TEXT_REQUEST,
            ParcelableType.COMPLETION_INFO,
            ParcelableType.CORRECTION_INFO,
            ParcelableType.KEY_EVENT,
            ParcelableType.INPUT_CONTENT_INFO,
    })
    @interface ParcelableType {
        int NULL = 0;
        int EXTRACTED_TEXT_REQUEST = 1;
        int COMPLETION_INFO = 2;
        int CORRECTION_INFO = 3;
        int KEY_EVENT = 4;
        int INPUT_CONTENT_INFO = 5;
    }

    @Retention(SOURCE)
    @IntDef(flag = true, value = {
            FieldMask.INT_ARG0,
            FieldMask.INT_ARG1,
            FieldMask.FLAGS,
            FieldMask.CHAR_SEQUENCE,
            FieldMask.STRING,
            FieldMask.BUNDLE,
            FieldMask.PARCELABLE,
            FieldMask.CALLBACK,
    })
    @interface FieldMask {
        int INT_ARG0 = 1 << 0;
        int INT_ARG1 = 1 << 1;
        int FLAGS = 1 << 2;
        int CHAR_SEQUENCE = 1 << 3;
        int STRING = 1 << 4;
        int BUNDLE = 1 << 5;
        int PARCELABLE = 1 << 6;
        int CALLBACK = 1 << 7;
    }

    @IntRange(from = InputConnectionCommandType.FIRST_COMMAND,
            to = InputConnectionCommandType.LAST_COMMAND)
    @InputConnectionCommandType
    public final int mCommandType;
    public final int mIntArg0;
    public final int mIntArg1;
    public final int mFlags;
    public final CharSequence mCharSequence;
    public final String mString;
    public final Bundle mBundle;
    @ParcelableType
    public final int mParcelableType;
    public final Parcelable mParcelable;
    @ResultCallbackType
    public final int mResultCallbackType;
    public final IBinder mResultCallback;

    private InputConnectionCommand(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type, int intArg0, int intArg1, int flags,
            @Nullable CharSequence charSequence, @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @ResultCallbackType int resultCallbackType, @Nullable IBinder resultCallback) {
        if (type < InputConnectionCommandType.FIRST_COMMAND
                || InputConnectionCommandType.LAST_COMMAND < type) {
            throw new IllegalArgumentException("Unknown type=" + type);
        }
        mCommandType = type;
        mIntArg0 = intArg0;
        mIntArg1 = intArg1;
        mFlags = flags;
        mCharSequence = charSequence;
        mString = string;
        mBundle = bundle;
        mParcelableType = parcelableType;
        mParcelable = parcelable;
        mResultCallbackType = resultCallbackType;
        mResultCallback = resultCallback;
    }

    /**
     * Creates {@link InputConnectionCommand} with the given {@link InputConnectionCommandType}.
     *
     * @param type {@link InputConnectionCommandType} to be set.
     * @return An {@link InputConnectionCommand} that is initialized with {@code type}.
     */
    @NonNull
    public static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type) {
        return create(type, 0);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type, int intArg0) {
        return create(type, intArg0, 0);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type, int intArg0, int intArg1) {
        return create(type, intArg0, intArg1, 0, null);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type, int intArg0,
            int intArg1, int flags, @Nullable CharSequence charSequence) {
        return create(type, intArg0, intArg1, flags, charSequence, null, null);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle) {
        return create(type, intArg0, intArg1, flags, charSequence, string,
                bundle, ParcelableType.NULL, null);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable, ResultCallbackType.NULL, null);
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @NonNull Completable.Boolean returnValue) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable,
                ResultCallbackType.BOOLEAN, ResultCallbacks.of(returnValue));
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @NonNull Completable.Int returnValue) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable,
                ResultCallbackType.INT, ResultCallbacks.of(returnValue));
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @NonNull Completable.CharSequence returnValue) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable,
                ResultCallbackType.CHAR_SEQUENCE, ResultCallbacks.of(returnValue));
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @NonNull Completable.ExtractedText returnValue) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable,
                ResultCallbackType.EXTRACTED_TEXT, ResultCallbacks.of(returnValue));
    }

    @NonNull
    static InputConnectionCommand create(
            @IntRange(
                    from = InputConnectionCommandType.FIRST_COMMAND,
                    to = InputConnectionCommandType.LAST_COMMAND)
            @InputConnectionCommandType int type,
            int intArg0, int intArg1, int flags, @Nullable CharSequence charSequence,
            @Nullable String string, @Nullable Bundle bundle,
            @ParcelableType int parcelableType, @Nullable Parcelable parcelable,
            @NonNull Completable.SurroundingText returnValue) {
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable,
                ResultCallbackType.SURROUNDING_TEXT, ResultCallbacks.of(returnValue));
    }

    /**
     * {@inheritDoc}
     */
    @AnyThread
    @Override
    public int describeContents() {
        int result = 0;
        if (mBundle != null) {
            result |= mBundle.describeContents();
        }
        if (mParcelable != null) {
            result |= mParcelable.describeContents();
        }
        // Here we assume other objects will never contain FDs to be parcelled.
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @AnyThread
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mCommandType);

        @FieldMask final int fieldMask = getFieldMask();
        dest.writeInt(fieldMask);
        if ((fieldMask & FieldMask.INT_ARG0) != 0) {
            dest.writeInt(mIntArg0);
        }
        if ((fieldMask & FieldMask.INT_ARG1) != 0) {
            dest.writeInt(mIntArg1);
        }
        if ((fieldMask & FieldMask.FLAGS) != 0) {
            dest.writeInt(mFlags);
        }
        if ((fieldMask & FieldMask.CHAR_SEQUENCE) != 0) {
            TextUtils.writeToParcel(mCharSequence, dest, flags);
        }
        if ((fieldMask & FieldMask.STRING) != 0) {
            dest.writeString(mString);
        }
        if ((fieldMask & FieldMask.BUNDLE) != 0) {
            dest.writeBundle(mBundle);
        }
        if ((fieldMask & FieldMask.PARCELABLE) != 0) {
            dest.writeInt(mParcelableType);
            dest.writeTypedObject(mParcelable, flags);
        }
        if ((fieldMask & FieldMask.CALLBACK) != 0) {
            dest.writeInt(mResultCallbackType);
            dest.writeStrongBinder(mResultCallback);
        }
    }

    @FieldMask
    @AnyThread
    private int getFieldMask() {
        return (mIntArg0 != 0 ? FieldMask.INT_ARG0 : 0)
                | (mIntArg1 != 0 ? FieldMask.INT_ARG1 : 0)
                | (mFlags != 0 ? FieldMask.FLAGS : 0)
                | (mCharSequence != null ? FieldMask.CHAR_SEQUENCE : 0)
                | (mString != null ? FieldMask.STRING : 0)
                | (mBundle != null ? FieldMask.BUNDLE : 0)
                | (mParcelableType != ParcelableType.NULL ? FieldMask.PARCELABLE : 0)
                | (mResultCallbackType != ResultCallbackType.NULL ? FieldMask.CALLBACK : 0);
    }

    @AnyThread
    @Nullable
    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;
        }

        @FieldMask final int fieldMask = source.readInt();
        final int intArg0 = (fieldMask & FieldMask.INT_ARG0) != 0 ? source.readInt() : 0;
        final int intArg1 = (fieldMask & FieldMask.INT_ARG1) != 0 ? source.readInt() : 0;
        final int flags = (fieldMask & FieldMask.FLAGS) != 0 ? source.readInt() : 0;
        final CharSequence charSequence = (fieldMask & FieldMask.CHAR_SEQUENCE) != 0
                ? TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source) : null;
        final String string = (fieldMask & FieldMask.STRING) != 0 ? source.readString() : null;
        final Bundle bundle = (fieldMask & FieldMask.BUNDLE) != 0 ? source.readBundle() : null;

        @ParcelableType final int parcelableType;
        final Parcelable 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;
                case ParcelableType.COMPLETION_INFO:
                    parcelable = source.readTypedObject(CompletionInfo.CREATOR);
                    break;
                case ParcelableType.CORRECTION_INFO:
                    parcelable = source.readTypedObject(CorrectionInfo.CREATOR);
                    break;
                case ParcelableType.KEY_EVENT:
                    parcelable = source.readTypedObject(KeyEvent.CREATOR);
                    break;
                case ParcelableType.INPUT_CONTENT_INFO:
                    parcelable = source.readTypedObject(InputContentInfo.CREATOR);
                    break;
                default:
                    Log.e(TAG, "Unknown ParcelableType=" + parcelableType);
                    return null;
            }
        } else {
            parcelableType = ParcelableType.NULL;
            parcelable = null;
        }
        @ResultCallbackType final int resultCallbackType;
        final IBinder resultCallback;
        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:
                case ResultCallbackType.EXTRACTED_TEXT:
                case ResultCallbackType.SURROUNDING_TEXT:
                    resultCallback = source.readStrongBinder();
                    break;
                default:
                    Log.e(TAG, "Unknown ResultCallbackType=" + resultCallbackType);
                    return null;
            }
        } else {
            resultCallbackType = ResultCallbackType.NULL;
            resultCallback = null;
        }
        return new InputConnectionCommand(type, intArg0, intArg1, flags, charSequence, string,
                bundle, parcelableType, parcelable, resultCallbackType, resultCallback);
    }

    /**
     * Used to make this class parcelable.
     */
    public static final Parcelable.Creator<InputConnectionCommand> CREATOR =
            new Parcelable.Creator<InputConnectionCommand>() {
                @AnyThread
                @Nullable
                @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;
                    }
                }

                @AnyThread
                @NonNull
                @Override
                public InputConnectionCommand[] newArray(int size) {
                    return new InputConnectionCommand[size];
                }
            };
}
+84 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading