diff options
| -rw-r--r-- | services/core/java/com/android/server/inputmethod/InputMethodManagerService.java | 158 | ||||
| -rw-r--r-- | services/core/java/com/android/server/inputmethod/InputMethodUtils.java | 57 |
2 files changed, 186 insertions, 29 deletions
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d45869e07586..840c6e48a190 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1674,40 +1674,93 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public List<InputMethodInfo> getInputMethodList() { - return getInputMethodList(false /* isVrOnly */); + final int callingUserId = UserHandle.getCallingUserId(); + synchronized (mMethodMap) { + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + mSettings.getCurrentUserId(), null); + if (resolvedUserIds.length != 1) { + return Collections.emptyList(); + } + final long ident = Binder.clearCallingIdentity(); + try { + return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } @Override public List<InputMethodInfo> getVrInputMethodList() { - return getInputMethodList(true /* isVrOnly */); - } - - private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) { + final int callingUserId = UserHandle.getCallingUserId(); synchronized (mMethodMap) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUserLocked()) { + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + mSettings.getCurrentUserId(), null); + if (resolvedUserIds.length != 1) { return Collections.emptyList(); } - ArrayList<InputMethodInfo> methodList = new ArrayList<>(); - for (InputMethodInfo info : mMethodList) { - - if (info.isVrOnly() == isVrOnly) { - methodList.add(info); - } + final long ident = Binder.clearCallingIdentity(); + try { + return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]); + } finally { + Binder.restoreCallingIdentity(ident); } - return methodList; } } @Override public List<InputMethodInfo> getEnabledInputMethodList() { + final int callingUserId = UserHandle.getCallingUserId(); synchronized (mMethodMap) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUserLocked()) { + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + mSettings.getCurrentUserId(), null); + if (resolvedUserIds.length != 1) { return Collections.emptyList(); } + final long ident = Binder.clearCallingIdentity(); + try { + return getEnabledInputMethodListLocked(resolvedUserIds[0]); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @GuardedBy("mMethodMap") + private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly, + @UserIdInt int userId) { + final ArrayList<InputMethodInfo> methodList; + if (userId == mSettings.getCurrentUserId()) { + // Create a copy. + methodList = new ArrayList<>(mMethodList); + } else { + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, + methodList); + } + methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly); + return methodList; + } + + @GuardedBy("mMethodMap") + private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) { + if (userId == mSettings.getCurrentUserId()) { return mSettings.getEnabledInputMethodListLocked(); } + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, + methodList); + final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(), + mContext.getContentResolver(), methodMap, methodList, userId, true); + return settings.getEnabledInputMethodListLocked(); } /** @@ -1717,11 +1770,27 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId, boolean allowsImplicitlySelectedSubtypes) { + final int callingUserId = UserHandle.getCallingUserId(); synchronized (mMethodMap) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUserLocked()) { + final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId, + mSettings.getCurrentUserId(), null); + if (resolvedUserIds.length != 1) { return Collections.emptyList(); } + final long ident = Binder.clearCallingIdentity(); + try { + return getEnabledInputMethodSubtypeListLocked(imiId, + allowsImplicitlySelectedSubtypes, resolvedUserIds[0]); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @GuardedBy("mMethodMap") + private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId, + boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) { + if (userId == mSettings.getCurrentUserId()) { final InputMethodInfo imi; if (imiId == null && mCurMethodId != null) { imi = mMethodMap.get(mCurMethodId); @@ -1734,6 +1803,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mSettings.getEnabledInputMethodSubtypeListLocked( mContext, imi, allowsImplicitlySelectedSubtypes); } + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, + methodList); + final InputMethodInfo imi = methodMap.get(imiId); + if (imi == null) { + return Collections.emptyList(); + } + final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(), + mContext.getContentResolver(), methodMap, methodList, userId, true); + return settings.getEnabledInputMethodSubtypeListLocked( + mContext, imi, allowsImplicitlySelectedSubtypes); } /** @@ -4545,6 +4629,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) { boolean all = false; boolean brief = false; + int userIdToBeResolved = UserHandle.USER_CURRENT; while (true) { final String nextOption = shellCommand.getNextOption(); if (nextOption == null) { @@ -4557,19 +4642,34 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case "-s": brief = true; break; + case "-u": + case "--user": + userIdToBeResolved = UserHandle.parseUserArg(shellCommand.getNextArgRequired()); + break; } } - final List<InputMethodInfo> methods = all ? - getInputMethodList() : getEnabledInputMethodList(); - final PrintWriter pr = shellCommand.getOutPrintWriter(); - final Printer printer = x -> pr.println(x); - final int N = methods.size(); - for (int i = 0; i < N; ++i) { - if (brief) { - pr.println(methods.get(i).getId()); - } else { - pr.print(methods.get(i).getId()); pr.println(":"); - methods.get(i).dump(printer, " "); + synchronized (mMethodMap) { + final PrintWriter pr = shellCommand.getOutPrintWriter(); + final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, + mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter()); + for (int userId : userIds) { + final List<InputMethodInfo> methods = all + ? getInputMethodListLocked(false, userId) + : getEnabledInputMethodListLocked(userId); + if (userIds.length > 1) { + pr.print("User #"); + pr.print(userId); + pr.println(":"); + } + for (InputMethodInfo info : methods) { + if (brief) { + pr.println(info.getId()); + } else { + pr.print(info.getId()); + pr.println(":"); + info.dump(pr::println, " "); + } + } } } return ShellCommandResult.SUCCESS; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 1137bf967d24..2f7687148e2e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -29,9 +29,12 @@ import android.content.res.Resources; import android.os.Build; import android.os.LocaleList; import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManagerInternal; import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.IntArray; import android.util.Pair; import android.util.Printer; import android.util.Slog; @@ -42,8 +45,10 @@ import android.view.textservice.SpellCheckerInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.StartInputFlags; +import com.android.server.LocalServices; import com.android.server.textservices.TextServicesManagerInternal; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; @@ -1286,4 +1291,56 @@ final class InputMethodUtils { return true; } + /** + * Converts a user ID, which can be a pseudo user ID such as {@link UserHandle#USER_ALL} to a + * list of real user IDs. + * + * <p>Currently this method also converts profile user ID to profile parent user ID.</p> + * + * @param userIdToBeResolved A user ID. Two pseudo user ID {@link UserHandle#USER_CURRENT} and + * {@link UserHandle#USER_ALL} are also supported + * @param currentUserId A real user ID, which will be used when {@link UserHandle#USER_CURRENT} + * is specified in {@code userIdToBeResolved}. + * @param warningWriter A {@link PrintWriter} to output some debug messages. {@code null} if + * no debug message is required. + * @return An integer array that contain user IDs. + */ + static int[] resolveUserId(@UserIdInt int userIdToBeResolved, + @UserIdInt int currentUserId, @Nullable PrintWriter warningWriter) { + final UserManagerInternal userManagerInternal = + LocalServices.getService(UserManagerInternal.class); + + if (userIdToBeResolved == UserHandle.USER_ALL) { + final IntArray result = new IntArray(); + for (int userId : userManagerInternal.getUserIds()) { + final int parentUserId = userManagerInternal.getProfileParentId(userId); + if (result.indexOf(parentUserId) < 0) { + result.add(parentUserId); + } + } + return result.toArray(); + } + + final int sourceUserId; + if (userIdToBeResolved == UserHandle.USER_CURRENT) { + sourceUserId = currentUserId; + } else if (userIdToBeResolved < 0) { + if (warningWriter != null) { + warningWriter.print("Pseudo user ID "); + warningWriter.print(userIdToBeResolved); + warningWriter.println(" is not supported."); + } + return new int[]{}; + } else if (userManagerInternal.exists(userIdToBeResolved)) { + sourceUserId = userIdToBeResolved; + } else { + if (warningWriter != null) { + warningWriter.print("User #"); + warningWriter.print(userIdToBeResolved); + warningWriter.println(" does not exit."); + } + return new int[]{}; + } + return new int[]{userManagerInternal.getProfileParentId(sourceUserId)}; + } } |