Kode Khusus Perangkat

Sistem pemulihan mencakup beberapa kait untuk memasukkan kode khusus perangkat sehingga pembaruan OTA juga dapat memperbarui bagian perangkat selain sistem Android (misalnya, pita dasar atau prosesor radio).

Bagian dan contoh berikut menyesuaikan perangkat tardis yang diproduksi oleh vendor yoyodyne .

Peta partisi

Pada Android 2.3, platform mendukung perangkat flash eMMc dan sistem file ext4 yang berjalan pada perangkat tersebut. 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 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, 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. "perangkat" harus jalur perangkat blok.
emmc
Perangkat blok eMMc mentah, digunakan untuk partisi yang dapat di-boot seperti boot dan pemulihan. Mirip dengan tipe mtd, eMMc tidak pernah benar-benar dipasang, tetapi string titik pemasangan digunakan untuk menemukan perangkat di 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 sistem coba pasang 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 mount point harus dimulai dengan garis miring dan tidak ada garis miring lainnya). Pembatasan ini hanya berlaku untuk pemasangan sistem file dalam pemulihan; sistem utama bebas untuk memasangnya di mana saja. Direktori /boot , /recovery , dan /misc harus bertipe mentah (mtd atau emmc), sedangkan direktori /system , /data , /cache , dan /sdcard (jika tersedia) harus tipe sistem file (yaffs2, ext4, atau vfat).

Mulai dari Android 3.0, file recovery.fstab mendapatkan bidang opsional tambahan, options . Saat ini satu-satunya opsi yang ditentukan adalah length , yang memungkinkan Anda menentukan panjang partisi secara eksplisit. Panjang ini digunakan saat memformat ulang partisi (misalnya, untuk partisi data pengguna selama operasi penghapusan data/pengaturan ulang pabrik, atau untuk partisi sistem selama penginstalan paket OTA lengkap). Jika nilai panjangnya negatif, maka ukuran untuk format diambil dengan menambahkan nilai panjang ke ukuran partisi yang sebenarnya. Misalnya, menyetel "length=-16384" berarti 16k terakhir dari partisi tersebut tidak akan ditimpa saat partisi tersebut diformat ulang. Ini mendukung fitur seperti enkripsi partisi data pengguna (di mana metadata enkripsi disimpan di akhir partisi yang tidak boleh ditimpa).

Catatan: Bidang device2 dan opsi bersifat opsional, membuat ambiguitas dalam penguraian. Jika entri di bidang keempat pada baris dimulai dengan karakter '/', itu dianggap entri device2 ; jika entri tidak dimulai dengan karakter '/', itu dianggap sebagai bidang opsi .

Animasi boot

Produsen perangkat memiliki kemampuan untuk menyesuaikan animasi yang ditampilkan saat perangkat Android melakukan booting. Untuk melakukan ini, buat file .zip yang diatur dan ditempatkan sesuai dengan spesifikasi dalam format bootanimation .

Untuk perangkat Android Things , Anda dapat mengunggah file zip di konsol Android Things agar gambar disertakan dalam produk yang dipilih.

Catatan: Gambar-gambar ini harus memenuhi pedoman merek Android .

UI pemulihan

Untuk mendukung perangkat dengan perangkat keras 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 membangun perpustakaan statis kecil dengan beberapa objek C++ untuk menyediakan fungsionalitas khusus perangkat. File bootable/recovery/default_device.cpp digunakan secara default, dan merupakan titik awal yang baik untuk menyalin saat menulis versi file ini untuk perangkat Anda.

device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h>

#include "common.h"
#include "device.h"
#include "screen_ui.h"

Fungsi tajuk dan item

Kelas Perangkat memerlukan fungsi untuk mengembalikan 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: Garis panjang terpotong (tidak terbungkus), jadi ingatlah lebar layar perangkat Anda.

Menyesuaikan CheckKey

Selanjutnya, tentukan implementasi 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 KUNCI

Konstanta KEY_* didefinisikan di linux/input.h . CheckKey() dipanggil tidak peduli apa yang terjadi di sisa pemulihan: ketika menu dimatikan, ketika aktif, selama instalasi paket, selama penghapusan data pengguna, dll. Itu dapat mengembalikan salah satu dari empat konstanta:

  • BERAKHIR . Mengaktifkan atau menonaktifkan tampilan menu dan/atau teks log
  • ULANG ULANG . Segera reboot perangkat
  • Abaikan . Abaikan penekanan tombol ini
  • ANTRIAN . Enqueue penekanan tombol ini untuk dikonsumsi secara serempak (yaitu, oleh sistem menu pemulihan jika tampilan diaktifkan)

CheckKey() dipanggil setiap kali event key-down diikuti oleh event key-up untuk kunci yang sama. (Urutan kejadian A-down B-down B-up A-up hanya menghasilkan CheckKey(B) yang dipanggil.) CheckKey() dapat memanggil IsKeyPressed() , untuk mengetahui apakah kunci lain sedang ditekan. (Dalam urutan peristiwa kunci di atas, jika CheckKey(B) memanggil IsKeyPressed(A) itu akan mengembalikan nilai true.)

CheckKey() dapat mempertahankan status di kelasnya; ini dapat berguna untuk mendeteksi urutan kunci. Contoh ini menunjukkan pengaturan yang sedikit lebih rumit: tampilan diaktifkan dengan menahan daya dan menekan volume naik, dan perangkat dapat segera di-boot 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;
    }
};

LayarPemulihanUI

Saat menggunakan gambar Anda sendiri (ikon kesalahan, animasi penginstalan, bilah kemajuan) dengan ScreenRecoveryUI, Anda dapat mengatur variabel animation_fps untuk mengontrol kecepatan animasi dalam bingkai per detik (FPS).

Catatan: Skrip interlace-frames.py saat ini memungkinkan Anda untuk menyimpan informasi animation_fps dalam gambar itu sendiri. Di versi Android sebelumnya, perlu mengatur sendiri animation_fps .

Untuk menyetel variabel animation_fps , timpa fungsi ScreenRecoveryUI::Init() di subkelas Anda. Tetapkan nilainya, lalu panggil fungsi parent Init() untuk menyelesaikan inisialisasi. Nilai default (20 FPS) sesuai dengan gambar pemulihan default; saat menggunakan gambar ini, Anda tidak perlu menyediakan fungsi Init() . Untuk detail tentang gambar, lihat Gambar UI Pemulihan .

Kelas Perangkat

Setelah Anda memiliki implementasi RecoveryUI, tentukan kelas perangkat Anda (disubklasifikasikan dari kelas Perangkat bawaan). Itu harus membuat satu instance kelas UI Anda dan mengembalikannya dari fungsi GetUI() :

class TardisDevice : public Device {
  private:
    TardisUI* ui;

  public:
    TardisDevice() :
        ui(new TardisUI) {
    }

    RecoveryUI* GetUI() { return ui; }

Mulai pemulihan

Metode StartRecovery() dipanggil pada awal pemulihan, setelah UI diinisialisasi dan setelah argumen diuraikan, tetapi sebelum tindakan apa pun diambil. Implementasi default tidak melakukan apa-apa, jadi Anda tidak perlu menyediakan ini di subkelas Anda jika Anda tidak ada hubungannya:

   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, ia mengembalikan array statis yang ditentukan di bagian atas file:

const char* const* GetMenuHeaders() { return HEADERS; }
const char* const* GetMenuItems() { return ITEMS; }

MenanganiMenuKey

Selanjutnya, sediakan fungsi HandleMenuKey() , yang mengambil penekanan tombol dan visibilitas menu saat ini, dan memutuskan tindakan apa yang harus 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 metode CheckKey() objek UI), dan status visibilitas log menu/teks saat ini. Nilai kembalian adalah bilangan bulat. Jika nilainya 0 atau lebih tinggi, itu diambil sebagai posisi item menu, yang segera dipanggil (lihat metode InvokeMenuItem() di bawah). Kalau tidak, itu bisa menjadi salah satu dari konstanta yang telah ditentukan sebelumnya:

  • kHighlightUp . Pindahkan sorotan menu ke item sebelumnya
  • kSorot Turun . Pindahkan sorotan menu ke item berikutnya
  • kInvokeItem . Panggil item yang saat ini disorot
  • kNoAction . Jangan lakukan apa pun dengan penekanan tombol ini

