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_UP
KeyEvent中对应的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_DOWN
KeyEvent的组合按键,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: "
不识别