Sistem pemulihan menyertakan 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. Fitur ini juga mendukung perangkat flash Memory Technology Device (MTD) dan sistem file yaffs2 dari rilis 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 tambahan). 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, yang digunakan untuk partisi yang dapat di-booting seperti booting dan pemulihan. MTD sebenarnya
tidak di-mount, 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, yang digunakan untuk partisi yang dapat di-booting seperti booting dan pemulihan. Serupa dengan jenis mtd, eMMc tidak pernah benar-benar dipasang, tetapi string titik pemasangan digunakan untuk menemukan perangkat dalam tabel.
- vfat
-
File sistem FAT di atas perangkat blok, biasanya untuk penyimpanan eksternal seperti kartu SD. Perangkat
adalah perangkat blok; device2 adalah perangkat blok kedua yang dicoba 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 dipasang di direktori root (yaitu, nilai titik pemasangan harus diawali dengan garis miring dan tidak memiliki garis miring lainnya). 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 length , yang memungkinkan Anda menentukan durasi partisi secara eksplisit. Panjang ini digunakan saat memformat ulang partisi (misalnya, untuk partisi userdata selama operasi penghapusan data/reset pabrik, atau untuk partisi sistem selama penginstalan paket OTA lengkap). Jika nilai panjang negatif, ukuran yang akan diformat diambil dengan menambahkan nilai panjang ke ukuran partisi yang sebenarnya. Misalnya, setelan "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 menyebabkan 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 memiliki kemampuan untuk menyesuaikan animasi yang ditampilkan saat perangkat Android melakukan booting. Untuk melakukannya, buat file .zip yang diatur dan ditempatkan sesuai dengan spesifikasi dalam format animasi booting.
Untuk perangkat Android Things, Anda dapat mengupload file zip di konsol Android Things agar gambar disertakan dalam produk yang dipilih.
Catatan: Gambar ini harus memenuhi pedoman brand 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.
Sasaran Anda adalah mem-build 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 mengalihkan teks, tahan tombol daya saat Anda menekan tombol naikkan volume. Jika perangkat Anda tidak memiliki kedua tombol tersebut, tekan lama tombol apa pun untuk beralih 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 Perangkat 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 ditandai).
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 akan terpotong (tidak digabungkan), jadi perhatikan lebar layar perangkat Anda.
Menyesuaikan 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 yang
dapat disesuaikan 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 di linux/input.h
. CheckKey()
dipanggil apa pun yang terjadi di bagian pemulihan lainnya: saat menu dinonaktifkan, saat
diaktifkan, selama penginstalan paket, selama penghapusan data pengguna, dll. Fungsi ini dapat menampilkan salah satu dari empat
konstanta:
- TOGGLE. Mengaktifkan atau menonaktifkan tampilan menu dan/atau log teks
- MULAI ULANG. Mulai ulang perangkat segera
- IGNORE. Abaikan penekanan tombol ini
- ENQUEUE. Antrekan penekanan tombol ini untuk digunakan secara sinkron (yaitu, oleh sistem menu pemulihan jika layar diaktifkan)
CheckKey()
dipanggil setiap kali peristiwa tombol ditekan diikuti dengan peristiwa tombol dilepaskan untuk
tombol yang sama. (Urutan peristiwa A-down B-down B-up A-up hanya menghasilkan
CheckKey(B)
yang dipanggil.) CheckKey()
dapat memanggil
IsKeyPressed()
, untuk mengetahui apakah tombol lain sedang ditahan. (Dalam urutan
peristiwa utama di atas, jika CheckKey(B)
memanggil IsKeyPressed(A)
,
nilai yang ditampilkan akan bernilai benar.)
CheckKey()
dapat mempertahankan status di class-nya; hal ini dapat berguna untuk mendeteksi
urutan kunci. Contoh ini menunjukkan penyiapan yang sedikit lebih kompleks: layar diaktifkan dengan
menahan tombol daya dan menekan tombol volume naik, dan perangkat dapat segera dimulai ulang dengan
menekan tombol daya lima kali berturut-turut (tanpa tombol lain yang mengganggu):
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 menetapkan 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 menetapkan 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 menyediakan
fungsi Init()
. Untuk mengetahui detail gambar, lihat
Gambar UI Pemulihan.
Class perangkat
Setelah Anda memiliki implementasi RecoveryUI, tentukan class perangkat (subclass dari
class Perangkat bawaan). Tindakan ini akan membuat satu instance class UI 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, sehingga Anda tidak perlu menyediakannya di
subclass jika tidak ada yang harus 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 ini 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 menggunakan penekanan tombol dan visibilitas menu
saat ini, dan 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 menggunakan kode kunci (yang sebelumnya telah diproses dan dimasukkan ke antrean oleh
metode CheckKey()
objek UI), dan status visibilitas log menu/teks
saat ini. Nilai yang ditampilkan adalah bilangan bulat. Jika nilainya 0 atau lebih tinggi, nilai tersebut akan diambil sebagai
posisi item menu, yang langsung dipanggil (lihat
metode InvokeMenuItem()
di bawah). Jika tidak, dapat berupa salah satu konstanta
standar berikut:
- kHighlightUp. Memindahkan sorotan menu ke item sebelumnya
- kHighlightDown. Memindahkan sorotan menu ke item berikutnya
- kInvokeItem. Memanggil item yang saat ini ditandai
- 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()
, metode ini tidak dipanggil saat pemulihan melakukan
sesuatu seperti menghapus total data atau menginstal paket—metode ini hanya dipanggil saat pemulihan tidak ada aktivitas
dan menunggu input.
Mekanisme trackball
Jika perangkat Anda memiliki mekanisme input seperti trackball (menghasilkan 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 di 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
mengalihkan tampilan.
Tombol pengubah
Untuk memeriksa tombol yang ditahan sebagai pengubah, panggil metode IsKeyPressed()
objek UI Anda sendiri. Misalnya, di beberapa perangkat, menekan Alt-W dalam mode pemulihan akan memulai wiped data, baik menu terlihat maupun tidak. Anda dapat menerapkannya 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 bernilai salah, tidak masuk akal untuk menampilkan nilai khusus yang memanipulasi menu (memindahkan sorotan, memanggil item yang ditandai) 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 untuk memberi tahu sistem agar mengambil tindakan tersebut (atau anggota NO_ACTION jika Anda ingin sistem tidak melakukan apa pun). Ini adalah tempat untuk menyediakan fungsi pemulihan tambahan di luar yang ada di sistem: Tambahkan item untuknya di menu Anda, jalankan di sini saat item menu tersebut dipanggil, dan tampilkan NO_ACTION sehingga sistem tidak melakukan hal lain.
BuiltinAction berisi nilai berikut:
- NO_ACTION. Tidak melakukan apa pun.
- MULAI ULANG. Keluar dari pemulihan dan mulai ulang perangkat seperti biasa.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Menginstal paket update dari berbagai tempat. Untuk mengetahui detailnya, lihat Sideload.
- WIPE_CACHE. Hanya memformat ulang partisi cache. Konfirmasi tidak diperlukan karena tindakan ini relatif tidak berbahaya.
- WIPE_DATA. Memformat ulang partisi userdata dan cache, yang juga dikenal sebagai reset data 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 memilih untuk melakukan
reset data pabrik dari sistem utama). Metode ini dipanggil sebelum data pengguna dan partisi cache wiped. Jika perangkat Anda menyimpan data pengguna di tempat 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 pengguna dan cache wiped, baik Anda menampilkan keberhasilan maupun 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(); }
Mem-build dan menautkan ke pemulihan perangkat
Setelah menyelesaikan file recovery_ui.cpp, build 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 board 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 akan 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...") beserta 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 penginstalan.
![]() Gambar 1. icon_error.png |
![]() Gambar 2. icon_installing.png |
Animasi penginstalan direpresentasikan sebagai satu gambar PNG dengan frame animasi
yang berbeda yang diselingi oleh baris (itulah sebabnya Gambar 2 tampak terhimpit). Misalnya, untuk
animasi tujuh frame 200x200, buat satu gambar 200x1400 dengan frame pertama adalah baris 0, 7,
14, 21, ...; frame kedua adalah baris 1, 8, 15, 22, ...; dll. Gambar gabungan menyertakan
bagian 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 dan digunakan oleh 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 menetapkan jumlah frame dalam
animasi ke 0 (ikon error tidak dianimasikan; 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 penginstalan 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 bingkai overlay di atasnya pada offset yang tepat. Di sini, kotak merah ditempatkan 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 gambar yang sudah ada; gambar dasar tidak digambar ulang.
Jumlah frame dalam animasi, kecepatan yang diinginkan, serta 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 bentuk "gambar dasar + gambar overlay" yang diperlukan oleh pemulihan, termasuk komputasi
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 dianimasikan; selalu berupa gambar statis).
Teks pemulihan yang dilokalkan
Android 5.x menampilkan string teks (misalnya, "Menginstal update sistem...") beserta image. Saat sistem utama melakukan booting ke pemulihan, sistem akan meneruskan lokalitas pengguna saat ini sebagai opsi command line ke pemulihan. Untuk setiap pesan yang ditampilkan, pemulihan menyertakan gambar komposit kedua dengan string teks yang 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 melakukan penghapusan data/reset ke setelan pabrik)
- Tidak ada perintah (saat pengguna mem-booting ke pemulihan secara manual)
Aplikasi Android di bootable/recovery/tools/recovery_l10n/
merender pelokalan
pesan dan membuat gambar gabungan. 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, lokalitas mungkin tidak tersedia dan tidak ada teks yang ditampilkan. Jangan membuat pesan teks menjadi penting untuk 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 utama (atau animasi). 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 kedua gambar diubah untuk menunjukkan progres. Misalnya, dengan pasangan gambar input di atas, tampilkan:

