Эмуляция ввода (input)

Рейтинг: 65.7% · 17 голосов
Полный курс по Android Debug Bridge: установка, подключение, shell, логи, dumpsys, автоматизация, root, беспроводная отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
android_roman
Сообщения: 45
Зарегистрирован: 11 май 2026, 05:31

Эмуляция ввода (input)

Сообщение android_roman »

АкадемияADB: Android Debug BridgeГлава 11 из 29
Оглавление курса (29)
  1. Введение в Android Debug Bridge
  2. Установка и настройка рабочей среды
  3. Подключение устройства (проводное и беспроводное)
  4. Базовые команды ADB и управление сервером
  5. Команды состояния и перезагрузки
  6. Навигация по файловой системе
  7. Управление пакетами приложений
  8. Логирование с помощью logcat
  9. Системные дампы и диагностика (dumpsys)
  10. Анализ производительности в реальном времени
  11. Эмуляция ввода (input) (вы здесь)
  12. Управление Activity и Intent (am)
  13. Работа с оконным менеджером (wm)
  14. Захват экрана и запись видео
  15. Root-доступ и его применение
  16. Модификация системных настроек через settings
  17. Команды для поставщиков контента (content)
  18. Резервное копирование и восстановление (backup)
  19. Проброс портов и туннелирование
  20. Беспроводная отладка (Wi-Fi)
  21. Взаимодействие с эмуляторами
  22. Написание скриптов на Bash/CMD/PowerShell
  23. ADB в языках программирования
  24. Автоматизация тестирования с ADB
  25. Безопасность и лучшие практики
  26. ADB на Android TV, Wear OS и IoT
  27. Восстановление и низкоуровневые операции
  28. Расширенные возможности оболочки и инструменты
  29. Отладка самого ADB и устранение неполадок
После глав про logcat и dumpsys вы умеете смотреть, что происходит на устройстве. Пора научиться вмешиваться. Команда input эмулирует нажатия клавиш, касания и ввод текста так, что приложение не отличает их от действий живого пользователя. Root не нужен: пользователь shell наделён правом INJECT_EVENTS, и события уходят через системный InputManager в любое окно. На input держится половина автоматизации, которую мы соберём в главах 22 и 24.

Как устроен input и что он умеет:

Бинарь живёт на устройстве, все вызовы идут через adb shell. Запуск без аргументов печатает справку, и это первое, что надо сделать на новой прошивке, потому что набор подкоманд зависит от версии Android.

Код: Выделить всё

$ adb shell input
Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]
...
The commands and default sources are:
      text <string> (Default: touchscreen)
      keyevent [--longpress|--doubletap] <key code number or name> ... (Default: keyboard)
      tap <x> <y> (Default: touchscreen)
      swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
      draganddrop <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen)
      motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)
      keycombination [-t duration(ms)] <key code 1> <key code 2> ... (Default: keyboard)
Это вывод Android 14. motionevent появился в Android 12, а draganddrop, keycombination и флаг --doubletap добавили в Android 13, на десятке ничего этого нет, только text, keyevent, tap, swipe и пара трекбольных команд. Успешная команда не печатает ничего и возвращает код 0, проверяйте через echo $?.

input keyevent, коды клавиш:

keyevent шлёт нажатие одной клавиши. Принимает числовой код или символьное имя из класса KeyEvent, причём имя работает и с префиксом KEYCODE_, и без него.

Код: Выделить всё

adb shell input keyevent 4
adb shell input keyevent KEYCODE_BACK
adb shell input keyevent BACK
Все три строки жмут "назад". Кодов около трёхсот, полный список в документации класса KeyEvent. Рабочий минимум:

Код: Выделить всё

3    KEYCODE_HOME          домой
4    KEYCODE_BACK          назад
24   KEYCODE_VOLUME_UP     громкость плюс
25   KEYCODE_VOLUME_DOWN   громкость минус
26   KEYCODE_POWER         кнопка питания (переключает экран!)
61   KEYCODE_TAB           следующее поле формы
66   KEYCODE_ENTER         ввод
67   KEYCODE_DEL           backspace
82   KEYCODE_MENU          меню
85   KEYCODE_MEDIA_PLAY_PAUSE
187  KEYCODE_APP_SWITCH    недавние приложения
223  KEYCODE_SLEEP         только гасит экран
224  KEYCODE_WAKEUP        только будит экран
Пара 223/224 в скриптах надёжнее, чем 26: POWER переключает состояние, и если экран уже горел, скрипт его погасит. WAKEUP и SLEEP идемпотентны.

В один вызов можно передать несколько кодов, они отработают последовательно:

Код: Выделить всё

adb shell input keyevent 25 25 25
Три щелчка громкости вниз. Флаг --longpress эмулирует долгое нажатие, --doubletap (Android 13+) двойное:

Код: Выделить всё

adb shell input keyevent --longpress KEYCODE_POWER
Так открывается меню выключения. Гадкая особенность: опечатка в имени не даёт ошибки. Неизвестное имя превращается в KEYCODE_UNKNOWN (код 0), и команда молча отрабатывает впустую. Если keyevent "не работает", первым делом сверьте имя.

Модификаторы и комбинации:

Послать Ctrl+A двумя отдельными keyevent не выйдет: каждый вызов это полный цикл нажал-отпустил, модификатор успевает отжаться до следующей клавиши. До Android 13 комбинации делали только через sendevent или scrcpy. С Android 13 есть keycombination:

Код: Выделить всё

adb shell input keycombination KEYCODE_CTRL_LEFT KEYCODE_A
adb shell input keycombination KEYCODE_ALT_LEFT KEYCODE_TAB
adb shell input keycombination KEYCODE_POWER KEYCODE_VOLUME_DOWN
Первая выделяет весь текст в поле, вторая переключает приложения, третья снимает скриншот, ровно как зажатие физических кнопок. Коды модификаторов: 59 SHIFT_LEFT, 57 ALT_LEFT, 113 CTRL_LEFT, 117 META_LEFT.

input tap и input swipe:

tap жмёт в точку экрана. Координаты в пикселях от левого верхнего угла в текущей ориентации, размер экрана узнаём так (подробно про wm в главе 13):

Код: Выделить всё

$ adb shell wm size
Physical size: 1080x2400

Код: Выделить всё

adb shell input tap 540 1200
Касание в центр экрана. Координаты конкретных элементов удобно снимать, включив в "Для разработчиков" пункт "Местоположение указателя": сверху появится полоса с координатами пальца. Второй способ, дамп интерфейса, покажу ниже в сценарии с формой.

swipe ведёт палец из точки в точку, последний аргумент это длительность в миллисекундах:

Код: Выделить всё

adb shell input swipe 540 1800 540 600 300
adb shell input swipe 100 960 980 960 80
Первая команда, плавный скролл вверх за 300 мс. Вторая, резкий горизонтальный флинг, то есть перелистывание с инерцией. Если длительность не указать, система подставит маленькую, и вместо аккуратного скролла получится бросок, частый источник нестабильных тестов.

Долгое нажатие делается свайпом в ту же самую точку:

Код: Выделить всё

adb shell input swipe 540 1200 540 1200 800
800 мс удержания, откроется контекстное меню. Для перетаскивания на Android 13+ есть честный draganddrop:

Код: Выделить всё

adb shell input draganddrop 540 1200 200 600 1000
На устройствах с несколькими дисплеями (DeX, складные) цель выбирается флагом -d с ID дисплея из dumpsys display.

input text:

Печатает строку в сфокусированное поле. Сначала tap по полю, потом text:

Код: Выделить всё

adb shell input tap 540 586
adb shell input text qa_user_77
С пробелами и спецсимволами начинается двухслойное экранирование: строку сначала разбирает шелл вашей машины, потом шелл устройства, и только остаток достаётся input. Надёжных приёма два. Либо последовательность %s, которую input сам заменяет на пробел:

Код: Выделить всё

adb shell input text 'S3cret%sPass'
Либо кавычки для шелла устройства целиком:

Код: Выделить всё

adb shell "input text 'hello world & co'"
Двойные кавычки съест локальный шелл, одинарные доедут до устройства и защитят пробелы с амперсандом. Без них шелл устройства порежет строку на аргументы:

Код: Выделить всё

$ adb shell input text hello world
Error: Invalid arguments for command: text
Символы & | ; $ ( ) < > без кавычек экранируются обратным слешем, с учётом обоих слоёв. Обратная сторона %s: буквальную последовательность "%s" напечатать через input text нельзя, замена на пробел безусловная.

Главное ограничение: input text молча игнорирует кириллицу. Символы идут через KeyCharacterMap виртуальной клавиатуры, а там только ASCII. Стандартный обход, клавиатура ADBKeyboard (проект senzhk/ADBKeyBoard, ставится обычным adb install):

Код: Выделить всё

adb install ADBKeyboard.apk
adb shell ime enable com.android.adbkeyboard/.AdbIME
adb shell ime set com.android.adbkeyboard/.AdbIME
adb shell am broadcast -a ADB_INPUT_TEXT --es msg 'Привет, Cyberlake'
Текст уходит широковещательным интентом (про am broadcast подробно в главе 12). Вернуть обычную клавиатуру: adb shell ime reset.

sendevent, сырые события и мультитач:

