Sistem pemulihan mencakup beberapa hook untuk menyisipkan kode khusus perangkat sehingga update OTA juga dapat mengupdate bagian perangkat selain sistem Android (misalnya, baseband atau prosesor radio).
Bagian dan contoh berikut menyesuaikan perangkat tardis yang diproduksi oleh vendor yoyodyne.
Peta partisi
Mulai Android 2.3, platform ini mendukung perangkat flash eMMC dan sistem file ext4 yang berjalan di perangkat tersebut. Kernel ini juga mendukung perangkat flash Memory Technology Device (MTD) dan sistem file yaffs2 dari rilis yang lebih lama.
File peta partisi ditentukan oleh TARGET_RECOVERY_FSTAB; file ini digunakan oleh biner pemulihan dan alat pembuatan paket. Anda dapat menentukan nama file peta di TARGET_RECOVERY_FSTAB di BoardConfig.mk.
Contoh file peta partisi mungkin terlihat seperti ini:
device/yoyodyne/tardis/recovery.fstab
# mount point fstype device [device2] [options (3.0+ only)] /sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 /cache yaffs2 cache /misc mtd misc /boot mtd boot /recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery /system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 /data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata
Dengan pengecualian /sdcard
, yang bersifat opsional, semua titik pemasangan dalam contoh ini harus ditentukan (perangkat juga dapat menambahkan partisi ekstra). Ada lima jenis sistem file yang didukung:
- yaffs2
-
Sistem file yaffs2 di atas perangkat flash MTD. "device" harus berupa nama partisi MTD
dan harus muncul di
/proc/mtd
. - mtd
-
Partisi MTD mentah, digunakan untuk partisi yang dapat di-boot seperti boot dan pemulihan. MTD sebenarnya tidak dipasang, tetapi titik pemasangan digunakan sebagai kunci untuk menemukan partisi. "device"
harus berupa nama partisi MTD di
/proc/mtd
. - ext4
- Sistem file ext4 di atas perangkat flash eMMc. "device" harus berupa jalur perangkat blok.
- emmc
- Perangkat blok eMMC mentah, digunakan untuk partisi yang dapat di-boot seperti boot dan pemulihan. Serupa dengan jenis mtd, eMMC tidak pernah benar-benar dipasang, tetapi string titik pemasangan digunakan untuk menemukan perangkat dalam tabel.
- vfat
-
Sistem file FAT di atas perangkat blok, biasanya untuk penyimpanan eksternal seperti kartu SD. Perangkat adalah perangkat blok; device2 adalah perangkat blok kedua yang coba dipasang oleh sistem jika pemasangan perangkat utama gagal (untuk kompatibilitas dengan kartu SD yang mungkin atau mungkin tidak diformat dengan tabel partisi).
Semua partisi harus di-mount di direktori root (yaitu, nilai titik pemasangan harus dimulai dengan garis miring dan tidak memiliki garis miring lain). Pembatasan ini hanya berlaku untuk pemasangan sistem file dalam pemulihan; sistem utama bebas memasangnya di mana saja. Direktori
/boot
,/recovery
, dan/misc
harus berupa jenis mentah (mtd atau emmc), sedangkan direktori/system
,/data
,/cache
, dan/sdcard
(jika tersedia) harus berupa jenis sistem file (yaffs2, ext4, atau vfat).
Mulai Android 3.0, file recovery.fstab mendapatkan kolom opsional tambahan, options. Saat ini, satu-satunya opsi yang ditentukan adalah panjang , yang memungkinkan Anda menentukan panjang partisi secara eksplisit. Panjang ini digunakan saat memformat ulang partisi (misalnya, untuk partisi userdata selama operasi penghapusan data/reset ke setelan pabrik, atau untuk partisi sistem selama penginstalan paket OTA penuh). Jika nilai panjang negatif, maka ukuran yang akan diformat diambil dengan menambahkan nilai panjang ke ukuran partisi sebenarnya. Misalnya, menyetel "length=-16384" berarti 16k terakhir dari partisi tersebut tidak akan ditimpa saat partisi tersebut diformat ulang. Hal ini mendukung fitur seperti enkripsi partisi userdata (tempat metadata enkripsi disimpan di akhir partisi yang tidak boleh ditimpa).
Catatan: Kolom device2 dan options bersifat opsional, sehingga menimbulkan ambiguitas dalam penguraian. Jika entri di kolom keempat pada baris dimulai dengan karakter ‘/' , entri tersebut dianggap sebagai entri device2; jika entri tidak dimulai dengan karakter ‘/' , entri tersebut dianggap sebagai kolom options.
Animasi booting
Produsen perangkat dapat menyesuaikan animasi yang ditampilkan saat perangkat Android sedang melakukan booting. Untuk melakukannya, buat file .zip yang disusun dan ditempatkan sesuai dengan spesifikasi dalam format bootanimation.
Untuk perangkat Android Things, Anda dapat mengupload file yang di-zip di konsol Android Things agar gambar disertakan dalam produk yang dipilih.
Catatan: Gambar ini harus memenuhi pedoman merek Android. Untuk panduan merek, lihat bagian Android di Hub Pemasaran Partner.
UI Pemulihan
Untuk mendukung perangkat dengan hardware yang tersedia berbeda (tombol fisik, LED, layar, dll.), Anda dapat menyesuaikan antarmuka pemulihan untuk menampilkan status dan mengakses fitur tersembunyi yang dioperasikan secara manual untuk setiap perangkat.
Tujuan Anda adalah membuat library statis kecil dengan beberapa objek C++ untuk menyediakan
fungsi khusus perangkat. File
bootable/recovery/default_device.cpp
digunakan secara default, dan merupakan
titik awal yang baik untuk disalin saat menulis versi file ini untuk perangkat Anda.
Catatan: Anda mungkin melihat pesan yang bertuliskan No Command di sini. Untuk mengganti teks, tahan tombol daya sambil menekan tombol naikkan volume. Jika perangkat Anda tidak memiliki kedua tombol, tekan lama tombol apa pun untuk mengganti teks.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
Fungsi header dan item
Class Device memerlukan fungsi untuk menampilkan header dan item yang muncul di menu pemulihan tersembunyi. Header menjelaskan cara mengoperasikan menu (yaitu kontrol untuk mengubah/memilih item yang disorot).
static const char* HEADERS[] = { "Volume up/down to move highlight;", "power button to select.", "", NULL }; static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", NULL };
Catatan: Baris panjang dipangkas (tidak dilipat), jadi perhatikan lebar layar perangkat Anda.
Sesuaikan CheckKey
Selanjutnya, tentukan penerapan RecoveryUI perangkat Anda. Contoh ini mengasumsikan perangkat
tardis memiliki layar, sehingga Anda dapat mewarisi dari
implementasi ScreenRecoveryUI bawaan (lihat petunjuk untuk
perangkat tanpa layar). Satu-satunya fungsi untuk
menyesuaikan dari ScreenRecoveryUI adalah CheckKey()
, yang melakukan penanganan
kunci asinkron awal:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
Konstanta KEY
Konstanta KEY_* ditentukan dalam linux/input.h
. CheckKey()
dipanggil terlepas dari apa yang terjadi dalam pemulihan: saat menu dinonaktifkan, saat menu diaktifkan, selama penginstalan paket, selama penghapusan data pengguna, dll. Metode ini dapat menampilkan salah satu dari empat konstanta:
- TOGGLE. Aktifkan atau nonaktifkan tampilan menu dan/atau log teks
- REBOOT. Segera mulai ulang perangkat
- ABAIKAN. Abaikan penekanan tombol ini
- ENQUEUE. Mengantrekan penekanan tombol ini untuk digunakan secara serentak (yaitu, oleh sistem menu pemulihan jika layar diaktifkan)
CheckKey()
dipanggil setiap kali peristiwa tombol ke bawah diikuti dengan peristiwa tombol ke atas untuk
tombol yang sama. (Urutan peristiwa A-down B-down B-up A-up hanya menghasilkan
panggilan CheckKey(B)
.) CheckKey()
dapat memanggil
IsKeyPressed()
, untuk mengetahui apakah tombol lain sedang ditahan. (Dalam urutan peristiwa utama di atas, jika CheckKey(B)
memanggil IsKeyPressed(A)
, CheckKey(B)
akan menampilkan nilai benar.)
CheckKey()
dapat mempertahankan status di class-nya; hal ini dapat berguna untuk mendeteksi urutan tombol. Contoh ini menunjukkan penyiapan yang sedikit lebih rumit: layar diaktifkan dengan
menahan tombol daya dan menekan tombol naikkan volume, dan perangkat dapat segera di-reboot dengan
menekan tombol daya lima kali berturut-turut (tanpa tombol lain di antaranya):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
ScreenRecoveryUI
Saat menggunakan gambar Anda sendiri (ikon error, animasi penginstalan, status progres) dengan
ScreenRecoveryUI, Anda dapat menyetel variabel animation_fps
untuk mengontrol kecepatan dalam
frame per detik (FPS) animasi.
Catatan: Skrip interlace-frames.py
saat ini memungkinkan Anda
menyimpan informasi animation_fps
dalam gambar itu sendiri. Pada versi Android sebelumnya, Anda harus menyetel animation_fps
sendiri.
Untuk menetapkan variabel animation_fps
, ganti
fungsi ScreenRecoveryUI::Init()
di subclass Anda. Tetapkan nilai, lalu panggil
fungsi parent Init()
untuk menyelesaikan inisialisasi. Nilai default (20 FPS)
sesuai dengan image pemulihan default; saat menggunakan image ini, Anda tidak perlu memberikan
fungsi Init()
. Untuk mengetahui detail tentang gambar, lihat
Gambar UI Pemulihan.
Class perangkat
Setelah Anda memiliki implementasi RecoveryUI, tentukan class perangkat Anda (subclass dari
class Device bawaan). Fungsi ini harus membuat satu instance class UI Anda dan menampilkannya
dari fungsi GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
Metode StartRecovery()
dipanggil di awal pemulihan, setelah UI diinisialisasi dan setelah argumen diuraikan, tetapi sebelum tindakan apa pun dilakukan. Implementasi default tidak melakukan apa pun, jadi Anda tidak perlu menyediakannya di subclass jika tidak ada yang perlu dilakukan:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
Menyediakan dan mengelola menu pemulihan
Sistem memanggil dua metode untuk mendapatkan daftar baris header dan daftar item. Dalam implementasi ini, fungsi menampilkan array statis yang ditentukan di bagian atas file:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
Selanjutnya, berikan fungsi HandleMenuKey()
, yang mengambil penekanan tombol dan visibilitas
menu saat ini, lalu memutuskan tindakan yang akan diambil:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
Metode ini mengambil kode tombol (yang sebelumnya telah diproses dan dimasukkan dalam antrean oleh
metode CheckKey()
objek UI), dan status visibilitas menu/log teks saat ini. Nilai yang ditampilkan adalah bilangan bulat. Jika nilainya 0 atau lebih tinggi, nilai tersebut dianggap sebagai
posisi item menu, yang dipanggil segera (lihat metode
InvokeMenuItem()
di bawah). Jika tidak, nilai dapat berupa salah satu konstanta yang telah ditentukan sebelumnya berikut:
- kHighlightUp. Memindahkan penyorotan menu ke item sebelumnya
- kHighlightDown. Memindahkan sorotan menu ke item berikutnya
- kInvokeItem. Memanggil item yang disoroti saat ini
- kNoAction. Tidak melakukan apa pun dengan penekanan tombol ini
Seperti yang tersirat oleh argumen yang terlihat, HandleMenuKey()
dipanggil meskipun menu tidak terlihat. Tidak seperti CheckKey()
, tidak dipanggil saat pemulihan sedang
melakukan sesuatu seperti menghapus data atau menginstal paket—fungsi ini hanya dipanggil saat pemulihan tidak ada aktivitas
dan menunggu input.
Mekanisme trackball
Jika perangkat Anda memiliki mekanisme input seperti trackball (membuat peristiwa input dengan jenis EV_REL
dan kode REL_Y), pemulihan akan menyintesis penekanan tombol KEY_UP dan KEY_DOWN setiap kali
perangkat input seperti trackball melaporkan gerakan pada sumbu Y. Yang perlu Anda lakukan adalah memetakan peristiwa KEY_UP dan
KEY_DOWN ke tindakan menu. Pemetaan ini tidak terjadi untuk
CheckKey()
, sehingga Anda tidak dapat menggunakan gerakan trackball sebagai pemicu untuk memulai ulang atau
mengganti tampilan.
Tombol pengubah
Untuk memeriksa apakah tombol ditekan sebagai pengubah, panggil metode IsKeyPressed()
objek UI Anda sendiri. Misalnya, di beberapa perangkat, menekan Alt-W dalam pemulihan akan memulai penghapusan data, baik menu terlihat atau tidak. Anda dapat menerapkan seperti ini:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
Catatan: Jika visible salah, tidak masuk akal untuk menampilkan nilai khusus yang memanipulasi menu (memindahkan sorotan, memanggil item yang disorot) karena pengguna tidak dapat melihat sorotan. Namun, Anda dapat menampilkan nilai jika diinginkan.
InvokeMenuItem
Selanjutnya, berikan metode InvokeMenuItem()
yang memetakan posisi bilangan bulat dalam array
item yang ditampilkan oleh GetMenuItems()
ke tindakan. Untuk array item dalam
contoh tardis, gunakan:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
Metode ini dapat menampilkan anggota enum BuiltinAction apa pun untuk memberi tahu sistem agar melakukan tindakan tersebut (atau anggota NO_ACTION jika Anda ingin sistem tidak melakukan apa pun). Di sinilah tempat untuk menyediakan fungsi pemulihan tambahan di luar yang ada di sistem: Tambahkan item untuk fungsi tersebut di menu Anda, jalankan di sini saat item menu tersebut dipanggil, dan tampilkan NO_ACTION sehingga sistem tidak melakukan apa pun lagi.
BuiltinAction berisi nilai berikut:
- NO_ACTION. Tidak melakukan apa pun.
- REBOOT. Keluar dari pemulihan dan mulai ulang perangkat seperti biasa.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Instal paket update dari berbagai tempat. Untuk mengetahui detailnya, lihat Memuat dari luar Play Store.
- WIPE_CACHE. Memformat ulang partisi cache saja. Konfirmasi tidak diperlukan karena ini relatif tidak berbahaya.
- WIPE_DATA. Memformat ulang partisi userdata dan cache, yang juga dikenal sebagai reset data ke setelan pabrik. Pengguna diminta untuk mengonfirmasi tindakan ini sebelum melanjutkan.
Metode terakhir, WipeData()
, bersifat opsional dan dipanggil setiap kali operasi penghapusan data dimulai (baik dari pemulihan melalui menu atau saat pengguna telah memilih untuk mereset data ke setelan pabrik dari sistem utama). Metode ini dipanggil sebelum partisi data dan cache pengguna dihapus. Jika perangkat Anda menyimpan data pengguna di tempat lain selain dua
partisi tersebut, Anda harus menghapusnya di sini. Anda harus menampilkan 0 untuk menunjukkan keberhasilan dan nilai lain untuk kegagalan, meskipun saat ini nilai yang ditampilkan diabaikan. Partisi data dan cache pengguna
dihapus, baik Anda menampilkan keberhasilan atau kegagalan.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Membuat perangkat
Terakhir, sertakan beberapa boilerplate di akhir file recovery_ui.cpp untuk
fungsi make_device()
yang membuat dan menampilkan instance class Perangkat Anda:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Membangun dan menautkan ke pemulihan perangkat
Setelah menyelesaikan file recovery_ui.cpp, bangun dan tautkan ke pemulihan di perangkat Anda. Di Android.mk, buat library statis yang hanya berisi file C++ ini:
device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery LOCAL_SRC_FILES := recovery_ui.cpp # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_tardis include $(BUILD_STATIC_LIBRARY)
Kemudian, dalam konfigurasi papan untuk perangkat ini, tentukan library statis Anda sebagai nilai TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
Gambar UI pemulihan
Antarmuka pengguna pemulihan terdiri dari gambar. Idealnya, pengguna tidak pernah berinteraksi dengan UI: Selama update normal, ponsel melakukan booting ke pemulihan, mengisi status progres penginstalan, dan melakukan booting kembali ke sistem baru tanpa input dari pengguna. Jika terjadi masalah update sistem, satu-satunya tindakan pengguna yang dapat dilakukan adalah menghubungi layanan pelanggan.
Antarmuka khusus gambar menghilangkan kebutuhan akan pelokalan. Namun, mulai Android 5.0, update dapat menampilkan string teks (misalnya, "Menginstal update sistem...") bersama dengan gambar. Untuk mengetahui detailnya, lihat Teks pemulihan yang dilokalkan.
Android 5.0 dan yang lebih baru
UI pemulihan Android 5.0 dan yang lebih baru menggunakan dua gambar utama: gambar error dan animasi menginstal.
![]() Gambar 1. icon_error.png |
![]() Gambar 2. icon_installing.png |
Animasi penginstalan ditampilkan sebagai satu gambar PNG dengan frame animasi yang berbeda-beda diselingi menurut baris (itulah sebabnya Gambar 2 tampak tertekan). Misalnya, untuk
animasi tujuh frame berukuran 200x200, buat satu gambar berukuran 200x1400 dengan frame pertama adalah baris 0, 7,
14, 21, ...; frame kedua adalah baris 1, 8, 15, 22, ...; dll. Gambar gabungan mencakup
chunk teks yang menunjukkan jumlah frame animasi dan jumlah frame per detik
(FPS). Alat bootable/recovery/interlace-frames.py
mengambil serangkaian frame input
dan menggabungkannya menjadi gambar komposit yang diperlukan untuk pemulihan.
Gambar default tersedia dalam berbagai kepadatan dan terletak di
bootable/recovery/res-$DENSITY/images
(misalnya,
bootable/recovery/res-hdpi/images
). Untuk menggunakan gambar statis selama penginstalan,
Anda hanya perlu memberikan gambar icon_installing.png dan menyetel jumlah frame dalam
animasi ke 0 (ikon error tidak beranimasi; ikon ini selalu berupa gambar statis).
Android 4.x dan yang lebih lama
UI pemulihan Android 4.x dan yang lebih lama menggunakan gambar error (ditampilkan di atas) dan animasi menginstal serta beberapa gambar overlay:
![]() Gambar 3. icon_installing.png |
![]() Gambar 4. icon-installing_overlay01.png |
![]() Gambar 5. icon_installing_overlay07.png |
Selama penginstalan, tampilan di layar dibuat dengan menggambar gambar icon_installing.png, lalu menggambar salah satu frame overlay di atasnya pada offset yang tepat. Di sini, kotak merah ditumpangkan untuk menandai tempat overlay ditempatkan di atas gambar dasar:
![]() Gambar 6. Menginstal frame animasi 1 (icon_installing.png + icon_installing_overlay01.png) |
![]() Gambar 7. Menginstal frame animasi 7 (icon_installing.png + icon_installing_overlay07.png) |
Frame berikutnya ditampilkan dengan menggambar hanya gambar overlay berikutnya di atas apa yang sudah ada; gambar dasar tidak digambar ulang.
Jumlah frame dalam animasi, kecepatan yang diinginkan, dan offset x dan y overlay
relatif terhadap dasar ditetapkan oleh variabel anggota class ScreenRecoveryUI. Saat menggunakan
gambar kustom, bukan gambar default, ganti metode Init()
di
subclass untuk mengubah nilai ini untuk gambar kustom Anda (untuk mengetahui detailnya, lihat
ScreenRecoveryUI). Skrip
bootable/recovery/make-overlay.py
dapat membantu mengonversi serangkaian frame gambar
ke dalam bentuk "gambar dasar + gambar overlay" yang diperlukan untuk pemulihan, termasuk penghitungan
offset yang diperlukan.
Gambar default terletak di bootable/recovery/res/images
. Untuk menggunakan gambar statis
selama penginstalan, Anda hanya perlu memberikan gambar icon_installing.png dan menetapkan jumlah
frame dalam animasi ke 0 (ikon error tidak beranimasi; selalu berupa gambar statis).
Teks pemulihan yang dilokalkan
Android 5.x menampilkan string teks (misalnya, "Menginstal update sistem...") bersama dengan gambar. Saat sistem utama melakukan booting ke pemulihan, sistem ini akan meneruskan lokalitas pengguna saat ini sebagai opsi command line ke pemulihan. Untuk setiap pesan yang ditampilkan, pemulihan mencakup gambar komposit kedua dengan string teks yang telah dirender sebelumnya untuk pesan tersebut di setiap lokalitas.
Contoh gambar string teks pemulihan:

