Merge "Offload user-switching task from startInputOrWindowGainedFocus()"
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: "
|
||||
|
||||
Reference in New Issue
Block a user