input работает на уровне системы, а sendevent пишет события прямо в файлы драйверов /dev/input/eventN. Root обычно не нужен: пользователь shell состоит в группе input. Это инструмент для того, чего input не умеет: мультитач, жесты с точным таймингом, эмуляция конкретного железа.

Сначала находим тачскрин:

Код: Выделить всё

$ adb shell getevent -lp
add device 5: /dev/input/event2
  name:     "goodix_ts"
  events:
    ABS (0003): ABS_MT_SLOT           : value 0, min 0, max 9
                ABS_MT_POSITION_X     : value 0, min 0, max 1079
                ABS_MT_POSITION_Y     : value 0, min 0, max 2399
                ABS_MT_TRACKING_ID    : value 0, min 0, max 65535
Здесь max совпадает с разрешением экрана, повезло. Нередко диапазон другой, например 0-4095, тогда пересчитывайте координаты пропорцией: raw_x = x * 4095 / 1079.

Живые события смотрим через getevent, так же "записывают" реальный жест, чтобы изучить его структуру:

Код: Выделить всё

$ adb shell getevent -l /dev/input/event2
EV_ABS       ABS_MT_TRACKING_ID   0000039c
EV_ABS       ABS_MT_POSITION_X    0000021c
EV_ABS       ABS_MT_POSITION_Y    000004b0
EV_SYN       SYN_REPORT           00000000
Ловушка: getevent печатает значения в hex, а sendevent принимает только десятичные. 0x21c это 540, копировать вывод напрямую нельзя.

Одиночный тап по протоколу multi-touch B (все современные тачскрины):

Код: Выделить всё

adb shell '
D=/dev/input/event2
sendevent $D 3 57 1001    # ABS_MT_TRACKING_ID, палец появился
sendevent $D 3 53 540     # ABS_MT_POSITION_X
sendevent $D 3 54 1200    # ABS_MT_POSITION_Y
sendevent $D 1 330 1      # BTN_TOUCH down
sendevent $D 0 0 0        # SYN_REPORT, фиксируем кадр
sendevent $D 3 57 -1      # палец исчез
sendevent $D 1 330 0
sendevent $D 0 0 0
'
Два пальца, заготовка для pinch: каждому пальцу свой слот (код 47, ABS_MT_SLOT) и свой tracking id:

Код: Выделить всё

D=/dev/input/event2
sendevent $D 3 47 0       # слот 0
sendevent $D 3 57 2001
sendevent $D 3 53 400
sendevent $D 3 54 1000
sendevent $D 3 47 1       # слот 1
sendevent $D 3 57 2002
sendevent $D 3 53 700
sendevent $D 3 54 1400
sendevent $D 1 330 1
sendevent $D 0 0 0
Дальше серия кадров движения: в цикле меняете X/Y в каждом слоте, закрывая каждый кадр через SYN_REPORT, в конце отпускаете оба tracking id значением -1. Честное предупреждение: каждый вызов sendevent это отдельный процесс, жест из сотни событий получится дёрганым. Для плавного мультитача кладите шелл-скрипт на устройство и запускайте целиком, либо пишите в /dev/input напрямую из своего кода (глава 23).

Сценарий 1, разблокировка экрана:

Устройство с PIN 4821 и свайпом вверх для показа пин-пада:

Код: Выделить всё

adb shell input keyevent KEYCODE_WAKEUP
adb shell input swipe 540 1900 540 700 200
adb shell input text 4821
adb shell input keyevent 66
Между свайпом и вводом PIN иногда нужна пауза, анимация не успевает. Проверка, что экран проснулся:

Код: Выделить всё

$ adb shell dumpsys power | grep mWakefulness=
  mWakefulness=Awake
PIN в скрипте допустим только для тестового парка, на личном устройстве это дыра, подробнее в главе 25.

Сценарий 2, запуск приложения:

Тыкать по иконкам рабочего стола, плохая идея, их позиции нестабильны. Запускают через am (этому посвящена глава 12) или monkey, а input берёт на себя то, что внутри приложения:

Код: Выделить всё

adb shell am start -n org.telegram.messenger/org.telegram.ui.LaunchActivity
adb shell monkey -p ru.yandex.searchplugin -c android.intent.category.LAUNCHER 1
monkey удобен тем, что не требует знать имя Activity, достаточно пакета.

Сценарий 3, заполнение формы логина:

Координаты полей берём из дампа интерфейса:

Код: Выделить всё

$ adb shell uiautomator dump
UI hierchary dumped to: /sdcard/window_dump.xml
$ adb pull /sdcard/window_dump.xml .
Опечатка hierchary не моя, она в AOSP. В xml ищем узел по resource-id, у него атрибут bounds="[84,520][996,652]", значит центр поля (540, 586). Дальше одним заходом в shell, чтобы не платить за каждый round-trip:

Код: Выделить всё

adb shell "
input tap 540 586 && sleep 0.4
input text 'qa2026@cyberlake.ru'
input keyevent 61 && sleep 0.2
input text 'S3cret%sPass9'
input keyevent 66
"
TAB (61) переносит фокус на следующее поле, ENTER (66) сабмитит форму. sleep в toybox принимает дробные секунды.

Частые ошибки:

События уходят в погасший экран и пропадают без всякой ошибки. Начинайте сценарий с WAKEUP и проверки mWakefulness.

На Xiaomi/Redmi (MIUI и HyperOS) input из adb по умолчанию кидает исключение:

Код: Выделить всё

java.lang.SecurityException: Injecting input events requires the caller (or the source of the instrumentation, if any) to have the INJECT_EVENTS permission.
Лечится в "Для разработчиков" пунктом "Отладка по USB (Настройки безопасности)", который требует Mi-аккаунт с SIM-картой и иногда слетает после обновления прошивки. Если собираете тестовый парк из Xiaomi, закладывайте это заранее.

Координаты tap привязаны к текущей ориентации: повернулся экран, и (540, 1200) указывает в другое место. Фиксируйте ориентацию перед прогоном (глава 16 про settings) или снимайте координаты заново.

Свайп без длительности превращается во флинг, скролл уезжает дальше, чем ожидалось, и тесты мигают. Явно указывайте 200-400 мс.

input text на строке с пробелом без кавычек падает с Invalid arguments, а на кириллице молча не делает ничего. Оба случая разобрали выше, держите под рукой %s и ADBKeyboard.

В следующей главе займёмся am вплотную: запуск Activity, сервисов и broadcast с параметрами, без которых сценарии автоматизации не собрать.
👍6 ❤️ 🔥1 😄 🤔
✔ Лучший ответ сформирован автоматически — nginxlord
android_roman писал(а):каждый вызов sendevent это отдельный процесс вот это в точку, мой pinch из 60 кадров дергался как припадочный. закинул скрипт в /data/local/tmp и запустил через sh прямо на девайсе, стало сильно лучше, но до плавности пальца все равно далеко. имеет смысл вставлять usleep между SYN_REPORT или наоборот выкинуть все паузы?
Перейти к ответу →
Аватара пользователя
mackensie
Сообщения: 1
Зарегистрирован: 17 май 2026, 02:50

Re: Эмуляция ввода (input)

Сообщение mackensie »

android_roman писал(а):input text молча игнорирует кириллицу
а через буфер обмена никак нельзя? пробовал нагуглить cmd clipboard, на моем android 14 такой команды не нашлось. в итоге спасаюсь scrcpy, он русский текст вставляет через синхронизацию буфера. но на CI без гуя как быть, ADBKeyboard единственный путь или есть что-то посвежее?
👍 ❤️1 🔥2 😄 🤔1
Аватара пользователя
pinball
Сообщения: 1
Зарегистрирован: 17 май 2026, 17:20

Re: Эмуляция ввода (input)

Сообщение pinball »

подтверждаю боль про сяоми. парк из redmi note 13 на работе, без галки 'настройки безопасности' любой input tap валится с SecurityException. галка хочет mi аккаунт и симку, а после OTA на hyperos 2 у половины девайсов слетела, пришлось руками перетыкивать. кто собирает ферму, берите пиксели или самсунги, нервы дороже
👍2 ❤️ 🔥 😄 🤔
Аватара пользователя
elixir_pro
Сообщения: 3
Зарегистрирован: 12 май 2026, 13:01

Re: Эмуляция ввода (input)

Сообщение elixir_pro »

а на android 11 как ctrl+a сделать? keycombination там нет, проверил, input ругается unknown command. пока выкрутился костылем: лонгтап свайпом в поле и потом tap по кнопке 'выбрать все' в контекстном меню, но координаты меню гуляют от прошивки к прошивке. есть способ почище без рута?
👍2 ❤️ 🔥 😄 🤔
Аватара пользователя
nginxlord
Сообщения: 3
Зарегистрирован: 19 май 2026, 18:11

Re: Эмуляция ввода (input)

Сообщение nginxlord »

✔ Лучший ответ — сформирован автоматически
android_roman писал(а):каждый вызов sendevent это отдельный процесс
вот это в точку, мой pinch из 60 кадров дергался как припадочный. закинул скрипт в /data/local/tmp и запустил через sh прямо на девайсе, стало сильно лучше, но до плавности пальца все равно далеко. имеет смысл вставлять usleep между SYN_REPORT или наоборот выкинуть все паузы?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Анализ производительности в реальном времени
Следующая глава →
Управление Activity и Intent (am)

Все главы курса «ADB: Android Debug Bridge»

Поделиться темой: ✈ Telegram VK

Вернуться в «ADB: Android Debug Bridge»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость