Merge "Offload user-switching task from startInputOrWindowGainedFocus()"
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1e6abd9..39d5f5c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1008,6 +1008,13 @@
}
@Override
+ public void scheduleStartInputIfNecessary(boolean fullscreen) {
+ // TODO(b/149859205): See if we can optimize this by having a fused dedicated operation.
+ mH.obtainMessage(MSG_SET_ACTIVE, 0 /* active */, fullscreen ? 1 : 0).sendToTarget();
+ mH.obtainMessage(MSG_SET_ACTIVE, 1 /* active */, fullscreen ? 1 : 0).sendToTarget();
+ }
+
+ @Override
public void reportFullscreenMode(boolean fullscreen) {
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
.sendToTarget();
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 41f902e..4509032 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -28,6 +28,7 @@
void onBindMethod(in InputBindResult res);
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
+ void scheduleStartInputIfNecessary(boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index a5964b5..f29e95c 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -89,57 +89,64 @@
*/
int SUCCESS_WAITING_IME_BINDING = 2;
/**
+ * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a
+ * pending operation to switch to a different user.
+ *
+ * <p>Note that in this state even what would be the next current IME is not determined.</p>
+ */
+ int SUCCESS_WAITING_USER_SWITCHING = 3;
+ /**
* Indicates that this is not intended for starting input but just for reporting window
* focus change from the application process.
*
* <p>All other fields do not have meaningful value.</p>
*/
- int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3;
+ int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4;
/**
* Indicates somehow
* {@link
* com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus}
* is trying to return null {@link InputBindResult}, which must never happen.
*/
- int ERROR_NULL = 4;
+ int ERROR_NULL = 5;
/**
* Indicates that {@link com.android.server.inputmethod.InputMethodManagerService}
* recognizes no IME.
*/
- int ERROR_NO_IME = 5;
+ int ERROR_NO_IME = 6;
/**
* Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
* the caller UID.
*
* @see android.view.inputmethod.EditorInfo#packageName
*/
- int ERROR_INVALID_PACKAGE_NAME = 6;
+ int ERROR_INVALID_PACKAGE_NAME = 7;
/**
* Indicates that the system is still in an early stage of the boot process and any 3rd
* party application is not allowed to run.
*
* @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
*/
- int ERROR_SYSTEM_NOT_READY = 7;
+ int ERROR_SYSTEM_NOT_READY = 8;
/**
* Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
* connect to an {@link android.inputmethodservice.InputMethodService} but failed.
*
* @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
*/
- int ERROR_IME_NOT_CONNECTED = 8;
+ int ERROR_IME_NOT_CONNECTED = 9;
/**
* Indicates that the caller is not the foreground user, does not have
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user
* specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not
* running.
*/
- int ERROR_INVALID_USER = 9;
+ int ERROR_INVALID_USER = 10;
/**
* Indicates that the caller should have specified non-null
* {@link android.view.inputmethod.EditorInfo}.
*/
- int ERROR_NULL_EDITOR_INFO = 10;
+ int ERROR_NULL_EDITOR_INFO = 11;
/**
* Indicates that the target window the client specified cannot be the IME target right now.
*
@@ -149,24 +156,24 @@
*
* @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int)
*/
- int ERROR_NOT_IME_TARGET_WINDOW = 11;
+ int ERROR_NOT_IME_TARGET_WINDOW = 12;
/**
* Indicates that focused view in the current window is not an editor.
*/
- int ERROR_NO_EDITOR = 12;
+ int ERROR_NO_EDITOR = 13;
/**
* Indicates that there is a mismatch in display ID between IME client and focused Window.
*/
- int ERROR_DISPLAY_ID_MISMATCH = 13;
+ int ERROR_DISPLAY_ID_MISMATCH = 14;
/**
* Indicates that current IME client is no longer allowed to access to the associated
* display.
*/
- int ERROR_INVALID_DISPLAY_ID = 14;
+ int ERROR_INVALID_DISPLAY_ID = 15;
/**
* Indicates that the client is not recognized by the system.
*/
- int ERROR_INVALID_CLIENT = 15;
+ int ERROR_INVALID_CLIENT = 16;
}
@ResultCode
@@ -299,6 +306,8 @@
return "SUCCESS_WAITING_IME_SESSION";
case ResultCode.SUCCESS_WAITING_IME_BINDING:
return "SUCCESS_WAITING_IME_BINDING";
+ case ResultCode.SUCCESS_WAITING_USER_SWITCHING:
+ return "SUCCESS_WAITING_USER_SWITCHING";
case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
case ResultCode.ERROR_NULL:
@@ -386,4 +395,11 @@
* Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
*/
public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
+
+ /**
+ * Predefined <strong>success</strong> object for
+ * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
+ */
+ public static final InputBindResult USER_SWITCHING =
+ error(ResultCode.SUCCESS_WAITING_USER_SWITCHING);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d7cb192..87262a8 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1388,6 +1388,44 @@
}
}
+ private static final class UserSwitchHandlerTask implements Runnable {
+ final InputMethodManagerService mService;
+
+ @UserIdInt
+ final int mToUserId;
+
+ @Nullable
+ IInputMethodClient mClientToBeReset;
+
+ UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId,
+ @Nullable IInputMethodClient clientToBeReset) {
+ mService = service;
+ mToUserId = toUserId;
+ mClientToBeReset = clientToBeReset;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mService.mMethodMap) {
+ if (mService.mUserSwitchHandlerTask != this) {
+ // This task was already canceled before it is handled here. So do nothing.
+ return;
+ }
+ mService.switchUserOnHandlerLocked(mService.mUserSwitchHandlerTask.mToUserId,
+ mClientToBeReset);
+ mService.mUserSwitchHandlerTask = null;
+ }
+ }
+ }
+
+ /**
+ * When non-{@code null}, this represents pending user-switch task, which is to be executed as
+ * a handler callback. This needs to be set and unset only within the lock.
+ */
+ @Nullable
+ @GuardedBy("mMethodMap")
+ private UserSwitchHandlerTask mUserSwitchHandlerTask;
+
public static final class Lifecycle extends SystemService {
private InputMethodManagerService mService;
@@ -1406,8 +1444,9 @@
@Override
public void onSwitchUser(@UserIdInt int userHandle) {
// Called on ActivityManager thread.
- // TODO: Dispatch this to a worker thread as needed.
- mService.onSwitchUser(userHandle);
+ synchronized (mService.mMethodMap) {
+ mService.scheduleSwitchUserTaskLocked(userHandle, null /* clientToBeReset */);
+ }
}
@Override
@@ -1447,10 +1486,20 @@
}
}
- void onSwitchUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
- switchUserLocked(userId);
+ @GuardedBy("mMethodMap")
+ void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
+ @Nullable IInputMethodClient clientToBeReset) {
+ if (mUserSwitchHandlerTask != null) {
+ if (mUserSwitchHandlerTask.mToUserId == userId) {
+ mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset;
+ return;
+ }
+ mHandler.removeCallbacks(mUserSwitchHandlerTask);
}
+ final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
+ clientToBeReset);
+ mUserSwitchHandlerTask = task;
+ mHandler.post(task);
}
public InputMethodManagerService(Context context) {
@@ -1538,7 +1587,8 @@
}
@GuardedBy("mMethodMap")
- private void switchUserLocked(int newUserId) {
+ private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
+ IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
@@ -1589,6 +1639,18 @@
+ " selectedIme=" + mSettings.getSelectedInputMethod());
mLastSwitchUserId = newUserId;
+
+ if (mIsInteractive && clientToBeReset != null) {
+ final ClientState cs = mClients.get(clientToBeReset.asBinder());
+ if (cs == null) {
+ // The client is already gone.
+ return;
+ }
+ try {
+ cs.client.scheduleStartInputIfNecessary(mInFullscreenMode);
+ } catch (RemoteException e) {
+ }
+ }
}
void updateCurrentProfileIds() {
@@ -3072,6 +3134,22 @@
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
+ if (mUserSwitchHandlerTask != null) {
+ // There is already an on-going pending user switch task.
+ final int nextUserId = mUserSwitchHandlerTask.mToUserId;
+ if (userId == nextUserId) {
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
+ }
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(nextUserId)) {
+ if (profileId == userId) {
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
+ }
+ }
+ return InputBindResult.INVALID_USER;
+ }
+
// cross-profile access is always allowed here to allow profile-switching.
if (!mSettings.isCurrentProfile(userId)) {
Slog.w(TAG, "A background user is requesting window. Hiding IME.");
@@ -3083,8 +3161,10 @@
}
if (userId != mSettings.getCurrentUserId()) {
- switchUserLocked(userId);
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
}
+
// Master feature flag that overrides other conditions and forces IME preRendering.
if (DEBUG) {
Slog.v(TAG, "IME PreRendering MASTER flag: "