Sistem pemulihan menyertakan beberapa hook untuk memasukkan kode khusus perangkat sehingga OTA update juga dapat mengupdate bagian perangkat selain sistem Android (misalnya, baseband atau prosesor radio).
Bagian dan contoh berikut menyesuaikan perangkat tardis yang dihasilkan oleh yoyodyne.
Peta partisi
Pada Android 2.3, platform ini mendukung perangkat flash eMMc dan sistem file ext4 yang menjalankan di perangkat tersebut. Hal ini juga mendukung perangkat flash Memory Technology Device (MTD) dan yaffs2 sistem file dari rilis lama.
File peta partisi ditentukan oleh TARGET_Recovery_FSTAB; file ini digunakan oleh biner pemulihan dan alat pembuatan paket. Anda dapat menetapkan 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 di
contoh harus ditentukan (perangkat juga dapat menambahkan partisi ekstra). Ada lima organisasi dukungan
jenis sistem file:
- yaffs2
-
Sistem file yaffs2 di atas perangkat flash MTD. "perangkat" harus berupa nama partisi MTD
dan harus muncul di
/proc/mtd
. - mtd
-
Partisi MTD mentah, digunakan untuk partisi yang dapat di-booting seperti booting dan pemulihan. MTD bukan
terpasang, tetapi titik pemasangan digunakan
sebagai kunci untuk menemukan partisi. "perangkat"
harus berupa nama partisi MTD di
/proc/mtd
. - ext4
- Sistem file ext4 di atas perangkat flash eMMc. "perangkat" harus berupa jalur perangkat blok.
- emmc
- Perangkat blok eMMc mentah, digunakan untuk partisi yang dapat di-booting seperti booting dan pemulihan. Mirip dengan tipe {i>mtd<i}, eMMc tidak pernah benar-benar dipasang, tetapi {i>string<i} titik pemasangan digunakan untuk menemukan perangkat di dalam tabel.
- vfat
-
Sistem file FAT di atas perangkat blok, biasanya untuk penyimpanan eksternal seperti kartu SD. Tujuan
{i>device<i} adalah perangkat blok; device2 adalah perangkat blok kedua yang coba
dipasang oleh sistem jika
pemasangan perangkat utama akan gagal (untuk kompatibilitas dengan kartu SD yang mungkin
diformat dengan tabel partisi).
Semua partisi harus dipasang di direktori {i>root<i} (misalnya, nilai direktori pemasangan harus diawali dengan garis miring dan tidak memiliki garis miring lainnya). Batasan ini hanya berlaku untuk pemasangan sistem file dalam pemulihan; sistem utama bebas untuk 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 isian opsional tambahan, opsi. Saat ini satu-satunya opsi yang ditentukan adalah panjang , yang memungkinkan Anda secara eksplisit menentukan panjang partisi. Panjang ini digunakan saat memformat ulang partisi (misalnya, untuk partisi data pengguna selama operasi penghapusan total data/reset ke setelan pabrik, atau untuk partisi sistem selama penginstalan paket OTA lengkap). Jika nilai panjangnya negatif, ukuran yang akan diformat diambil dengan menambahkan nilai {i>length<i} ke ukuran partisi yang sebenarnya. Sebagai instance, menyetel "length=-16384" berarti 16 ribu terakhir dari partisi tersebut tidak akan akan ditimpa ketika partisi itu diformat ulang. Hal ini mendukung fitur seperti enkripsi data, partisi {i>userdata<i} (di mana {i>metadata <i}enkripsi disimpan di akhir partisi yang tidak boleh ditimpa).
Catatan: Kolom device2 dan options bersifat opsional, sehingga ambiguitas dalam penguraian. Jika entri di kolom keempat pada baris diawali dengan ‘/' karakter, itu dianggap sebagai entri device2 ; jika entri tidak dimulai dengan '/' karakter, elemen tersebut dianggap sebagai kolom options.
Animasi boot
Produsen perangkat memiliki kemampuan untuk menyesuaikan animasi yang ditampilkan saat perangkat Android sedang melakukan booting. Untuk melakukannya, buat file {i> .zip<i} yang terorganisir dan terletak sesuai dengan spesifikasi di format animasibooting.
Sebagai perangkat Android Things, Anda dapat mengunggah file zip di konsol Android Things untuk menyertakan gambar dalam produk yang dipilih.
Catatan: Gambar ini harus memenuhi pedoman merek Android. Untuk pedoman brand, lihat bagian Android pada Pemasaran Partner Hub.
UI Pemulihan
Untuk mendukung perangkat dengan hardware yang tersedia yang berbeda (tombol fisik, LED, layar, dll.), Anda dapat menyesuaikan antarmuka pemulihan untuk menampilkan status dan mengakses fitur tersembunyi untuk setiap perangkat.
Tujuan Anda adalah membangun pustaka statis kecil dengan beberapa objek C++ untuk menyediakan
fungsi khusus perangkat. File
bootable/recovery/default_device.cpp
digunakan secara default, dan menjadikan
titik awal untuk menyalin saat menulis versi file ini untuk perangkat Anda.
Catatan: Anda mungkin melihat pesan yang bertuliskan No Command di sini. Untuk beralih teks, tahan tombol daya sambil menekan tombol naikkan volume. Jika perangkat Anda tidak memiliki kedua tombol, tekan lama tombol apa saja 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 kolom menu pemulihan. Header menjelaskan cara mengoperasikan menu (yaitu kontrol untuk mengubah/memilih item yang disoroti).
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: Garis panjang terpotong (tidak digabungkan), jadi pertahankan lebar layar perangkat.
Sesuaikan CheckKey
Berikutnya, tentukan implementasi RecoveryUI perangkat Anda. Contoh ini mengasumsikan
tardis memiliki layar, jadi Anda dapat mewarisi dari
Implementasi ScreenRecoveryUI (lihat petunjuk untuk
perangkat tanpa layar.) Satu-satunya fungsi untuk
menyesuaikan dari ScreenRecoveryUI adalah CheckKey()
, yang melakukan
penanganan kunci asinkron:
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()
sama dengan
dipanggil apa pun yang terjadi selama proses pemulihan: ketika menu dimatikan, ketika
aktif, selama instalasi paket, selama penghapusan data pengguna, dll. Aplikasi ini dapat mengembalikan satu dari empat
konstanta:
- GANTI. Mengaktifkan atau menonaktifkan tampilan menu dan/atau log teks
- BOOT ULANG. Segera reboot perangkat
- Abaikan. Abaikan penekanan tombol ini
- ANTREAN. Mengantrekan penekanan tombol ini untuk digunakan secara sinkron (yaitu, oleh pemulihan sistem menu jika layar diaktifkan)
CheckKey()
dipanggil setiap kali peristiwa key-down diikuti dengan peristiwa key-up untuk
kunci yang sama. (Urutan kejadian A-bawah B-down B-up A-up hanya menghasilkan
CheckKey(B)
dipanggil.) CheckKey()
dapat menelepon
IsKeyPressed()
, untuk mengetahui apakah kunci lain ditahan. (Dalam contoh di atas
urutan peristiwa tombol, jika CheckKey(B)
memanggil IsKeyPressed(A)
akan menghasilkan nilai benar).
CheckKey()
dapat mempertahankan status di class-nya; hal ini berguna untuk mendeteksi
urutan huruf yang sama. Contoh ini menunjukkan penyiapan yang sedikit lebih kompleks: tampilan dialihkan oleh
tahan daya dan menekan volume-naik, dan perangkat dapat segera dimulai ulang dengan
menekan tombol daya lima kali berturut-turut (tanpa tombol intervensi lainnya):
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; } };
UI Pemulihan Layar
Saat menggunakan gambar Anda sendiri (ikon error, animasi penginstalan, status progres) dengan
ScreenRecoveryUI, Anda dapat menetapkan variabel animation_fps
untuk mengontrol kecepatan dalam
{i>frame per detik <i}(FPS) animasi.
Catatan: Skrip interlace-frames.py
saat ini memungkinkan Anda
menyimpan informasi animation_fps
dalam gambar itu sendiri. Di versi sebelumnya
Android harus menyetel animation_fps
sendiri.
Untuk menetapkan variabel animation_fps
, ganti
Fungsi ScreenRecoveryUI::Init()
di subclass Anda. Tetapkan nilai, lalu panggil metode
parent Init()
untuk menyelesaikan inisialisasi. Nilai default (20 FPS)
sesuai dengan {i>
image<i} pemulihan {i>default<i}; saat menggunakan gambar ini, Anda tidak perlu menyediakan
fungsi Init()
. Untuk mengetahui detail tentang gambar, lihat
Gambar UI Pemulihan.
Class perangkat
Setelah Anda memiliki implementasi RecoveryUI, tentukan kelas perangkat Anda (disubkelaskan dari
class Perangkat bawaan). Tindakan ini akan 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; }
MemulaiPemulihan
Metode StartRecovery()
dipanggil pada awal pemulihan, setelah UI memiliki
telah diinisialisasi dan setelah argumen diuraikan, tetapi sebelum tindakan apa pun
yang diambil. Implementasi default ini tidak melakukan apa pun, jadi Anda tidak perlu menyediakannya di
subclass jika Anda tidak perlu melakukan apa pun:
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. Di sini , ia akan mengembalikan array statis yang ditentukan di bagian atas file:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
{i>HandleMenuKey<i}
Selanjutnya, sediakan fungsi HandleMenuKey()
, yang memerlukan penekanan tombol dan
visibilitas menu, 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 mengambil kode kunci (yang sebelumnya telah diproses dan diantrekan oleh
CheckKey()
objek UI), dan status log menu/teks saat ini
visibilitas tinggi. Nilai hasil berupa bilangan bulat. Jika nilainya 0 atau lebih tinggi, nilai tersebut dianggap sebagai
posisi item menu, yang segera dipanggil (lihat
InvokeMenuItem()
di bawah). Jika tidak, parameter tersebut bisa berupa salah satu dari berikut
konstanta yang telah ditentukan sebelumnya:
- kHighlightUp. Memindahkan sorotan menu ke item sebelumnya
- kHighlightDown. Memindahkan sorotan menu ke item berikutnya
- kInvokeItem. Memanggil item yang sedang disorot
- kNoAction. Tidak melakukan apa-apa dengan penekanan tombol ini
Seperti yang tersirat oleh argumen yang terlihat, HandleMenuKey()
dipanggil meskipun menu
tidak terlihat. Tidak seperti CheckKey()
, fungsi ini tidak dipanggil saat pemulihan sedang berlangsung
sesuatu seperti menghapus data atau menginstal paket. Fungsi ini hanya dipanggil ketika pemulihan sedang 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 mensintesis tombol KEY_UP dan KEY_DOWN setiap kali
perangkat input seperti trackball melaporkan
gerakan di sumbu Y. Yang perlu Anda lakukan adalah
memetakan 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 layar.
Tombol pengubah
Untuk memeriksa kunci yang ditahan sebagai pengubah, panggil metode IsKeyPressed()
objek UI Anda sendiri. Misalnya, pada sebagian perangkat yang menekan Alt-W dalam pemulihan akan memulai
menghapus total data, terlepas dari apakah
menu dapat terlihat atau tidak. YOu dapat mengimplementasikan 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 adalah salah, tidak masuk akal untuk menampilkan spesial nilai yang memanipulasi menu (memindahkan sorotan, memanggil item yang disorot) karena pengguna tidak dapat melihat tanda. Namun, Anda dapat menampilkan nilai tersebut jika diinginkan.
InvokeMenuItem
Selanjutnya, sediakan metode InvokeMenuItem()
yang memetakan posisi bilangan bulat dalam array
item yang ditampilkan oleh GetMenuItems()
untuk 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 (atau anggota NO_ACTION jika Anda ingin sistem tidak melakukan apa pun). Ini adalah tempat untuk menyediakan fungsi pemulihan tambahan di luar apa yang ada dalam sistem: Menambahkan item di dalamnya menu Anda, jalankan di sini ketika item menu itu dipanggil, dan kembalikan NO_ACTION sehingga sistem tidak melakukan apa pun.
BuiltinAction berisi nilai berikut:
- TIDAK_TINDAKAN. Tidak melakukan apa pun.
- BOOT ULANG. Keluar dari pemulihan dan mulai ulang perangkat seperti biasa.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. Menginstal paket pembaruan dari berbagai tempat. Untuk mengetahui detailnya, lihat Sideload.
- WIPE_CACHE. Format ulang partisi cache saja. Konfirmasi tidak diperlukan karena hal ini relatif tidak berbahaya.
- WIPE_DATA. Memformat ulang partisi cache dan data pengguna, yang juga dikenal sebagai data pabrik {i>reset<i}. Pengguna diminta untuk mengonfirmasi tindakan ini sebelum melanjutkan.
Metode terakhir, WipeData()
, bersifat opsional dan dipanggil setiap kali penghapusan total data
dimulai (baik dari pemulihan melalui menu atau ketika pengguna telah memilih untuk melakukan
reset ke setelan pabrik dari sistem utama). Metode ini dipanggil sebelum data pengguna dan cache
partisi akan dihapus. Jika perangkat Anda menyimpan data pengguna di mana pun selain kedua hal tersebut
partisi, Anda harus
menghapusnya di sini. Anda harus mengembalikan 0 untuk menunjukkan keberhasilan dan lainnya
jika gagal, meskipun saat ini nilai yang ditampilkan diabaikan. Data pengguna dan cache
partisi akan dihapus, baik Anda
kembali berhasil atau gagal.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
Buat perangkat
Terakhir, sertakan beberapa boilerplate di akhir file recovery_ui.cpp untuk
Fungsi make_device()
yang membuat dan menampilkan instance class Perangkat:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
Bangun dan tautkan ke pemulihan perangkat
Setelah menyelesaikan file recovery_ui.cpp, buat dan tautkan ke pemulihan di perangkat Anda. Di beberapa 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, tetapkan library statis 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 pembaruan normal, ponsel melakukan {i> recovery<i}, mengisi bilah kemajuan instalasi, dan melakukan {i>booting<i} kembali ke sistem baru tanpa input dari pengguna. Jika sistem masalah pembaruan, satu-satunya tindakan pengguna yang dapat dilakukan adalah menghubungi layanan pelanggan.
Antarmuka khusus gambar meniadakan kebutuhan pelokalan. Namun, pada Android 5.0 dapat menampilkan string teks (mis. "Menginstal update sistem...") bersama 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.
Animasi instalasi direpresentasikan sebagai satu gambar PNG dengan berbagai {i>frame<i}
animasi yang bertautan oleh baris (itulah sebabnya Gambar 2 tampak diciutkan). Misalnya, untuk
Animasi tujuh bingkai 200x200, buat satu gambar berukuran 200x1400 dengan bingkai pertama adalah baris 0, 7,
14, 21, ...; frame kedua adalah baris 1, 8, 15, 22, ...; dll. Gabungan gambar berisi
potongan teks yang menunjukkan jumlah frame animasi dan jumlah frame per detik
(FPS). Alat bootable/recovery/interlace-frames.py
mengambil sekumpulan frame input
dan menggabungkannya ke dalam gambar komposit yang diperlukan oleh pemulihan.
Gambar default tersedia dalam berbagai kepadatan dan berada di
bootable/recovery/res-$DENSITY/images
(mis.,
bootable/recovery/res-hdpi/images
). Untuk menggunakan gambar
statis selama instalasi,
Anda hanya perlu menyediakan gambar icon_installing.png dan mengatur jumlah bingkai di
animasi ke 0 (ikon error tidak dianimasikan; ikon selalu berupa gambar statis).
Android 4.x dan yang lebih lama
UI pemulihan Android 4.x dan versi yang lebih lama menggunakan gambar error (ditampilkan di atas) dan animasi menginstal plus beberapa gambar overlay:
Selama instalasi, tampilan pada layar dibuat dengan menggambar icon_installing.png gambar, lalu menggambar salah satu bingkai overlay di atasnya dengan offset yang tepat. Di sini, ditumpangkan untuk menyorot di mana overlay ditempatkan di atas gambar dasar:
Bingkai berikutnya ditampilkan dengan hanya menggambar gambar overlay berikutnya di atas apa yang sudah ada; gambar dasar tidak digambar ulang.
Jumlah frame dalam animasi, kecepatan yang diinginkan, serta offset x dan y overlay
relatif terhadap basis diatur oleh variabel anggota class ScreenRecoveryUI. Saat menggunakan
gambar kustom, bukan gambar default, ganti metode Init()
di
subclass untuk mengubah nilai ini bagi gambar kustom Anda (untuk detailnya, lihat
ScreenRecoveryUI). Naskah
bootable/recovery/make-overlay.py
dapat membantu mengonversi kumpulan bingkai gambar
ke "gambar dasar + gambar overlay" bentuk yang dibutuhkan oleh pemulihan, termasuk komputasi jumlah
offset yang diperlukan.
Gambar default terletak di bootable/recovery/res/images
. Untuk menggunakan gambar statis
selama instalasi, Anda hanya perlu menyediakan
gambar icon_installing.png dan mengatur 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 gambar. Bila sistem utama melakukan booting ke pemulihan, sistem ini akan meneruskan lokalitas pengguna saat ini sebagai opsi command line untuk pemulihan. Untuk setiap pesan yang ditampilkan, pemulihan mencakup gambar gabungan dengan string teks yang telah dipra-render untuk pesan tersebut di setiap lokalitas.
Gambar contoh string teks pemulihan:
Teks pemulihan dapat menampilkan pesan berikut:
- Menginstal pembaruan sistem...
- Kesalahan!
- Menghapus... (saat melakukan penghapusan total data/reset ke setelan pabrik)
- Tidak ada perintah (saat pengguna melakukan booting ke pemulihan secara manual)
Aplikasi Android di bootable/recovery/tools/recovery_l10n/
merender pelokalan
pada pesan dan membuat gambar gabungan. Untuk detail tentang cara menggunakan 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 menjadikan pesan teks penting untuk proses pemulihan.
Catatan: Antarmuka tersembunyi yang menampilkan pesan log dan memungkinkan pengguna tindakan tertentu dari menu hanya tersedia dalam bahasa Inggris.
Bilah progres
Status progres dapat muncul di bawah gambar (atau animasi) utama. Status progres dibuat oleh yang menggabungkan dua gambar input, yang harus memiliki ukuran yang sama:
Ujung kiri gambar isi ditampilkan di samping ujung kanan gambar kosong untuk membuat status progres. Posisi batas antara keduanya gambar diubah untuk menunjukkan kemajuannya. Misalnya, dengan pasangan gambar input di atas, tampilkan:
Anda dapat menyediakan versi khusus perangkat untuk gambar tersebut dengan menempatkannya ke dalam (dalam
contoh) device/yoyodyne/tardis/recovery/res/images
kami. Nama file harus cocok dengan yang tercantum di atas; ketika sebuah file ditemukan di
direktori tersebut,
sistem build menggunakannya sebagai preferensi pada image default terkait. Hanya PNG dalam RGB atau
Format RGBA dengan kedalaman warna 8-bit didukung.
Catatan: Di Android 5.x, jika lokalitas diketahui untuk pemulihan dan bahasa kanan-ke-kiri (RTL) (Arab, Ibrani, dll.), status progres diisi dari kanan ke kiri.
Perangkat tanpa layar
Tidak semua perangkat Android memiliki layar. Jika perangkat Anda adalah perangkat headless atau memiliki khusus audio saja, Anda mungkin perlu melakukan penyesuaian UI pemulihan yang lebih ekstensif. Sebagai gantinya pembuatan subclass ScreenRecoveryUI, buat subclass untuk RecoveryUI kelas induknya secara langsung.
RecoveryUI memiliki metode untuk menangani operasi UI tingkat rendah seperti "mengalihkan layar",
"memperbarui status progres," "tampilkan menu," "mengubah pilihan menu," dll. Anda dapat mengganti
ini untuk menyediakan antarmuka yang
sesuai bagi perangkat Anda. Mungkin perangkat Anda
memiliki LED di mana
Anda bisa menggunakan beragam warna atau pola kedipan
untuk menunjukkan keadaan, atau mungkin Anda bisa
audio. (Mungkin Anda sama sekali tidak ingin mendukung menu atau mode "tampilan teks"; Anda dapat
mencegah mereka mengaksesnya dengan CheckKey()
dan
Implementasi HandleMenuKey()
yang tidak pernah mengaktifkan tampilan atau memilih menu
yang bermanfaat. Dalam hal ini, banyak metode RecoveryUI yang perlu Anda sediakan bisa saja kosong
stub.)
Lihat bootable/recovery/ui.h
untuk deklarasi RecoveryUI guna melihat metode apa
yang harus Anda dukung. RecoveryUI bersifat abstrak—beberapa metode murni virtual dan harus disediakan oleh
Subclass—tetapi memang berisi kode untuk melakukan pemrosesan input kunci. Anda dapat menggantinya
juga, jika perangkat Anda tidak memiliki kunci atau Anda ingin
memprosesnya secara berbeda.
Updater
Anda bisa menggunakan kode khusus perangkat dalam penginstalan paket update dengan memberikan fungsi ekstensi Anda sendiri yang dapat dipanggil dari dalam skrip {i>updater<i} Anda. Berikut contohnya 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
fungsi dipanggil, 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— milik fungsi
logika menentukan mana dari mereka
yang dievaluasi dan berapa kali. Dengan demikian, Anda dapat menggunakan ekstensi
fungsi untuk mengimplementasikan
struktur kontrol Anda sendiri. Call Evaluate()
untuk mengevaluasi
argumen Expr*
, yang menampilkan Value*
. Jika Evaluate()
mengembalikan NULL, Anda harus membebaskan sumber daya yang Anda simpan dan segera mengembalikan NULL (ini
akan menyebarkan pembatalan stack edify). Jika tidak, Anda mengambil kepemilikan Nilai yang dikembalikan dan
bertanggung jawab untuk memanggil
FreeValue()
.
Misalnya fungsi memerlukan dua argumen: kunci bernilai string dan bernilai blob image. 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 telah dievaluasi sebelumnya bisa membosankan untuk banyak
argumen. Fungsi ReadValueArgs()
dapat mempermudah hal ini. Alih-alih kode
di atas, Anda bisa 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
lebih mudah untuk melakukannya dengan satu pernyataan if dengan biaya menghasilkan
jika gagal. Namun, ReadValueArgs()
menangani evaluasi
setiap argumen dan membebaskan semua argumen yang dievaluasi sebelumnya (serta menetapkan
pesan error) jika ada evaluasi yang gagal. Anda dapat menggunakan
ReadValueVarArgs()
fungsi praktis untuk mengevaluasi angka variabel
argumen (akan mengembalikan array Value*
).
Setelah mengevaluasi argumen, lakukan fungsi fungsi tersebut:
// 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 panggilan ini
Value*
—khususnya anggota data.
Dalam contoh ini, Anda ingin menampilkan nilai benar atau salah untuk menunjukkan keberhasilan. Ingatlah
bahwa string kosong adalah false dan semua string lainnya bernilai true. Anda
harus melakukan malloc objek Value dengan salinan malloc dari string konstan yang akan ditampilkan, karena
pemanggil akan free()
keduanya. Jangan lupa untuk menghubungi FreeValue()
di
objek yang Anda dapatkan dengan mengevaluasi argumen.
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 secara lebih ringkas:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
Untuk mengaitkan fungsi ke edify interpreter, sediakan fungsi
Register_foo
dengan foo adalah nama library statis yang berisi
pada kode ini. Panggil RegisterFunction()
untuk mendaftarkan setiap fungsi ekstensi. Menurut
khusus, beri nama fungsi khusus perangkat device.whatever
untuk menghindari
bentrok dengan fungsi bawaan yang ditambahkan di masa mendatang.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
Sekarang Anda dapat mengonfigurasi makefile untuk membangun library statis dengan kode Anda. (Ini sama dengan makefile yang digunakan untuk menyesuaikan UI pemulihan di bagian sebelumnya; perangkat Anda mungkin memiliki keduanya library statis yang didefinisikan 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, konfigurasikan build pemulihan untuk mengakses koleksi Anda. Tambahkan koleksi Anda ke
TARGET_RECOVERY_UPDATER_LIBS (yang mungkin berisi beberapa library; semuanya didaftarkan).
Jika kode Anda bergantung pada library statis lain yang bukan ekstensi yang dibuat sendiri (yaitu,
yang tidak memiliki fungsi Register_libname
), Anda dapat mencantumkannya di
TARGET_Recovery_UPDATER_EXTRA_LIBS untuk menautkannya ke updater tanpa memanggil
(tidak ada). Misalnya, jika kode khusus perangkat Anda ingin menggunakan
{i>zlib<i} 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 kini dapat memanggil fungsi Anda seperti yang lain. Untuk memprogram ulang
di perangkat tardis Anda, skrip pembaruan mungkin berisi:
tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. Hal ini menggunakan
versi argumen tunggal dari fungsi bawaan package_extract_file()
,
yang menampilkan konten file yang diekstrak dari paket update sebagai blob untuk menghasilkan
argumen kedua ke fungsi ekstensi yang baru.
Pembuatan paket OTA
Komponen terakhir adalah membuat alat pembuat paket OTA mengetahui tentang data khusus perangkat dan menghasilkan skrip updater yang menyertakan panggilan ke fungsi ekstensi Anda.
Pertama, buat sistem build mengetahui tentang blob data khusus perangkat. Dengan asumsi data Anda
file berada di device/yoyodyne/tardis/tardis.dat
, deklarasikan hal berikut di
AndroidBoard.mk perangkat:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
Anda juga bisa memasukkannya ke dalam Android.mk, tetapi objek tersebut harus dilindungi oleh perangkat karena semua file Android.mk di hierarki dimuat, apa pun perangkat yang dibuat. (Jika hierarki Anda mencakup beberapa perangkat, Anda hanya perlu menambahkan file tardis.dat 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 sejarah; mereka mungkin tidak ada hubungannya dengan
radio perangkat (jika ada). Mereka hanyalah blob data buram yang disalin oleh sistem build ke dalam
{i> .zip<i} file target yang digunakan
oleh alat pembuat OTA. Saat Anda membangun, {i>tardis.dat<i}
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) alat tersebut yang dapat dihubungi 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 ini misalnya, Anda perlu memprogram ulang tardis hanya ketika 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 menjelang awal pembuatan OTA penuh. Inilah tempat yang baik untuk memunculkan pernyataan tentang status perangkat saat ini. Jangan memunculkan perintah skrip yang membuat perubahan pada perangkat seluler.
FullOTA_InstallBegin()
- Dipanggil setelah semua pernyataan tentang status perangkat berlalu, tetapi sebelum perubahan apa pun dilakukan. Anda dapat memberikan perintah untuk update khusus perangkat yang harus dijalankan sebelumnya hal lain pada perangkat telah diubah.
FullOTA_InstallEnd()
- Dipanggil pada akhir pembuatan skrip, setelah perintah skrip untuk memperbarui {i>boot<i} dan partisi sistem telah dimunculkan. Anda juga dapat memberikan perintah tambahan untuk update untuk perangkat tertentu.
IncrementalOTA_Assertions()
-
Mirip dengan
FullOTA_Assertions()
, tetapi dipanggil saat membuat inkremental memperbarui paket. IncrementalOTA_VerifyBegin()
- Dipanggil setelah semua pernyataan tentang status perangkat diteruskan, tetapi sebelum perubahan apa pun dilakukan. Anda dapat memberikan perintah untuk update khusus perangkat yang harus dijalankan sebelum melakukan apa pun {i>else<i} pada perangkat telah diubah.
IncrementalOTA_VerifyEnd()
- Dipanggil pada akhir fase verifikasi, ketika skrip telah selesai mengonfirmasi file yang akan disentuh memiliki konten awal yang diharapkan. Saat ini, tidak ada perangkat telah diubah. Anda juga dapat memunculkan kode untuk perangkat tambahan khusus verifikasi.
IncrementalOTA_InstallBegin()
- Dipanggil setelah file yang akan di-patch telah diverifikasi memiliki sebelum, tetapi sebelum perubahan apa pun dibuat. Anda dapat memunculkan perintah untuk pembaruan khusus perangkat yang harus dijalankan sebelum hal lain pada perangkat diubah.
IncrementalOTA_InstallEnd()
- Mirip dengan paket OTA lengkap, metode ini dipanggil di akhir skrip setelah perintah {i>script<i} untuk memperbarui partisi {i>boot<i} dan sistem telah dikeluarkan. Anda juga bisa memberikan perintah tambahan untuk update khusus perangkat.
Catatan: Jika perangkat kehilangan daya, penginstalan OTA dapat dimulai ulang dari memulai. Bersiaplah untuk menangani perangkat yang telah menjalankan perintah ini, seluruhnya atau sebagian.
Meneruskan fungsi ke objek info
Teruskan fungsi ke satu objek info yang berisi berbagai item berguna:
-
info.input_zip. (Khusus OTA lengkap) Objek
zipfile.ZipFile
untuk masukkan .zip file target. -
info.source_zip. (Khusus OTA inkremental) Objek
zipfile.ZipFile
untuk file target-sumber .zip (build sudah ada di perangkat ketika paket inkremental sedang diinstal). -
info.target_zip. (Khusus OTA inkremental) Objek
zipfile.ZipFile
untuk file target target .zip (build yang dimasukkan paket inkremental ke perangkat). -
info.output_zip. Paket sedang dibuat; objek
zipfile.ZipFile
dibuka untuk menulis. Gunakan common.ZipWriteStr(info.output_zip, filename, data) untuk menambahkan file ke paket. -
info.script. Objek skrip tempat Anda dapat menambahkan perintah. Telepon
info.script.AppendExtra(script_text)
untuk menghasilkan teks ke dalam skrip. Pastikan teks output diakhiri dengan titik koma agar tidak mengalami perintah yang ditampilkan setelah itu.
Untuk 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 file:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
Jika TARGET_RELEASETOOLS_EXTENSIONS tidak ditetapkan, setelan default 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 target
File .zip (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
yang ada, lebih disukai daripada yang berasal dari hierarki sumber Android. Anda juga dapat secara eksplisit
tentukan jalur ke ekstensi khusus perangkat dengan -s
(atau
--device_specific
), yang merupakan prioritas utama. Hal ini memungkinkan Anda untuk
memperbaiki error dan mengubah ekstensi releasetools dan menerapkan perubahan tersebut ke
file target.
Sekarang, saat Anda menjalankan ota_from_target_files
, kode ini otomatis mengambil
modul khusus perangkat dari file .zip target_files dan menggunakannya saat membuat OTA
paket:
./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 Anda 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 daftar lengkap opsi, lihat
ota_from_target_files
komentar di
build/make/tools/releasetools/ota_from_target_files
.
Mekanisme sideload
Pemulihan memiliki mekanisme sideload untuk menginstal paket update secara manual tanpa mengunduhnya secara {i>over-the-air<i} oleh sistem utama. {i>Sideload<i} berguna untuk {i> debug<i} atau membuat perubahan pada perangkat di mana sistem utama tidak dapat di-{i>booting<i}.
Secara historis, {i>sideload<i} telah dilakukan melalui pemuatan paket dari kartu SD perangkat; inci pada perangkat non-{i>booting<i}, paket dapat ditempatkan ke kartu SD menggunakan komputer dan kemudian kartu SD yang 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 memuatnya melalui USB menggunakan adb.
Untuk memanggil setiap mekanisme sideload, metode Device::InvokeMenuItem()
perangkat Anda
dapat mengembalikan nilai BuiltinAction berikut:
-
APPLY_EXT. Melakukan sideload paket update dari penyimpanan eksternal (
/sdcard
direktori). recovery.fstab Anda harus menentukan direktori pemasangan/sdcard
. Ini adalah tidak dapat digunakan di perangkat yang mengemulasi kartu SD dengan symlink ke/data
(atau beberapa mekanisme serupa)./data
biasanya tidak dapat dipulihkan karena mungkin dienkripsi. UI pemulihan menampilkan menu file .zip di/sdcard
dan memungkinkan pengguna untuk memilih satu. -
APPLY_CACHE. Mirip dengan memuat paket dari
/sdcard
, hanya saja Direktori/cache
(yang selalu tersedia untuk pemulihan) digunakan sebagai gantinya. Dari sistem reguler,/cache
hanya dapat ditulis oleh pengguna berhak istimewa, dan jika perangkat tidak dapat di-{i>booting<i} maka direktori/cache
tidak dapat ditulis ke sama sekali (yang menjadikan mekanisme ini 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
versi {i>daemon<i} adbd agar adb
komputer {i>host<i} yang terhubung dapat berbicara dengannya. Mini ini
hanya mendukung satu perintah:
adb sideload filename
. File yang dinamai dikirim dari mesin {i>host<i} ke perangkat, yang kemudian memverifikasi dan menginstalnya seolah-olah telah berada di penyimpanan lokal.
Beberapa hal yang perlu diwaspadai:
- Hanya transportasi USB yang didukung.
-
Jika pemulihan Anda menjalankan adbd secara normal (biasanya benar untuk userdebug dan build ing), hal itu akan
dimatikan saat perangkat dalam mode sideload adb dan akan dimulai ulang saat adb
{i>sideload<i} telah selesai menerima paket. Saat dalam mode sideload adb, tidak ada perintah adb lainnya
dari
sideload
pekerjaan (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 maka perangkat akan gagal untuk memverifikasinya dan menghentikan prosedur instalasi. RecoveryUI metodeCheckKey()
implementasi akan terus dipanggil untuk penekanan tombol, sehingga Anda dapat memberikan urutan tombol yang akan memulai ulang perangkat dan bekerja dalam mode sideload adb.