Shell-скриптинг в середовищі Android

  1. Зміст статті Android заснований на ядрі Linux, включає в себе набір стандартних UNIX-команд і простий...
  2. перший приклад
  3. автозапуск
  4. Запуск скриптів до і після установки прошивки
  5. INFO
  6. Що ще?
  7. замість висновків

Зміст статті

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

перший приклад

Тепер давайте спробуємо написати перший скрипт. Робити це краще на компі, а ще краще в 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 мають докладну довідку

Маючи рут, можна навіть зробити бекап налаштувань всіх додатків за допомогою копіювання та архівації каталогу / 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

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

Майже кожен, хто встановлює на свій гаджет сторонню прошивку, також ставить поверх неї пакет з фірмовими додатками 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 ;; ...

Цей шматок скрипта прекрасно ілюструє, як зробити бекап файлів. Ключові елементи тут: функція list files, яка при запуску виводить лістинг файлів, і функція backup file, яка є частиною консолі відновлення (визначена у файлі /tmp/backuptool.functions). Вона робить бекап файлів в циклі.

Вміст /system/addon.d/ в CyanogenMod 11 на Motorola Defy

d/ в CyanogenMod 11 на Motorola Defy

Скрипт бекапа додатків 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 "[email protected]" 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

замість висновків

Для кого-то все описане в статті може здатися дещо надуманим. Мовляв, все це можна зробити за допомогою стандартного софта і Tasker. Але навіщо використовувати важкий Java-софт там, де потрібне дію можна виконати за допомогою простенького скрипта, який не займає зайвого пам'яті і може бути легко перенесений на інший пристрій? Скрипти зручні, прості, швидко відпрацьовують і дають можливість тонкої настройки під себе.

Отже, що ми можемо помістити в автозавантаження?
Що ще?
Як ми можемо використовувати таку функціональність?
Але навіщо використовувати важкий Java-софт там, де потрібне дію можна виконати за допомогою простенького скрипта, який не займає зайвого пам'яті і може бути легко перенесений на інший пристрій?