diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 0693617e7b93a..ace88087ec5a6 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -319,6 +319,25 @@ public final class InputMethodManager { int mCursorCandStart; int mCursorCandEnd; + /** + * Represents an invalid action notification sequence number. {@link InputMethodManagerService} + * always issues a positive integer for action notification sequence numbers. Thus -1 is + * guaranteed to be different from any valid sequence number. + */ + private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1; + /** + * The next sequence number that is to be sent to {@link InputMethodManagerService} via + * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed. + */ + private int mNextUserActionNotificationSequenceNumber = + NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; + + /** + * The last sequence number that is already sent to {@link InputMethodManagerService}. + */ + private int mLastSentUserActionNotificationSequenceNumber = + NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER; + /** * The instance that has previously been sent to the input method. */ @@ -364,6 +383,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_CURSOR_ANCHOR_MONITOR_MODE = 8; + static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9; class H extends Handler { H(Looper looper) { @@ -503,6 +523,11 @@ public final class InputMethodManager { } return; } + case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: { + synchronized (mH) { + mNextUserActionNotificationSequenceNumber = msg.arg1; + } + } } } } @@ -572,6 +597,12 @@ public final class InputMethodManager { public void setCursorAnchorMonitorMode(int monitorMode) { mH.sendMessage(mH.obtainMessage(MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0)); } + + @Override + public void setUserActionNotificationSequenceNumber(int sequenceNumber) { + mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, + sequenceNumber, 0)); + } }; final InputConnection mDummyInputConnection = new BaseInputConnection(this, false); @@ -1214,6 +1245,8 @@ public final class InputMethodManager { mBindSequence = res.sequence; mCurMethod = res.method; mCurId = res.id; + mNextUserActionNotificationSequenceNumber = + res.userActionNotificationSequenceNumber; } else { if (res.channel != null && res.channel != mCurChannel) { res.channel.dispose(); @@ -1918,8 +1951,28 @@ public final class InputMethodManager { */ public void notifyUserAction() { synchronized (mH) { + if (mLastSentUserActionNotificationSequenceNumber == + mNextUserActionNotificationSequenceNumber) { + if (DEBUG) { + Log.w(TAG, "Ignoring notifyUserAction as it has already been sent." + + " mLastSentUserActionNotificationSequenceNumber: " + + mLastSentUserActionNotificationSequenceNumber + + " mNextUserActionNotificationSequenceNumber: " + + mNextUserActionNotificationSequenceNumber); + } + return; + } try { - mService.notifyUserAction(); + if (DEBUG) { + Log.w(TAG, "notifyUserAction: " + + " mLastSentUserActionNotificationSequenceNumber: " + + mLastSentUserActionNotificationSequenceNumber + + " mNextUserActionNotificationSequenceNumber: " + + mNextUserActionNotificationSequenceNumber); + } + mService.notifyUserAction(mNextUserActionNotificationSequenceNumber); + mLastSentUserActionNotificationSequenceNumber = + mNextUserActionNotificationSequenceNumber; } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } @@ -2103,6 +2156,10 @@ public final class InputMethodManager { + " mCursorSelEnd=" + mCursorSelEnd + " mCursorCandStart=" + mCursorCandStart + " mCursorCandEnd=" + mCursorCandEnd); + p.println(" mNextUserActionNotificationSequenceNumber=" + + mNextUserActionNotificationSequenceNumber + + " mLastSentUserActionNotificationSequenceNumber=" + + mLastSentUserActionNotificationSequenceNumber); } /** diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl index 9e8d12b6a8605..b100d27d41550 100644 --- a/core/java/com/android/internal/view/IInputMethodClient.aidl +++ b/core/java/com/android/internal/view/IInputMethodClient.aidl @@ -28,4 +28,5 @@ oneway interface IInputMethodClient { void onUnbindMethod(int sequence); void setActive(boolean active); void setCursorAnchorMonitorMode(int monitorMode); + void setUserActionNotificationSequenceNumber(int sequenceNumber); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 4590520610aa4..b84c3592c60f6 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -77,6 +77,6 @@ interface IInputMethodManager { boolean setInputMethodEnabled(String id, boolean enabled); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); int getInputMethodWindowVisibleHeight(); - oneway void notifyUserAction(); + oneway void notifyUserAction(int sequenceNumber); void setCursorAnchorMonitorMode(in IBinder token, int monitorMode); } diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java index 14afe21d0619a..3a3e56d59e7e9 100644 --- a/core/java/com/android/internal/view/InputBindResult.java +++ b/core/java/com/android/internal/view/InputBindResult.java @@ -47,13 +47,19 @@ public final class InputBindResult implements Parcelable { * Sequence number of this binding. */ public final int sequence; - + + /** + * Sequence number of user action notification. + */ + public final int userActionNotificationSequenceNumber; + public InputBindResult(IInputMethodSession _method, InputChannel _channel, - String _id, int _sequence) { + String _id, int _sequence, int _userActionNotificationSequenceNumber) { method = _method; channel = _channel; id = _id; sequence = _sequence; + userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber; } InputBindResult(Parcel source) { @@ -65,12 +71,15 @@ public final class InputBindResult implements Parcelable { } id = source.readString(); sequence = source.readInt(); + userActionNotificationSequenceNumber = source.readInt(); } @Override public String toString() { return "InputBindResult{" + method + " " + id - + " #" + sequence + "}"; + + " sequence:" + sequence + + " userActionNotificationSequenceNumber:" + userActionNotificationSequenceNumber + + "}"; } /** @@ -90,6 +99,7 @@ public final class InputBindResult implements Parcelable { } dest.writeString(id); dest.writeInt(sequence); + dest.writeInt(userActionNotificationSequenceNumber); } /** diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 251247c26b9ca..8fb2e9f484a10 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -149,6 +149,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_BIND_METHOD = 3010; static final int MSG_SET_ACTIVE = 3020; static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 3030; + static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040; static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; @@ -172,7 +173,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private final HardKeyboardListener mHardKeyboardListener; private final WindowManagerService mWindowManagerService; - final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1); + final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1); // All known input methods. mMethodMap also serves as the global // lock for this class. @@ -379,6 +380,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mScreenOn = true; + int mCurUserActionNotificationSequenceNumber = 0; + int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; int mImeWindowVis; @@ -1129,7 +1132,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub showCurrentInputLocked(getAppShowFlags(), null); } return new InputBindResult(session.session, - session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq); + (session.channel != null ? session.channel.dup() : null), + mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber); } InputBindResult startInputLocked(IInputMethodClient client, @@ -1205,7 +1209,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Return to client, and we will get back with it when // we have had a session made for it. requestClientSessionLocked(cs); - return new InputBindResult(null, null, mCurId, mCurSeq); + return new InputBindResult(null, null, mCurId, mCurSeq, + mCurUserActionNotificationSequenceNumber); } else if (SystemClock.uptimeMillis() < (mLastBindTime+TIME_TO_RECONNECT)) { // In this case we have connected to the service, but @@ -1215,7 +1220,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // we can report back. If it has been too long, we want // to fall through so we can try a disconnect/reconnect // to see if we can get back in touch with the service. - return new InputBindResult(null, null, mCurId, mCurSeq); + return new InputBindResult(null, null, mCurId, mCurSeq, + mCurUserActionNotificationSequenceNumber); } else { EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0); @@ -1234,7 +1240,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!mSystemReady) { // If the system is not yet ready, we shouldn't be running third // party code. - return new InputBindResult(null, null, mCurMethodId, mCurSeq); + return new InputBindResult(null, null, mCurMethodId, mCurSeq, + mCurUserActionNotificationSequenceNumber); } InputMethodInfo info = mMethodMap.get(mCurMethodId); @@ -1263,7 +1270,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub WindowManager.LayoutParams.TYPE_INPUT_METHOD); } catch (RemoteException e) { } - return new InputBindResult(null, null, mCurId, mCurSeq); + return new InputBindResult(null, null, mCurId, mCurSeq, + mCurUserActionNotificationSequenceNumber); } else { mCurIntent = null; Slog.w(TAG, "Failure connecting to input method service: " @@ -2310,11 +2318,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void notifyUserAction() { + public void notifyUserAction(int sequenceNumber) { if (DEBUG) { - Slog.d(TAG, "Got the notification of a user action"); + Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber); } synchronized (mMethodMap) { + if (mCurUserActionNotificationSequenceNumber != sequenceNumber) { + if (DEBUG) { + Slog.d(TAG, "Ignoring the user action notification due to the sequence number " + + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber + + " actual: " + sequenceNumber); + } + return; + } final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi != null) { mSwitchingController.onUserActionLocked(imi, mCurrentSubtype); @@ -2588,6 +2604,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " uid " + ((ClientState)msg.obj).uid); } return true; + case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: { + final int sequenceNumber = msg.arg1; + final IInputMethodClient client = (IInputMethodClient)msg.obj; + try { + client.setUserActionNotificationSequenceNumber(sequenceNumber); + } catch (RemoteException e) { + Slog.w(TAG, "Got RemoteException sending " + + "setUserActionNotificationSequenceNumber(" + + sequenceNumber + ") notification to pid " + + ((ClientState)msg.obj).pid + " uid " + + ((ClientState)msg.obj).uid); + } + return true; + } // -------------------------------------------------------------- case MSG_HARD_KEYBOARD_SWITCH_CHANGED: @@ -2999,6 +3029,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Update the history of InputMethod and Subtype mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype); + mCurUserActionNotificationSequenceNumber = + Math.max(mCurUserActionNotificationSequenceNumber + 1, 1); + if (DEBUG) { + Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:" + + mCurUserActionNotificationSequenceNumber); + } + + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( + MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER, + mCurUserActionNotificationSequenceNumber, mCurClient.client)); + } + // Set Subtype here if (imi == null || subtypeId < 0) { mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); @@ -3490,6 +3533,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " mShowExplicitlyRequested=" + mShowExplicitlyRequested + " mShowForced=" + mShowForced + " mInputShown=" + mInputShown); + p.println(" mCurUserActionNotificationSequenceNumber=" + + mCurUserActionNotificationSequenceNumber); p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index 66268f28602a6..9ec6f4d526027 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -214,7 +214,7 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override - public void notifyUserAction() throws RemoteException { + public void notifyUserAction(int sequenceNumber) throws RemoteException { // TODO Auto-generated method stub }