IMS:Android中Input命令--Android12
- 1、Android12 Input命令更新
 - 1.1 shell脚本
 - 1.2 InputShellCommand#onCommand 命令解析
 
- 2、Input相关命令参数
 - 2.1 text
 - 2.2 keyevent
 - 2.3 tap
 - 2.4 swipe
 - 2.5 draganddrop
 - 2.6 press
 - 2.7 roll
 - 2.8 motionevent
 - 2.9 keycombination
 - 2.10 默认handleDefaultCommands(arg)
 
android12-release
1、Android12 Input命令更新

ANdroid12之前可查看一下 IMS:injectInputEvent注入Input事件,Android12更新相关代码:
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
frameworks/base/services/core/java/com/android/server/input/InputShellCommand.java
frameworks/base/core/java/android/os/ShellCommand.java
frameworks/libs/modules-utils/java/com/android/modules/utils/BasicShellCommandHandler.java
emulator64_x86_64_arm64:/system/bin # input -d
Exception occurred while executing '-d':
java.lang.IllegalArgumentException: Argument expected after "-d"
        at com.android.modules.utils.BasicShellCommandHandler.getNextArgRequired(BasicShellCommandHandler.java:295)
        at com.android.server.input.InputShellCommand.getDisplayId(InputShellCommand.java:85)
        at com.android.server.input.InputShellCommand.onCommand(InputShellCommand.java:182)
        at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
        at android.os.ShellCommand.exec(ShellCommand.java:38)
        at com.android.server.input.InputManagerService.onShellCommand(InputManagerService.java:3478)
        at android.os.Binder.shellCommand(Binder.java:950)
        at android.os.Binder.onTransact(Binder.java:834)
        at android.hardware.input.IInputManager$Stub.onTransact(IInputManager.java:1125)
        at android.os.Binder.execTransactInternal(Binder.java:1184)
        at android.os.Binder.execTransact(Binder.java:1143)
255|emulator64_x86_64_arm64:/system/bin # input keyevent KEYCODE_VOLUME_UP
 
1.1 shell脚本
input脚本使用cmd bin执行,获取到Input服务Context.INPUT_SERVICE,通过IBinder::shellCommand(service, ......)最终调用的InputManagerService服务端onShellCommand方法
 
frameworks/base/cmds/input/input
#!/system/bin/sh
cmd input "$@"
 
frameworks/native/cmds/cmd/cmd.cpp
int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,
            int in, int out, int err, RunMode runMode) {
    sp<ProcessState> proc = ProcessState::self();
    proc->startThreadPool();
#if DEBUG
    ALOGD("cmd: starting");
#endif
    sp<IServiceManager> sm = defaultServiceManager();
    if (runMode == RunMode::kStandalone) {
        fflush(stdout);
    }
    if (sm == nullptr) {
        ALOGW("Unable to get default service manager!");
        errorLog << "cmd: Unable to get default service manager!" << endl;
        return 20;
    }
    int argc = argv.size();
    if (argc == 0) {
        errorLog << "cmd: No service specified; use -l to list all running services. Use -w to start and wait for a service." << endl;
        return 20;
    }
    if ((argc == 1) && (argv[0] == "-l")) {
        Vector<String16> services = sm->listServices();
        services.sort(sort_func);
        outputLog << "Currently running services:" << endl;
        for (size_t i=0; i<services.size(); i++) {
            sp<IBinder> service = sm->checkService(services[i]);
            if (service != nullptr) {
                outputLog << "  " << services[i] << endl;
            }
        }
        return 0;
    }
    bool waitForService = ((argc > 1) && (argv[0] == "-w"));
    int serviceIdx = (waitForService) ? 1 : 0;
    const auto cmd = argv[serviceIdx];
    Vector<String16> args;
    String16 serviceName = String16(cmd.data(), cmd.size());
    for (int i = serviceIdx + 1; i < argc; i++) {
        args.add(String16(argv[i].data(), argv[i].size()));
    }
    sp<IBinder> service;
    if(waitForService) {
        service = sm->waitForService(serviceName);
    } else {
        service = sm->checkService(serviceName);
    }
    if (service == nullptr) {
        if (runMode == RunMode::kStandalone) {
            ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());
        }
        errorLog << "cmd: Can't find service: " << cmd << endl;
        return 20;
    }
    sp<MyShellCallback> cb = new MyShellCallback(errorLog);
    sp<MyResultReceiver> result = new MyResultReceiver();
