Kode khusus perangkat

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();
}

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 yang ditampilkan selama error OTA

Gambar 1. icon_error.png

gambar yang ditampilkan selama penginstalan OTA

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 yang ditampilkan selama penginstalan OTA

Gambar 3. icon_installing.png

gambar ditampilkan sebagai overlay
pertama

Gambar 4. icon-installing_overlay01.png

gambar ditampilkan sebagai overlay ketujuh

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 komposit
penginstalan plus overlay pertama

Gambar 6. Menginstal frame animasi 1 (icon_installing.png + icon_installing_overlay01.png)

gambar gabungan
overlay ketujuh plus penginstalan

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 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:

status progres kosong

Gambar 9. progress_empty.png

status progres penuh

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:

status progres di 1%

Gambar 11. Status progres di 1%>

status progres di 10%

Gambar 12. Status progres di 10%

status progres di 50%

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. Metode CheckKey() implementasi RecoveryUI akan terus dipanggil untuk penekanan tombol, sehingga Anda dapat memberikan urutan tombol yang memulai ulang perangkat dan berfungsi dalam mode sideload adb.