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

Mulai Android 2.3, platform ini 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 lama.

File peta partisi ditentukan oleh TARGET_RECOVERY_FSTAB; file ini digunakan oleh biner pemulihan dan alat pembuat 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 tipe sistem file yang didukung:

yaffs2
Sistem file yaffs2 di atas perangkat flash MTD. "perangkat" harus berupa nama partisi MTD dan harus muncul di /proc/mtd .
mtd
Partisi MTD mentah, digunakan untuk partisi bootable seperti boot dan pemulihan. MTD sebenarnya tidak di-mount, tetapi titik mount digunakan sebagai kunci untuk menemukan lokasi partisi. "perangkat" harus berupa nama partisi MTD di /proc/mtd .
ext4
Sistem file ext4 di atas perangkat flash eMMc. "perangkat" harus menjadi 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 lokasi perangkat di tabel.
vfat
Sistem file FAT di atas perangkat blok, biasanya untuk penyimpanan eksternal seperti kartu SD. Perangkat tersebut 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 diformat dengan tabel partisi atau tidak).

Semua partisi harus dipasang di direktori root (yaitu nilai titik mount harus dimulai dengan garis miring dan tidak ada 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 bertipe mentah (mtd atau emmc), sedangkan direktori /system , /data , /cache , dan /sdcard (jika tersedia) harus bertipe 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 panjang partisi secara eksplisit. Panjang ini digunakan ketika memformat ulang partisi (misalnya, untuk partisi data pengguna selama operasi penghapusan data/reset pabrik, atau untuk partisi sistem selama instalasi paket OTA lengkap). Jika nilai panjangnya negatif, maka ukuran yang akan diformat diambil dengan menambahkan nilai panjang ke ukuran partisi sebenarnya. Misalnya, pengaturan "length=-16384" berarti 16k terakhir dari partisi tersebut tidak akan ditimpa ketika 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 perangkat2 dan opsi bersifat opsional, sehingga menimbulkan ambiguitas dalam penguraian. Jika entri pada kolom keempat pada baris dimulai dengan karakter '/', maka dianggap sebagai entri perangkat2 ; jika entri tidak diawali dengan karakter '/', maka entri tersebut dianggap sebagai kolom 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 gambarnya disertakan dalam produk yang dipilih.

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

UI Pemulihan

Untuk mendukung perangkat dengan perangkat keras berbeda yang tersedia (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.

Catatan: Anda mungkin melihat pesan yang mengatakan Tidak Ada Perintah di sini. Untuk mengganti teks, tahan tombol daya sambil menekan tombol volume atas. Jika perangkat Anda tidak memiliki kedua tombol tersebut, tekan lama tombol mana saja untuk beralih teks.

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

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

Fungsi header dan item

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 dibungkus), jadi perhatikan lebar layar perangkat Anda.

Sesuaikan Kunci Periksa

Selanjutnya, tentukan implementasi RecoveryUI perangkat Anda. Contoh ini mengasumsikan perangkat tardis memiliki layar, sehingga Anda dapat mewarisi implementasi ScreenRecoveryUI bawaan (lihat instruksi untuk perangkat tanpa screen .) 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 dalam linux/input.h . CheckKey() dipanggil apa pun yang terjadi di sisa pemulihan: saat menu dinonaktifkan, saat aktif, saat instalasi paket, saat penghapusan data pengguna, dll. CheckKey() dapat mengembalikan salah satu dari empat konstanta:

  • BERALIH . Mengaktifkan atau menonaktifkan tampilan menu dan/atau teks log in
  • MENYALAKAN ULANG . Segera reboot perangkat
  • MENGABAIKAN . Abaikan penekanan tombol ini
  • ENQUEUE . Entrikan penekanan tombol ini untuk digunakan secara serempak (yaitu, oleh sistem menu pemulihan jika tampilan diaktifkan)

CheckKey() dipanggil setiap kali peristiwa key-down diikuti oleh peristiwa 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 ditahan. (Dalam rangkaian peristiwa penting di atas, jika CheckKey(B) memanggil IsKeyPressed(A) maka hasilnya akan benar.)

CheckKey() dapat mempertahankan status di kelasnya; ini berguna untuk mendeteksi urutan kunci. Contoh ini menunjukkan pengaturan yang sedikit lebih rumit: tampilan diubah dengan menahan daya dan menekan volume atas, dan perangkat dapat segera di-boot ulang dengan menekan tombol daya lima kali berturut-turut (tanpa tombol lain):

class TardisUI : public ScreenRecoveryUI {
  private:
    int consecutive_power_keys;

  public:
    TardisUI() : consecutive_power_keys(0) {}

    virtual KeyAction CheckKey(int key) {
        if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {
            return TOGGLE;
        }
        if (key == KEY_POWER) {
            ++consecutive_power_keys;
            if (consecutive_power_keys >= 5) {
                return REBOOT;
            }
        } else {
            consecutive_power_keys = 0;
        }
        return ENQUEUE;
    }
};

UI Pemulihan Layar

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

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

Untuk menyetel variabel animation_fps , ganti 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). Ini 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 diurai, namun sebelum tindakan apa pun diambil. Implementasi default tidak melakukan apa pun, jadi Anda tidak perlu menyediakan ini di subkelas jika Anda tidak melakukan apa pun:

   void StartRecovery() {
       // ... do something tardis-specific here, if needed ....
    }

