Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Add IC#closeConnection().
Browse files Browse the repository at this point in the history
It turns out that BaseInputConnection has still depended on a private
API named BaseInputConnection#reportFinish(), which was introduced
4 years ago to work around a UI freeze due to an unbalanced batch edit
count [1].  Note that such an unbalanced batch edit count cannot always
be avoidable.  It can easily occur in the following situations.
 - The current IME crashed during batch edit.
 - The user changed the View focus during batch edit.
 - The current IME called IMM#switchToNextInputMethod() during batch
   edit.

The remaining problem is that #reportFinish() is still an internal API
and only subclasses of BaseInputConnection can implement it, and IMM
calls it when and only when the current InputConnection is
BaseInputConnection or its subclass.  InputConnectionWrapper and any
other InputConnection implementations will never receive such a callback
to clean up InputConnection#{begin, end}BatchEdit(), which is considered
to be a major contributor to UI freeze.

To address the above issue, we unhide BaseInputConnection#reportFinish()
as InputConnection#closeConnection() so that application developers can
receive an appropriate callback to clean up internal state including
unfinished batch edit.

  [1] I5525d776916f0c42d5e6d4a4282aed590d7f0e9a
      9d69ecb

Bug: 24688781
Bug: 25332806
Change-Id: I234309c5880c9fe0b299b8bd0f8862796d4dda0d
  • Loading branch information
yukawa committed Mar 30, 2016
1 parent 0caf007 commit 9f9afe5
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 25 deletions.
4 changes: 4 additions & 0 deletions api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44641,6 +44641,7 @@ package android.view.inputmethod {
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -44808,6 +44809,7 @@ package android.view.inputmethod {
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -44840,6 +44842,7 @@ package android.view.inputmethod {
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -66433,3 +66436,4 @@ package org.xmlpull.v1.sax2 {
}

}

4 changes: 4 additions & 0 deletions api/system-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47370,6 +47370,7 @@ package android.view.inputmethod {
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -47537,6 +47538,7 @@ package android.view.inputmethod {
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -47569,6 +47571,7 @@ package android.view.inputmethod {
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -69498,3 +69501,4 @@ package org.xmlpull.v1.sax2 {
}

}

4 changes: 4 additions & 0 deletions api/test-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44715,6 +44715,7 @@ package android.view.inputmethod {
ctor public BaseInputConnection(android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -44882,6 +44883,7 @@ package android.view.inputmethod {
public abstract interface InputConnection {
method public abstract boolean beginBatchEdit();
method public abstract boolean clearMetaKeyStates(int);
method public abstract void closeConnection();
method public abstract boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public abstract boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public abstract boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -44914,6 +44916,7 @@ package android.view.inputmethod {
ctor public InputConnectionWrapper(android.view.inputmethod.InputConnection, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method public void closeConnection();
method public boolean commitCompletion(android.view.inputmethod.CompletionInfo);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(java.lang.CharSequence, int);
Expand Down Expand Up @@ -66507,3 +66510,4 @@ package org.xmlpull.v1.sax2 {
}

}

10 changes: 5 additions & 5 deletions core/java/android/view/inputmethod/BaseInputConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package android.view.inputmethod;

import android.annotation.CallSuper;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
Expand Down Expand Up @@ -154,12 +155,11 @@ public boolean endBatchEdit() {
}

/**
* Called when this InputConnection is no longer used by the InputMethodManager.
*
* @hide
* Default implementation calls {@link #finishComposingText()}.
*/
public void reportFinish() {
// Intentionally empty
@CallSuper
public void closeConnection() {
finishComposingText();
}

/**
Expand Down
16 changes: 16 additions & 0 deletions core/java/android/view/inputmethod/InputConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
* was introduced in {@link android.os.Build.VERSION_CODES#N}.</li>
* <li>{@link #getHandler()}}, which was introduced in
* {@link android.os.Build.VERSION_CODES#N}.</li>
* <li>{@link #closeConnection()}}, which was introduced in
* {@link android.os.Build.VERSION_CODES#N}.</li>
* </ul>
*
* <h3>Implementing an IME or an editor</h3>
Expand Down Expand Up @@ -820,4 +822,18 @@ public ExtractedText getExtractedText(ExtractedTextRequest request,
* @return {@code null} to use the default {@link Handler}.
*/
public Handler getHandler();

/**
* Called by the system up to only once to notify that the system is about to invalidate
* connection between the input method and the application.
*
* <p><strong>Editor authors</strong>: You can clear all the nested batch edit right now and
* you no longer need to handle subsequent callbacks on this connection, including
* {@link #beginBatchEdit()}}. Note that although the system tries to call this method whenever
* possible, there may be a chance that this method is not called in some exceptional
* situations.</p>
*
* <p>Note: This does nothing when called from input methods.</p>
*/
public void closeConnection();
}
23 changes: 23 additions & 0 deletions core/java/android/view/inputmethod/InputConnectionInspector.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public final class InputConnectionInspector {
* {@link android.os.Build.VERSION_CODES#N} and later.
*/
int GET_HANDLER = 1 << 5;
/**
* {@link InputConnection#closeConnection()}} is available in
* {@link android.os.Build.VERSION_CODES#N} and later.
*/
int CLOSE_CONNECTION = 1 << 6;
}

private static final Map<Class, Integer> sMissingMethodsMap = Collections.synchronizedMap(
Expand Down Expand Up @@ -119,6 +124,9 @@ public static int getMissingMethodFlagsInternal(@NonNull final Class clazz) {
if (!hasGetHandler(clazz)) {
flags |= MissingMethodFlags.GET_HANDLER;
}
if (!hasCloseConnection(clazz)) {
flags |= MissingMethodFlags.CLOSE_CONNECTION;
}
sMissingMethodsMap.put(clazz, flags);
return flags;
}
Expand Down Expand Up @@ -178,6 +186,15 @@ private static boolean hasGetHandler(@NonNull final Class clazz) {
}
}

private static boolean hasCloseConnection(@NonNull final Class clazz) {
try {
final Method method = clazz.getMethod("closeConnection");
return !Modifier.isAbstract(method.getModifiers());
} catch (NoSuchMethodException e) {
return false;
}
}

public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int flags) {
final StringBuilder sb = new StringBuilder();
boolean isEmpty = true;
Expand Down Expand Up @@ -219,6 +236,12 @@ public static String getMissingMethodFlagsAsString(@MissingMethodFlags final int
}
sb.append("getHandler()");
}
if ((flags & MissingMethodFlags.CLOSE_CONNECTION) != 0) {
if (!isEmpty) {
sb.append(",");
}
sb.append("closeConnection()");
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,12 @@ public boolean requestCursorUpdates(int cursorUpdateMode) {
public Handler getHandler() {
return mTarget.getHandler();
}

/**
* {@inheritDoc}
* @throws NullPointerException if the target is {@code null}.
*/
public void closeConnection() {
mTarget.closeConnection();
}
}
2 changes: 1 addition & 1 deletion core/java/android/view/inputmethod/InputMethodManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ void deactivate() {
// reportFinish() will take effect.
return;
}
reportFinish();
closeConnection();
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions core/java/android/widget/AbsListView.java
Original file line number Diff line number Diff line change
Expand Up @@ -5937,6 +5937,11 @@ public boolean requestCursorUpdates(int cursorUpdateMode) {
public Handler getHandler() {
return getTarget().getHandler();
}

@Override
public void closeConnection() {
getTarget().closeConnection();
}
}

/**
Expand Down
24 changes: 11 additions & 13 deletions core/java/com/android/internal/view/IInputConnectionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;

import java.lang.ref.WeakReference;
import android.view.inputmethod.InputConnectionInspector;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;

public abstract class IInputConnectionWrapper extends IInputContext.Stub {
static final String TAG = "IInputConnectionWrapper";
Expand Down Expand Up @@ -61,7 +60,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_REPORT_FINISH = 150;
private static final int DO_CLOSE_CONNECTION = 150;

@GuardedBy("mLock")
@Nullable
Expand Down Expand Up @@ -222,8 +221,8 @@ public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
seq, callback));
}

public void reportFinish() {
dispatchMessage(obtainMessage(DO_REPORT_FINISH));
public void closeConnection() {
dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
}

void dispatchMessage(Message msg) {
Expand Down Expand Up @@ -501,10 +500,10 @@ void executeMessage(Message msg) {
}
return;
}
case DO_REPORT_FINISH: {
case DO_CLOSE_CONNECTION: {
// Note that we do not need to worry about race condition here, because 1) mFinished
// is updated only inside this block, and 2) the code here is running on a Handler
// hence we assume multiple DO_REPORT_FINISH messages will not be handled at the
// hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
// same time.
if (isFinished()) {
return;
Expand All @@ -518,11 +517,10 @@ void executeMessage(Message msg) {
if (ic == null) {
return;
}
ic.finishComposingText();
// TODO: Make reportFinish() public method of InputConnection to remove this
// check.
if (ic instanceof BaseInputConnection) {
((BaseInputConnection) ic).reportFinish();
@MissingMethodFlags
final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
ic.closeConnection();
}
} finally {
synchronized (mLock) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@ public Handler getHandler() {
return null;
}

public void closeConnection() {
// Nothing should happen when called from input method.
}

private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
return (mMissingMethods & methodFlag) == methodFlag;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,9 @@ public boolean endBatchEdit() {
return false;
}

/**
* @hide
*/
@Override
public void reportFinish() {
super.reportFinish();

public void closeConnection() {
super.closeConnection();
synchronized(this) {
while (mBatchEditNesting > 0) {
endBatchEdit();
Expand Down

0 comments on commit 9f9afe5

Please sign in to comment.