Seperti tersirat oleh argumen yang terlihat, HandleMenuKey() dipanggil meskipun menu tidak terlihat. Tidak seperti CheckKey() , ini tidak dipanggil saat pemulihan sedang melakukan sesuatu seperti menghapus data atau menginstal paket—itu dipanggil hanya saat pemulihan tidak digunakan dan menunggu input.

Mekanisme Trackball

Jika perangkat Anda memiliki mekanisme input seperti trackball (menghasilkan kejadian input dengan tipe EV_REL dan kode REL_Y), pemulihan mensintesis 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() , jadi Anda tidak dapat menggunakan gerakan trackball sebagai pemicu untuk mem-boot ulang atau mengalihkan tampilan.

Tombol Pengubah

Untuk memeriksa kunci yang ditahan sebagai pengubah, panggil metode IsKeyPressed() dari objek UI Anda sendiri. Misalnya, pada beberapa perangkat menekan Alt-W dalam pemulihan akan memulai penghapusan data apakah menu terlihat atau tidak. Anda bisa 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 terlihat salah, tidak masuk akal untuk mengembalikan nilai khusus yang memanipulasi menu (memindahkan sorotan, memanggil item yang disorot) karena pengguna tidak dapat melihat sorotan. Namun, Anda dapat mengembalikan nilai jika diinginkan.

PanggilMenuItem

Selanjutnya, berikan metode InvokeMenuItem() yang memetakan posisi integer dalam larik item yang dikembalikan oleh GetMenuItems() ke tindakan. Untuk larik 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 mengembalikan setiap anggota enum BuiltinAction untuk memberi tahu sistem untuk mengambil tindakan itu (atau anggota NO_ACTION jika Anda ingin sistem tidak melakukan apa-apa). Ini adalah tempat untuk menyediakan fungsionalitas pemulihan tambahan di luar apa yang ada di sistem: Tambahkan item untuknya di menu Anda, jalankan di sini saat item menu itu dipanggil, dan kembalikan NO_ACTION sehingga sistem tidak melakukan hal lain.

BuiltinAction berisi nilai-nilai berikut:

  • TIDAK_TINDAKAN . Tidak melakukan apapun.
  • ULANG ULANG . Keluar dari pemulihan dan reboot perangkat secara normal.
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD . Instal paket pembaruan dari berbagai tempat. Untuk detailnya, lihat Sideloading .
  • WIPE_CACHE . Format ulang partisi cache saja. Tidak diperlukan konfirmasi karena ini relatif tidak berbahaya.
  • WIPE_DATA . Format ulang data pengguna dan partisi cache, juga dikenal sebagai reset data pabrik. Pengguna diminta untuk mengkonfirmasi tindakan ini sebelum melanjutkan.

Metode terakhir, WipeData() , adalah opsional dan dipanggil setiap kali operasi penghapusan data dimulai (baik dari pemulihan melalui menu atau ketika pengguna telah memilih untuk melakukan reset data pabrik dari sistem utama). Metode ini dipanggil sebelum data pengguna dan partisi cache dihapus. Jika perangkat Anda menyimpan data pengguna di mana pun selain kedua partisi tersebut, Anda harus menghapusnya di sini. Anda harus mengembalikan 0 untuk menunjukkan keberhasilan dan nilai lain untuk kegagalan, meskipun saat ini nilai pengembalian diabaikan. Data pengguna dan partisi cache akan dihapus apakah Anda berhasil atau gagal kembali.

   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 mengembalikan instance kelas Perangkat Anda:

class TardisDevice : public Device {
   // ... all the above methods ...
};

Device* make_device() {
    return new TardisDevice();
}

Setelah menyelesaikan file recovery_ui.cpp, buat dan tautkan ke pemulihan di perangkat Anda. Di Android.mk, buat pustaka 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 perpustakaan 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 pembaruan normal, ponsel melakukan booting ke pemulihan, mengisi bilah kemajuan penginstalan, dan melakukan booting kembali ke sistem baru tanpa masukan dari pengguna. Jika terjadi masalah pembaruan sistem, satu-satunya tindakan pengguna yang dapat dilakukan adalah menghubungi layanan pelanggan.

