Introduce InputMethodSessionWrapper for InputMethodManager

To simplify InputMethodManager code logic, have a wrapper
class which encapsulates IPCs from InputMethodManager to
InputMethodService.

Bug: 167948374
Test: atest CtsInputMethodTestCases

Change-Id: Ifb4f86098011fe74cf4159446106d6c15c30fa30
This commit is contained in:
Wilson Wu
2021-03-11 18:57:18 +08:00
parent 76186b256e
commit 17827d754a
2 changed files with 251 additions and 109 deletions

View File

@@ -462,11 +462,25 @@ public final class InputMethodManager {
*/
@UnsupportedAppUsage
String mCurId;
/**
* The actual instance of the method to make calls on it.
* Kept for {@link UnsupportedAppUsage}. Not officially maintained.
*
* @deprecated New code should use {@link #mCurrentInputMethodSession}.
*/
@Deprecated
@GuardedBy("mH")
@Nullable
@UnsupportedAppUsage
IInputMethodSession mCurMethod;
/**
* Encapsulates IPCs to the currently connected InputMethodService.
*/
@Nullable
@GuardedBy("mH")
private InputMethodSessionWrapper mCurrentInputMethodSession = null;
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
@@ -623,11 +637,8 @@ public final class InputMethodManager {
public void finishInputAndReportToIme() {
synchronized (mH) {
finishInputLocked();
if (mCurMethod != null) {
try {
mCurMethod.finishInput();
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.finishInput();
}
}
}
@@ -754,7 +765,8 @@ public final class InputMethodManager {
@Override
public boolean hasActiveConnection(View view) {
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurMethod == null) {
if (!hasServedByInputMethodLocked(view)
|| mCurrentInputMethodSession == null) {
return false;
}
@@ -861,7 +873,9 @@ public final class InputMethodManager {
REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE;
setInputChannelLocked(res.channel);
mCurMethod = res.method;
mCurMethod = res.method; // for @UnsupportedAppUsage
mCurrentInputMethodSession =
InputMethodSessionWrapper.createOrNull(res.method);
mCurId = res.id;
mBindSequence = res.sequence;
mActivityViewToScreenMatrix = res.getActivityViewToScreenMatrix();
@@ -1004,7 +1018,7 @@ public final class InputMethodManager {
}
mActivityViewToScreenMatrix.setValues(matrixValues);
if (mCursorAnchorInfo == null || mCurMethod == null
if (mCursorAnchorInfo == null || mCurrentInputMethodSession == null
|| mServedInputConnectionWrapper == null) {
return;
}
@@ -1015,13 +1029,9 @@ public final class InputMethodManager {
}
// Since the host ActivityView is moved, we need to issue
// IMS#updateCursorAnchorInfo() again.
try {
mCurMethod.updateCursorAnchorInfo(
CursorAnchorInfo.createForAdditionalParentMatrix(
mCursorAnchorInfo, mActivityViewToScreenMatrix));
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
mCurrentInputMethodSession.updateCursorAnchorInfo(
CursorAnchorInfo.createForAdditionalParentMatrix(
mCursorAnchorInfo, mActivityViewToScreenMatrix));
}
return;
}
@@ -1496,7 +1506,8 @@ public final class InputMethodManager {
setInputChannelLocked(null);
mBindSequence = -1;
mCurId = null;
mCurMethod = null;
mCurMethod = null; // for @UnsupportedAppUsage
mCurrentInputMethodSession = null;
}
void setInputChannelLocked(InputChannel channel) {
@@ -1560,11 +1571,8 @@ public final class InputMethodManager {
}
mCompletions = completions;
if (mCurMethod != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.displayCompletions(mCompletions);
}
}
}
@@ -1583,11 +1591,8 @@ public final class InputMethodManager {
return;
}
if (mCurMethod != null) {
try {
mCurMethod.updateExtractedText(token, text);
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.updateExtractedText(token, text);
}
}
}
@@ -1848,11 +1853,8 @@ public final class InputMethodManager {
if (servedView == null || servedView.getWindowToken() != windowToken) {
return;
}
if (mCurMethod != null) {
try {
mCurMethod.toggleSoftInput(showFlags, hideFlags);
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.toggleSoftInput(showFlags, hideFlags);
}
}
}
@@ -1873,11 +1875,8 @@ public final class InputMethodManager {
ImeTracing.getInstance().triggerClientDump(
"InputMethodManager#toggleSoftInput", InputMethodManager.this,
null /* icProto */);
if (mCurMethod != null) {
try {
mCurMethod.toggleSoftInput(showFlags, hideFlags);
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null) {
mCurrentInputMethodSession.toggleSoftInput(showFlags, hideFlags);
}
}
@@ -2060,7 +2059,8 @@ public final class InputMethodManager {
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurMethod = res.method; // for @UnsupportedAppUsage
mCurrentInputMethodSession = InputMethodSessionWrapper.createOrNull(res.method);
mCurId = res.id;
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
@@ -2070,11 +2070,8 @@ public final class InputMethodManager {
mRestartOnNextWindowFocus = true;
break;
}
if (mCurMethod != null && mCompletions != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
if (mCurrentInputMethodSession != null && mCompletions != null) {
mCurrentInputMethodSession.displayCompletions(mCompletions);
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
@@ -2232,12 +2229,9 @@ public final class InputMethodManager {
ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this,
null /* icProto */);
synchronized (mH) {
try {
if (mCurMethod != null && mCurRootView != null
&& mCurRootView.getWindowToken() == windowToken) {
mCurMethod.notifyImeHidden();
}
} catch (RemoteException re) {
if (mCurrentInputMethodSession != null && mCurRootView != null
&& mCurRootView.getWindowToken() == windowToken) {
mCurrentInputMethodSession.notifyImeHidden();
}
}
}
@@ -2282,7 +2276,7 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
|| mCurMethod == null) {
|| mCurrentInputMethodSession == null) {
return;
}
@@ -2291,22 +2285,20 @@ public final class InputMethodManager {
|| mCursorCandEnd != candidatesEnd) {
if (DEBUG) Log.d(TAG, "updateSelection");
try {
if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
final int oldSelStart = mCursorSelStart;
final int oldSelEnd = mCursorSelEnd;
// Update internal values before sending updateSelection to the IME, because
// if it changes the text within its onUpdateSelection handler in a way that
// does not move the cursor we don't want to call it again with the same values.
mCursorSelStart = selStart;
mCursorSelEnd = selEnd;
mCursorCandStart = candidatesStart;
mCursorCandEnd = candidatesEnd;
mCurMethod.updateSelection(oldSelStart, oldSelEnd,
selStart, selEnd, candidatesStart, candidatesEnd);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
if (DEBUG) {
Log.v(TAG, "SELECTION CHANGE: " + mCurrentInputMethodSession);
}
final int oldSelStart = mCursorSelStart;
final int oldSelEnd = mCursorSelEnd;
// Update internal values before sending updateSelection to the IME, because
// if it changes the text within its onUpdateSelection handler in a way that
// does not move the cursor we don't want to call it again with the same values.
mCursorSelStart = selStart;
mCursorSelEnd = selEnd;
mCursorCandStart = candidatesStart;
mCursorCandEnd = candidatesEnd;
mCurrentInputMethodSession.updateSelection(
oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
}
}
}
@@ -2340,15 +2332,11 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
|| mCurMethod == null) {
|| mCurrentInputMethodSession == null) {
return;
}
try {
if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
mCurMethod.viewClicked(focusChanged);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
mCurrentInputMethodSession.viewClicked(focusChanged);
}
}
@@ -2409,21 +2397,16 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
|| mCurMethod == null) {
|| mCurrentInputMethodSession == null) {
return;
}
mTmpCursorRect.set(left, top, right, bottom);
if (!mCursorRect.equals(mTmpCursorRect)) {
if (DEBUG) Log.d(TAG, "updateCursor");
if (DEBUG) Log.d(TAG, "updateCursor: " + mCurrentInputMethodSession);
try {
if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
mCurMethod.updateCursor(mTmpCursorRect);
mCursorRect.set(mTmpCursorRect);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
mCurrentInputMethodSession.updateCursor(mTmpCursorRect);
mCursorRect.set(mTmpCursorRect);
}
}
}
@@ -2446,7 +2429,7 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
|| mCurMethod == null) {
|| mCurrentInputMethodSession == null) {
return;
}
// If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has
@@ -2463,21 +2446,16 @@ public final class InputMethodManager {
return;
}
if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
try {
if (mActivityViewToScreenMatrix != null) {
mCurMethod.updateCursorAnchorInfo(
CursorAnchorInfo.createForAdditionalParentMatrix(
cursorAnchorInfo, mActivityViewToScreenMatrix));
} else {
mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
}
mCursorAnchorInfo = cursorAnchorInfo;
// Clear immediate bit (if any).
mRequestUpdateCursorAnchorInfoMonitorMode &=
~InputConnection.CURSOR_UPDATE_IMMEDIATE;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
if (mActivityViewToScreenMatrix != null) {
mCurrentInputMethodSession.updateCursorAnchorInfo(
CursorAnchorInfo.createForAdditionalParentMatrix(
cursorAnchorInfo, mActivityViewToScreenMatrix));
} else {
mCurrentInputMethodSession.updateCursorAnchorInfo(cursorAnchorInfo);
}
mCursorAnchorInfo = cursorAnchorInfo;
// Clear immediate bit (if any).
mRequestUpdateCursorAnchorInfoMonitorMode &= ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
}
}
@@ -2503,15 +2481,11 @@ public final class InputMethodManager {
checkFocus();
synchronized (mH) {
if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null
|| mCurMethod == null) {
|| mCurrentInputMethodSession == null) {
return;
}
try {
if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
mCurMethod.appPrivateCommand(action, data);
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
mCurrentInputMethodSession.appPrivateCommand(action, data);
}
}
@@ -2669,7 +2643,7 @@ public final class InputMethodManager {
public int dispatchInputEvent(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
if (mCurMethod != null) {
if (mCurrentInputMethodSession != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
@@ -2680,7 +2654,9 @@ public final class InputMethodManager {
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
if (DEBUG) {
Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurrentInputMethodSession);
}
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
@@ -2770,8 +2746,7 @@ public final class InputMethodManager {
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event);
Log.w(TAG, "Unable to send input event to IME: " + mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
@@ -3228,7 +3203,11 @@ public final class InputMethodManager {
+ " mBindSequence=" + mBindSequence
+ " mCurId=" + mCurId);
p.println(" mFullscreenMode=" + mFullscreenMode);
p.println(" mCurMethod=" + mCurMethod);
if (mCurrentInputMethodSession != null) {
p.println(" mCurMethod=" + mCurrentInputMethodSession);
} else {
p.println(" mCurMethod= null");
}
p.println(" mCurRootView=" + mCurRootView);
p.println(" mServedView=" + getServedViewLocked());
p.println(" mNextServedView=" + getNextServedViewLocked());
@@ -3344,7 +3323,7 @@ public final class InputMethodManager {
*/
@GuardedBy("mH")
public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) {
if (mCurMethod == null) {
if (mCurrentInputMethodSession == null) {
return;
}

View File

@@ -0,0 +1,163 @@
/*
* 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 android.view.inputmethod;
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.view.IInputMethodSession;
/**
* This class wrap the {@link IInputMethodSession} object from {@link InputMethodManager}.
* Using current {@link IInputMethodSession} object to communicate with
* {@link android.inputmethodservice.InputMethodService}.
*/
final class InputMethodSessionWrapper {
private static final String TAG = "InputMethodSessionWrapper";
/**
* The actual instance of the method to make calls on it.
*/
@NonNull
private final IInputMethodSession mSession;
private InputMethodSessionWrapper(@NonNull IInputMethodSession inputMethodSession) {
mSession = inputMethodSession;
}
/**
* Create a {@link InputMethodSessionWrapper} instance if applicability.
*
* @param inputMethodSession {@link IInputMethodSession} object to be wrapped.
* @return an instance of {@link InputMethodSessionWrapper} if {@code inputMethodSession} is not
* {@code null}. {@code null} otherwise.
*/
@Nullable
public static InputMethodSessionWrapper createOrNull(
@NonNull IInputMethodSession inputMethodSession) {
return inputMethodSession != null ? new InputMethodSessionWrapper(inputMethodSession)
: null;
}
@AnyThread
void finishInput() {
try {
mSession.finishInput();
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
try {
mSession.updateCursorAnchorInfo(cursorAnchorInfo);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void displayCompletions(CompletionInfo[] completions) {
try {
mSession.displayCompletions(completions);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void updateExtractedText(int token, ExtractedText text) {
try {
mSession.updateExtractedText(token, text);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void toggleSoftInput(int showFlags, int hideFlags) {
try {
mSession.toggleSoftInput(showFlags, hideFlags);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void appPrivateCommand(String action, Bundle data) {
try {
mSession.appPrivateCommand(action, data);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void notifyImeHidden() {
try {
mSession.notifyImeHidden();
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void viewClicked(boolean focusChanged) {
try {
mSession.viewClicked(focusChanged);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void updateCursor(Rect newCursor) {
try {
mSession.updateCursor(newCursor);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
@AnyThread
void updateSelection(int oldSelStart, int oldSelEnd, int selStart, int selEnd,
int candidatesStart, int candidatesEnd) {
try {
mSession.updateSelection(
oldSelStart, oldSelEnd, selStart, selEnd, candidatesStart, candidatesEnd);
} catch (RemoteException e) {
Log.w(TAG, "IME died", e);
}
}
/**
* @return {@link IInputMethodSession#toString()} as a debug string.
*/
@AnyThread
@NonNull
@Override
public String toString() {
return mSession.toString();
}
}