#if DEBUG
    ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",
          static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif
    // TODO: block until a result is returned to MyResultReceiver.
    status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);
    if (error < 0) {
        const char* errstr;
        switch (error) {
            case BAD_TYPE: errstr = "Bad type"; break;
            case FAILED_TRANSACTION: errstr = "Failed transaction"; break;
            case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break;
            case UNEXPECTED_NULL: errstr = "Unexpected null"; break;
            default: errstr = strerror(-error); break;
        }
        if (runMode == RunMode::kStandalone) {
            ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(),
                  errstr, -error);
        }
        outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error)
                  << ")" << endl;
        return error;
    }
    cb->mActive = false;
    status_t res = result->waitForResult();
#if DEBUG
    ALOGD("result=%d", (int)res);
#endif
    return res;
}
 
1.2 InputShellCommand#onCommand 命令解析
frameworks/base/services/core/java/com/android/server/input/InputShellCommand.java
 frameworks/base/core/java/android/os/ShellCommand.java
 frameworks/libs/modules-utils/java/com/android/modules/utils/BasicShellCommandHandler.java
"text":runText(inputSource, displayId);"keyevent":runKeyEvent(inputSource, displayId);"tap":runTap(inputSource, displayId);"swipe":runSwipe(inputSource, displayId);"draganddrop":runDragAndDrop(inputSource, displayId);"press":runPress(inputSource, displayId);"roll":runRoll(inputSource, displayId);"motionevent":runMotionEvent(inputSource, displayId);"keycombination":runKeyCombination(inputSource, displayId);handleDefaultCommands(arg);
@Override
public final int onCommand(String cmd) {
    String arg = cmd;
    int inputSource = InputDevice.SOURCE_UNKNOWN;
    // Get source (optional).
    if (SOURCES.containsKey(arg)) {
        inputSource = SOURCES.get(arg);
        arg = getNextArgRequired();
    }
    // Get displayId (optional).
    int displayId = INVALID_DISPLAY;
    if ("-d".equals(arg)) {
        displayId = getDisplayId();
        arg = getNextArgRequired();
    }
    try {
        if ("text".equals(arg)) {
            runText(inputSource, displayId);
        } else if ("keyevent".equals(arg)) {
            runKeyEvent(inputSource, displayId);
        } else if ("tap".equals(arg)) {
            runTap(inputSource, displayId);
        } else if ("swipe".equals(arg)) {
            runSwipe(inputSource, displayId);
        } else if ("draganddrop".equals(arg)) {
            runDragAndDrop(inputSource, displayId);
        } else if ("press".equals(arg)) {
            runPress(inputSource, displayId);
        } else if ("roll".equals(arg)) {
            runRoll(inputSource, displayId);
        }  else if ("motionevent".equals(arg)) {
            runMotionEvent(inputSource, displayId);
        } else if ("keycombination".equals(arg)) {
            runKeyCombination(inputSource, displayId);
        } else {
            handleDefaultCommands(arg);
        }
    } catch (NumberFormatException ex) {
        throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
    }
    return 0;
}
 
2、Input相关命令参数
2.1 text
input text c输入键盘字符,injectInputEvent注入KeyEvent事件

private void sendText(int source, final String text, int displayId) {
    final StringBuilder buff = new StringBuilder(text);
    boolean escapeFlag = false;
    for (int i = 0; i < buff.length(); i++) {
        if (escapeFlag) {
            escapeFlag = false;
            if (buff.charAt(i) == 's') {
                buff.setCharAt(i, ' ');
                buff.deleteCharAt(--i);
            }
        }
        if (buff.charAt(i) == '%') {
            escapeFlag = true;
        }
    }
    final char[] chars = buff.toString().toCharArray();
    final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
    final KeyEvent[] events = kcm.getEvents(chars);
    for (int i = 0; i < events.length; i++) {
        KeyEvent e = events[i];
        if (source != e.getSource()) {
            e.setSource(source);
        }
        e.setDisplayId(displayId);
        injectKeyEvent(e);
    }
}
 
2.2 keyevent
input keyevent KEYCODE_VOLUME_UPKeyEvent中对应的KEYCODE_*事件,injectInputEvent注入KeyEvent事件