Antarmuka gambar saja meniadakan kebutuhan akan pelokalan. Namun, pada Android 5.0 pembaruan dapat menampilkan string teks (mis. "Menginstal pembaruan sistem...") bersama dengan gambar. Untuk 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 kesalahan dan animasi penginstalan .

gambar ditampilkan selama kesalahan ota

Gambar 1. icon_error.png

gambar yang ditampilkan selama ota install

Gambar 2. icon_installing.png

Animasi penginstalan direpresentasikan sebagai gambar PNG tunggal dengan bingkai berbeda dari animasi yang disatukan oleh baris (itulah sebabnya Gambar 2 tampak terjepit). Misalnya, untuk animasi tujuh bingkai 200x200, buat satu gambar 200x1400 di mana bingkai pertama adalah baris 0, 7, 14, 21, ...; bingkai kedua adalah baris 1, 8, 15, 22, ...; dll. Gambar gabungan mencakup potongan teks yang menunjukkan jumlah bingkai animasi dan jumlah bingkai per detik (FPS). Alat bootable/recovery/interlace-frames.py mengambil satu set bingkai input dan menggabungkannya ke dalam gambar komposit yang diperlukan yang digunakan oleh pemulihan.

Gambar default tersedia dalam kepadatan yang berbeda dan terletak 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 dalam animasi ke 0 (ikon kesalahan tidak dianimasikan; selalu gambar statis).

Android 4.x dan sebelumnya

Android 4.x dan UI pemulihan sebelumnya menggunakan gambar kesalahan (ditunjukkan di atas) dan animasi pemasangan ditambah beberapa gambar hamparan:

gambar yang ditampilkan selama ota install

Gambar 3. icon_installing.png

gambar ditampilkan sebagai overlay pertama

Gambar 4. icon-installing_overlay01.png

gambar ditampilkan sebagai hamparan ketujuh

Gambar 5. icon_installing_overlay07.png

Selama instalasi, tampilan di layar dibuat dengan menggambar gambar icon_installing.png, lalu menggambar salah satu bingkai overlay di atasnya dengan offset yang tepat. Di sini, kotak merah ditumpangkan untuk menyorot tempat overlay ditempatkan di atas gambar dasar:

gambar komposit pemasangan plus overlay pertama

Gambar 6. Memasang bingkai animasi 1 (icon_installing.png + icon_installing_overlay01.png)

gambar komposit pemasangan ditambah hamparan ketujuh

Gambar 7. Memasang bingkai 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 bingkai dalam animasi, kecepatan yang diinginkan, dan offset x dan y dari overlay relatif terhadap basis ditetapkan oleh variabel anggota kelas ScreenRecoveryUI. Saat menggunakan gambar khusus alih-alih gambar default, ganti metode Init() di subkelas Anda untuk mengubah nilai ini untuk gambar khusus Anda (untuk detailnya, lihat ScreenRecoveryUI ). Skrip bootable/recovery/make-overlay.py dapat membantu dalam mengonversi satu set bingkai gambar ke bentuk "gambar dasar + gambar overlay" yang diperlukan oleh pemulihan, termasuk menghitung 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 bingkai dalam animasi ke 0 (ikon kesalahan tidak dianimasikan; selalu gambar statis).

Teks pemulihan yang dilokalkan

Android 5.x menampilkan string teks (misalnya, "Menginstal pembaruan sistem...") bersama dengan gambar. Ketika sistem utama melakukan boot ke pemulihan, ia melewati lokal pengguna saat ini sebagai opsi baris perintah untuk pemulihan. Untuk setiap pesan yang ditampilkan, pemulihan menyertakan gambar komposit kedua dengan string teks yang telah dirender sebelumnya untuk pesan tersebut di setiap lokal.

Contoh gambar string teks pemulihan:

gambar teks pemulihan

Gambar 8. Teks lokal untuk pesan pemulihan

Teks pemulihan dapat menampilkan pesan berikut:

  • Menginstal pembaruan sistem...
  • Kesalahan!
  • Menghapus... (saat melakukan penghapusan data/pengaturan ulang pabrik)
  • Tidak ada perintah (ketika pengguna melakukan boot ke pemulihan secara manual)

Aplikasi Android di bootable/recovery/tools/recovery_l10n/ merender pelokalan pesan dan membuat gambar komposit. Untuk detail tentang penggunaan aplikasi ini, lihat komentar di bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java .

