Введение в использование unix shell в рекавери
от Senderman
Эта статья будет полезна тем кто хочет писать свои установщики/патчи/оптимизаторы для recovery андроида. Перед прочтением рекомендуется знать bash-скриптинг хотя бы минимально.
Install zip: как работает?
Когда вы в twrp или cwm выбираете архив для прошивки, update-binary из него распаковывается и запускается, передавая ему 3 переменные:
$1 - API level: число от 1 до 3, нам это не пригодится
$2 - Дескриптор pipe для обратной связи с рекавери
$3 - путь к zip файлу.
Структура zip архива
В корне зипа обязательно должен быть файл META-INF/com/google/android/update-binary. Не смотря на то что он называется binary, он может быть и shell-скриптом. Кроме того, в папке META-INF могут располагаться файлы сертификатов (если зип подписан). Все остальные файлы пользователь кладет в архив по своему усмотрению.
Update-binary: Edify vs Unix Shell
Update-binary - исполняемый файл, который запускается в первую очередь. От него зависит что будет делать зип архив.
В настоящий момент популярно 2 типа update-binary: бинарник, написанный на C неким Edify, и скрипт на unix shell. В случае с edify, в папке с update-binary должен лежать файл updater-script, который вы пишите сами. Мануал по updater-script для edify можно посмотреть вот тут. Недостатки edify - во-первых, не на всех рекавери он может запуститься, а во-вторых, edify script не удобен. Сравните сами:
Удаление папки:
Edify: delete_recursive("/path/folder");
shell: rm -r /path/folder
Выставление разрешений:
Edify: set_perm("0777","file");
Shell: chmod 777 file
В этой статье будет рассмотрен скриптинг именно на shell.
Hello, world!
Казалось бы, что может быть проще. echo "Hello world"? А вот и нет! В рекавери все нужно отправлять в /proc/self/fd/$2. Так что сразу запишем этот путь в переменную, например, outfd. echo "hello world" > $outfd? И опять не правильно! outfd имеет свои команды. Итак, рассмотрим готовый пример:
Как видите, в начале скрипта мы создали функцию ui_print, которая облегчит процесс передачи текста на экран. echo тут выполняется 2 раза, чтобы не было проблем с переносом строк. И, как вы успели заметить, первая строчка всегда начинается с #!/sbin/sh - такой путь к интерпретатору в рекавери (в самом андроиде путь /system/bin/sh)
Прошиваем что-нибудь
Положите пустой файл foo.txt в корень архива. Нам нужно скопировать его в /data/local/ update-binary будет выглядеть вот так:
Тут я обошелся без ui_print за ненадобностью. Ну из комментариев вроде все понятно, объяснять ничего не нужно.
Важно: иногда unzip не работает с архивами, сделанными ES проводником. Используйте Zarchiver.
Еще кое что: Когда вы распаковываете файл в /tmp, вы распаковываете его в ОЗУ. Если ваш архив слишком большой, распаковывайте его в /data/local.
Подводные камни
1.Если вам что-то нужно изменить в каком-то разделе, смонтируйте его командой mount.
mount /system
mount /data
и т.п. В конце скрипта лучше всего их потом отмонтировать (umount /data например).
2. Не используйте кириллицу в скрипте, кроме названий файлов.
Архитектура процессора
Большинство Android-устройств имеют архитектуру ARMv7 или v6. Реже - x86, еще реже - mips, Некоторые программы/бинарники заточены именно под определенную архитектуру. Узнать ее легко:
getprop ro.product.cpu.abi
Но getprop не всегда возвращает то что нужно. В этом случае лучше брать значения из build.prop и default.prop.
.
Ну а что дальше с этим делать, думайте сами. Можно создать в корне зипа папки arm,x86,mips, положить в них соотв. файлы и скопировать их:
Прогрессбар
Во время прошивки zip, внизу отображается прогрессбар. Мы можем изменять его "заполненность".
Первую команду мы используем всего лишь один раз. При значениях 1 и 0 в set_progress мы можем использовать любые числа от 0 до 1. При значениях 0 и 2 - от 0 до 2. Ну вы поняли.
А вообще, изменения прогрессбара не обязательны. Их можно вообще не включать в скрипт - они сделаны для крастоты
Подводим итоги
Итак, задача: распаковать архив, скопировать бинарник в /data/local (с учетом архитектуры), сделать на него симлинк в /system/xbin и сделать его исполняемым. Поехали.
Что там и как - вы поймете, если прочитали всю статью
Данная статья всего лишь показала вам, какие функции, команды и фичи можно использовать в рекавери. Для написания полноценных скриптов, необходимо знание языка bash.
Удачи вам, и помните: неправильный скрипт может окирпичить устройство!
Install zip: как работает?
Когда вы в twrp или cwm выбираете архив для прошивки, update-binary из него распаковывается и запускается, передавая ему 3 переменные:
$1 - API level: число от 1 до 3, нам это не пригодится
$2 - Дескриптор pipe для обратной связи с рекавери
$3 - путь к zip файлу.
Структура zip архива
В корне зипа обязательно должен быть файл META-INF/com/google/android/update-binary. Не смотря на то что он называется binary, он может быть и shell-скриптом. Кроме того, в папке META-INF могут располагаться файлы сертификатов (если зип подписан). Все остальные файлы пользователь кладет в архив по своему усмотрению.
Update-binary: Edify vs Unix Shell
Update-binary - исполняемый файл, который запускается в первую очередь. От него зависит что будет делать зип архив.
В настоящий момент популярно 2 типа update-binary: бинарник, написанный на C неким Edify, и скрипт на unix shell. В случае с edify, в папке с update-binary должен лежать файл updater-script, который вы пишите сами. Мануал по updater-script для edify можно посмотреть вот тут. Недостатки edify - во-первых, не на всех рекавери он может запуститься, а во-вторых, edify script не удобен. Сравните сами:
Удаление папки:
Edify: delete_recursive("/path/folder");
shell: rm -r /path/folder
Выставление разрешений:
Edify: set_perm("0777","file");
Shell: chmod 777 file
В этой статье будет рассмотрен скриптинг именно на shell.
Hello, world!
Казалось бы, что может быть проще. echo "Hello world"? А вот и нет! В рекавери все нужно отправлять в /proc/self/fd/$2. Так что сразу запишем этот путь в переменную, например, outfd. echo "hello world" > $outfd? И опять не правильно! outfd имеет свои команды. Итак, рассмотрим готовый пример:
- #!/sbin/sh
- outfd=/proc/self/fd/$2
- ui_print() {
- echo "ui_print $1" > $outfd
- echo "ui_print" > $outfd
- }
- ui_print "Hello, world!"
Прошиваем что-нибудь
Положите пустой файл foo.txt в корень архива. Нам нужно скопировать его в /data/local/ update-binary будет выглядеть вот так:
- #!/sbin/sh
- ZIP="$3"
- mkdir -p /tmp/test # создать папку /tmp/test
- cd /tmp/test # перейти в нее
- unzip -o $ZIP # распаковать туда наш архив
- cp foo.txt /data/local # скопировать файл
Важно: иногда unzip не работает с архивами, сделанными ES проводником. Используйте Zarchiver.
Еще кое что: Когда вы распаковываете файл в /tmp, вы распаковываете его в ОЗУ. Если ваш архив слишком большой, распаковывайте его в /data/local.
Подводные камни
1.Если вам что-то нужно изменить в каком-то разделе, смонтируйте его командой mount.
mount /system
mount /data
и т.п. В конце скрипта лучше всего их потом отмонтировать (umount /data например).
2. Не используйте кириллицу в скрипте, кроме названий файлов.
Архитектура процессора
Большинство Android-устройств имеют архитектуру ARMv7 или v6. Реже - x86, еще реже - mips, Некоторые программы/бинарники заточены именно под определенную архитектуру. Узнать ее легко:
getprop ro.product.cpu.abi
Но getprop не всегда возвращает то что нужно. В этом случае лучше брать значения из build.prop и default.prop.
- #!/sbin/sh
- outfd=/proc/self/fd/$2
- ui_print(){
- echo "ui_print $1" > $outfd
- echo "ui_print" > $outfd
- }
- getabi(){
- grep ^ro.product.cpu.abi $1 | cut -d = -f 2
- }
- mount /system
- abi=`getprop ro.product.cpu.abi`
- case $abi in
- arm*|x86*|mips*) ;;
- *) abi=`getabi /system/build.prop` ;; # если getprop не вернул то что нужно
- esac
- case $abi in
- arm*|x86*|mips*) ;;
- *) abi=`getabi /default.prop` ;; # если build.prop не вернул то что нужно
- esac
- case $abi in
- arm*) arch=arm ;;
- x86*) arch=x86 ;;
- mips*) arch=mips ;;
- *) ui_print "Your architecture is not supported" ; exit 0 ;; # если архитектура не подошла
- esac
- ui_print "Using architecture: $arch"
Ну а что дальше с этим делать, думайте сами. Можно создать в корне зипа папки arm,x86,mips, положить в них соотв. файлы и скопировать их:
- cp $arch/file /system/file
Прогрессбар
Во время прошивки zip, внизу отображается прогрессбар. Мы можем изменять его "заполненность".
- echo "progress 1 0" > $outfd # 1 - макс. значение, 0 - минимальное. Их можно менять.
- set_progress(){
- echo "set_progress $1" > $outfd # изменение значения прогрессбара
- }
- set_progress 0.2
- set_progress 0.5 # тут прогрессбар будет заполнен наполовину
- set_progress 0.8
- set_progress 1 # а тут он будет заполнен полностью.
А вообще, изменения прогрессбара не обязательны. Их можно вообще не включать в скрипт - они сделаны для крастоты
Подводим итоги
Итак, задача: распаковать архив, скопировать бинарник в /data/local (с учетом архитектуры), сделать на него симлинк в /system/xbin и сделать его исполняемым. Поехали.
- #!/sbin/sh
- outfd=/proc/self/fd/$2
- zip="$3"
- ui_print(){
- echo "ui_print $1" > $outfd
- echo "ui_print" > $outfd
- }
- echo "progress 1 0" > $outfd
- set_progress(){
- echo "set_progress $1" > $outfd
- }
- getabi(){
- grep ^ro.product.cpu.abi $1 | cut -d = -f 2
- }
- set_progress 0.1
- ui_print "Mounting partitions..."
- mount /system
- mount /data
- set_progress 0.3
- ui_print "Extacting files..."
- mkdir -p /tmp/test
- cd /tmp/test
- unzip -o "$zip"
- ui_print "Installing binary"
- set_progress 0.5
- abi=`getprop ro.product.cpu.abi`
- case $abi in
- arm*|x86*|mips*) ;;
- *) abi=`getabi /system/build.prop` ;;
- esac
- case $abi in
- arm*|x86*|mips*) ;;
- *) abi=`getabi /default.prop` ;;
- esac
- case $abi in
- arm*) arch=arm ;;
- x86*) arch=x86 ;;
- mips*) arch=mips ;;
- *) ui_print "Your architecture is not supported" ; exit 0 ;;
- esac
- ui_print "Using architecture: $arch"
- set_progress 0.7
- cp $arch/binary /data/local
- ui_print "Setting permissions..."
- chmod 755 /data/local/binary
- ui_print "Symlinking..."
- set_progress 0.9
- ln -s /data/local/binary /system/xbin/binary
- ui_print "Instalation complete!"
- set_progress 1
- exit 0
Что там и как - вы поймете, если прочитали всю статью
Данная статья всего лишь показала вам, какие функции, команды и фичи можно использовать в рекавери. Для написания полноценных скриптов, необходимо знание языка bash.
Удачи вам, и помните: неправильный скрипт может окирпичить устройство!