Fix potential failures in InputMethodService (2nd try)

It had the chance for calling into IMS but IMS
already destroyed.

Ideally we should suppress any callbacks after
InputMethodService#onDestroy() and log them.

First try is CL[1], but it wrongly add the API to
IInputMethod. Using the InputMethodServiceInternal
is make more sense for IInputMethodWrapper to
communicate with InputMethodService.

[1]: I10f26a92e739b60c5b8010e8476f8f5a403e0a7b

Bug: 121269861
Bug: 211062619
Bug: 148086656
Test: presubmit
Change-Id: I7b4f34eb38f44dea6872eb46866c9ad7dc1c185c
This commit is contained in:
Wilson Wu
2022-09-20 20:08:58 +08:00
parent fe8bf190f9
commit 4bcdf9b201
3 changed files with 101 additions and 53 deletions

View File

@@ -147,119 +147,146 @@ class IInputMethodWrapper extends IInputMethod.Stub
@MainThread
@Override
public void executeMessage(Message msg) {
InputMethod inputMethod = mInputMethod.get();
// Need a valid reference to the inputMethod for everything except a dump.
if (inputMethod == null && msg.what != DO_DUMP) {
Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
return;
}
final InputMethod inputMethod = mInputMethod.get();
final InputMethodServiceInternal target = mTarget.get();
switch (msg.what) {
case DO_DUMP: {
InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
SomeArgs args = (SomeArgs)msg.obj;
try {
target.dump((FileDescriptor) args.arg1,
(PrintWriter) args.arg2, (String[]) args.arg3);
} catch (RuntimeException e) {
((PrintWriter)args.arg2).println("Exception: " + e);
}
synchronized (args.arg4) {
((CountDownLatch)args.arg4).countDown();
if (isValid(inputMethod, target, "DO_DUMP")) {
final FileDescriptor fd = (FileDescriptor) args.arg1;
final PrintWriter fout = (PrintWriter) args.arg2;
final String[] dumpArgs = (String[]) args.arg3;
final CountDownLatch latch = (CountDownLatch) args.arg4;
try {
target.dump(fd, fout, dumpArgs);
} catch (RuntimeException e) {
fout.println("Exception: " + e);
} finally {
latch.countDown();
}
}
args.recycle();
return;
}
case DO_INITIALIZE_INTERNAL:
inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj);
if (isValid(inputMethod, target, "DO_INITIALIZE_INTERNAL")) {
inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj);
}
return;
case DO_SET_INPUT_CONTEXT: {
inputMethod.bindInput((InputBinding)msg.obj);
if (isValid(inputMethod, target, "DO_SET_INPUT_CONTEXT")) {
inputMethod.bindInput((InputBinding) msg.obj);
}
return;
}
case DO_UNSET_INPUT_CONTEXT:
inputMethod.unbindInput();
if (isValid(inputMethod, target, "DO_UNSET_INPUT_CONTEXT")) {
inputMethod.unbindInput();
}
return;
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
final InputConnection inputConnection = (InputConnection) args.arg1;
final IInputMethod.StartInputParams params =
(IInputMethod.StartInputParams) args.arg2;
inputMethod.dispatchStartInput(inputConnection, params);
if (isValid(inputMethod, target, "DO_START_INPUT")) {
final InputConnection inputConnection = (InputConnection) args.arg1;
final IInputMethod.StartInputParams params =
(IInputMethod.StartInputParams) args.arg2;
inputMethod.dispatchStartInput(inputConnection, params);
}
args.recycle();
return;
}
case DO_ON_NAV_BUTTON_FLAGS_CHANGED:
inputMethod.onNavButtonFlagsChanged(msg.arg1);
if (isValid(inputMethod, target, "DO_ON_NAV_BUTTON_FLAGS_CHANGED")) {
inputMethod.onNavButtonFlagsChanged(msg.arg1);
}
return;
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel) args.arg1,
(IInputMethodSessionCallback) args.arg2));
if (isValid(inputMethod, target, "DO_CREATE_SESSION")) {
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel) args.arg1,
(IInputMethodSessionCallback) args.arg2));
}
args.recycle();
return;
}
case DO_SET_SESSION_ENABLED:
inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0);
if (isValid(inputMethod, target, "DO_SET_SESSION_ENABLED")) {
inputMethod.setSessionEnabled((InputMethodSession) msg.obj, msg.arg1 != 0);
}
return;
case DO_SHOW_SOFT_INPUT: {
final SomeArgs args = (SomeArgs)msg.obj;
inputMethod.showSoftInputWithToken(
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) {
inputMethod.showSoftInputWithToken(
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
}
args.recycle();
return;
}
case DO_HIDE_SOFT_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
(IBinder) args.arg1);
if (isValid(inputMethod, target, "DO_HIDE_SOFT_INPUT")) {
inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
(IBinder) args.arg1);
}
args.recycle();
return;
}
case DO_CHANGE_INPUTMETHOD_SUBTYPE:
inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
if (isValid(inputMethod, target, "DO_CHANGE_INPUTMETHOD_SUBTYPE")) {
inputMethod.changeInputMethodSubtype((InputMethodSubtype) msg.obj);
}
return;
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.onCreateInlineSuggestionsRequest(
(InlineSuggestionsRequestInfo) args.arg1,
(IInlineSuggestionsRequestCallback) args.arg2);
if (isValid(inputMethod, target, "DO_CREATE_INLINE_SUGGESTIONS_REQUEST")) {
inputMethod.onCreateInlineSuggestionsRequest(
(InlineSuggestionsRequestInfo) args.arg1,
(IInlineSuggestionsRequestCallback) args.arg2);
}
args.recycle();
return;
}
case DO_CAN_START_STYLUS_HANDWRITING: {
inputMethod.canStartStylusHandwriting(msg.arg1);
if (isValid(inputMethod, target, "DO_CAN_START_STYLUS_HANDWRITING")) {
inputMethod.canStartStylusHandwriting(msg.arg1);
}
return;
}
case DO_UPDATE_TOOL_TYPE: {
inputMethod.updateEditorToolType(msg.arg1);
if (isValid(inputMethod, target, "DO_UPDATE_TOOL_TYPE")) {
inputMethod.updateEditorToolType(msg.arg1);
}
return;
}
case DO_START_STYLUS_HANDWRITING: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
(List<MotionEvent>) args.arg2);
if (isValid(inputMethod, target, "DO_START_STYLUS_HANDWRITING")) {
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
(List<MotionEvent>) args.arg2);
}
args.recycle();
return;
}
case DO_INIT_INK_WINDOW: {
inputMethod.initInkWindow();
if (isValid(inputMethod, target, "DO_INIT_INK_WINDOW")) {
inputMethod.initInkWindow();
}
return;
}
case DO_FINISH_STYLUS_HANDWRITING: {
inputMethod.finishStylusHandwriting();
if (isValid(inputMethod, target, "DO_FINISH_STYLUS_HANDWRITING")) {
inputMethod.finishStylusHandwriting();
}
return;
}
case DO_REMOVE_STYLUS_HANDWRITING_WINDOW: {
inputMethod.removeStylusHandwritingWindow();
if (isValid(inputMethod, target, "DO_REMOVE_STYLUS_HANDWRITING_WINDOW")) {
inputMethod.removeStylusHandwritingWindow();
}
return;
}
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -445,4 +472,15 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void removeStylusHandwritingWindow() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW));
}
private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target,
String msg) {
if (inputMethod != null && target != null && !target.isServiceDestroyed()) {
return true;
} else {
Log.w(TAG, "Ignoring " + msg + ", InputMethod:" + inputMethod
+ ", InputMethodServiceInternal:" + target);
return false;
}
}
}

View File

@@ -700,11 +700,6 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public final void initializeInternal(@NonNull IInputMethod.InitParams params) {
if (mDestroyed) {
Log.i(TAG, "The InputMethodService has already onDestroyed()."
+ "Ignore the initialization.");
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
mConfigTracker.onInitialize(params.configChanges);
mPrivOps.set(params.privilegedOperations);
@@ -3938,6 +3933,14 @@ public class InputMethodService extends AbstractInputMethodService {
public void triggerServiceDump(String where, @Nullable byte[] icProto) {
ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isServiceDestroyed() {
return mDestroyed;
}
};
}

View File

@@ -85,4 +85,11 @@ interface InputMethodServiceInternal {
*/
default void triggerServiceDump(@NonNull String where, @Nullable byte[] icProto) {
}
/**
* @return {@code true} if {@link InputMethodService} is destroyed.
*/
default boolean isServiceDestroyed() {
return false;
};
}