Saat pengguna melakukan boot ke pemulihan secara manual, lokal mungkin tidak tersedia dan tidak ada teks yang ditampilkan. Jangan membuat pesan teks penting untuk proses pemulihan.

Catatan: Antarmuka tersembunyi yang menampilkan pesan log dan memungkinkan pengguna untuk memilih tindakan dari menu hanya tersedia dalam bahasa Inggris.

Bilah kemajuan

Bilah kemajuan dapat muncul di bawah gambar utama (atau animasi). Bilah kemajuan dibuat dengan menggabungkan dua gambar input, yang harus berukuran sama:

bilah kemajuan kosong

Gambar 9. progress_empty.png

bilah kemajuan penuh

Gambar 10. progress_fill.png

Ujung kiri gambar isian ditampilkan di sebelah ujung kanan gambar kosong untuk membuat bilah kemajuan. Posisi batas antara dua gambar diubah untuk menunjukkan kemajuan. Misalnya, dengan pasangan gambar input di atas, tampilkan:

bilah kemajuan di 1%

Gambar 11. Bilah kemajuan pada 1%>

bilah kemajuan di 10%

Gambar 12. Progress bar pada 10%

bilah kemajuan di 50%

Gambar 13. Progress bar di 50%

Anda dapat memberikan versi khusus perangkat dari gambar-gambar ini dengan menempatkannya ke dalam (dalam contoh ini) device/yoyodyne/tardis/recovery/res/images . Nama file harus sesuai dengan yang tercantum di atas; ketika file ditemukan di direktori itu, sistem build akan menggunakannya dalam preferensi ke gambar default yang sesuai. Hanya PNG dalam format RGB atau RGBA dengan kedalaman warna 8-bit yang didukung.

Catatan: Di Android 5.x, jika lokal diketahui pemulihan dan merupakan bahasa kanan-ke-kiri (RTL) (Arab, Ibrani, dll.), bilah kemajuan terisi dari kanan ke kiri.

Perangkat tanpa layar

Tidak semua perangkat Android memiliki layar. Jika perangkat Anda adalah alat tanpa kepala atau memiliki antarmuka audio saja, Anda mungkin perlu melakukan penyesuaian UI pemulihan yang lebih ekstensif. Alih-alih membuat subkelas ScreenRecoveryUI, subkelaskan kelas induknya RecoveryUI secara langsung.

RecoveryUI memiliki metode untuk menangani operasi UI tingkat rendah seperti "beralih tampilan", "memperbarui bilah kemajuan", "tampilkan menu", "mengubah pilihan menu", dll. Anda dapat menimpanya untuk menyediakan antarmuka yang sesuai untuk perangkat Anda. Mungkin perangkat Anda memiliki LED di mana Anda 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 mengaksesnya dengan CheckKey() dan HandleMenuKey() yang tidak pernah mengaktifkan tampilan atau memilih item menu. Dalam kasus ini , banyak metode RecoveryUI yang perlu Anda berikan hanya berupa rintisan kosong.)

Lihat bootable/recovery/ui.h untuk deklarasi RecoveryUI untuk melihat metode apa yang harus Anda dukung. RecoveryUI bersifat abstrak—beberapa metode adalah virtual murni dan harus disediakan oleh subkelas—tetapi memang berisi kode untuk melakukan pemrosesan input kunci. Anda juga dapat menggantinya, jika perangkat Anda tidak memiliki kunci atau Anda ingin memprosesnya secara berbeda.

Pembaru

Anda dapat menggunakan kode khusus perangkat dalam penginstalan paket pembaruan dengan menyediakan fungsi ekstensi Anda sendiri yang dapat dipanggil dari dalam skrip pembaru 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 fungsi yang dipanggil, cookie State* , jumlah argumen yang masuk, dan array pointer Expr* yang mewakili argumen. Nilai yang dikembalikan 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 mana yang dievaluasi dan berapa kali. Dengan demikian, Anda dapat menggunakan fungsi ekstensi untuk mengimplementasikan struktur kontrol Anda sendiri. Call Evaluate() untuk mengevaluasi argumen Expr* , mengembalikan Value* . Jika Evaluate() mengembalikan NULL, Anda harus membebaskan semua sumber daya yang Anda pegang dan segera mengembalikan NULL (ini menyebar membatalkan tumpukan edify). Jika tidak, Anda mengambil kepemilikan Nilai yang dikembalikan dan bertanggung jawab untuk akhirnya memanggil FreeValue() di atasnya.

