Kode khusus perangkat

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

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

Gambar 1. icon_error.png

gambar yang ditampilkan selama penginstalan ota

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 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 bingkai overlay di atasnya pada offset yang tepat. Di sini, kotak merah ditempatkan untuk menandai tempat overlay ditempatkan di atas gambar dasar:

gambar gabungan
penginstalan plus overlay pertama

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

gambar gabungan
penginstalan plus overlay ketujuh

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

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 kedua gambar diubah untuk menunjukkan progres. Misalnya, dengan pasangan gambar input di atas, tampilkan:

status progres 1%

Gambar 11. Status progres di 1%>

status progres 10%

Gambar 12. Status progres 10%

status progres 50%

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