Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14f96351 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use IBinder#shellCommand() for 'adb shell ime'"

parents 4ae4005b 926488d7
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -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 −7
Original line number Diff line number Diff line
#!/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 "$@"
+0 −248
Original line number Diff line number Diff line
/*
 * 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.");
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -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);
+179 −24
Original line number Diff line number Diff line
@@ -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;
    }
}