Misalkan fungsi membutuhkan dua argumen: kunci bernilai string dan gambar bernilai gumpalan . Anda bisa 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 bisa membosankan untuk beberapa argumen. Fungsi ReadValueArgs() dapat membuat ini lebih mudah. 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 pengecekan tipe, jadi Anda harus melakukannya di sini; lebih mudah untuk melakukannya dengan satu pernyataan if dengan biaya menghasilkan pesan kesalahan yang agak kurang spesifik ketika gagal. Tetapi ReadValueArgs() menangani evaluasi setiap argumen dan membebaskan semua argumen yang dievaluasi sebelumnya (serta menyetel pesan kesalahan yang berguna) jika ada evaluasi yang gagal. Anda dapat menggunakan fungsi kemudahan ReadValueVarArgs() untuk mengevaluasi sejumlah variabel argumen (ini mengembalikan larik Value* ).

Setelah mengevaluasi argumen, lakukan pekerjaan 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 dikembalikan harus berupa objek Value* ; kepemilikan objek ini akan diteruskan ke pemanggil. Penelepon mengambil kepemilikan atas data apa pun yang ditunjukkan oleh Value* —khususnya anggota data.

Dalam contoh ini, Anda ingin mengembalikan nilai benar atau salah untuk menunjukkan keberhasilan. Ingat konvensi bahwa string kosong adalah salah dan semua string lainnya adalah benar . Anda harus malloc objek Nilai dengan salinan malloc dari string konstan untuk kembali, 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 kenyamanan StringValue() membungkus string menjadi objek Nilai baru. Gunakan untuk menulis kode di atas lebih ringkas:

   FreeValue(key);
    FreeValue(image);

    return StringValue(strdup(successful ? "t" : ""));
}

Untuk menghubungkan fungsi ke interpreter edify, berikan fungsi Register_ foo di mana foo adalah nama perpustakaan statis yang berisi kode ini. Panggil RegisterFunction() untuk mendaftarkan setiap fungsi ekstensi. Berdasarkan konvensi, beri nama perangkat fungsi khusus device . whatever untuk menghindari konflik dengan fungsi bawaan yang ditambahkan di masa mendatang.

void Register_librecovery_updater_tardis() {
    RegisterFunction("tardis.reprogram", ReprogramTardisFn);
}

Anda sekarang dapat mengonfigurasi makefile untuk membangun perpustakaan statis dengan kode Anda. (Ini adalah makefile yang sama yang digunakan untuk menyesuaikan UI pemulihan di bagian sebelumnya; perangkat Anda mungkin memiliki kedua pustaka 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 perpustakaan statis harus sesuai dengan nama fungsi Register_ libname yang ada di dalamnya.

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

Terakhir, konfigurasikan build pemulihan untuk menarik perpustakaan Anda. Tambahkan perpustakaan Anda ke TARGET_RECOVERY_UPDATER_LIBS (yang mungkin berisi beberapa perpustakaan; semuanya terdaftar). Jika kode Anda bergantung pada pustaka statis lain yang bukan ekstensi yang membangun (yaitu, mereka tidak memiliki fungsi Register_ libname ), Anda dapat mencantumkannya di TARGET_RECOVERY_UPDATER_EXTRA_LIBS untuk menautkannya ke pembaru tanpa memanggil fungsi pendaftarannya (tidak ada). Misalnya, jika kode khusus perangkat Anda ingin menggunakan zlib untuk mendekompresi data, Anda harus 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 pembaru dalam paket OTA Anda sekarang dapat memanggil fungsi Anda seperti yang lain. Untuk memprogram ulang perangkat tardis Anda, skrip pembaruan mungkin berisi: tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) . Ini menggunakan versi argumen tunggal dari fungsi package_extract_file() , yang mengembalikan konten file yang diekstrak dari paket pembaruan sebagai gumpalan untuk menghasilkan argumen kedua ke fungsi ekstensi baru.

Pembuatan paket OTA