Gambar 8. Teks yang dilokalkan untuk pesan pemulihan
Teks pemulihan dapat menampilkan pesan berikut:
- Menginstal update sistem...
- Kesalahan!
- Menghapus... (saat menghapus data/mereset ke setelan pabrik)
- Tidak ada perintah (saat pengguna melakukan booting ke pemulihan secara manual)
Aplikasi Android di bootable/recovery/tools/recovery_l10n/
merender pelokalan
pesan dan membuat gambar komposit. Untuk mengetahui detail tentang penggunaan aplikasi ini, lihat
komentar di
bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
Saat pengguna melakukan booting ke pemulihan secara manual, lokal mungkin tidak tersedia dan tidak ada teks yang ditampilkan. Jangan jadikan pesan teks sebagai hal yang penting dalam proses pemulihan.
Catatan: Antarmuka tersembunyi yang menampilkan pesan log dan memungkinkan pengguna memilih tindakan dari menu hanya tersedia dalam bahasa Inggris.
Status progres
Status progres dapat muncul di bawah gambar (atau animasi) utama. Status progres dibuat dengan menggabungkan dua gambar input, yang harus berukuran sama:

Gambar 9. progress_empty.png

Gambar 10. progress_fill.png
Ujung kiri gambar isi ditampilkan di samping ujung kanan gambar kosong untuk membuat status progres. Posisi batas antara dua gambar diubah untuk menunjukkan progres. Misalnya, dengan pasangan gambar input di atas, tampilan:

