/* * Copyright (C) 2007 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.IntDef; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; import android.view.InputChannel; import java.lang.annotation.Retention; /** * Bundle of information returned by input method manager about a successful * binding to an input method. */ public final class InputBindResult implements Parcelable { @Retention(SOURCE) @IntDef({ ResultCode.SUCCESS_WITH_IME_SESSION, ResultCode.SUCCESS_WAITING_IME_SESSION, ResultCode.SUCCESS_WAITING_IME_BINDING, ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, ResultCode.ERROR_NULL, ResultCode.ERROR_NO_IME, ResultCode.ERROR_INVALID_PACKAGE_NAME, ResultCode.ERROR_SYSTEM_NOT_READY, ResultCode.ERROR_IME_NOT_CONNECTED, ResultCode.ERROR_INVALID_USER, ResultCode.ERROR_NULL_EDITOR_INFO, ResultCode.ERROR_NOT_IME_TARGET_WINDOW, ResultCode.ERROR_NO_EDITOR, ResultCode.ERROR_DISPLAY_ID_MISMATCH, ResultCode.ERROR_INVALID_DISPLAY_ID, ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION }) public @interface ResultCode { /** * Indicates that everything in this result object including {@link #method} is valid. */ int SUCCESS_WITH_IME_SESSION = 0; /** * Indicates that this is a temporary binding until the * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS). * *

Note that in this state the IMS is already bound to IMMS but the logical session * is not yet established on top of the IPC channel.

* *

Some of fields such as {@link #channel} is not yet available.

* * @see android.inputmethodservice.InputMethodService#onCreateInputMethodSessionInterface() **/ int SUCCESS_WAITING_IME_SESSION = 1; /** * Indicates that this is a temporary binding until the * {@link android.inputmethodservice.InputMethodService} (IMS) establishes a valid session * to {@link com.android.server.inputmethod.InputMethodManagerService} (IMMS). * *

Note that in this state the IMMS has already initiated a connection to the IMS but * the binding process is not completed yet.

* *

Some of fields such as {@link #channel} is not yet available.

* @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder) */ int SUCCESS_WAITING_IME_BINDING = 2; /** * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a * pending operation to switch to a different user. * *

Note that in this state even what would be the next current IME is not determined.

*/ int SUCCESS_WAITING_USER_SWITCHING = 3; /** * Indicates that this is not intended for starting input but just for reporting window * focus change from the application process. * *

All other fields do not have meaningful value.

*/ int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4; /** * Indicates somehow * {@link * com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus} * is trying to return null {@link InputBindResult}, which must never happen. */ int ERROR_NULL = 5; /** * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} * recognizes no IME. */ int ERROR_NO_IME = 6; /** * Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match * the caller UID. * * @see android.view.inputmethod.EditorInfo#packageName */ int ERROR_INVALID_PACKAGE_NAME = 7; /** * Indicates that the system is still in an early stage of the boot process and any 3rd * party application is not allowed to run. * * @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START */ int ERROR_SYSTEM_NOT_READY = 8; /** * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to * connect to an {@link android.inputmethodservice.InputMethodService} but failed. * * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, * android.os.UserHandle) */ int ERROR_IME_NOT_CONNECTED = 9; /** * Indicates that the caller is not the foreground user, does not have * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user * specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not * running. */ int ERROR_INVALID_USER = 10; /** * Indicates that the caller should have specified non-null * {@link android.view.inputmethod.EditorInfo}. */ int ERROR_NULL_EDITOR_INFO = 11; /** * Indicates that the target window the client specified cannot be the IME target right now. * *

Due to the asynchronous nature of Android OS, we cannot completely avoid this error. * The client should try to restart input when its {@link android.view.Window} is focused * again.

* * @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int) */ int ERROR_NOT_IME_TARGET_WINDOW = 12; /** * Indicates that focused view in the current window is not an editor. */ int ERROR_NO_EDITOR = 13; /** * Indicates that there is a mismatch in display ID between IME client and focused Window. */ int ERROR_DISPLAY_ID_MISMATCH = 14; /** * Indicates that current IME client is no longer allowed to access to the associated * display. */ int ERROR_INVALID_DISPLAY_ID = 15; /** * Indicates that a valid session is created and result is ready for accessibility. */ int SUCCESS_WITH_ACCESSIBILITY_SESSION = 16; } @ResultCode public final int result; /** * The input method service. */ public final IInputMethodSession method; /** * The accessibility services. */ public final SparseArray accessibilitySessions; /** * The input channel used to send input events to this IME. */ public final InputChannel channel; /** * The ID for this input method, as found in InputMethodInfo; null if * no input method will be bound. */ public final String id; /** * Sequence number of this binding. */ public final int sequence; /** * {@code true} if the IME explicitly specifies {@code suppressesSpellChecker="true"}. */ public final boolean isInputMethodSuppressingSpellChecker; /** * Creates a new instance of {@link InputBindResult}. * * @param result A result code defined in {@link ResultCode}. * @param method {@link IInputMethodSession} to interact with the IME. * @param accessibilitySessions {@link IAccessibilityInputMethodSession} to interact with * accessibility services. * @param channel {@link InputChannel} to forward input events to the IME. * @param id The {@link String} representations of the IME, which is the same as * {@link android.view.inputmethod.InputMethodInfo#getId()} and * {@link android.content.ComponentName#flattenToShortString()}. * @param sequence A sequence number of this binding. * @param isInputMethodSuppressingSpellChecker {@code true} if the IME explicitly specifies * {@code suppressesSpellChecker="true"}. */ public InputBindResult(@ResultCode int result, IInputMethodSession method, SparseArray accessibilitySessions, InputChannel channel, String id, int sequence, boolean isInputMethodSuppressingSpellChecker) { this.result = result; this.method = method; this.accessibilitySessions = accessibilitySessions; this.channel = channel; this.id = id; this.sequence = sequence; this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker; } private InputBindResult(Parcel source) { result = source.readInt(); method = IInputMethodSession.Stub.asInterface(source.readStrongBinder()); int n = source.readInt(); if (n < 0) { accessibilitySessions = null; } else { accessibilitySessions = new SparseArray<>(n); while (n > 0) { int key = source.readInt(); IAccessibilityInputMethodSession value = IAccessibilityInputMethodSession.Stub.asInterface( source.readStrongBinder()); accessibilitySessions.append(key, value); n--; } } if (source.readInt() != 0) { channel = InputChannel.CREATOR.createFromParcel(source); } else { channel = null; } id = source.readString(); sequence = source.readInt(); isInputMethodSuppressingSpellChecker = source.readBoolean(); } /** * {@inheritDoc} */ @Override public String toString() { return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id + " sequence=" + sequence + " result=" + result + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker + "}"; } /** * {@inheritDoc} */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(result); dest.writeStrongInterface(method); if (accessibilitySessions == null) { dest.writeInt(-1); } else { int n = accessibilitySessions.size(); dest.writeInt(n); int i = 0; while (i < n) { dest.writeInt(accessibilitySessions.keyAt(i)); dest.writeStrongInterface(accessibilitySessions.valueAt(i)); i++; } } if (channel != null) { dest.writeInt(1); channel.writeToParcel(dest, flags); } else { dest.writeInt(0); } dest.writeString(id); dest.writeInt(sequence); dest.writeBoolean(isInputMethodSuppressingSpellChecker); } /** * Used to make this class parcelable. */ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public InputBindResult createFromParcel(Parcel source) { return new InputBindResult(source); } @Override public InputBindResult[] newArray(int size) { return new InputBindResult[size]; } }; /** * {@inheritDoc} */ @Override public int describeContents() { return channel != null ? channel.describeContents() : 0; } private String getResultString() { switch (result) { case ResultCode.SUCCESS_WITH_IME_SESSION: return "SUCCESS_WITH_IME_SESSION"; case ResultCode.SUCCESS_WAITING_IME_SESSION: return "SUCCESS_WAITING_IME_SESSION"; case ResultCode.SUCCESS_WAITING_IME_BINDING: return "SUCCESS_WAITING_IME_BINDING"; case ResultCode.SUCCESS_WAITING_USER_SWITCHING: return "SUCCESS_WAITING_USER_SWITCHING"; case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY: return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY"; case ResultCode.ERROR_NULL: return "ERROR_NULL"; case ResultCode.ERROR_NO_IME: return "ERROR_NO_IME"; case ResultCode.ERROR_NO_EDITOR: return "ERROR_NO_EDITOR"; case ResultCode.ERROR_INVALID_PACKAGE_NAME: return "ERROR_INVALID_PACKAGE_NAME"; case ResultCode.ERROR_SYSTEM_NOT_READY: return "ERROR_SYSTEM_NOT_READY"; case ResultCode.ERROR_IME_NOT_CONNECTED: return "ERROR_IME_NOT_CONNECTED"; case ResultCode.ERROR_INVALID_USER: return "ERROR_INVALID_USER"; case ResultCode.ERROR_NULL_EDITOR_INFO: return "ERROR_NULL_EDITOR_INFO"; case ResultCode.ERROR_NOT_IME_TARGET_WINDOW: return "ERROR_NOT_IME_TARGET_WINDOW"; case ResultCode.ERROR_DISPLAY_ID_MISMATCH: return "ERROR_DISPLAY_ID_MISMATCH"; case ResultCode.ERROR_INVALID_DISPLAY_ID: return "ERROR_INVALID_DISPLAY_ID"; case ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION: return "SUCCESS_WITH_ACCESSIBILITY_SESSION"; default: return "Unknown(" + result + ")"; } } private static InputBindResult error(@ResultCode int result) { return new InputBindResult(result, null, null, null, null, -1, false); } /** * Predefined error object for {@link ResultCode#ERROR_NULL}. */ public static final InputBindResult NULL = error(ResultCode.ERROR_NULL); /** * Predefined error object for {@link ResultCode#NO_IME}. */ public static final InputBindResult NO_IME = error(ResultCode.ERROR_NO_IME); /** * Predefined error object for {@link ResultCode#NO_EDITOR}. */ public static final InputBindResult NO_EDITOR = error(ResultCode.ERROR_NO_EDITOR); /** * Predefined error object for {@link ResultCode#ERROR_INVALID_PACKAGE_NAME}. */ public static final InputBindResult INVALID_PACKAGE_NAME = error(ResultCode.ERROR_INVALID_PACKAGE_NAME); /** * Predefined error object for {@link ResultCode#ERROR_NULL_EDITOR_INFO}. */ public static final InputBindResult NULL_EDITOR_INFO = error(ResultCode.ERROR_NULL_EDITOR_INFO); /** * Predefined error object for {@link ResultCode#ERROR_NOT_IME_TARGET_WINDOW}. */ public static final InputBindResult NOT_IME_TARGET_WINDOW = error(ResultCode.ERROR_NOT_IME_TARGET_WINDOW); /** * Predefined error object for {@link ResultCode#ERROR_IME_NOT_CONNECTED}. */ public static final InputBindResult IME_NOT_CONNECTED = error(ResultCode.ERROR_IME_NOT_CONNECTED); /** * Predefined error object for {@link ResultCode#ERROR_INVALID_USER}. */ public static final InputBindResult INVALID_USER = error(ResultCode.ERROR_INVALID_USER); /** * Predefined error object for {@link ResultCode#ERROR_DISPLAY_ID_MISMATCH}. */ public static final InputBindResult DISPLAY_ID_MISMATCH = error(ResultCode.ERROR_DISPLAY_ID_MISMATCH); /** * Predefined error object for {@link ResultCode#ERROR_INVALID_DISPLAY_ID}. */ public static final InputBindResult INVALID_DISPLAY_ID = error(ResultCode.ERROR_INVALID_DISPLAY_ID); /** * Predefined success object for * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}. */ public static final InputBindResult USER_SWITCHING = error(ResultCode.SUCCESS_WAITING_USER_SWITCHING); }