Komponen terakhir adalah membuat alat pembuatan paket OTA mengetahui tentang data khusus perangkat Anda dan mengeluarkan skrip pembaru yang menyertakan panggilan ke fungsi ekstensi Anda.

Pertama, buat sistem build mengetahui tentang gumpalan data khusus perangkat. Dengan asumsi file data Anda ada di device/yoyodyne/tardis/tardis.dat , nyatakan yang berikut di AndroidBoard.mk perangkat Anda:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

Anda juga bisa meletakkannya di Android.mk, tetapi harus dijaga oleh pemeriksaan perangkat, karena semua file Android.mk di pohon dimuat tidak peduli perangkat apa yang sedang dibangun. (Jika hierarki Anda menyertakan beberapa perangkat, Anda hanya ingin file tardis.dat ditambahkan saat membuat 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

Ini disebut file radio karena alasan historis; mereka mungkin tidak ada hubungannya dengan radio perangkat (jika ada). Mereka hanyalah gumpalan data buram yang disalin sistem build ke file target .zip yang digunakan oleh alat generasi OTA. Saat Anda membangun, 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 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 tambahan. Untuk contoh ini, misalkan 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-fungsi berikut dalam modul (hanya mengimplementasikan yang Anda butuhkan).

FullOTA_Assertions()
Disebut dekat awal menghasilkan OTA penuh. Ini adalah tempat yang baik untuk menyampaikan pernyataan tentang status perangkat saat ini. Jangan memancarkan perintah skrip yang membuat perubahan pada perangkat.
FullOTA_InstallBegin()
Dipanggil setelah semua pernyataan tentang status perangkat telah berlalu tetapi sebelum perubahan apa pun dilakukan. Anda dapat mengeluarkan perintah untuk pembaruan khusus perangkat yang harus dijalankan sebelum hal lain pada perangkat diubah.
FullOTA_InstallEnd()
Disebut di akhir pembuatan skrip, setelah perintah skrip untuk memperbarui boot dan partisi sistem telah dipancarkan. Anda juga dapat mengeluarkan perintah tambahan untuk pembaruan khusus perangkat.
IncrementalOTA_Assertions()
Mirip dengan FullOTA_Assertions() tetapi dipanggil saat membuat paket pembaruan tambahan.
IncrementalOTA_VerifyBegin()
Dipanggil setelah semua pernyataan tentang status perangkat telah berlalu tetapi sebelum perubahan apa pun dilakukan. Anda dapat mengeluarkan perintah untuk pembaruan khusus perangkat yang harus dijalankan sebelum hal lain pada perangkat diubah.
IncrementalOTA_VerifyEnd()
Disebut pada akhir fase verifikasi, ketika skrip selesai mengonfirmasi file yang akan disentuh memiliki konten awal yang diharapkan. Pada titik ini tidak ada pada perangkat yang diubah. Anda juga dapat memancarkan kode untuk verifikasi khusus perangkat tambahan.
IncrementalOTA_InstallBegin()
Dipanggil setelah file yang akan ditambal telah diverifikasi memiliki status yang diharapkan sebelum tetapi sebelum perubahan apa pun dilakukan. Anda dapat mengeluarkan perintah untuk pembaruan khusus perangkat yang harus dijalankan sebelum hal lain pada perangkat diubah.
IncrementalOTA_InstallEnd()
Mirip dengan rekanan paket OTA lengkapnya, ini disebut di akhir pembuatan skrip, setelah perintah skrip untuk memperbarui boot dan partisi sistem telah dipancarkan. Anda juga dapat mengeluarkan perintah tambahan untuk pembaruan khusus perangkat.

Catatan: Jika perangkat kehilangan daya, penginstalan OTA dapat dimulai ulang dari awal. Bersiaplah untuk mengatasi perangkat di mana perintah ini telah dijalankan, sepenuhnya atau sebagian.

Lulus fungsi ke objek info

Lewati fungsi ke satu objek info yang berisi berbagai item berguna:

  • info.input_zip . (Khusus OTA penuh) Objek zipfile.ZipFile untuk file target input .zip.
  • info.source_zip . (Hanya OTA tambahan) Objek zipfile.ZipFile untuk file target sumber .zip (build sudah ada di perangkat saat paket inkremental sedang diinstal).
  • info.target_zip . (Hanya OTA inkremental) Objek zipfile.ZipFile untuk target-files .zip (build yang dipasang oleh paket inkremental pada 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. Panggil info.script.AppendExtra( script_text ) untuk menampilkan teks ke dalam skrip. Pastikan teks keluaran diakhiri dengan titik koma agar tidak menjalankan perintah yang dikeluarkan setelahnya.

Untuk detail tentang objek info, lihat dokumentasi Python Software Foundation untuk arsip ZIP .

Tentukan lokasi modul

Tentukan lokasi skrip releasetools.py perangkat Anda di file BoardConfig.mk Anda:

device/yoyodyne/tardis/BoardConfig.mk
 [...]

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

Jika TARGET_RELEASETOOLS_EXTENSIONS tidak disetel, defaultnya adalah direktori $(TARGET_DEVICE_DIR)/../common ( device/yoyodyne/common dalam contoh ini). Sebaiknya tentukan lokasi skrip releasetools.py secara eksplisit. Saat membuat perangkat tardis, skrip releasetools.py disertakan dalam file target-file .zip ( META/releasetools.py ).

Saat Anda menjalankan alat rilis (baik img_from_target_files atau ota_from_target_files ), skrip releasetools.py dalam file target .zip, jika ada, lebih disukai daripada skrip dari pohon sumber Android. Anda juga dapat secara eksplisit menentukan jalur ke ekstensi khusus perangkat dengan opsi -s (atau --device_specific ), yang menjadi prioritas utama. Ini memungkinkan Anda untuk memperbaiki kesalahan dan membuat perubahan pada ekstensi releasetools dan menerapkan perubahan tersebut ke file target lama.

Sekarang, ketika Anda menjalankan ota_from_target_files , secara otomatis mengambil modul khusus perangkat dari file target_files .zip 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 daftar opsi yang lengkap, lihat komentar ota_from_target_files di build/make/tools/releasetools/ota_from_target_files .

Sideloading

Pemulihan memiliki mekanisme sideloading untuk menginstal paket pembaruan secara manual tanpa mengunduhnya melalui udara oleh sistem utama. Sideloading berguna untuk debugging atau membuat perubahan pada perangkat di mana sistem utama tidak dapat di-boot.

Secara historis, sideloading telah dilakukan melalui pemuatan paket dari kartu SD perangkat; dalam kasus perangkat non-boot, paket dapat dimasukkan ke kartu SD menggunakan komputer lain dan kemudian 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 mengembalikan nilai BuiltinAction berikut:

  • APPLY_EXT . Sideload paket pembaruan dari penyimpanan eksternal ( direktori /sdcard ). Recovery.fstab Anda harus menentukan titik pemasangan /sdcard . This is not usable on devices that emulate an SD card with a symlink to /data (or some similar mechanism). /data is typically not available to recovery because it may be encrypted. The recovery UI displays a menu of .zip files in /sdcard and allows the user to select one.
  • APPLY_CACHE . Similar to loading a package from /sdcard except that the /cache directory (which is always available to recovery) is used instead. From the regular system, /cache is only writable by privileged users, and if the device isn't bootable then the /cache directory can't be written to at all (which makes this mechanism of limited utility).
  • APPLY_ADB_SIDELOAD . Allows user to send a package to the device via a USB cable and the adb development tool. When this mechanism is invoked, recovery starts up its own mini version of the adbd daemon to let adb on a connected host computer talk to it. This mini version supports only a single command: adb sideload filename . The named file is sent from the host machine to the device, which then verifies and installs it just as if it had been on local storage.

A few caveats:

  • Only USB transport is supported.
  • If your recovery runs adbd normally (usually true for userdebug and eng builds), that will be shut down while the device is in adb sideload mode and will be restarted when adb sideload has finished receiving a package. While in adb sideload mode, no adb commands other than sideload work ( logcat , reboot , push , pull , shell , etc. all fail).
  • You cannot exit adb sideload mode on the device. To abort, you can send /dev/null (or anything else that's not a valid package) as the package, and then the device will fail to verify it and stop the installation procedure. The RecoveryUI implementation's CheckKey() method will continue to be called for keypresses, so you can provide a key sequence that reboots the device and works in adb sideload mode.