Merge "Offload user-switching task from startInputOrWindowGainedFocus()"

This commit is contained in:
Yohei Yukawa
2020-02-20 03:27:26 +00:00
committed by Android (Google) Code Review
4 changed files with 124 additions and 20 deletions

View File

@@ -1007,6 +1007,13 @@ public final class InputMethodManager {
mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget();
}
@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)

View File

@@ -28,6 +28,7 @@ oneway interface IInputMethodClient {
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);

View File

@@ -88,58 +88,65 @@ public final class InputBindResult implements Parcelable {
* @see android.content.ServiceConnection#onServiceConnected(ComponentName, IBinder)
*/
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 @@ public final class InputBindResult implements Parcelable {
*
* @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 @@ public final class InputBindResult implements Parcelable {
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 @@ public final class InputBindResult implements Parcelable {
* 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);
}

View File

@@ -1388,6 +1388,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
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: "