Gambar 11. Status progres di 1%>

Gambar 12. Status progres di 10%

Gambar 13. Status progres 50%
Anda dapat menyediakan versi khusus perangkat dari gambar ini dengan menempatkannya ke (dalam contoh ini) device/yoyodyne/tardis/recovery/res/images
. Nama file harus cocok dengan yang tercantum di atas; saat file ditemukan di direktori tersebut, sistem build akan menggunakannya daripada gambar default yang sesuai. Hanya PNG dalam format RGB atau
RGBA dengan kedalaman warna 8 bit yang didukung.
Catatan: Di Android 5.x, jika lokalitas diketahui untuk pemulihan dan merupakan bahasa kanan-ke-kiri (RTL) (Arab, Ibrani, dll.), status progres akan diisi dari kanan ke kiri.
Perangkat tanpa layar
Tidak semua perangkat Android memiliki layar. Jika perangkat Anda adalah peralatan tanpa layar atau memiliki antarmuka khusus audio, Anda mungkin perlu melakukan penyesuaian UI pemulihan yang lebih ekstensif. Daripada membuat subclass ScreenRecoveryUI, buat subclass class induknya, RecoveryUI, secara langsung.
RecoveryUI memiliki metode untuk menangani operasi UI tingkat bawah seperti "mengalihkan tampilan",
"mengupdate status progres", "menampilkan menu", "mengubah pilihan menu", dll. Anda dapat mengganti
metode ini untuk menyediakan antarmuka yang sesuai untuk perangkat Anda. Mungkin perangkat Anda memiliki LED yang
dapat menggunakan warna atau pola berkedip yang berbeda untuk menunjukkan status, atau mungkin Anda dapat memutar
audio. (Mungkin Anda tidak ingin mendukung menu atau mode "tampilan teks" sama sekali; Anda dapat
mencegah akses ke menu atau mode tersebut dengan penerapan CheckKey()
dan
HandleMenuKey()
yang tidak pernah mengalihkan tampilan atau memilih item
menu. Dalam hal ini, banyak metode RecoveryUI yang perlu Anda berikan dapat berupa stub kosong
saja.)
Lihat bootable/recovery/ui.h
untuk deklarasi RecoveryUI guna melihat metode yang harus Anda dukung. RecoveryUI bersifat abstrak—beberapa metodenya bersifat virtual murni dan harus disediakan oleh
subclass—tetapi berisi kode untuk melakukan pemrosesan input utama. Anda juga dapat menggantinya jika perangkat Anda tidak memiliki tombol atau Anda ingin memprosesnya secara berbeda.
Updater
Anda dapat menggunakan kode khusus perangkat dalam penginstalan paket update dengan menyediakan fungsi ekstensi Anda sendiri yang dapat dipanggil dari dalam skrip updater Anda. Berikut adalah contoh fungsi untuk perangkat tardis:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
Setiap fungsi ekstensi memiliki tanda tangan yang sama. Argumennya adalah nama yang digunakan untuk
memanggil fungsi, cookie State*
, jumlah argumen masuk, dan
array pointer Expr*
yang merepresentasikan argumen. Nilai yang ditampilkan adalah
Value*
yang baru dialokasikan.
Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); }
Argumen Anda belum dievaluasi pada saat fungsi Anda dipanggil—logika
fungsi Anda menentukan argumen mana yang dievaluasi dan berapa kali. Dengan demikian, Anda dapat menggunakan fungsi
ekstensi untuk menerapkan struktur kontrol Anda sendiri. Call Evaluate()
untuk mengevaluasi
argumen Expr*
, yang menampilkan Value*
. Jika Evaluate()
menampilkan NULL, Anda harus membebaskan semua resource yang Anda pegang dan segera menampilkan NULL (tindakan ini
menyebarkan pembatalan ke atas stack edify). Jika tidak, Anda akan mengambil kepemilikan Nilai yang ditampilkan dan
bertanggung jawab untuk memanggil
FreeValue()
pada nilai tersebut.
Misalkan fungsi memerlukan dua argumen: key bernilai string dan image bernilai blob. Anda dapat membaca argumen seperti ini:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
Memeriksa NULL dan membebaskan argumen yang dievaluasi sebelumnya dapat menjadi membosankan untuk beberapa
argumen. Fungsi ReadValueArgs()
dapat mempermudah hal ini. Daripada kode
di atas, Anda dapat menulis kode ini:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
ReadValueArgs()
tidak melakukan pemeriksaan jenis, jadi Anda harus melakukannya di sini; lebih
nyaman melakukannya dengan satu pernyataan if dengan mengorbankan pesan error yang kurang
spesifik saat gagal. Namun, ReadValueArgs()
menangani evaluasi
setiap argumen dan membebaskan semua argumen yang dievaluasi sebelumnya (serta menetapkan
pesan error yang berguna) jika salah satu evaluasi gagal. Anda dapat menggunakan
fungsi praktis ReadValueVarArgs()
untuk mengevaluasi sejumlah variabel
argumen (fungsi ini menampilkan array Value*
).
Setelah mengevaluasi argumen, lakukan tugas fungsi:
// key->data is a NUL-terminated string // image->data and image->size define a block of binary data // // ... some device-specific magic here to // reprogram the tardis using those two values ...
Nilai yang ditampilkan harus berupa objek Value*
; kepemilikan objek ini akan diteruskan ke
pemanggil. Pemanggil mengambil kepemilikan atas data apa pun yang ditunjukkan oleh
Value*
—khususnya datamember.
Dalam hal ini, Anda ingin menampilkan nilai benar atau salah untuk menunjukkan keberhasilan. Ingatlah konvensi bahwa string kosong adalah false dan semua string lainnya adalah true. Anda
harus mengalokasikan objek Value dengan salinan string konstanta yang dialokasikan dengan malloc untuk ditampilkan, karena
pemanggil akan free()
keduanya. Jangan lupa untuk memanggil FreeValue()
pada
objek yang Anda dapatkan dengan mengevaluasi argumen Anda.
FreeValue(key); FreeValue(image); Value* result = malloc(sizeof(Value)); result->type = VAL_STRING; result->data = strdup(successful ? "t" : ""); result->size = strlen(result->data); return result; }
Fungsi praktis StringValue()
membungkus string ke dalam objek Value baru.
Gunakan untuk menulis kode di atas dengan lebih ringkas:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Untuk mengaitkan fungsi ke interpreter edify, berikan fungsi
Register_foo
dengan foo adalah nama library statis yang berisi
kode ini. Panggil RegisterFunction()
untuk mendaftarkan setiap fungsi ekstensi. Menurut
konvensi, beri nama fungsi khusus perangkat device.whatever
untuk menghindari
konflik dengan fungsi bawaan yang akan ditambahkan pada masa mendatang.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
Sekarang Anda dapat mengonfigurasi makefile untuk membuat library statis dengan kode Anda. (Ini adalah makefile yang sama yang digunakan untuk menyesuaikan UI pemulihan di bagian sebelumnya; perangkat Anda mungkin memiliki kedua library statis yang ditentukan di sini.)
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
Nama pustaka statis harus sama dengan nama fungsi Register_libname
yang ada di dalamnya.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Terakhir, konfigurasi build pemulihan untuk menarik library Anda. Tambahkan library Anda ke
TARGET_RECOVERY_UPDATER_LIBS (yang mungkin berisi beberapa library; semuanya akan didaftarkan).
Jika kode Anda bergantung pada library statis lain yang bukan merupakan ekstensi edify (yaitu,
tidak memiliki fungsi Register_libname
), Anda dapat mencantumkannya di
TARGET_RECOVERY_UPDATER_EXTRA_LIBS untuk menautkannya ke updater tanpa memanggil
fungsi pendaftaran (yang tidak ada). Misalnya, jika kode khusus perangkat Anda ingin menggunakan
zlib untuk mendekompresi data, Anda akan menyertakan libz di sini.
device/yoyodyne/tardis/BoardConfig.mk
[...] # add device-specific extensions to the updater binary TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=
Skrip updater dalam paket OTA Anda kini dapat memanggil fungsi Anda seperti fungsi lainnya. Untuk memprogram ulang
perangkat tardis Anda, skrip update mungkin berisi:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Hal ini menggunakan
versi satu argumen dari fungsi bawaan package_extract_file()
,
yang menampilkan konten file yang diekstrak dari paket update sebagai blob untuk menghasilkan
argumen kedua ke fungsi ekstensi baru.
Pembuatan paket OTA
Komponen terakhir adalah membuat alat pembuatan paket OTA mengetahui data khusus perangkat Anda dan memunculkan skrip updater yang menyertakan panggilan ke fungsi ekstensi Anda.
Pertama, buat sistem build mengetahui blob data khusus perangkat. Dengan asumsi file data Anda ada di device/yoyodyne/tardis/tardis.dat
, deklarasikan hal berikut di AndroidBoard.mk perangkat Anda:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Anda juga dapat menempatkannya di Android.mk, tetapi harus dilindungi oleh pemeriksaan perangkat, karena semua file Android.mk di tree dimuat, apa pun perangkat yang di-build. (Jika pohon Anda menyertakan beberapa perangkat, Anda hanya ingin file tardis.dat ditambahkan saat membangun perangkat tardis.)
device/yoyodyne/tardis/Android.mk
[...] # an alternative to specifying it in AndroidBoard.mk ifeq (($TARGET_DEVICE),tardis) $(call add-radio-file,tardis.dat) endif
File ini disebut file radio karena alasan historis; file ini mungkin tidak ada hubungannya dengan radio perangkat (jika ada). File ini hanyalah blob data buram yang disalin sistem build ke dalam .zip target-files yang digunakan oleh alat pembuatan OTA. Saat Anda melakukan build, tardis.dat akan
disimpan di target-files.zip sebagai RADIO/tardis.dat
. Anda dapat memanggil
add-radio-file
beberapa kali untuk menambahkan file sebanyak yang Anda inginkan.
Modul Python
Untuk memperluas alat rilis, tulis modul Python (harus diberi nama releasetools.py) yang dapat dipanggil oleh alat jika ada. Contoh:
device/yoyodyne/tardis/releasetools.py
import common def FullOTA_InstallEnd(info): # copy the data into the package. tardis_dat = info.input_zip.read("RADIO/tardis.dat") common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
Fungsi terpisah menangani kasus pembuatan paket OTA inkremental. Untuk contoh ini, misalkan Anda perlu memprogram ulang tardis hanya jika file tardis.dat telah berubah di antara dua build.
def IncrementalOTA_InstallEnd(info): # copy the data into the package. source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") if source_tardis_dat == target_tardis_dat: # tardis.dat is unchanged from previous build; no # need to reprogram it return # include the new tardis.dat in the OTA package common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
Fungsi modul
Anda dapat menyediakan fungsi berikut dalam modul (hanya terapkan fungsi yang Anda butuhkan).
FullOTA_Assertions()
- Dipanggil di dekat awal pembuatan OTA penuh. Ini adalah tempat yang tepat untuk memancarkan pernyataan tentang status perangkat saat ini. Jangan mengeluarkan perintah skrip yang membuat perubahan pada perangkat.
FullOTA_InstallBegin()
- Dipanggil setelah semua pernyataan tentang status perangkat berhasil, tetapi sebelum perubahan dilakukan. Anda dapat mengeluarkan perintah untuk update khusus perangkat yang harus dijalankan sebelum hal lain di perangkat diubah.
FullOTA_InstallEnd()
- Dipanggil di akhir pembuatan skrip, setelah perintah skrip untuk memperbarui partisi boot dan sistem dikeluarkan. Anda juga dapat mengeluarkan perintah tambahan untuk update khusus perangkat.
IncrementalOTA_Assertions()
-
Mirip dengan
FullOTA_Assertions()
, tetapi dipanggil saat membuat paket update inkremental. IncrementalOTA_VerifyBegin()
- Dipanggil setelah semua pernyataan tentang status perangkat berhasil, tetapi sebelum perubahan dilakukan. Anda dapat mengeluarkan perintah untuk update khusus perangkat yang harus dijalankan sebelum hal lain di perangkat diubah.
IncrementalOTA_VerifyEnd()
- Dipanggil di akhir fase verifikasi, saat skrip telah selesai mengonfirmasi bahwa file yang akan diubah memiliki konten awal yang diharapkan. Pada titik ini, tidak ada yang diubah di perangkat. Anda juga dapat memancarkan kode untuk verifikasi tambahan khusus perangkat.
IncrementalOTA_InstallBegin()
- Dipanggil setelah file yang akan di-patch telah diverifikasi memiliki status sebelum yang diharapkan, tetapi sebelum perubahan dilakukan. Anda dapat mengeluarkan perintah untuk update khusus perangkat yang harus dijalankan sebelum hal lain di perangkat diubah.
IncrementalOTA_InstallEnd()
- Mirip dengan paket OTA lengkapnya, fungsi ini dipanggil di akhir pembuatan skrip, setelah perintah skrip untuk mengupdate partisi boot dan sistem dikeluarkan. Anda juga dapat mengeluarkan perintah tambahan untuk update khusus perangkat.
Catatan: Jika perangkat kehabisan daya, penginstalan OTA dapat dimulai ulang dari awal. Bersiaplah untuk menangani perangkat yang telah menjalankan perintah ini, sepenuhnya atau sebagian.
Meneruskan fungsi ke objek info
Teruskan fungsi ke satu objek info yang berisi berbagai item berguna:
-
info.input_zip. (Hanya OTA Penuh) Objek
zipfile.ZipFile
untuk file .zip target input. -
info.source_zip. (Hanya OTA inkremental) Objek
zipfile.ZipFile
untuk file target .zip sumber (build sudah ada di perangkat saat paket inkremental sedang diinstal). -
info.target_zip. (Hanya OTA inkremental) Objek
zipfile.ZipFile
untuk target file .zip (build yang ditempatkan paket inkremental di perangkat). -
info.output_zip. Paket sedang dibuat; objek
zipfile.ZipFile
dibuka untuk penulisan. Gunakan common.ZipWriteStr(info.output_zip, filename, data) untuk menambahkan file ke paket. -
info.script. Objek skrip yang dapat Anda tambahkan perintahnya. Panggil
info.script.AppendExtra(script_text)
untuk menghasilkan teks ke dalam skrip. Pastikan teks output diakhiri dengan titik koma agar tidak bertabrakan dengan perintah yang dikeluarkan setelahnya.
Untuk mengetahui detail tentang objek info, lihat dokumentasi Python Software Foundation untuk arsip ZIP.
Menentukan lokasi modul
Tentukan lokasi skrip releasetools.py perangkat Anda di file BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Jika TARGET_RELEASETOOLS_EXTENSIONS tidak ditetapkan, defaultnya adalah
direktori $(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
dalam contoh ini). Sebaiknya tentukan lokasi skrip releasetools.py secara eksplisit.
Saat membangun perangkat tardis, skrip releasetools.py disertakan dalam file .zip target-files (META/releasetools.py
).
Saat Anda menjalankan alat rilis (img_from_target_files
atau
ota_from_target_files
), skrip releasetools.py dalam .zip target-files, jika
ada, lebih diutamakan daripada yang ada dari hierarki sumber Android. Anda juga dapat menentukan jalur ke ekstensi khusus perangkat secara eksplisit dengan opsi -s
(atau --device_specific
), yang memiliki prioritas tertinggi. Hal ini memungkinkan Anda
memperbaiki error dan membuat perubahan di ekstensi releasetools serta menerapkan perubahan tersebut ke file target lama.
Sekarang, saat Anda menjalankan ota_from_target_files
, alat ini akan otomatis mengambil
modul khusus perangkat dari file .zip target_files dan menggunakannya saat membuat paket
OTA:
./build/make/tools/releasetools/ota_from_target_files \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
Atau, Anda dapat menentukan ekstensi khusus perangkat saat menjalankan
ota_from_target_files
.
./build/make/tools/releasetools/ota_from_target_files \
-s device/yoyodyne/tardis \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
Catatan: Untuk mengetahui daftar lengkap opsi, lihat komentar ota_from_target_files
di build/make/tools/releasetools/ota_from_target_files
.
Mekanisme sideload
Pemulihan memiliki mekanisme sideloading untuk menginstal paket update secara manual tanpa mendownloadnya melalui jaringan nirkabel oleh sistem utama. Sideload berguna untuk men-debug atau membuat perubahan pada perangkat yang sistem utamanya tidak dapat di-boot.
Secara historis, sideloading telah dilakukan dengan memuat paket dari kartu SD perangkat; dalam kasus perangkat yang tidak dapat melakukan booting, paket dapat dimasukkan ke kartu SD menggunakan komputer lain, lalu kartu SD dimasukkan ke perangkat. Untuk mengakomodasi perangkat Android tanpa penyimpanan eksternal yang dapat dilepas, pemulihan mendukung dua mekanisme tambahan untuk sideloading: memuat paket dari partisi cache, dan memuatnya melalui USB menggunakan adb.
Untuk memanggil setiap mekanisme sideload, metode Device::InvokeMenuItem()
perangkat Anda
dapat menampilkan nilai BuiltinAction berikut:
-
APPLY_EXT. Melakukan sideload paket update dari penyimpanan eksternal (direktori
/sdcard
). recovery.fstab Anda harus menentukan titik pemasangan/sdcard
. Hal ini tidak dapat digunakan di perangkat yang mengemulasi kartu SD dengan symlink ke/data
(atau mekanisme serupa lainnya)./data
biasanya tidak tersedia untuk pemulihan karena mungkin dienkripsi. UI pemulihan menampilkan menu file .zip di/sdcard
dan memungkinkan pengguna memilih salah satunya. -
APPLY_CACHE. Mirip dengan memuat paket dari
/sdcard
, kecuali direktori/cache
(yang selalu tersedia untuk pemulihan) digunakan sebagai gantinya. Dari sistem reguler,/cache
hanya dapat ditulis oleh pengguna yang memiliki hak istimewa, dan jika perangkat tidak dapat di-boot, direktori/cache
tidak dapat ditulis sama sekali (yang membuat mekanisme ini memiliki utilitas terbatas). -
APPLY_ADB_SIDELOAD. Mengizinkan pengguna mengirim paket ke perangkat melalui kabel USB dan
alat pengembangan adb. Saat mekanisme ini dipanggil, pemulihan akan memulai versi mini daemon adbd-nya sendiri agar adb di komputer host yang terhubung dapat berkomunikasi dengannya. Versi mini
ini hanya mendukung satu perintah:
adb sideload filename
. File bernama dikirim dari komputer host ke perangkat, yang kemudian memverifikasi dan menginstalnya seperti jika file tersebut ada di penyimpanan lokal.
Beberapa hal yang perlu diperhatikan:
- Hanya transfer USB yang didukung.
-
Jika pemulihan Anda menjalankan adbd secara normal (biasanya benar untuk build userdebug dan eng), adbd akan dimatikan saat perangkat dalam mode sideload adb dan akan dimulai ulang saat sideload adb selesai menerima paket. Saat dalam mode sideload adb, tidak ada perintah adb selain
sideload
yang berfungsi (logcat
,reboot
,push
,pull
,shell
, dll. semuanya gagal). -
Anda tidak dapat keluar dari mode sideload adb di perangkat. Untuk membatalkan, Anda dapat mengirim
/dev/null
(atau apa pun yang bukan paket yang valid) sebagai paket, dan kemudian perangkat akan gagal memverifikasinya dan menghentikan prosedur penginstalan. MetodeCheckKey()
implementasi RecoveryUI akan terus dipanggil untuk penekanan tombol, sehingga Anda dapat memberikan urutan tombol yang memulai ulang perangkat dan berfungsi dalam mode sideload adb.