private void runKeyEvent(int inputSource, int displayId) {
    String arg = getNextArgRequired();
    final boolean longpress = "--longpress".equals(arg);
    if (longpress) {
        arg = getNextArgRequired();
    } else {
        final boolean doubleTap = "--doubletap".equals(arg);
        if (doubleTap) {
            arg = getNextArgRequired();
            final int keycode = KeyEvent.keyCodeFromString(arg);
            sendKeyDoubleTap(inputSource, keycode, displayId);
            return;
        }
    }
    do {
        final int keycode = KeyEvent.keyCodeFromString(arg);
        sendKeyEvent(inputSource, keycode, longpress, displayId);
    } while ((arg = getNextArg()) != null);
}
private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
    final long now = SystemClock.uptimeMillis();
    KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */,
            0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
            inputSource);
    event.setDisplayId(displayId);
    injectKeyEvent(event);
    if (longpress) {
        // Some long press behavior would check the event time, we set a new event time here.
        final long nextEventTime = now + ViewConfiguration.getGlobalActionKeyTimeout();
        injectKeyEvent(KeyEvent.changeTimeRepeat(event, nextEventTime, 1 /* repeatCount */,
                KeyEvent.FLAG_LONG_PRESS));
    }
    injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
}
 
2.3 tap
input tap 1000 2000坐标点(x, y)的MotionEvent点解事件,injectInputEvent注入MotionEvent事件

private void runTap(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
    sendTap(inputSource, Float.parseFloat(getNextArgRequired()),
            Float.parseFloat(getNextArgRequired()), displayId);
}
private void sendTap(int inputSource, float x, float y, int displayId) {
    final long now = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
            displayId);
    injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
}
 
2.4 swipe
input swipe 1000 2000 100 200从坐标点(x1, y1)滑动到坐标点(x2, y2)的MotionEvent事件,injectInputEvent注入MotionEvent事件

private void runSwipe(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
    sendSwipe(inputSource, displayId, false);
}
private void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
    // Parse two points and duration.
    final float x1 = Float.parseFloat(getNextArgRequired());
    final float y1 = Float.parseFloat(getNextArgRequired());
    final float x2 = Float.parseFloat(getNextArgRequired());
    final float y2 = Float.parseFloat(getNextArgRequired());
    String durationArg = getNextArg();
    int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
    if (duration < 0) {
        duration = 300;
    }
    final long down = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
            displayId);
    if (isDragDrop) {
        // long press until drag start.
        try {
            Thread.sleep(ViewConfiguration.getLongPressTimeout());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    long now = SystemClock.uptimeMillis();
    final long endTime = down + duration;
    while (now < endTime) {
        final long elapsedTime = now - down;
        final float alpha = (float) elapsedTime / duration;
        injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
                lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
        now = SystemClock.uptimeMillis();
    }
    injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
            displayId);
}
 
2.5 draganddrop
input draganddrop 1000 2000 100 200与参数swipe不同的是isDragDrop=true从坐标点(x1, y1)拖拽到坐标点(x2, y2)的MotionEvent事件,injectInputEvent注入MotionEvent事件
private void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
    // Parse two points and duration.
    final float x1 = Float.parseFloat(getNextArgRequired());
    final float y1 = Float.parseFloat(getNextArgRequired());
    final float x2 = Float.parseFloat(getNextArgRequired());
    final float y2 = Float.parseFloat(getNextArgRequired());
    String durationArg = getNextArg();
    int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
    if (duration < 0) {
        duration = 300;
    }
    final long down = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
            displayId);
    if (isDragDrop) {
        // long press until drag start.
        try {
            Thread.sleep(ViewConfiguration.getLongPressTimeout());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    long now = SystemClock.uptimeMillis();
    final long endTime = down + duration;
    while (now < endTime) {
        final long elapsedTime = now - down;
        final float alpha = (float) elapsedTime / duration;
        injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
                lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
        now = SystemClock.uptimeMillis();
    }
    injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
            displayId);
}
private void runDragAndDrop(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
    sendSwipe(inputSource, displayId, true);
}
 
2.6 press
input press与参数tap不同的是规定固定在(0,0)的SOURCE_TRACKBALL事件,injectInputEvent注入MotionEvent事件
private void sendTap(int inputSource, float x, float y, int displayId) {
    final long now = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
            displayId);
    injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
}
private void runPress(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
    sendTap(inputSource, 0.0f, 0.0f, displayId);
}
 
2.7 roll
input roll 1000 2000就是MotionEvent.ACTION_MOVE事件,injectInputEvent注入MotionEvent事件

private void runRoll(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
    sendMove(inputSource, Float.parseFloat(getNextArgRequired()),
            Float.parseFloat(getNextArgRequired()), displayId);
}
/**
 * Sends a simple zero-pressure move event.
 *
 * @param inputSource the InputDevice.SOURCE_* sending the input event
 * @param dx change in x coordinate due to move
 * @param dy change in y coordinate due to move
 */
private void sendMove(int inputSource, float dx, float dy, int displayId) {
    final long now = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
            displayId);
}
 
2.8 motionevent
input motionevent DOWN 1000 2000/input motionevent UP 1000 2000就是MotionEvent各种事件,injectInputEvent注入MotionEvent事件

private int getAction() {
    String actionString = getNextArgRequired();
    switch (actionString.toUpperCase()) {
        case "DOWN":
            return MotionEvent.ACTION_DOWN;
        case "UP":
            return MotionEvent.ACTION_UP;
        case "MOVE":
            return MotionEvent.ACTION_MOVE;
        case "CANCEL":
            return MotionEvent.ACTION_CANCEL;
        default:
            throw new IllegalArgumentException("Unknown action: " + actionString);
    }
}
private void runMotionEvent(int inputSource, int displayId) {
    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
    int action = getAction();
    float x = 0, y = 0;
    if (action == MotionEvent.ACTION_DOWN
            || action == MotionEvent.ACTION_MOVE
            || action == MotionEvent.ACTION_UP) {
        x = Float.parseFloat(getNextArgRequired());
        y = Float.parseFloat(getNextArgRequired());
    } else {
        // For ACTION_CANCEL, the positions are optional
        String xString = getNextArg();
        String yString = getNextArg();
        if (xString != null && yString != null) {
            x = Float.parseFloat(xString);
            y = Float.parseFloat(yString);
        }
    }
    sendMotionEvent(inputSource, action, x, y, displayId);
}
private void sendMotionEvent(int inputSource, int action, float x, float y,
        int displayId) {
    float pressure = NO_PRESSURE;
    if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
        pressure = DEFAULT_PRESSURE;
    }
    final long now = SystemClock.uptimeMillis();
    injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
}
 
2.9 keycombination
input keycombination KEYCODE_POWER KEYCODE_VOLUME_DOWNKeyEvent的组合按键,injectInputEvent连续注入KeyEvent事件

private void runKeyCombination(int inputSource, int displayId) {
    String arg = getNextArgRequired();
    ArrayList<Integer> keyCodes = new ArrayList<>();
    while (arg != null) {
        final int keyCode = KeyEvent.keyCodeFromString(arg);
        if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
            throw new IllegalArgumentException("Unknown keycode: " + arg);
        }
        keyCodes.add(keyCode);
        arg = getNextArg();
    }
    // At least 2 keys.
    if (keyCodes.size() < 2) {
        throw new IllegalArgumentException("keycombination requires at least 2 keycodes");
    }
    sendKeyCombination(inputSource, keyCodes, displayId);
}
private void injectKeyEventAsync(KeyEvent event) {
    InputManager.getInstance().injectInputEvent(event,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
private void sendKeyCombination(int inputSource, ArrayList<Integer> keyCodes, int displayId) {
    final long now = SystemClock.uptimeMillis();
    final int count = keyCodes.size();
    final KeyEvent[] events = new KeyEvent[count];
    for (int i = 0; i < count; i++) {
        final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCodes.get(i), 0,
                0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/,
                inputSource);
        event.setDisplayId(displayId);
        events[i] = event;
    }
    for (KeyEvent event: events) {
        // Use async inject so interceptKeyBeforeQueueing or interceptKeyBeforeDispatching could
        // handle keys.
        injectKeyEventAsync(event);
    }
    try {
        Thread.sleep(ViewConfiguration.getTapTimeout());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    for (KeyEvent event: events) {
        injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
    }
}
 
2.10 默认handleDefaultCommands(arg)
-  
dump
 
 -  
help
 
 -  
"Unknown command: "不识别

 



















