Merge "Use IBinder#shellCommand() for 'adb shell ime'"
This commit is contained in:
committed by
Android (Google) Code Review
commit
14f9635110
@@ -2,15 +2,8 @@
|
||||
#
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
LOCAL_MODULE := imelib
|
||||
LOCAL_MODULE_STEM := ime
|
||||
include $(BUILD_JAVA_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ime
|
||||
LOCAL_MODULE_CLASS := EXECUTABLES
|
||||
LOCAL_SRC_FILES := ime
|
||||
LOCAL_REQUIRED_MODULES := imelib
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
#!/system/bin/sh
|
||||
# Script to start "pm" on the device, which has a very rudimentary
|
||||
# shell.
|
||||
#
|
||||
base=/system
|
||||
export CLASSPATH=$base/framework/ime.jar
|
||||
exec app_process $base/bin com.android.commands.ime.Ime "$@"
|
||||
|
||||
exec cmd input_method "$@"
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.ime;
|
||||
|
||||
import com.android.internal.view.IInputMethodManager;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.PrintStreamPrinter;
|
||||
import android.util.Printer;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class Ime {
|
||||
IInputMethodManager mImm;
|
||||
|
||||
private String[] mArgs;
|
||||
private int mNextArg;
|
||||
private String mCurArgData;
|
||||
|
||||
private static final String IMM_NOT_RUNNING_ERR =
|
||||
"Error: Could not access the Input Method Manager. Is the system running?";
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Ime().run(args);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
if (args.length < 1) {
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
mImm = IInputMethodManager.Stub.asInterface(ServiceManager.getService("input_method"));
|
||||
if (mImm == null) {
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mArgs = args;
|
||||
String op = args[0];
|
||||
mNextArg = 1;
|
||||
|
||||
if ("list".equals(op)) {
|
||||
runList();
|
||||
return;
|
||||
}
|
||||
|
||||
if ("enable".equals(op)) {
|
||||
runSetEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("disable".equals(op)) {
|
||||
runSetEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("set".equals(op)) {
|
||||
runSet();
|
||||
return;
|
||||
}
|
||||
|
||||
if (op != null) {
|
||||
System.err.println("Error: unknown command '" + op + "'");
|
||||
}
|
||||
showUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list sub-command.
|
||||
*/
|
||||
private void runList() {
|
||||
String opt;
|
||||
boolean all = false;
|
||||
boolean brief = false;
|
||||
while ((opt=nextOption()) != null) {
|
||||
if (opt.equals("-a")) {
|
||||
all = true;
|
||||
} else if (opt.equals("-s")) {
|
||||
brief = true;
|
||||
} else {
|
||||
System.err.println("Error: Unknown option: " + opt);
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<InputMethodInfo> methods;
|
||||
if (!all) {
|
||||
try {
|
||||
methods = mImm.getEnabledInputMethodList();
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
methods = mImm.getInputMethodList();
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (methods != null) {
|
||||
Printer pr = new PrintStreamPrinter(System.out);
|
||||
for (int i=0; i<methods.size(); i++) {
|
||||
InputMethodInfo imi = methods.get(i);
|
||||
if (brief) {
|
||||
System.out.println(imi.getId());
|
||||
} else {
|
||||
System.out.println(imi.getId() + ":");
|
||||
imi.dump(pr, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runSetEnabled(boolean state) {
|
||||
String id = nextArg();
|
||||
if (id == null) {
|
||||
System.err.println("Error: no input method ID specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean res = mImm.setInputMethodEnabled(id, state);
|
||||
if (state) {
|
||||
System.out.println("Input method " + id + ": "
|
||||
+ (res ? "already enabled" : "now enabled"));
|
||||
} else {
|
||||
System.out.println("Input method " + id + ": "
|
||||
+ (res ? "now disabled" : "already disabled"));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void runSet() {
|
||||
String id = nextArg();
|
||||
if (id == null) {
|
||||
System.err.println("Error: no input method ID specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mImm.setInputMethod(null, id);
|
||||
System.out.println("Input method " + id + " selected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String nextOption() {
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String arg = mArgs[mNextArg];
|
||||
if (!arg.startsWith("-")) {
|
||||
return null;
|
||||
}
|
||||
mNextArg++;
|
||||
if (arg.equals("--")) {
|
||||
return null;
|
||||
}
|
||||
if (arg.length() > 1 && arg.charAt(1) != '-') {
|
||||
if (arg.length() > 2) {
|
||||
mCurArgData = arg.substring(2);
|
||||
return arg.substring(0, 2);
|
||||
} else {
|
||||
mCurArgData = null;
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
mCurArgData = null;
|
||||
return arg;
|
||||
}
|
||||
|
||||
private String nextOptionData() {
|
||||
if (mCurArgData != null) {
|
||||
return mCurArgData;
|
||||
}
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String data = mArgs[mNextArg];
|
||||
mNextArg++;
|
||||
return data;
|
||||
}
|
||||
|
||||
private String nextArg() {
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String arg = mArgs[mNextArg];
|
||||
mNextArg++;
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static void showUsage() {
|
||||
System.err.println("usage: ime list [-a] [-s]");
|
||||
System.err.println(" ime enable ID");
|
||||
System.err.println(" ime disable ID");
|
||||
System.err.println(" ime set ID");
|
||||
System.err.println("");
|
||||
System.err.println("The list command prints all enabled input methods. Use");
|
||||
System.err.println("the -a option to see all input methods. Use");
|
||||
System.err.println("the -s option to see only a single summary line of each.");
|
||||
System.err.println("");
|
||||
System.err.println("The enable command allows the given input method ID to be used.");
|
||||
System.err.println("");
|
||||
System.err.println("The disable command disallows the given input method ID from use.");
|
||||
System.err.println("");
|
||||
System.err.println("The set command switches to the given input method ID.");
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,6 @@ interface IInputMethodManager {
|
||||
boolean switchToLastInputMethod(in IBinder token);
|
||||
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
|
||||
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
|
||||
boolean setInputMethodEnabled(String id, boolean enabled);
|
||||
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
|
||||
int getInputMethodWindowVisibleHeight();
|
||||
void clearLastInputMethodWindowForTransition(in IBinder token);
|
||||
|
||||
@@ -49,12 +49,14 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -104,6 +106,8 @@ import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
@@ -178,6 +182,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
static final boolean DEBUG_RESTORE = DEBUG || false;
|
||||
static final String TAG = "InputMethodManagerService";
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({ShellCommandResult.SUCCESS, ShellCommandResult.FAILURE})
|
||||
private @interface ShellCommandResult {
|
||||
int SUCCESS = 0;
|
||||
int FAILURE = -1;
|
||||
}
|
||||
|
||||
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
|
||||
static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
|
||||
static final int MSG_SHOW_IM_CONFIG = 3;
|
||||
@@ -3885,30 +3896,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean setInputMethodEnabled(String id, boolean enabled) {
|
||||
// TODO: Make this work even for non-current users?
|
||||
if (!calledFromValidUser()) {
|
||||
return false;
|
||||
}
|
||||
synchronized (mMethodMap) {
|
||||
if (mContext.checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException(
|
||||
"Requires permission "
|
||||
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return setInputMethodEnabledLocked(id, enabled);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean setInputMethodEnabledLocked(String id, boolean enabled) {
|
||||
// Make sure this is a valid input method.
|
||||
InputMethodInfo imm = mMethodMap.get(id);
|
||||
@@ -4633,4 +4620,172 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
p.println("No input method service.");
|
||||
}
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
|
||||
@Nullable FileDescriptor err,
|
||||
@NonNull String[] args, @Nullable ShellCallback callback,
|
||||
@NonNull ResultReceiver resultReceiver) throws RemoteException {
|
||||
new ShellCommandImpl(this).exec(
|
||||
this, in, out, err, args, callback, resultReceiver);
|
||||
}
|
||||
|
||||
private static final class ShellCommandImpl extends ShellCommand {
|
||||
@NonNull
|
||||
final InputMethodManagerService mService;
|
||||
|
||||
ShellCommandImpl(InputMethodManagerService service) {
|
||||
mService = service;
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@ShellCommandResult
|
||||
@Override
|
||||
public int onCommand(@Nullable String cmd) {
|
||||
if (cmd == null) {
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
switch (cmd) {
|
||||
case "list":
|
||||
return mService.handleShellCommandListInputMethods(this);
|
||||
case "enable":
|
||||
return mService.handleShellCommandEnableDisableInputMethod(this, true);
|
||||
case "disable":
|
||||
return mService.handleShellCommandEnableDisableInputMethod(this, false);
|
||||
case "set":
|
||||
return mService.handleShellCommandSetInputMethod(this);
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onHelp() {
|
||||
try (PrintWriter pw = getOutPrintWriter()) {
|
||||
pw.println("InputMethodManagerService commands:");
|
||||
pw.println(" help");
|
||||
pw.println(" Prints this help text.");
|
||||
pw.println(" dump [options]");
|
||||
pw.println(" Synonym of dumpsys.");
|
||||
pw.println(" list [-a] [-s]");
|
||||
pw.println(" prints all enabled input methods.");
|
||||
pw.println(" -a: see all input methods");
|
||||
pw.println(" -s: only a single summary line of each");
|
||||
pw.println(" enable <ID>");
|
||||
pw.println(" allows the given input method ID to be used.");
|
||||
pw.println(" disable <ID>");
|
||||
pw.println(" disallows the given input method ID to be used.");
|
||||
pw.println(" set <ID>");
|
||||
pw.println(" switches to the given input method ID.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Shell command handlers:
|
||||
|
||||
/**
|
||||
* Handles {@code adb shell ime list}.
|
||||
* @param shellCommand {@link ShellCommand} object that is handling this command.
|
||||
* @return Exit code of the command.
|
||||
*/
|
||||
@BinderThread
|
||||
@ShellCommandResult
|
||||
private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
|
||||
boolean all = false;
|
||||
boolean brief = false;
|
||||
while (true) {
|
||||
final String nextOption = shellCommand.getNextOption();
|
||||
if (nextOption == null) {
|
||||
break;
|
||||
}
|
||||
switch (nextOption) {
|
||||
case "-a":
|
||||
all = true;
|
||||
break;
|
||||
case "-s":
|
||||
brief = true;
|
||||
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, " ");
|
||||
}
|
||||
}
|
||||
return ShellCommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
|
||||
* @param shellCommand {@link ShellCommand} object that is handling this command.
|
||||
* @param enabled {@code true} if the command was {@code adb shell ime enable}.
|
||||
* @return Exit code of the command.
|
||||
*/
|
||||
@BinderThread
|
||||
@ShellCommandResult
|
||||
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
private int handleShellCommandEnableDisableInputMethod(
|
||||
@NonNull ShellCommand shellCommand, boolean enabled) {
|
||||
if (!calledFromValidUser()) {
|
||||
shellCommand.getErrPrintWriter().print(
|
||||
"Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
|
||||
return ShellCommandResult.FAILURE;
|
||||
}
|
||||
final String id = shellCommand.getNextArgRequired();
|
||||
|
||||
final boolean previouslyEnabled;
|
||||
synchronized (mMethodMap) {
|
||||
if (mContext.checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.WRITE_SECURE_SETTINGS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
shellCommand.getErrPrintWriter().print(
|
||||
"Caller must have WRITE_SECURE_SETTINGS permission");
|
||||
throw new SecurityException(
|
||||
"Requires permission "
|
||||
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
|
||||
}
|
||||
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
final PrintWriter pr = shellCommand.getOutPrintWriter();
|
||||
pr.print("Input method ");
|
||||
pr.print(id);
|
||||
pr.print(": ");
|
||||
pr.print((enabled == previouslyEnabled) ? "already " : "now ");
|
||||
pr.println(enabled ? "enabled" : "disabled");
|
||||
return ShellCommandResult.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles {@code adb shell ime set}.
|
||||
* @param shellCommand {@link ShellCommand} object that is handling this command.
|
||||
* @return Exit code of the command.
|
||||
*/
|
||||
@BinderThread
|
||||
@ShellCommandResult
|
||||
private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
|
||||
final String id = shellCommand.getNextArgRequired();
|
||||
setInputMethod(null, id);
|
||||
final PrintWriter pr = shellCommand.getOutPrintWriter();
|
||||
pr.print("Input method ");
|
||||
pr.print(id);
|
||||
pr.println(" selected");
|
||||
return ShellCommandResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user