Menyediakan dan mengelola menu pemulihan

Sistem memanggil dua metode untuk mendapatkan daftar baris header dan daftar item. 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

Berikutnya, sediakan fungsi HandleMenuKey() , yang akan melakukan penekanan tombol dan visibilitas menu saat ini, serta memutuskan tindakan yang akan diambil:

   int HandleMenuKey(int key, int visible) {
        if (visible) {
            switch (key) {
              case KEY_VOLUMEDOWN: return kHighlightDown;
              case KEY_VOLUMEUP:   return kHighlightUp;
              case KEY_POWER:      return kInvokeItem;
            }
        }
        return kNoAction;
    }

Metode ini mengambil kode kunci (yang sebelumnya telah diproses dan dimasukkan ke dalam antrean oleh metode CheckKey() objek UI), dan status visibilitas log menu/teks saat ini. Nilai yang dikembalikan adalah bilangan bulat. Jika nilainya 0 atau lebih tinggi, maka itu diambil sebagai posisi item menu, yang langsung dipanggil (lihat metode InvokeMenuItem() di bawah). Kalau tidak, itu bisa menjadi salah satu dari konstanta yang telah ditentukan berikut ini:

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

Seperti yang tersirat dalam argumen yang terlihat, HandleMenuKey() dipanggil meskipun menu tidak terlihat. Berbeda dengan CheckKey() , ini tidak dipanggil ketika pemulihan sedang melakukan sesuatu seperti menghapus data atau menginstal paket—ini dipanggil hanya ketika pemulihan tidak aktif dan menunggu masukan.

Mekanisme trackball

Jika perangkat Anda memiliki mekanisme masukan seperti trackball (menghasilkan peristiwa masukan dengan tipe EV_REL dan kode REL_Y), pemulihan mensintesis penekanan tombol KEY_UP dan KEY_DOWN setiap kali perangkat masukan seperti trackball melaporkan gerakan di sumbu Y. Yang perlu Anda lakukan hanyalah 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 melakukan boot ulang atau mengalihkan tampilan.

Kunci 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 baik menunya 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 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 nilainya jika diinginkan.

PanggilMenuItem

Selanjutnya, sediakan metode InvokeMenuItem() yang memetakan posisi integer dalam array item yang dikembalikan 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 mengembalikan anggota enum BuiltinAction mana pun 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 fungsionalitas pemulihan tambahan di luar apa yang ada di sistem: Tambahkan item untuk itu di menu Anda, jalankan di sini ketika item menu tersebut dipanggil, dan kembalikan NO_ACTION sehingga sistem tidak melakukan apa pun.

BuiltinAction berisi nilai-nilai berikut:

  • TIDAK ADA TINDAKAN . Tidak melakukan apapun.
  • MENYALAKAN 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 Sideload .
  • 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() , bersifat opsional dan dipanggil setiap kali operasi penghapusan data dimulai (baik dari pemulihan melalui menu atau ketika pengguna 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 yang dikembalikan diabaikan. Data pengguna dan partisi cache akan dihapus baik Anda berhasil atau gagal.

   int WipeData() {
       // ... do something tardis-specific here, if needed ....
       return 0;
    }

Buat perangkat

Terakhir, sertakan beberapa boilerplate di akhir file recovery_ui.cpp untuk fungsi make_device() yang membuat dan 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 perpustakaan 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, memenuhi bilah kemajuan instalasi, dan melakukan booting kembali ke sistem baru tanpa masukan dari pengguna. Jika terjadi masalah pembaruan sistem, satu-satunya tindakan pengguna yang dapat diambil adalah menghubungi layanan pelanggan.

Antarmuka hanya gambar menghilangkan kebutuhan akan pelokalan. Namun, mulai Android 5.0, pembaruan dapat menampilkan serangkaian teks (misalnya, "Menginstal pembaruan sistem...") bersama dengan gambar. Untuk mengetahui detailnya, lihat Teks pemulihan yang dilokalkan .

Android 5.0 dan lebih baru

UI pemulihan Android 5.0 dan yang lebih baru menggunakan dua gambar utama: gambar kesalahan dan animasi pemasangan .

gambar ditampilkan selama kesalahan ota

Gambar 1. icon_error.png

gambar ditampilkan selama instalasi ota

Gambar 2. icon_installing.png

Animasi pemasangan direpresentasikan sebagai gambar PNG tunggal dengan bingkai animasi berbeda yang dijalin berdasarkan baris (itulah sebabnya Gambar 2 tampak terjepit). Misalnya, untuk animasi tujuh bingkai berukuran 200x200, buatlah satu gambar berukuran 200x1400 dengan bingkai pertama adalah baris 0, 7, 14, 21, ...; frame kedua adalah baris 1, 8, 15, 22, ...; dll. Gambar gabungan menyertakan potongan teks yang menunjukkan jumlah bingkai animasi dan jumlah bingkai per detik (FPS). Alat bootable/recovery/interlace-frames.py mengambil sekumpulan bingkai masukan dan menggabungkannya ke dalam gambar komposit yang diperlukan yang digunakan oleh pemulihan.

Gambar default tersedia dalam kepadatan berbeda dan terletak di bootable/recovery/res-$DENSITY/images (misalnya, bootable/recovery/res-hdpi/images ). Untuk menggunakan gambar statis selama instalasi, Anda hanya perlu menyediakan gambar icon_installing.png dan mengatur jumlah frame dalam animasi ke 0 (ikon kesalahan tidak dianimasikan; selalu berupa gambar statis).

Android 4.x dan sebelumnya

UI pemulihan Android 4.x dan versi sebelumnya menggunakan gambar kesalahan (ditampilkan di atas) dan animasi penginstalan ditambah beberapa gambar overlay:

gambar ditampilkan selama instalasi ota

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 ditambah overlay pertama

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

gambar komposit pemasangan ditambah hamparan ketujuh

Gambar 7. Memasang animasi frame 7 (icon_installing.png + icon_installing_overlay07.png)

Bingkai berikutnya ditampilkan dengan hanya menggambar gambar overlay berikutnya di atas gambar yang sudah ada; gambar dasar tidak digambar ulang.

Jumlah frame dalam animasi, kecepatan yang diinginkan, dan offset x dan y dari overlay relatif terhadap basis ditentukan oleh variabel anggota kelas ScreenRecoveryUI. Saat menggunakan gambar khusus dan bukan 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 mengonversi sekumpulan bingkai gambar ke 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 instalasi, Anda hanya perlu menyediakan gambar icon_installing.png dan mengatur jumlah frame dalam animasi ke 0 (ikon kesalahan tidak dianimasikan; selalu berupa gambar statis).

Teks pemulihan yang dilokalkan

Android 5.x menampilkan string teks (misalnya, "Menginstal pembaruan sistem...") beserta gambarnya. Saat sistem utama melakukan booting ke pemulihan, sistem tersebut meneruskan lokal pengguna saat ini sebagai opsi baris perintah ke pemulihan. Untuk setiap pesan yang akan 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 yang dilokalkan untuk pesan pemulihan

Teks pemulihan dapat menampilkan pesan berikut:

  • Menginstal pembaruan sistem...
  • Kesalahan!
  • Menghapus... (saat melakukan penghapusan data/reset pabrik)
  • Tidak ada perintah (saat 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 booting ke pemulihan secara manual, lokal mungkin tidak tersedia dan tidak ada teks yang ditampilkan. Jangan jadikan pesan teks penting untuk proses pemulihan.

Catatan: Antarmuka tersembunyi yang menampilkan pesan log dan memungkinkan pengguna 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 masukan, yang ukurannya harus 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 masukan di atas, tampilkan:

bilah kemajuan sebesar 1%

Gambar 11. Bilah kemajuan pada 1%>

bilah kemajuan sebesar 10%

Gambar 12. Bilah kemajuan sebesar 10%

bilah kemajuan di 50%

Gambar 13. Bilah kemajuan sebesar 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 sebuah file ditemukan di direktori itu, sistem pembangunan akan menggunakannya sebagai preferensi terhadap 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 untuk pemulihan dan merupakan bahasa kanan-ke-kiri (RTL) (Arab, Ibrani, dll.), bilah kemajuan akan terisi dari kanan ke kiri.

Perangkat tanpa layar

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

RecoveryUI memiliki metode untuk menangani operasi UI tingkat rendah seperti "mengalihkan tampilan", "memperbarui bilah kemajuan", "menampilkan menu", "mengubah pilihan menu", dll. Anda dapat menggantinya untuk menyediakan antarmuka yang sesuai untuk perangkat Anda. Mungkin perangkat Anda memiliki LED sehingga Anda dapat menggunakan warna atau pola kedipan 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 aksesnya dengan implementasi CheckKey() dan HandleMenuKey() yang tidak pernah mengaktifkan tampilan atau memilih item menu. Dalam hal ini , banyak metode RecoveryUI yang perlu Anda sediakan hanya berupa stub kosong.)

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

Pembaru

Anda dapat menggunakan kode khusus perangkat dalam instalasi paket pembaruan dengan menyediakan fungsi ekstensi Anda sendiri yang dapat dipanggil dari dalam skrip pembaru Anda. Berikut 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 pemanggilan fungsi, 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 argumen mana yang akan 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 mengosongkan sumber daya apa pun yang Anda miliki dan segera mengembalikan NULL (ini akan membatalkan tumpukan edify). Jika tidak, Anda mengambil kepemilikan atas Nilai yang dikembalikan dan bertanggung jawab untuk memanggil FreeValue() pada akhirnya.

Misalkan fungsi memerlukan dua argumen: kunci bernilai string dan image bernilai blob. 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 jadi membosankan untuk banyak argumen. Fungsi ReadValueArgs() dapat mempermudah hal ini. Daripada 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; akan lebih mudah untuk melakukannya dengan satu pernyataan if dengan mengorbankan menghasilkan pesan kesalahan yang kurang spesifik ketika gagal. Namun ReadValueArgs() menangani evaluasi setiap argumen dan membebaskan semua argumen yang telah dievaluasi sebelumnya (serta menyetel pesan kesalahan yang berguna) jika salah satu evaluasi gagal. Anda dapat menggunakan fungsi kemudahan ReadValueVarArgs() untuk mengevaluasi sejumlah variabel argumen (fungsi ini mengembalikan array 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 ditunjuk oleh Value* ini —khususnya anggota data.

Dalam contoh ini, Anda ingin mengembalikan nilai benar atau salah untuk menunjukkan keberhasilan. Ingat konvensi bahwa string kosong adalah false dan semua string lainnya adalah true . Anda harus malloc objek Value dengan salinan string konstan malloc untuk dikembalikan, karena pemanggil akan free() keduanya. Jangan lupa 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 menjadi 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 dalam penerjemah edify, sediakan 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 fungsi khusus device . whatever untuk menghindari konflik dengan fungsi bawaan di masa depan yang ditambahkan.

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 terdapat 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 banyak perpustakaan; semuanya terdaftar). Jika kode Anda bergantung pada perpustakaan statis lain yang bukan merupakan ekstensi edify (yaitu, mereka tidak memiliki fungsi Register_ libname ), Anda dapat mencantumkannya di TARGET_RECOVERY_UPDATER_EXTRA_LIBS untuk menautkannya ke pembaru tanpa memanggil fungsi pendaftarannya (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 pembaru dalam paket OTA Anda sekarang dapat memanggil fungsi Anda seperti fungsi lainnya. 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 bawaan package_extract_file() , yang mengembalikan konten file yang diekstraksi dari paket pembaruan sebagai gumpalan untuk menghasilkan argumen kedua ke fungsi ekstensi baru.

Pembuatan paket OTA

Komponen terakhir adalah membuat alat pembuat paket OTA mengetahui 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 , deklarasikan perintah berikut di AndroidBoard.mk perangkat Anda:

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

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

Anda juga bisa meletakkannya di Android.mk, namun harus dijaga dengan pemeriksaan perangkat, karena semua file Android.mk di pohon dimuat, apa pun perangkat yang sedang dibuat. (Jika pohon Anda mencakup 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 oleh sistem build ke dalam file target .zip yang digunakan oleh alat generasi 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 piton

Untuk memperluas alat rilis, tulis modul Python (harus diberi nama releasetools.py) yang dapat dipanggil oleh alat tersebut 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 (implementasi hanya fungsi-fungsi yang Anda perlukan).

FullOTA_Assertions()
Dipanggil mendekati awal pembuatan OTA penuh. Ini adalah tempat yang baik untuk menyampaikan pernyataan tentang kondisi perangkat saat ini. Jangan mengeluarkan 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()
Dipanggil pada akhir pembuatan skrip, setelah perintah skrip untuk memperbarui boot dan partisi sistem dikeluarkan. 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()
Dipanggil pada akhir fase verifikasi, ketika skrip telah selesai mengonfirmasi bahwa file yang akan disentuhnya memiliki konten awal yang diharapkan. Saat ini, tidak ada perubahan apa pun pada perangkat. Anda juga dapat mengeluarkan kode untuk verifikasi khusus perangkat tambahan.
IncrementalOTA_InstallBegin()
Dipanggil setelah file yang akan ditambal telah diverifikasi memiliki status sebelumnya yang diharapkan 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 paket OTA lengkapnya, ini dipanggil pada akhir pembuatan skrip, setelah perintah skrip untuk memperbarui boot dan partisi sistem dikeluarkan. Anda juga dapat mengeluarkan perintah tambahan untuk pembaruan khusus perangkat.

Catatan: Jika perangkat kehilangan daya, instalasi OTA mungkin dimulai ulang dari awal. Bersiaplah untuk menghadapi perangkat yang perintahnya telah dijalankan, seluruhnya atau sebagian.

Meneruskan fungsi ke objek info

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

  • info.input_zip . (Hanya OTA lengkap) Objek zipfile.ZipFile untuk file target input .zip.
  • info.source_zip . (Hanya OTA inkremental) 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 file target target .zip (build yang dipasang oleh paket inkremental pada 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.skrip . 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 sehingga tidak ada 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). Yang terbaik adalah menentukan secara eksplisit lokasi skrip releasetools.py. Saat membuat perangkat tardis, skrip releasetools.py disertakan dalam file .zip file target ( META/releasetools.py ).

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

Sekarang, saat Anda menjalankan ota_from_target_files , modul tersebut secara 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

Alternatifnya, 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 selengkapnya, lihat komentar ota_from_target_files di build/make/tools/releasetools/ota_from_target_files .

Mekanisme sideloading

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

Secara historis, sideloading dilakukan dengan memuat paket dari kartu SD perangkat; dalam hal perangkat non-boot, paket dapat dimasukkan ke kartu SD menggunakan komputer lain dan kemudian 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 memuatnya melalui USB menggunakan adb.

Untuk menjalankan setiap mekanisme sideload, metode Device::InvokeMenuItem() perangkat Anda dapat mengembalikan nilai BuiltinAction berikut:

  • BERLAKU_EXT . Sideload paket pembaruan dari penyimpanan eksternal (direktori /sdcard ). Recovery.fstab Anda harus menentukan titik pemasangan /sdcard . Ini tidak dapat digunakan pada perangkat yang meniru kartu SD dengan symlink ke /data (atau mekanisme serupa). /data biasanya tidak tersedia untuk pemulihan karena mungkin dienkripsi. UI pemulihan menampilkan menu file .zip di /sdcard dan memungkinkan pengguna untuk memilih salah satu.
  • APPLY_CACHE . Mirip dengan memuat paket dari /sdcard kecuali direktori /cache ( yang selalu tersedia untuk pemulihan) yang digunakan. Dari sistem reguler, /cache hanya dapat ditulis oleh pengguna yang memiliki hak istimewa, dan jika perangkat tidak dapat di-boot maka direktori /cache tidak dapat ditulisi sama sekali (yang menjadikan mekanisme ini memiliki utilitas terbatas).
  • APPLY_ADB_SIDELOAD . Memungkinkan pengguna mengirim paket ke perangkat melalui kabel USB dan alat pengembangan adb. Ketika mekanisme ini dipanggil, pemulihan akan memulai daemon adbd versi mininya sendiri agar adb pada komputer host yang terhubung dapat berkomunikasi dengannya. Versi mini ini hanya mendukung satu perintah: adb sideload filename . File bernama dikirim dari mesin host ke perangkat, yang kemudian memverifikasi dan menginstalnya seolah-olah file tersebut berada di penyimpanan lokal.

Beberapa peringatan:

  • Hanya transportasi USB yang didukung.
  • Jika pemulihan Anda berjalan adbd secara normal (biasanya berlaku untuk userdebug dan build eng), pemulihan tersebut akan dimatikan saat perangkat berada dalam mode adb sideload dan akan dimulai ulang ketika adb sideload 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 pada perangkat. Untuk membatalkan, Anda dapat mengirim /dev/null (atau apa pun yang bukan paket valid) sebagai paket, lalu perangkat akan gagal memverifikasinya dan menghentikan prosedur instalasi. Metode CheckKey() implementasi RecoveryUI akan terus dipanggil saat tombol ditekan, sehingga Anda dapat memberikan urutan kunci yang me-reboot perangkat dan bekerja dalam mode sideload adb.