Client-side reimplementation of IMM#setCurrentInputMethodSubtype()

This is a preparation to mark
InputMethodManager#setCurrentInputMethodSubtype() deprecated.

InputMethodManager#setCurrentInputMethodSubtype(), which was
introduced in Android 4.0 ICS [1], was probably mistakenly exposed as
a public API, because it has required WRITE_SECURE_SETTINGS that
typical applications cannot have.

Keeping maintaining InputMethodManager#setCurrentInputMethodSubtype()
is not that simple because now we are about to enable per-profile IME
mode, where this method needs to have a more clear spec about what
"Current" means.

An ideal solution is just removing this method, because if the caller
already has WRITE_SECURE_SETTINGS permission, they can just directly
update Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE to achieve the
same goal.  However, given that this has been a public API, it would
probably make sense to provide a fallback implementation like I did
for null IME token in InputMethodManager#setInputMethod() [2].

Either way, InputMethodManager#setCurrentInputMethodSubtype() will be
marked as deprecated in a subsequent CL.

 [1]: I55daa19ba924999def544bf841f00bf54852f3e1
      b66d287e30
 [2]: I42dd0325b01c527009bf85566ca8ba0766b2294e
      0c1ebffdb3

Bug: 123249820
Test: manually done with a test app that has WRITE_SECURE_SETTINGS
Change-Id: I76da83c57cffc6b73defccfd4a1b5734c958a97e
This commit is contained in:
Yohei Yukawa
2019-01-22 19:25:44 -08:00
parent bb4e16d581
commit fd8f72188c
4 changed files with 42 additions and 31 deletions

View File

@@ -27,6 +27,7 @@ import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -2492,11 +2493,48 @@ public final class InputMethodManager {
*/
@RequiresPermission(WRITE_SECURE_SETTINGS)
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
try {
return mService.setCurrentInputMethodSubtype(subtype);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
if (Process.myUid() == Process.SYSTEM_UID) {
Log.w(TAG, "System process should not call setCurrentInputMethodSubtype() because "
+ "almost always it is a bug under multi-user / multi-profile environment. "
+ "Consider directly interacting with InputMethodManagerService "
+ "via LocalServices.");
return false;
}
if (subtype == null) {
// See the JavaDoc. This is how this method has worked.
return false;
}
final Context fallbackContext = ActivityThread.currentApplication();
if (fallbackContext == null) {
return false;
}
if (fallbackContext.checkSelfPermission(WRITE_SECURE_SETTINGS)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
final ContentResolver contentResolver = fallbackContext.getContentResolver();
final String imeId = Settings.Secure.getString(contentResolver,
Settings.Secure.DEFAULT_INPUT_METHOD);
if (ComponentName.unflattenFromString(imeId) == null) {
// Null or invalid IME ID format.
return false;
}
final List<InputMethodSubtype> enabledSubtypes;
try {
enabledSubtypes = mService.getEnabledInputMethodSubtypeList(imeId, true);
} catch (RemoteException e) {
return false;
}
final int numSubtypes = enabledSubtypes.size();
for (int i = 0; i < numSubtypes; ++i) {
final InputMethodSubtype enabledSubtype = enabledSubtypes.get(i);
if (enabledSubtype.equals(subtype)) {
Settings.Secure.putInt(contentResolver,
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, enabledSubtype.hashCode());
return true;
}
}
return false;
}
/**

View File

@@ -64,7 +64,6 @@ interface IInputMethodManager {
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
boolean isInputMethodPickerShownForTest();
InputMethodSubtype getCurrentInputMethodSubtype();
boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.

View File

@@ -4106,25 +4106,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return mCurrentSubtype;
}
@Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mMethodMap) {
// TODO: Make this work even for non-current users?
if (!calledFromValidUserLocked()) {
return false;
}
if (subtype != null && mCurMethodId != null) {
InputMethodInfo imi = mMethodMap.get(mCurMethodId);
int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
if (subtypeId != NOT_A_SUBTYPE_ID) {
setInputMethodLocked(mCurMethodId, subtypeId);
return true;
}
}
return false;
}
}
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
synchronized (mMethodMap) {
return getInputMethodListLocked(userId);

View File

@@ -1517,13 +1517,6 @@ public final class MultiClientInputMethodManagerService {
return null;
}
@BinderThread
@Override
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
reportNotSupported();
return false;
}
@BinderThread
@Override
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {