Android основан на ядре Linux, включает в себя набор стандартных UNIX-команд и простой шелл sh. Все это значит, что мы можем не только использовать командную строку для выполнения низкоуровневых операций, но и писать шелл-скрипты, которые будут выполнять функции, недоступные из графического интерфейса. В этой статье мы поговорим о том, что с их помощью можно сделать и зачем все это нужно.

Для прошлого номера журнала я написал статью о Tasker — системе, которая позволяет автоматизировать работу Android и заменить сотни сторонних приложений. К сожалению, Tasker ограничен высокоуровневыми функциями Android и не позволяет выполнять такие низкоуровневые операции, как монтирование файловых систем, изменение параметров ядра, системных переменных или запуск демонов. Зато все это можно сделать с помощью скриптов.

Сразу оговорюсь, что в этой статье речь пойдет о шелл-скриптах в традиционном для Linux понимании, без использования инструментов вроде SL4A, QPython или Roboto. Главное назначение таких скриптов — изменение поведения системы, параметров ядра, работа с демонами (ADB, например) и тому подобное. Скрипты могут стартовать на этапе загрузки ОС, установки новой прошивки, после тапа по кнопке или же по традиции — из терминала.

В статье я расскажу, как писать такие скрипты, как заставить их стартовать автоматически, привязывать к определенному системному событию. В качестве бонуса также объясню, как заставить консоль восстановления (recovery) выполнить необходимые тебе действия перед установкой или сразу после установки новой прошивки. Начинаем.

 

Особенности Android-окружения

В самой своей основе, там, где нет Java и Dalvik, Android представляет собой минималистичный Linux-дистрибутив со всеми свойственными ему атрибутами: ядром, системой инициализации, набором библиотек, демонов, консольных команд и, конечно же, шеллом. Последний — это не что иное, как mksh из MirBSD, переименованный в sh; простой командный интерпретатор с поддержкой языковых конструкций классического Bourne shell из UNIX и автодополнением по нажатию Tab.

В качестве комплекта базовых UNIX-команд здесь используется toolbox, своего рода урезанная альтернатива BusyBox, которая позволяет вызывать несколько разных команд из одного бинарника (с помощью симлинков). Toolbox включает в себя очень ограниченный набор команд, в котором нет не только grep или sort, но даже cp. Поэтому для полноценной работы со скриптами настоятельно рекомендуется установка BusyBox, благо в маркете полно бесплатных инсталляторов.

Сам шелл располагается не совсем по адресу, поэтому «шибанг» в скриптах будет выглядеть несколько по-иному, а именно #!/system/bin/sh. Зато о расположении бинарников можно не думать вообще, так как в переменной $PATH всегда прописаны правильные значения. Каталогов для поиска команд тут всегда три: /system/bin/, /system/sbin/ и /system/xbin/ для внешних бинарников. Туда обычно устанавливается BusyBox.

Основное назначение скриптинга в Android — работа с ядром и системными утилитами. Ядро тут стандартное и экспортирует все те же интерфейсы /proc и /sys, через которые можно рулить железом и состоянием системы. Плюс есть набор специфичных для Android утилит, которые будут очень полезны при разработке скриптов:

  • pm — менеджер пакетов, позволяет устанавливать, удалять и перемещать софт;
  • am — менеджер активностей (Activity), может быть использован для запуска приложений;
  • dumpsys — дамп в консоль массы различной информации о состоянии системы;
  • screencap — утилита для снятия скриншота;
  • screenrecord — утилита для записи скринкастов;
  • getprop/setprop — команды для чтения и изменения системных переменных;
  • start/stop — запуск и остановка системных служб;
  • input — позволяет отправлять в текущее окно кей-коды (эмуляция клавиатуры);
  • service — утилита для управления Java-сервисами, имеет очень много возможностей;
  • svc — позволяет управлять Wi-Fi, USB-подключением и питанием.
Часть вывода команды dumpsys
Часть вывода команды dumpsys
 

Первый пример

Теперь давайте попробуем написать первый скрипт. Делать это лучше на компе, а еще лучше в Linux или редакторе, который умеет создавать текстовые файлы без символа возврата каретки (который при открытии в Android будет выглядеть как ^M в конце каждой строки). Наш первый скрипт будет состоять всего из двух строк, которые делают бэкап всех установленных приложений на карту памяти. Его код (требует BusyBox):

#!/system/bin/sh

mkdir /sdcard/backup
cp /data/app/*.apk /sdcard/backup

Сохраняем (пусть он называется apk_backup.sh) и перекидываем на смартфон с помощью ADB:

$ adb push apk_backup.sh /sdcard/

Теперь его нужно запустить. Проще всего сделать это с помощью все того же ADB:

$ adb shell sh /sdcard/apk_backup.sh

Примерно таким же образом скрипт можно запустить из консоли на самом смартфоне/планшете:

$ sh /sdcard/apk_backup.sh

Само собой, такой способ не очень удобен. Поэтому нам нужен какой-то быстрый способ запуска скрипта. Наиболее удобное из найденных мной решений — это приложение QuickTerminal. Устанавливаем, запускаем, переходим на вкладку Quick Command, нажимаем кнопку «+», вбиваем имя (произвольное) и команду (sh /sdcard/apk_backup.sh), в поле Output Type выбираем либо Dialog Output, либо Nothing. В первом случае во время выполнения скрипта на экране появится окно с результатом, во втором все пройдет в фоне. Кому что удобнее. Далее сохраняем и получаем кнопку, с помощью которой скрипт можно будет запустить быстро и легко.

Теперь напишем скрипт, который восстановит наш бэкап:

#!/system/bin/sh

for i in /sdcard/backup/*; do
pm install -t -r $i
done

В нем мы задействовали команду pm с опцией install и флагами -t и -r, которые заставляют систему устанавливать приложения, даже если они подписаны тестовым ключом или уже установлены. Также можно использовать флаг -s, который принуждает приложения к установке на карту памяти (если такая возможность есть), или -f — установка во внутреннюю память устройства.

Почти все команды Android имеют подробную справку
Почти все команды Android имеют подробную справку

Имея рут, можно даже сделать бэкап настроек всех приложений с помощью копирования и архивации каталога /data/data/, однако восстановить его будет очень проблематично, так как в Android каждое приложение исполняется от имени созданного специально для него Linux-юзера и хранит настройки внутри каталога, принадлежащего этому пользователю. Проблема здесь в том, что идентификатор Linux-юзера для каждого приложения генерируется динамически, поэтому после восстановления бэкапа в заново установленной системе идентификаторы не будут совпадать и приложения не смогут прочитать свои настройки. Придется вручную выяснять ID юзера для каждого приложения и менять права доступа на каталоги с данными.

С другой стороны, мы можем использовать встроенный в Android Backup Manager, позволяющий сторонним приложениям использовать возможности системы для бэкапа и восстановления приложений и их данных. Управлять им можно из консоли (а значит, и с помощью скриптов), но сам по себе он никакого бэкапа не производит, а возлагает эту работу на сторонние приложения. Helium — одно из таких приложений. Если установить и настроить его, операцию бэкапа и восстановления можно будет заскриптовать. Например, следующий простой скрипт сделает резервную копию всех сторонних приложений:

#!/system/bin/sh
# Получаем список всех сторонних приложений
for i in `pm list packages -e`; do
# Добавляем каждое из них в очередь
bmgr backup ${i:8}
done
# Запускаем операцию бэкапа
bmgr run

Конструкция ${i:8} здесь нужна, чтобы обрезать слово «packages:», которое pm добавляет в начало имени каждого пакета. Чтобы восстановить бэкап, можно использовать либо тот же Helium, либо команду bmgr:

$ bmgr list sets # Получаем список бэкапов
$ bmgr restore <тег> # Восстанавливаем нужный бэкап
 

Автозапуск

«Это все круто, но скрипты должны запускаться сами», — скажешь ты и будешь абсолютно прав. Без автозапуска от скриптов толку мало, но это легко исправить, если воспользоваться все тем же Tasker. Он умеет запускать любые шелл-команды в ответ на любое событие. Чтобы воспользоваться этой функциональностью, достаточно создать новый профиль, выбрать событие (для бэкапа лучшим событием будет время), затем добавляем действие, выбираем Script -> Run Shell, вбиваем команду (sh /sdcard/script.sh), выбираем, если необходимо, файл для записи результата и включаем профиль.

Другой популярный способ автозапуска — это использование средств автоматического исполнения скриптов при загрузке в сторонних прошивках. Сегодня почти все сколько-нибудь известные кастомные прошивки умеют стартовать скрипты из каталога /system/etc/init.d/, а в стоке такую функциональность можно получить с помощью приложения Universal init.d из маркета. С последним, однако, надо быть осторожным, так как оно запускает скрипты не на раннем этапе загрузки, как это происходит в том же CyanogenMod, а уже после полной загрузки системы.

Итак, что мы можем поместить в автозагрузку? Например, скрипт запуска демона ADB в сетевом режиме:

#!/system/bin/sh
setprop service.adb.tcp.port 5555
stop adbd
start adbd

Для подключения к нему с ПК набираем такую команду:

$ adb connect IP-смартфона

Также мы можем применить некоторые оптимизации подсистемы виртуальной памяти:

#!/system/bin/sh
echo "4096" > /proc/sys/vm/min_free_kbytes
echo "0" > /proc/sys/vm/oom_kill_allocating_task;
echo "0" > /proc/sys/vm/panic_on_oom;
echo "0" > /proc/sys/vm/laptop_mode;
echo "0" > /proc/sys/vm/swappiness
echo "50" > /proc/sys/vm/vfs_cache_pressure
echo "90" > /proc/sys/vm/dirty_ratio
echo "70" > /proc/sys/vm/dirty_background_ratio

Ну или подогнать механизм lowmemorykiller (автоматическое убийство фоновых приложений при нехватке памяти) под наши нужды:

#!/system/bin/sh
echo "2048,3072,6144,15360,17920,20480" > /sys/module/lowmemorykiller/parameters/minfree

Ну и конечно же, автоматический выбор планировщика процессов:

#!/system/bin/sh
echo "powersave" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

Все это можно сделать с помощью специализированного софта, но зачем загружать систему дополнительным ПО, которое еще и будет висеть в фоне, когда можно обойтись несколькими простыми скриптами?

Как запустить скрипт с помощью Tasker
Как запустить скрипт с помощью Tasker
 

Запуск скриптов до и после установки прошивки

Почти каждый, кто устанавливает на свой гаджет стороннюю прошивку, также ставит поверх нее пакет с фирменными приложениями Google (gapps), который включает в себя маркет, YouTube, Gmail и другой софт. Каждый раз, когда происходит обновление прошивки, раздел /system, содержащий ее и gapps, полностью стирается, но приложения Google всегда остаются на месте. Это происходит потому, что, кроме всего прочего, gapps содержит в своем составе специальный скрипт, который размещается в каталоге /system/addon.d/ и запускается консолью восстановления до и после установки прошивки. Этот скрипт делает бэкап и восстановление приложений Google.

Мы можем использовать эту возможность для выполнения наших собственных действий до и после установки прошивки. Вот так, например, выглядит мой скрипт восстановления, который ничего не бэкапит, но подчищает прошивку от мусора сразу после ее установки:

#!/sbin/sh

# Загружаем подсобные функции
. /tmp/backuptool.functions

# Рингтон и звук уведомления, которые должны остаться в системе
RINGTONE=Machina
NOTIFICATION=Argon

case "$1" in
backup)
    # Пусто :)
;;
restore)
    # Рингтоны, уведомления и звук будильника
    cd /system/media/audio/ringtones/
    rm [!${RINGTONE}]*.ogg
    cd /system/media/audio/notifications/
    rm [!${NOTIFICATION}]*.ogg
    rm /system/media/audio/alarms/*

    # Языки синтеза и офлайн-распознавания речи
    rm /system/tts/lang_pico/*
    rm -rf /system/usr/srec/config/*

    # Приложения
    A=/system/app/
    rm $A/Email.apk
    rm $A/Exchange2.apk
    rm $A/LockClock.apk
    rm $A/PicoTts.apk
    rm $A/Term.apk
    rm $A/ThemeChooser.apk
    rm $APPS/WAPPushManager.apk
    rm $A/LiveWallpapers.apk
    rm $A/LiveWallpapersPicker.apk
    rm $A/VisualizationWallpapers.apk
    A=/system/priv-app/
    rm $A/CMUpdater.apk
    rm $A/ThemeManager.apk
;;
pre-backup)
    # ...
;;
post-backup)
    # ...
;;
pre-restore)
    # ...
;;
post-restore)
    # ...
;;
esac

Скрипт удаляет рингтоны, уведомления, движок синтеза речи и несколько приложений. Все эти действия запускаются в ответ на передачу скрипту опции командной строки restore (это делает консоль восстановления после установки прошивки), однако также предусмотрены и варианты обработки таких опций, как backup, pre-backup, post-backup, pre-restore и post-restore. Здесь это просто заглушки, но если бы мы захотели сделать бэкап некоторых файлов и приложений перед установкой прошивки, мы могли бы добавить их в блок backup, как это сделано в скрипте /system/addon.d/70-gapps.sh:

. /tmp/backuptool.functions

list_files() {
cat <<EOF
app/GoogleContactsSyncAdapter.apk
etc/permissions/com.google.android.maps.xml
etc/permissions/com.google.android.media.effects.xml
...
EOF
}
case "$1" in
backup)
    list_files | while read FILE DUMMY; do
    backup_file $S/$FILE
done
;;
...

Этот кусок скрипта прекрасно иллюстрирует, как сделать бэкап файлов. Ключевые элементы здесь: функция listfiles, которая при запуске выводит листинг файлов, и функция backupfile, которая является частью консоли восстановления (определена в файле /tmp/backuptool.functions). Она делает бэкап файлов в цикле.

Содержимое /system/addon.d/ в CyanogenMod 11 на Motorola Defy
Содержимое /system/addon.d/ в CyanogenMod 11 на Motorola Defy

 

Скрипт бэкапа приложений Google
Скрипт бэкапа приложений Google

 

INFO

По словам разработчика mksh, изначально пользовательские версии Android-смартфонов вообще не должны были иметь в своем составе шелл, но после выпуска смартфона для разработчиков HTC (T-Mobile) G1 он фактически стал стандартной частью системы.

Версии Android 2.3 и ниже вместо mksh использовали минималистичный шелл ash, который входит в базовый комплект всех BSD-систем.

Чтобы получить одни и те же скрипты на всех устройствах, можно использовать приложение DropSync или FolderSync (автоматическая синхронизация через Dropbox).

 

Что еще?

С помощью скриптов в Android можно сделать намного больше, чем бэкапы и настройка параметров системы. Вот, например, скрипт, который просыпается каждые десять минут и, если уровень заряда батареи стал меньше 30%, отключает Wi-Fi и Bluetooth:

#!/system/bin/sh

while true; do
if [ `cat /sys/class/power_supply/battery/capacity` -lt 30 ]; then
    svc wifi disable
    service call bluetooth_manager 8
fi
sleep 600
done

Чтобы скрипт работал в фоне, достаточно вызвать его следующим образом:

$ script.sh &

А это скрипт, который позволяет быстро заполнять формы, требующие ввода имэйла и пароля (в приложениях и на веб-сайтах):

#!/system/bin/sh

adb shell input text "user@gmail.com"
adb shell input keyevent 23
adb shell input keyevent 20
adb shell input text "ПАРОЛЬ"
adb shell input keyevent 23
adb shell input keyevent 20

Запускать его можно разными способами. Либо перед запуском приложения, установив задержку:

$ sleep 15; sh /sdcard/script.sh

Либо повесить на какое-то событие Tasker, например на взмах смартфоном. Другой вариант — использовать буфер обмена. В Android, чтобы вставить нужный текст в буфер обмена, достаточно выполнить такую команду:

$ service call clipboard 2 i32 1 i32 1 s16 "Этот текст появится в буфере обмена"

Не ахти как удобно, зато работает. Как мы можем использовать такую функциональность? Например, сделать простенький скрипт clip.sh:

#!/system/bin/sh
service call clipboard 2 i32 1 i32 1 s16 "$1"

Соль в том, что скрипт можно вызывать через удаленный ADB либо вообще поместить в /system/etc/init.d/, заменив $1 на нужный текст. Так нужные нам данные всегда будут под рукой, а бесполезный на смартфоне механизм копирования/вставки получит хоть какое-то назначение. Консольные команды можно использовать и для более высокоуровневых операций, например позвонить по указанному номеру:

$ am start -a android.intent.action.CALL tel:123

Или просто открыть окно номеронабирателя с нужным номером:

$ am start -a android.intent.action.DIAL tel:123

Примерно таким же образом можно отправить SMS:

#!/system/bin/sh
am start -a android.intent.action.SENDTO -d sms:$1 --es sms_body "$2" --ez exit_on_sent true
sleep 1
input keyevent 22
sleep 1
input keyevent 66

Скрипт принимает два аргумента: номер телефона и содержимое SMS. После запуска он откроет окно SMS-приложения, вставит в него нужный текст, а затем нажмет кнопку Enter для отправки, после чего окно закроется.

Другие полезные при скриптинге команды:

  • Перезагрузка в режим recovery:
    $ su -c reboot recovery
    
  • Мягкая перезагрузка (без перезапуска ядра):
    $ setprop ctl.stop zygote
    
  • Открыть нужное приложение (в данном примере — «Настройки»):
    $ am start -n com.android.settings/com.android.settings.Settings
    
  • Открыть веб-страницу:
    $ am start -a android.intent.action.VIEW http://www.google.com
    
  • Сообщить приложениям о низком уровне заряда батареи (есть софт, который при этом снижает свою активность):
    $ am broadcast -a android.intent.action.BATTERY_LOW
    
  • Изменить MAC-адрес:
    $ ip link set eth0 address 00:11:22:33:44:55
    
  • Активировать вибратор:
    $ echo 100 > /sys/devices/virtual/timed_output/vibrator/enable
    
  • Включить фонарик:
    $ echo 1 > /sys/devices/platform/flashlight/leds/flashlight/brightness
    
  • Проиграть файл (может не сработать):
    $ stagefright -a -o file.mp3
    
  • Отключить указанное приложение (можно организовать цикл для отключения bloatware по списку):
    $ pm disable com.google.android.calendar
    
  • Получить список приложений, которые имеют уведомления в строке состояния:
    $ dumpsys statusbar | grep StatusBarNotification | awk '{ print $2 }' | cut -d '=' -f2
    
  • Оптимизировать внутренние базы данных с настройками (можно добавить скрипт в автозагрузку, требуется BusyBox):
    #!/system/bin/sh
    for i in `find /data -iname "*.db"`; do
    sqlite3 $i 'VACUUM;'
    done
    
  • Переключить Wi-Fi-тизеринг на основной интерфейс (нужно для обмана операторов, которые ограничивают скорость соединения при раздаче интернета по Wi-Fi):
    $ settings put global tether_dun_required 0
Для «отлова» нажатий кнопок можно использовать команду getevent
Для «отлова» нажатий кнопок можно использовать команду getevent
 

Вместо выводов

Для кого-то все описанное в статье может показаться несколько надуманным. Дескать, все это можно сделать с помощью стандартного софта и Tasker. Но зачем использовать тяжелый Java-софт там, где нужное действие можно выполнить с помощью простенького скрипта, который не занимает лишней памяти и может быть легко перенесен на другое устройство? Скрипты удобны, просты, быстро отрабатывают и дают возможность тонкой настройки под себя.

 

 

6 комментариев

  1. 26.10.2014 at 14:01

    Я еще создал себе на карте ext4 раздел, сделал алиасы для (де)монтирования:
    alias mountext=»mount /dev/block/mmcblk0p2 /sd-ext»
    alias umountext=»umount /sd-ext»
    еще сделал себе удобный PS1 для bash’a… Создал директории
    /sd-ext/bin — бинарники всякие
    /sd-ext/lib — либы для этих бинарников
    /sd-ext/opt — некоторый софт. Тут например nmap лежит (ох, да, надо еще гидру скачать)
    Ну и само собой zRam swap на 72 МиБ (работает из коробки в CM7)
    Да и вообще, вот вам скрин: htt*p:*//*u.*to/*q9RHCQ (без звездочек ясное дело)

  2. 30.01.2015 at 09:39

    На планшете нет stagefright. Как еще можно воспроизвести mp3 файл но так, что бы не поднимался ни какой gui?

  3. stre10k

    14.03.2016 at 16:12

    Есть ли возможность в скрипте сделать что-то аналогичное команде fastboot oem-unlock («%~dp0adb\fastboot» -i 0x0421 oem unlock)? Имеется необходимость на сток прошивке устройства без ПК сменить recovery. Вот прям по-любому надо без ПК…

  4. leomaks

    08.08.2016 at 16:31

    позвонить по указанному номеру:
    $ am start -a android.intent.action.CALL tel:123

    а если позвонить по номеру 123;99 то как автоматически ответить ДА на сообщение «отправить следующие тоны?»

  5. yuhrim

    17.09.2017 at 09:02

    Я тут мимо проходил, подумал, вдруг этот способ мне поможет) У меня на телефоне не возможно установить кастомное recovery. Можно ли с помощью ADB устанавливать архивы, предназначенные для TWRP, запуская скрипт оттуда? В нём могут быть не только команды копирования файлов, но и редактирования строк в конфигах. При запуске этих скриптов в ScripRuner на телефоне, выходит ошибка. Может я что-то не так делаю… Или может есть специализированная утилита для этого? А то рассовывать архив по нужным местам вручную бывает хлопотно, потом ещё права не забыть выставить. А редактировать конфиги это вообще запара. Где-то что-то упустил, и всё пропало.

Оставить мнение

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

В компаниях часто встречается задача уведомлять сотрудников о приближающихся днях рождения…