Merge "Eliminate out-of-sync IMM#mFullscreenMode error"

This commit is contained in:
Yohei Yukawa
2017-02-09 16:55:00 +00:00
committed by Android (Google) Code Review
10 changed files with 117 additions and 97 deletions

View File

@@ -387,8 +387,9 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
InputConnection ic = getCurrentInputConnection();
if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);
}
initialize();
onBindInput();
}
@@ -1027,8 +1028,9 @@ public class InputMethodService extends AbstractInputMethodService {
if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
changed = true;
mIsFullscreen = isFullscreen;
InputConnection ic = getCurrentInputConnection();
if (ic != null) ic.reportFullscreenMode(isFullscreen);
if (mImm != null && mToken != null) {
mImm.reportFullscreenMode(mToken, mIsFullscreen);
}
mFullscreenApplied = true;
initialize();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)

View File

@@ -18,6 +18,7 @@ package android.view.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyCharacterMap;
@@ -751,13 +752,19 @@ public interface InputConnection {
public boolean clearMetaKeyStates(int states);
/**
* Called by the IME to tell the client when it switches between
* fullscreen and normal modes. This will normally be called for
* you by the standard implementation of
* {@link android.inputmethodservice.InputMethodService}.
* Called back when the connected IME switches between fullscreen and normal modes.
*
* @return true on success, false if the input connection is no longer
* valid.
* <p>Note: On {@link android.os.Build.VERSION_CODES#O} and later devices, input methods are no
* longer allowed to directly call this method at any time. To signal this event in the target
* application, input methods should always call
* {@link InputMethodService#updateFullscreenMode()} instead. This approach should work on API
* {@link android.os.Build.VERSION_CODES#N_MR1} and prior devices.</p>
*
* @return For editor authors, the return value will always be ignored. For IME authors, this
* always returns {@code true} on {@link android.os.Build.VERSION_CODES#N_MR1} and prior
* devices and {@code false} on {@link android.os.Build.VERSION_CODES#O} and later
* devices.
* @see InputMethodManager#isFullscreenMode()
*/
public boolean reportFullscreenMode(boolean enabled);

View File

@@ -34,7 +34,6 @@ import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.Trace;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.util.Pools.Pool;
@@ -396,6 +395,7 @@ public final class InputMethodManager {
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
class H extends Handler {
H(Looper looper) {
@@ -476,12 +476,13 @@ public final class InputMethodManager {
}
case MSG_SET_ACTIVE: {
final boolean active = msg.arg1 != 0;
final boolean fullscreen = msg.arg2 != 0;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_SET_ACTIVE " + active + ", was " + mActive);
}
synchronized (mH) {
mActive = active;
mFullscreenMode = false;
mFullscreenMode = fullscreen;
if (!active) {
// Some other client has starting using the IME, so note
// that this happened and make sure our own editor's
@@ -523,6 +524,21 @@ public final class InputMethodManager {
synchronized (mH) {
mNextUserActionNotificationSequenceNumber = msg.arg1;
}
return;
}
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
InputConnection ic = null;
synchronized (mH) {
mFullscreenMode = fullscreen;
if (mServedInputConnectionWrapper != null) {
ic = mServedInputConnectionWrapper.getInputConnection();
}
}
if (ic != null) {
ic.reportFullscreenMode(fullscreen);
}
return;
}
}
}
@@ -556,19 +572,12 @@ public final class InputMethodManager {
mParentInputMethodManager.notifyUserAction();
}
@Override
protected void onReportFullscreenMode(boolean enabled, boolean calledInBackground) {
mParentInputMethodManager.onReportFullscreenMode(enabled, calledInBackground,
getInputMethodId());
}
@Override
public String toString() {
return "ControlledInputConnectionWrapper{"
+ "connection=" + getInputConnection()
+ " finished=" + isFinished()
+ " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ " mInputMethodId=" + getInputMethodId()
+ "}";
}
}
@@ -600,24 +609,31 @@ public final class InputMethodManager {
@Override
public void onBindMethod(InputBindResult res) {
mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
mH.obtainMessage(MSG_BIND, res).sendToTarget();
}
@Override
public void onUnbindMethod(int sequence, @InputMethodClient.UnbindReason int unbindReason) {
mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, unbindReason));
mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
}
@Override
public void setActive(boolean active) {
mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
public void setActive(boolean active, boolean fullscreen) {
mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget();
}
@Override
public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
sequenceNumber, 0));
mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, sequenceNumber, 0)
.sendToTarget();
}
@Override
public void reportFullscreenMode(boolean fullscreen) {
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
.sendToTarget();
}
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -730,16 +746,6 @@ public final class InputMethodManager {
}
}
/** @hide */
public void onReportFullscreenMode(boolean fullScreen, boolean calledInBackground,
String inputMethodId) {
synchronized (mH) {
if (!calledInBackground || TextUtils.equals(mCurId, inputMethodId)) {
mFullscreenMode = fullScreen;
}
}
}
/** @hide */
public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
try {
@@ -769,6 +775,17 @@ public final class InputMethodManager {
}
}
/**
* @hide
*/
public void reportFullscreenMode(IBinder token, boolean fullscreen) {
try {
mService.reportFullscreenMode(token, fullscreen);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Return true if the given view is the currently active view for the
* input method.
@@ -1274,9 +1291,6 @@ public final class InputMethodManager {
mCurId = res.id;
mNextUserActionNotificationSequenceNumber =
res.userActionNotificationSequenceNumber;
if (mServedInputConnectionWrapper != null) {
mServedInputConnectionWrapper.setInputMethodId(mCurId);
}
} else {
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
@@ -2341,6 +2355,7 @@ public final class InputMethodManager {
+ " mHasBeenInactive=" + mHasBeenInactive
+ " mBindSequence=" + mBindSequence
+ " mCurId=" + mCurId);
p.println(" mFullscreenMode=" + mFullscreenMode);
p.println(" mCurMethod=" + mCurMethod);
p.println(" mCurRootView=" + mCurRootView);
p.println(" mServedView=" + mServedView);

View File

@@ -57,7 +57,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
private static final int DO_BEGIN_BATCH_EDIT = 90;
private static final int DO_END_BATCH_EDIT = 95;
private static final int DO_REPORT_FULLSCREEN_MODE = 100;
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;
@@ -73,8 +72,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private Object mLock = new Object();
@GuardedBy("mLock")
private boolean mFinished = false;
@GuardedBy("mLock")
private String mInputMethodId;
static class SomeArgs {
Object arg1;
@@ -113,18 +110,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
}
}
public String getInputMethodId() {
synchronized (mLock) {
return mInputMethodId;
}
}
public void setInputMethodId(final String inputMethodId) {
synchronized (mLock) {
mInputMethodId = inputMethodId;
}
}
abstract protected boolean isActive();
/**
@@ -133,14 +118,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
*/
abstract protected void onUserAction();
/**
* Called when the input method started or stopped full-screen mode.
* @param enabled {@code true} if the input method starts full-screen mode.
* @param calledInBackground {@code true} if this input connection is in a state when incoming
* events are usually ignored.
*/
abstract protected void onReportFullscreenMode(boolean enabled, boolean calledInBackground);
public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
}
@@ -225,10 +202,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
}
public void reportFullscreenMode(boolean enabled) {
dispatchMessage(obtainMessageII(DO_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0));
}
public void performPrivateCommand(String action, Bundle data) {
dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
}
@@ -486,23 +459,6 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub {
ic.endBatchEdit();
return;
}
case DO_REPORT_FULLSCREEN_MODE: {
InputConnection ic = getInputConnection();
boolean isBackground = false;
if (ic == null || !isActive()) {
Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
isBackground = true;
}
final boolean enabled = msg.arg1 == 1;
if (!isBackground) {
ic.reportFullscreenMode(enabled);
}
// Due to the nature of asynchronous event handling, currently InputMethodService
// has relied on the fact that #reportFullscreenMode() can be handled even when the
// InputConnection is inactive. We have to notify this event to InputMethodManager.
onReportFullscreenMode(enabled, isBackground);
return;
}
case DO_PERFORM_PRIVATE_COMMAND: {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {

View File

@@ -62,9 +62,7 @@ import com.android.internal.view.IInputContextCallback;
void beginBatchEdit();
void endBatchEdit();
void reportFullscreenMode(boolean enabled);
void sendKeyEvent(in KeyEvent event);
void clearMetaKeyStates(int states);

View File

@@ -27,6 +27,7 @@ oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
// unbindReason corresponds to InputMethodClient.UnbindReason.
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active);
void setActive(boolean active, boolean fullscreen);
void setUserActionNotificationSequenceNumber(int sequenceNumber);
void reportFullscreenMode(boolean fullscreen);
}

View File

@@ -86,5 +86,7 @@ interface IInputMethodManager {
IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri,
in String packageName);
void reportFullscreenMode(in IBinder token, boolean fullscreen);
oneway void notifyUserAction(int sequenceNumber);
}

View File

@@ -467,12 +467,8 @@ public class InputConnectionWrapper implements InputConnection {
}
public boolean reportFullscreenMode(boolean enabled) {
try {
mIInputContext.reportFullscreenMode(enabled);
return true;
} catch (RemoteException e) {
return false;
}
// Nothing should happen when called from input method.
return false;
}
public boolean performPrivateCommand(String action, Bundle data) {

View File

@@ -181,6 +181,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
static final int MSG_SWITCH_IME = 3050;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -410,6 +411,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
boolean mInputShown;
/**
* {@code true} if the current input method is in fullscreen mode.
*/
boolean mInFullscreenMode;
/**
* The Intent used to connect to the current input method.
*/
@@ -1265,8 +1271,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_SET_ACTIVE, 0, mCurClient));
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
MSG_SET_ACTIVE, 0, 0, mCurClient));
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
mCurClient.sessionRequested = false;
@@ -1649,6 +1655,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (mStatusBar != null) {
mStatusBar.setIconVisibility(mSlotIme, false);
}
mInFullscreenMode = false;
}
@Override
@@ -2915,7 +2922,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
case MSG_SET_ACTIVE:
try {
((ClientState)msg.obj).client.setActive(msg.arg1 != 0);
((ClientState)msg.obj).client.setActive(msg.arg1 != 0, msg.arg2 != 0);
} catch (RemoteException e) {
Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
+ ((ClientState)msg.obj).pid + " uid "
@@ -2942,6 +2949,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
return true;
}
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
try {
clientState.client.reportFullscreenMode(fullscreen);
} catch (RemoteException e) {
Slog.w(TAG, "Got RemoteException sending "
+ "reportFullscreen(" + fullscreen + ") notification to pid="
+ clientState.pid + " uid=" + clientState.uid);
}
return true;
}
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
@@ -2962,8 +2981,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// Inform the current client of the change in active status
if (mCurClient != null && mCurClient.client != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mCurClient));
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0,
mCurClient));
}
}
}
@@ -3962,6 +3982,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
@Override
public void reportFullscreenMode(IBinder token, boolean fullscreen) {
if (!calledFromValidUser()) {
return;
}
synchronized (mMethodMap) {
if (!calledWithValidToken(token)) {
return;
}
if (mCurClient != null && mCurClient.client != null) {
mInFullscreenMode = fullscreen;
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, mCurClient));
}
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -4014,6 +4051,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
p.println(" mInFullscreenMode=" + mInFullscreenMode);
p.println(" mCurUserActionNotificationSequenceNumber="
+ mCurUserActionNotificationSequenceNumber);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);

View File

@@ -248,4 +248,9 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
// TODO Auto-generated method stub
return null;
}
@Override
public void reportFullscreenMode(IBinder token, boolean fullscreen) {
// TODO Auto-generated method stub
}
}