Clarify which InputMethodManagerInternal method takes lock internally
InputMethodManagerInternal is the callback where we need to pay extra attention to potential dead-locks. To make code reading easier, this CL makes it clear about which method needs to acquire ImfLock and which method is always thread-safe. Basically this is a mechanical code refactoring. There should be no observable behavior change. Bug: 192412909 Test: presubmit Change-Id: I98958e6c149582b0a24997c9ca814e355f8b2a2b
This commit is contained in:
@@ -1495,8 +1495,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
LocalServices.addService(InputMethodManagerInternal.class,
|
||||
new LocalServiceImpl(mService));
|
||||
mService.publishLocalService();
|
||||
publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
|
||||
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
|
||||
}
|
||||
@@ -4866,26 +4865,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
return mCurrentSubtype;
|
||||
}
|
||||
|
||||
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
|
||||
synchronized (ImfLock.class) {
|
||||
return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
|
||||
synchronized (ImfLock.class) {
|
||||
return getEnabledInputMethodListLocked(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
|
||||
InlineSuggestionsRequestInfo requestInfo,
|
||||
IInlineSuggestionsRequestCallback callback) {
|
||||
synchronized (ImfLock.class) {
|
||||
onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) {
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
|
||||
final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
|
||||
@@ -4897,161 +4876,153 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
return methodMap;
|
||||
}
|
||||
|
||||
private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
|
||||
synchronized (ImfLock.class) {
|
||||
if (userId == mSettings.getCurrentUserId()) {
|
||||
if (!mMethodMap.containsKey(imeId)
|
||||
|| !mSettings.getEnabledInputMethodListLocked()
|
||||
.contains(mMethodMap.get(imeId))) {
|
||||
return false; // IME is not found or not enabled.
|
||||
}
|
||||
setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
|
||||
return true;
|
||||
}
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
|
||||
final InputMethodSettings settings = new InputMethodSettings(
|
||||
mContext.getResources(), mContext.getContentResolver(), methodMap,
|
||||
userId, false);
|
||||
if (!methodMap.containsKey(imeId)
|
||||
|| !settings.getEnabledInputMethodListLocked()
|
||||
.contains(methodMap.get(imeId))) {
|
||||
@GuardedBy("ImfLock.class")
|
||||
private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {
|
||||
if (userId == mSettings.getCurrentUserId()) {
|
||||
if (!mMethodMap.containsKey(imeId)
|
||||
|| !mSettings.getEnabledInputMethodListLocked()
|
||||
.contains(mMethodMap.get(imeId))) {
|
||||
return false; // IME is not found or not enabled.
|
||||
}
|
||||
settings.putSelectedInputMethod(imeId);
|
||||
settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
|
||||
setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
|
||||
return true;
|
||||
}
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
|
||||
final InputMethodSettings settings = new InputMethodSettings(
|
||||
mContext.getResources(), mContext.getContentResolver(), methodMap,
|
||||
userId, false);
|
||||
if (!methodMap.containsKey(imeId)
|
||||
|| !settings.getEnabledInputMethodListLocked()
|
||||
.contains(methodMap.get(imeId))) {
|
||||
return false; // IME is not found or not enabled.
|
||||
}
|
||||
settings.putSelectedInputMethod(imeId);
|
||||
settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
|
||||
synchronized (ImfLock.class) {
|
||||
if (userId == mSettings.getCurrentUserId()) {
|
||||
if (!mMethodMap.containsKey(imeId)) {
|
||||
return false; // IME is not found.
|
||||
}
|
||||
setInputMethodEnabledLocked(imeId, enabled);
|
||||
return true;
|
||||
}
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
|
||||
final InputMethodSettings settings = new InputMethodSettings(
|
||||
mContext.getResources(), mContext.getContentResolver(), methodMap,
|
||||
userId, false);
|
||||
if (!methodMap.containsKey(imeId)) {
|
||||
return false; // IME is not found.
|
||||
}
|
||||
if (enabled) {
|
||||
if (!settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) {
|
||||
settings.appendAndPutEnabledInputMethodLocked(imeId, false);
|
||||
}
|
||||
} else {
|
||||
settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
|
||||
new StringBuilder(),
|
||||
settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private void publishLocalService() {
|
||||
LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl());
|
||||
}
|
||||
|
||||
private boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
|
||||
int displayId) {
|
||||
//TODO(b/150843766): Check if Input Token is valid.
|
||||
final IBinder curHostInputToken;
|
||||
synchronized (ImfLock.class) {
|
||||
if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
|
||||
return false;
|
||||
}
|
||||
curHostInputToken = mCurHostInputToken;
|
||||
}
|
||||
return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
|
||||
}
|
||||
|
||||
private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
|
||||
synchronized (ImfLock.class) {
|
||||
if (mCurFocusedWindow != windowToken) {
|
||||
// mCurPerceptible was set by the focused window, but it is no longer in control,
|
||||
// so we reset mCurPerceptible.
|
||||
mCurPerceptible = true;
|
||||
}
|
||||
if (imeParentChanged) {
|
||||
// Hide the IME method menu earlier when the IME surface parent will change in
|
||||
// case seeing the dialog dismiss flickering during the next focused window
|
||||
// starting the input connection.
|
||||
mMenuController.hideInputMethodMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LocalServiceImpl extends InputMethodManagerInternal {
|
||||
@NonNull
|
||||
private final InputMethodManagerService mService;
|
||||
|
||||
LocalServiceImpl(@NonNull InputMethodManagerService service) {
|
||||
mService = service;
|
||||
}
|
||||
private final class LocalServiceImpl extends InputMethodManagerInternal {
|
||||
|
||||
@Override
|
||||
public void setInteractive(boolean interactive) {
|
||||
// Do everything in handler so as not to block the caller.
|
||||
mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
|
||||
.sendToTarget();
|
||||
mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
|
||||
mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
|
||||
mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
|
||||
mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
|
||||
mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
|
||||
return mService.getInputMethodListAsUser(userId);
|
||||
synchronized (ImfLock.class) {
|
||||
return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
|
||||
return mService.getEnabledInputMethodListAsUser(userId);
|
||||
synchronized (ImfLock.class) {
|
||||
return getEnabledInputMethodListLocked(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
|
||||
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
|
||||
mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
|
||||
synchronized (ImfLock.class) {
|
||||
onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
|
||||
return mService.switchToInputMethod(imeId, userId);
|
||||
synchronized (ImfLock.class) {
|
||||
return switchToInputMethodLocked(imeId, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
|
||||
return mService.setInputMethodEnabled(imeId, enabled, userId);
|
||||
synchronized (ImfLock.class) {
|
||||
if (userId == mSettings.getCurrentUserId()) {
|
||||
if (!mMethodMap.containsKey(imeId)) {
|
||||
return false; // IME is not found.
|
||||
}
|
||||
setInputMethodEnabledLocked(imeId, enabled);
|
||||
return true;
|
||||
}
|
||||
final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
|
||||
final InputMethodSettings settings = new InputMethodSettings(
|
||||
mContext.getResources(), mContext.getContentResolver(), methodMap,
|
||||
userId, false);
|
||||
if (!methodMap.containsKey(imeId)) {
|
||||
return false; // IME is not found.
|
||||
}
|
||||
if (enabled) {
|
||||
if (!settings.getEnabledInputMethodListLocked().contains(
|
||||
methodMap.get(imeId))) {
|
||||
settings.appendAndPutEnabledInputMethodLocked(imeId, false);
|
||||
}
|
||||
} else {
|
||||
settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
|
||||
new StringBuilder(),
|
||||
settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInputMethodListListener(InputMethodListListener listener) {
|
||||
mService.mInputMethodListListeners.addIfAbsent(listener);
|
||||
mInputMethodListListeners.addIfAbsent(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
|
||||
int displayId) {
|
||||
return mService.transferTouchFocusToImeWindow(sourceInputToken, displayId);
|
||||
//TODO(b/150843766): Check if Input Token is valid.
|
||||
final IBinder curHostInputToken;
|
||||
synchronized (ImfLock.class) {
|
||||
if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
|
||||
return false;
|
||||
}
|
||||
curHostInputToken = mCurHostInputToken;
|
||||
}
|
||||
return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
|
||||
mService.reportImeControl(windowToken, imeParentChanged);
|
||||
synchronized (ImfLock.class) {
|
||||
if (mCurFocusedWindow != windowToken) {
|
||||
// mCurPerceptible was set by the focused window, but it is no longer in
|
||||
// control, so we reset mCurPerceptible.
|
||||
mCurPerceptible = true;
|
||||
}
|
||||
if (imeParentChanged) {
|
||||
// Hide the IME method menu earlier when the IME surface parent will change in
|
||||
// case seeing the dialog dismiss flickering during the next focused window
|
||||
// starting the input connection.
|
||||
mMenuController.hideInputMethodMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeImeSurface() {
|
||||
mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
|
||||
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateImeWindowStatus(boolean disableImeIcon) {
|
||||
mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
|
||||
mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
|
||||
.sendToTarget();
|
||||
}
|
||||
}
|
||||
@@ -5689,7 +5660,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
if (!userHasDebugPriv(userId, shellCommand)) {
|
||||
continue;
|
||||
}
|
||||
boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId);
|
||||
boolean failedToSelectUnknownIme = !switchToInputMethodLocked(imeId, userId);
|
||||
if (failedToSelectUnknownIme) {
|
||||
error.print("Unknown input method ");
|
||||
error.print(imeId);
|
||||
|
||||
Reference in New Issue
Block a user