Gambar 11. Status progres di 1%>

Gambar 12. Status progres 10%

Gambar 13. Status progres 50%
Anda dapat menyediakan versi gambar khusus perangkat ini dengan menempatkannya ke (dalam
contoh ini) device/yoyodyne/tardis/recovery/res/images
. Nama file harus cocok dengan nama yang tercantum di atas; saat file ditemukan di direktori tersebut, sistem build akan menggunakannya sebagai preferensi untuk image 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 terisi dari kanan ke kiri.
Perangkat tanpa layar
Tidak semua perangkat Android memiliki layar. Jika perangkat Anda adalah perangkat headless atau memiliki antarmuka khusus audio, Anda mungkin perlu melakukan penyesuaian UI pemulihan yang lebih luas. Daripada membuat subclass ScreenRecoveryUI, subclass class induknya RecoveryUI secara langsung.
RecoveryUI memiliki metode untuk menangani operasi UI tingkat rendah seperti "mengubah tampilan",
"memperbarui status progres", "menampilkan menu", "mengubah pemilihan menu", dll. Anda dapat mengganti
metode ini untuk menyediakan antarmuka yang sesuai untuk perangkat Anda. Mungkin perangkat Anda memiliki LED yang
dapat Anda gunakan dengan 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 tersebut dengan implementasi CheckKey()
dan
HandleMenuKey()
yang tidak pernah mengaktifkan tampilan atau memilih item
menu. Dalam hal ini, banyak metode RecoveryUI yang perlu Anda berikan dapat berupa stub
kosong.)
Lihat bootable/recovery/ui.h
untuk deklarasi RecoveryUI guna melihat metode
yang harus Anda dukung. RecoveryUI bersifat abstrak—beberapa metode bersifat virtual murni dan harus disediakan oleh
subclass—tetapi berisi kode untuk melakukan pemrosesan input utama. Anda juga dapat menggantinya
jika perangkat tidak memiliki kunci 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. 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. Argumen adalah nama yang digunakan untuk memanggil fungsi, cookie State*
, jumlah argumen yang masuk, dan array pointer Expr*
yang mewakili 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 dipanggil—logika fungsi 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 mengosongkan resource apa pun yang Anda pegang dan segera menampilkan NULL (tindakan ini
akan menyebarkan pembatalan ke stack edify). Jika tidak, Anda akan memiliki Nilai yang ditampilkan dan
bertanggung jawab untuk memanggil
FreeValue()
pada akhirnya.
Misalkan fungsi memerlukan dua argumen: kunci bernilai string dan gambar 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 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
mudah untuk melakukannya dengan satu pernyataan if dengan mengorbankan pesan error yang sedikit kurang
spesifik saat gagal. Namun, ReadValueArgs()
menangani evaluasi
setiap argumen dan membebaskan semua argumen yang dievaluasi sebelumnya (serta menetapkan pesan error
yang berguna) jika ada evaluasi yang gagal. Anda dapat menggunakan
fungsi praktis ReadValueVarArgs()
untuk mengevaluasi jumlah argumen
variabel (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 data apa pun yang ditunjuk oleh
Value*
ini—khususnya datamember.
Dalam hal ini, Anda ingin menampilkan nilai true atau false untuk menunjukkan keberhasilan. Ingatkonvensi bahwa string kosong adalah false dan semua string lainnya adalah true. Anda
harus melakukan malloc pada objek Value dengan salinan string konstanta yang di-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()
menggabungkan 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 menghubungkan fungsi ke penafsir 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 mem-build library statis dengan kode Anda. (Ini adalah makefile yang sama dengan yang digunakan untuk menyesuaikan UI pemulihan di bagian sebelumnya; perangkat Anda mungkin memiliki 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 library statis harus cocok dengan nama
fungsi Register_libname
yang ada di dalamnya.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
Terakhir, konfigurasikan build pemulihan untuk mengambil 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 pendaftarannya (tidak ada). Misalnya, jika kode khusus perangkat 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 update di paket OTA Anda kini dapat memanggil fungsi Anda seperti yang lainnya. Untuk memprogram ulang
perangkat tardis, skrip update mungkin berisi:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. 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 menghasilkan skrip update yang menyertakan panggilan ke fungsi ekstensi Anda.
Pertama, buat sistem build mengetahui blob data khusus perangkat. Dengan asumsi file data
Anda berada 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 dalam hierarki dimuat, apa pun perangkat yang sedang di-build. (Jika hierarki Anda menyertakan beberapa perangkat, Anda hanya ingin file tardis.dat ditambahkan saat mem-build 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). Blob ini hanyalah blob data buram yang disalin sistem build ke
file target .zip yang digunakan oleh alat pembuatan OTA. Saat Anda melakukan build, tardis.dat
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 bernama 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 lengkap. Ini adalah tempat yang tepat untuk menampilkan pernyataan tentang status perangkat saat ini. Jangan tampilkan perintah skrip yang membuat perubahan pada perangkat.
FullOTA_InstallBegin()
- Dipanggil setelah semua pernyataan tentang status perangkat berlalu, 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 mengupdate partisi booting dan sistem telah 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 telah lulus, 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 selesai mengonfirmasi file yang akan disentuh memiliki konten awal yang diharapkan. Pada tahap ini, tidak ada yang diubah di perangkat. Anda juga dapat memunculkan kode untuk verifikasi khusus perangkat tambahan.
IncrementalOTA_InstallBegin()
- Dipanggil setelah file yang akan di-patch telah diverifikasi memiliki status sebelum yang diharapkan, tetapi sebelum perubahan apa pun dilakukan. Anda dapat mengeluarkan perintah untuk update khusus perangkat yang harus berjalan sebelum hal lain di perangkat diubah.
IncrementalOTA_InstallEnd()
- Serupa dengan paket OTA lengkapnya, ini dipanggil di akhir pembuatan skrip, setelah perintah skrip untuk mengupdate partisi booting dan sistem telah 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 tempat perintah ini telah dijalankan, sepenuhnya atau sebagian.
Meneruskan fungsi ke objek info
Teruskan fungsi ke satu objek info yang berisi berbagai item yang berguna:
-
info.input_zip. (Khusus OTA Lengkap) Objek
zipfile.ZipFile
untuk file target input .zip. -
info.source_zip. (Khusus OTA Inkremental) Objek
zipfile.ZipFile
untuk file target sumber .zip (build sudah ada di perangkat saat paket inkremental sedang diinstal). -
info.target_zip. (Khusus OTA Inkremental) Objek
zipfile.ZipFile
untuk file target target .zip (build yang ditempatkan paket inkremental di perangkat). -
info.output_zip. Paket sedang dibuat; objek
zipfile.ZipFile
dibuka untuk ditulis. Gunakan common.ZipWriteStr(info.output_zip, filename, data) untuk menambahkan file ke paket. -
info.script. Objek skrip yang dapat Anda tambahkan perintah. Panggil
info.script.AppendExtra(script_text)
untuk menghasilkan teks ke dalam skrip. Pastikan teks output diakhiri dengan titik koma sehingga tidak berjalan ke 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 dalam file BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Jika TARGET_RELEASETOOLS_EXTENSIONS tidak ditetapkan, setelan defaultnya adalah
direktori $(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
dalam contoh ini). Sebaiknya tentukan lokasi skrip releasetools.py secara eksplisit.
Saat mem-build perangkat tardis, skrip releasetools.py disertakan dalam file .zip
file target (META/releasetools.py
).
Saat Anda menjalankan alat rilis (img_from_target_files
atau
ota_from_target_files
), skrip releasetools.py dalam .zip file target, jika
ada, lebih disukai daripada skrip 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 pada ekstensi releasetools serta menerapkan perubahan tersebut ke file target
lama.
Sekarang, saat Anda menjalankan ota_from_target_files
, modul khusus perangkat akan otomatis diambil
dari file .zip target_files dan digunakan 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 sideload untuk menginstal paket update secara manual tanpa mendownloadnya secara over-the-air oleh sistem utama. Sideload berguna untuk men-debug atau membuat perubahan pada perangkat yang sistem utamanya tidak dapat di-booting.
Secara historis, sideload telah dilakukan melalui pemuatan paket dari kartu SD perangkat; dalam kasus perangkat yang tidak melakukan booting, paket dapat dimasukkan ke kartu SD menggunakan beberapa komputer lain, lalu kartu SD dimasukkan ke dalam perangkat. Untuk mengakomodasi perangkat Android tanpa penyimpanan eksternal yang dapat dilepas, pemulihan mendukung dua mekanisme tambahan untuk sideload: memuat paket dari partisi cache, dan memuat paket 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 beberapa mekanisme serupa)./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. Serupa 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 dengan hak istimewa, dan jika perangkat tidak dapat di-booting, direktori/cache
tidak dapat ditulis sama sekali (yang membuat mekanisme ini memiliki utilitas terbatas). -
APPLY_ADB_SIDELOAD. Memungkinkan pengguna mengirim paket ke perangkat melalui kabel USB dan
alat pengembangan adb. Saat mekanisme ini dipanggil, pemulihan akan memulai daemon adbd versi
mini-nya sendiri untuk memungkinkan adb di komputer host yang terhubung berkomunikasi dengannya. Versi mini
ini hanya mendukung satu perintah:
adb sideload filename
. File yang diberi nama dikirim dari mesin host ke perangkat, yang kemudian memverifikasi dan menginstalnya seolah-olah berada di penyimpanan lokal.
Beberapa hal yang perlu diperhatikan:
- Hanya transpor USB yang didukung.
-
Jika pemulihan Anda menjalankan adbd secara normal (biasanya berlaku untuk build userdebug dan eng), adbd akan
dinonaktifkan 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, lalu perangkat akan gagal memverifikasinya dan menghentikan prosedur penginstalan. MetodeCheckKey()
penerapan RecoveryUI akan terus dipanggil untuk penekanan tombol, sehingga Anda dapat memberikan urutan tombol yang memulai ulang perangkat dan berfungsi dalam mode sideload adb.