รหัสเฉพาะอุปกรณ์

ระบบการกู้คืนมี Hook หลายรายการสำหรับการแทรกรหัสเฉพาะของอุปกรณ์เพื่อให้การอัปเดต OTA สามารถอัปเดตส่วนต่างๆ ของอุปกรณ์นอกเหนือจากระบบ Android ได้ด้วย (เช่น เบสแบนด์ หรือโปรเซสเซอร์วิทยุ)

ส่วนและตัวอย่างต่อไปนี้จะปรับแต่งอุปกรณ์ tardis ที่ผลิตโดยผู้ให้บริการ yoyodyne

แผนที่พาร์ติชัน

ตั้งแต่ Android 2.3 เป็นต้นมา แพลตฟอร์มรองรับอุปกรณ์แฟลช eMMc และระบบไฟล์ ext4 ที่ทำงาน ในอุปกรณ์เหล่านั้น นอกจากนี้ยังรองรับอุปกรณ์แฟลช Memory Technology Device (MTD) และระบบไฟล์ yaffs2 จากรุ่นเก่าด้วย

ไฟล์แผนที่พาร์ติชันระบุโดย TARGET_RECOVERY_FSTAB ซึ่งไบนารีการกู้คืนและเครื่องมือสร้างแพ็กเกจจะใช้ไฟล์นี้ คุณระบุชื่อของไฟล์แผนที่ได้ใน TARGET_RECOVERY_FSTAB ใน BoardConfig.mk

ตัวอย่างไฟล์แผนที่พาร์ติชันอาจมีลักษณะดังนี้

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

ยกเว้น /sdcard ซึ่งไม่บังคับ คุณต้องกำหนดจุดติดตั้งทั้งหมดในตัวอย่างนี้ (อุปกรณ์อาจเพิ่มพาร์ติชันพิเศษด้วย) ระบบไฟล์ที่รองรับมี 5 ประเภทดังนี้

yaffs2
ระบบไฟล์ yaffs2 บนอุปกรณ์แฟลช MTD "device" ต้องเป็นชื่อของพาร์ติชัน MTD และต้องปรากฏใน /proc/mtd
mtd
พาร์ติชัน MTD ดิบที่ใช้สำหรับพาร์ติชันที่บูตได้ เช่น บูตและการกู้คืน MTD ไม่ได้ ต่อเชื่อมจริง แต่ใช้จุดต่อเชื่อมเป็นคีย์เพื่อค้นหาพาร์ติชัน "device" ต้องเป็นชื่อของพาร์ติชัน MTD ใน /proc/mtd
ext4
ระบบไฟล์ ext4 บนอุปกรณ์แฟลช eMMc "device" ต้องเป็นเส้นทางของอุปกรณ์บล็อก
emmc
อุปกรณ์บล็อก eMMc ดิบ ใช้สำหรับพาร์ติชันที่บูตได้ เช่น บูตและการกู้คืน เช่นเดียวกับ ประเภท mtd ระบบจะไม่ต่อเชื่อม eMMc จริงๆ แต่จะใช้สตริงจุดต่อเชื่อมเพื่อค้นหา อุปกรณ์ในตาราง
vfat
ระบบไฟล์ FAT ที่อยู่บนอุปกรณ์บล็อก ซึ่งโดยทั่วไปใช้สำหรับพื้นที่เก็บข้อมูลภายนอก เช่น การ์ด SD device คืออุปกรณ์บล็อก ส่วน device2 คืออุปกรณ์บล็อกที่ 2 ที่ระบบพยายามติดตั้งหาก การติดตั้งอุปกรณ์หลักไม่สำเร็จ (เพื่อความเข้ากันได้กับการ์ด SD ซึ่งอาจมีหรือไม่มี การจัดรูปแบบด้วยตารางพาร์ติชัน)

ต้องติดตั้งพาร์ติชันทั้งหมดในไดเรกทอรีราก (กล่าวคือ ค่าจุดติดตั้งต้องขึ้นต้นด้วยเครื่องหมายทับและไม่มีเครื่องหมายทับอื่นๆ) ข้อจำกัดนี้มีผลกับการต่อเชื่อมระบบไฟล์ใน Recovery เท่านั้น ส่วนระบบหลักจะต่อเชื่อมได้ทุกที่ ไดเรกทอรี /boot, /recovery และ /misc ควรเป็นประเภทดิบ (mtd หรือ emmc) ส่วนไดเรกทอรี /system, /data, /cache และ /sdcard (หากมี) ควรเป็นประเภทระบบไฟล์ (yaffs2, ext4 หรือ vfat)

ตั้งแต่ Android 3.0 เป็นต้นไป ไฟล์ recovery.fstab จะมีฟิลด์เพิ่มเติมที่ไม่บังคับ options ปัจจุบันตัวเลือกที่กำหนดไว้มีเพียงความยาว ซึ่งช่วยให้คุณระบุความยาวของพาร์ติชันได้อย่างชัดเจน ระบบจะใช้ความยาวนี้เมื่อจัดรูปแบบพาร์ติชันใหม่ (เช่น สำหรับพาร์ติชัน userdata ระหว่างการล้างข้อมูล/การรีเซ็ตเป็นค่าเริ่มต้น หรือสำหรับ พาร์ติชันระบบระหว่างการติดตั้งแพ็กเกจ OTA แบบเต็ม) หากค่าความยาวเป็นค่าลบ ระบบจะกำหนดขนาดที่จะจัดรูปแบบโดยการบวกค่าความยาวกับขนาดพาร์ติชันจริง ตัวอย่างเช่น การตั้งค่า "length=-16384" หมายความว่าระบบจะไม่เขียนทับข้อมูล 16k สุดท้ายของพาร์ติชันนั้นเมื่อมีการจัดรูปแบบพาร์ติชันใหม่ ซึ่งรองรับฟีเจอร์ต่างๆ เช่น การเข้ารหัส พาร์ติชัน userdata (ที่จัดเก็บข้อมูลเมตาการเข้ารหัสไว้ที่ส่วนท้ายของพาร์ติชันที่ ไม่ควรเขียนทับ)

หมายเหตุ: ฟิลด์ device2 และ options เป็นฟิลด์ที่ไม่บังคับ ซึ่งจะทำให้เกิด ความคลุมเครือในการแยกวิเคราะห์ หากรายการในช่องที่ 4 ของบรรทัดขึ้นต้นด้วยอักขระ "/" ระบบจะถือว่าเป็นรายการ device2 หากรายการไม่ได้ขึ้นต้นด้วยอักขระ "/" ระบบจะถือว่าเป็นช่องตัวเลือก

ภาพเคลื่อนไหวขณะเปิดเครื่อง

ผู้ผลิตอุปกรณ์สามารถปรับแต่งภาพเคลื่อนไหวที่แสดงเมื่ออุปกรณ์ Android กำลังบูตได้ โดยสร้างไฟล์ .zip ที่จัดระเบียบและอยู่ในตำแหน่งตาม ข้อกำหนดใน รูปแบบภาพเคลื่อนไหวขณะบูต

สำหรับอุปกรณ์ Android Things คุณสามารถอัปโหลดไฟล์ที่ซิปไว้ในคอนโซล Android Things เพื่อให้ระบบรวมรูปภาพไว้ใน ผลิตภัณฑ์ที่เลือก

หมายเหตุ: รูปภาพเหล่านี้ต้องเป็นไปตามหลักเกณฑ์การใช้แบรนด์ Android ดูหลักเกณฑ์การใช้แบรนด์ได้ในส่วน Android ของฮับการตลาดของพาร์ทเนอร์

UI การกู้คืน

หากต้องการรองรับอุปกรณ์ที่มีฮาร์ดแวร์ที่พร้อมใช้งานแตกต่างกัน (ปุ่มจริง, LED, หน้าจอ ฯลฯ) คุณสามารถปรับแต่งอินเทอร์เฟซการกู้คืนเพื่อแสดงสถานะและเข้าถึงฟีเจอร์ที่ซ่อนอยู่ซึ่งดำเนินการด้วยตนเอง สำหรับอุปกรณ์แต่ละเครื่องได้

เป้าหมายของคุณคือการสร้างไลบรารีแบบคงที่ขนาดเล็กที่มีออบเจ็กต์ C++ 2-3 รายการเพื่อมอบฟังก์ชันการทำงานเฉพาะอุปกรณ์ ระบบจะใช้ไฟล์ bootable/recovery/default_device.cpp โดยค่าเริ่มต้น และเป็นจุดเริ่มต้นที่ดี ในการคัดลอกเมื่อเขียนเวอร์ชันของไฟล์นี้สำหรับอุปกรณ์

หมายเหตุ: คุณอาจเห็นข้อความที่ระบุว่าไม่มีคำสั่งที่นี่ หากต้องการสลับ ข้อความ ให้กดปุ่มเปิด/ปิดค้างไว้ขณะกดปุ่มเพิ่มระดับเสียง หากอุปกรณ์ไม่มีปุ่มทั้ง 2 ปุ่ม ให้กดปุ่มใดก็ได้ค้างไว้เพื่อสลับข้อความ

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

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

ฟังก์ชันส่วนหัวและรายการ

คลาสอุปกรณ์ต้องมีฟังก์ชันสำหรับแสดงส่วนหัวและรายการที่ปรากฏในเมนูการกู้คืนที่ซ่อนอยู่ ส่วนหัวจะอธิบายวิธีใช้งานเมนู (เช่น ตัวควบคุมเพื่อเปลี่ยน/เลือก รายการที่ไฮไลต์)

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

หมายเหตุ: ระบบจะตัดบรรทัดยาวๆ (ไม่ตัดคำ) ดังนั้นโปรดคำนึงถึงความกว้างของหน้าจอ อุปกรณ์

ปรับแต่ง CheckKey

จากนั้นกำหนดการใช้งาน RecoveryUI ของอุปกรณ์ ตัวอย่างนี้ถือว่าอุปกรณ์ tardis มีหน้าจอ คุณจึงรับค่าจาก ScreenRecoveryUIimplementation ในตัวได้ (ดูวิธีการสำหรับ อุปกรณ์ที่ไม่มีหน้าจอ) ฟังก์ชันเดียวที่ใช้ ปรับแต่งจาก ScreenRecoveryUI คือ CheckKey() ซึ่งจะจัดการคีย์แบบอะซิงโครนัส เริ่มต้น

class TardisUI : public ScreenRecoveryUI {
  public:
    virtual KeyAction CheckKey(int key) {
        if (key == KEY_HOME) {
            return TOGGLE;
        }
        return ENQUEUE;
    }
};

ค่าคงที่ KEY

ค่าคงที่ KEY_* กำหนดไว้ใน linux/input.h CheckKey() จะเรียกใช้ไม่ว่าจะมีอะไรเกิดขึ้นในการกู้คืนส่วนอื่นๆ ก็ตาม เช่น เมื่อสลับเมนูเป็นปิด เมื่อเมนูเปิดอยู่ ระหว่างการติดตั้งแพ็กเกจ ระหว่างการล้างข้อมูลผู้ใช้ ฯลฯ โดยจะแสดงค่าคงที่ 1 ใน 4 ค่าต่อไปนี้

  • สลับ เปิดหรือปิดการแสดงเมนูและ/หรือบันทึกข้อความ
  • รีบูต รีบูตอุปกรณ์ทันที
  • ไม่สนใจ เพิกเฉยต่อการกดแป้นนี้
  • ENQUEUE จัดคิวการกดแป้นนี้เพื่อใช้แบบพร้อมกัน (เช่น โดยระบบเมนูการกู้คืน หากเปิดใช้จอแสดงผล)

CheckKey() จะเรียกใช้ทุกครั้งที่เหตุการณ์กดแป้นตามด้วยเหตุการณ์ปล่อยแป้นสำหรับ แป้นเดียวกัน (ลำดับเหตุการณ์ A-down B-down B-up A-up จะทำให้มีการเรียกใช้ CheckKey(B) เท่านั้น) CheckKey() สามารถเรียกใช้ IsKeyPressed() เพื่อดูว่ามีการกดปุ่มอื่นๆ ค้างไว้หรือไม่ (ในลําดับเหตุการณ์สําคัญข้างต้น หากCheckKey(B) เรียก IsKeyPressed(A) จะ แสดงผลเป็นจริง)

CheckKey() สามารถคงสถานะไว้ในคลาสของตัวเองได้ ซึ่งอาจมีประโยชน์ในการตรวจจับลำดับของคีย์ ตัวอย่างนี้แสดงการตั้งค่าที่ซับซ้อนขึ้นเล็กน้อย โดยจะสลับจอแสดงผลโดย กดปุ่มเปิด/ปิดค้างไว้แล้วกดปุ่มเพิ่มระดับเสียง และรีบูตอุปกรณ์ได้ทันทีโดย กดปุ่มเปิด/ปิด 5 ครั้งติดต่อกัน (โดยไม่มีปุ่มอื่นแทรก)

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

เมื่อใช้รูปภาพของคุณเอง (ไอคอนข้อผิดพลาด ภาพเคลื่อนไหวการติดตั้ง แถบความคืบหน้า) กับ ScreenRecoveryUI คุณสามารถตั้งค่าตัวแปร animation_fps เพื่อควบคุมความเร็วใน เฟรมต่อวินาที (FPS) ของภาพเคลื่อนไหวได้

หมายเหตุ: สคริปต์ interlace-frames.py ปัจจุบันช่วยให้คุณ จัดเก็บข้อมูล animation_fps ไว้ในรูปภาพได้ ใน Android เวอร์ชันก่อนหน้า คุณต้องตั้งค่า animation_fps เอง

หากต้องการตั้งค่าตัวแปร animation_fps ให้ลบล้างฟังก์ชัน ScreenRecoveryUI::Init() ในคลาสย่อย ตั้งค่า แล้วเรียกใช้ฟังก์ชัน parent Init() เพื่อทำการเริ่มต้นให้เสร็จสมบูรณ์ ค่าเริ่มต้น (20 FPS) สอดคล้องกับรูปภาพการกู้คืนเริ่มต้น เมื่อใช้รูปภาพเหล่านี้ คุณไม่จำเป็นต้องระบุ Init()ฟังก์ชัน ดูรายละเอียดเกี่ยวกับรูปภาพได้ที่ รูปภาพ UI การกู้คืน

คลาสของอุปกรณ์

หลังจากติดตั้งใช้งาน RecoveryUI แล้ว ให้กำหนดคลาสอุปกรณ์ (คลาสย่อยจาก คลาสอุปกรณ์ในตัว) โดยควรสร้างอินสแตนซ์เดียวของคลาส UI และแสดงผลอินสแตนซ์นั้น จากฟังก์ชัน GetUI()

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

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

    RecoveryUI* GetUI() { return ui; }

StartRecovery

ระบบจะเรียกใช้เมธอด StartRecovery() เมื่อเริ่มการกู้คืน หลังจากที่ UI ได้รับการเริ่มต้นและหลังจากที่ได้แยกวิเคราะห์อาร์กิวเมนต์แล้ว แต่ก่อนที่จะมีการดำเนินการใดๆ การติดตั้งใช้งานเริ่มต้นจะไม่ดำเนินการใดๆ ดังนั้นคุณจึงไม่จำเป็นต้องระบุในคลาสย่อยหากไม่มีอะไรต้องทำ

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

จัดหาและจัดการเมนูการกู้คืน

ระบบจะเรียกใช้ 2 เมธอดเพื่อรับรายการบรรทัดส่วนหัวและรายการสินค้า ในการ ติดตั้งใช้งานนี้ ฟังก์ชันจะแสดงอาร์เรย์แบบคงที่ที่กำหนดไว้ที่ด้านบนของไฟล์

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

HandleMenuKey

จากนั้นให้ระบุHandleMenuKey()ฟังก์ชัน ซึ่งจะรับการกดแป้นและการแสดงเมนูปัจจุบัน และตัดสินใจว่าจะดำเนินการใด

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

เมธอดนี้ใช้รหัสคีย์ (ซึ่งได้รับการประมวลผลและจัดคิวไว้ก่อนหน้านี้โดย CheckKey() เมธอดของออบเจ็กต์ UI) และสถานะปัจจุบันของเมนู/บันทึกข้อความ ระดับการเข้าถึง ค่าที่ส่งคืนจะเป็นจำนวนเต็ม หากค่าเป็น 0 ขึ้นไป ระบบจะถือว่าค่าดังกล่าวเป็น ตำแหน่งของรายการเมนู ซึ่งจะเรียกใช้ทันที (ดูเมธอด InvokeMenuItem() ด้านล่าง) มิเช่นนั้น อาจเป็นค่าใดค่าหนึ่งต่อไปนี้ ค่าคงที่ที่กำหนดไว้ล่วงหน้า

  • kHighlightUp ย้ายไฮไลต์เมนูไปยังรายการก่อนหน้า
  • kHighlightDown ย้ายไฮไลต์เมนูไปยังรายการถัดไป
  • kInvokeItem เรียกใช้รายการที่ไฮไลต์อยู่
  • kNoAction ไม่ดำเนินการใดๆ กับการกดแป้นนี้

ตามที่อาร์กิวเมนต์ที่มองเห็นได้ระบุไว้ ระบบจะเรียกใช้ HandleMenuKey() แม้ว่าเมนูจะไม่แสดงก็ตาม ซึ่งแตกต่างจาก CheckKey() ตรงที่ ไม่ได้เรียกใช้ขณะที่การกู้คืนกำลังทำ บางอย่าง เช่น ล้างข้อมูลหรือติดตั้งแพ็กเกจ แต่จะเรียกใช้เฉพาะเมื่อการกู้คืนไม่ได้ใช้งาน และรอรับอินพุต

กลไกแทร็กบอล

หากอุปกรณ์มีกลไกการป้อนข้อมูลแบบแทร็กบอล (สร้างเหตุการณ์อินพุตที่มีประเภท EV_REL และโค้ด REL_Y) การกู้คืนจะสังเคราะห์การกดปุ่ม KEY_UP และ KEY_DOWN ทุกครั้งที่ อุปกรณ์ป้อนข้อมูลแบบแทร็กบอลรายงานการเคลื่อนไหวในแกน Y คุณเพียงแค่ต้องแมปเหตุการณ์ KEY_UP และ KEY_DOWN กับการดำเนินการในเมนู การแมปนี้ไม่เกิดขึ้นสำหรับ CheckKey() คุณจึงใช้การเคลื่อนที่ของแทร็กบอลเป็นทริกเกอร์สำหรับการรีบูตหรือ สลับการแสดงผลไม่ได้

แป้นกดร่วม

หากต้องการตรวจสอบว่ามีการกดคีย์ค้างไว้เป็นตัวปรับแต่งหรือไม่ ให้เรียกใช้เมธอด IsKeyPressed() ของออบเจ็กต์ UI ของคุณเอง เช่น ในอุปกรณ์บางรุ่น การกด Alt-W ในการกู้คืนจะเริ่มล้างข้อมูลไม่ว่าเมนูจะแสดงหรือไม่ก็ตาม คุณสามารถติดตั้งใช้งานได้ดังนี้

   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
        }
        ...
    }

หมายเหตุ: หาก visible เป็นเท็จ ก็ไม่สมเหตุสมผลที่จะแสดงค่าพิเศษ ที่จัดการเมนู (ย้ายไฮไลต์ เรียกใช้รายการที่ไฮไลต์) เนื่องจากผู้ใช้ไม่สามารถ เห็นไฮไลต์ แต่คุณสามารถคืนค่าได้หากต้องการ

InvokeMenuItem

จากนั้นระบุInvokeMenuItem()เมธอดที่แมปตำแหน่งจำนวนเต็มในอาร์เรย์ ของรายการที่แสดงผลโดย GetMenuItems() ไปยังการดำเนินการ สําหรับอาร์เรย์ของรายการใน ตัวอย่าง tardis ให้ใช้

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

เมธอดนี้จะแสดงสมาชิกใดก็ได้ของ enum BuiltinAction เพื่อบอกให้ระบบดำเนินการนั้น (หรือสมาชิก NO_ACTION หากคุณต้องการให้ระบบไม่ดำเนินการใดๆ) ส่วนนี้ใช้สำหรับ ระบุฟังก์ชันการกู้คืนเพิ่มเติมนอกเหนือจากที่มีอยู่ในระบบ: เพิ่มรายการสำหรับฟังก์ชันนี้ใน เมนูของคุณ ดำเนินการที่นี่เมื่อมีการเรียกใช้รายการเมนูนั้น และส่งคืน NO_ACTION เพื่อให้ระบบ ไม่ต้องทำอย่างอื่น

BuiltinAction มีค่าต่อไปนี้

  • NO_ACTION ไม่ดำเนินการใดๆ
  • รีบูต ออกจากโหมดการกู้คืนและรีบูตอุปกรณ์ตามปกติ
  • APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD ติดตั้งแพ็กเกจอัปเดตจากที่ต่างๆ โปรดดูรายละเอียดที่หัวข้อการโหลดแอปจากแหล่งที่ไม่รู้จัก
  • WIPE_CACHE จัดรูปแบบพาร์ติชันแคชใหม่เท่านั้น ไม่ต้องมีการยืนยันเนื่องจาก ค่อนข้างไม่มีอันตราย
  • WIPE_DATA ฟอร์แมตพาร์ติชัน userdata และแคชใหม่ หรือที่เรียกว่าการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น ระบบจะขอให้ผู้ใช้ยืนยันการดำเนินการนี้ก่อนดำเนินการต่อ

ส่วนวิธีสุดท้าย WipeData() เป็นวิธีที่ไม่บังคับและจะเรียกใช้เมื่อใดก็ตามที่มีการเริ่มการล้างข้อมูล (ไม่ว่าจะกู้คืนผ่านเมนูหรือเมื่อผู้ใช้เลือกที่จะรีเซ็ตข้อมูลเป็นค่าเริ่มต้นจากระบบหลัก ) ระบบจะเรียกใช้เมธอดนี้ก่อนที่จะล้างข้อมูลผู้ใช้และแคช พาร์ติชัน หากอุปกรณ์จัดเก็บข้อมูลผู้ใช้ในที่อื่นนอกเหนือจากพาร์ติชันทั้ง 2 คุณควรลบข้อมูลที่นี่ คุณควรส่งคืน 0 เพื่อระบุว่าสำเร็จ และส่งคืนค่าอื่น เพื่อระบุว่าไม่สำเร็จ แม้ว่าปัจจุบันระบบจะไม่สนใจค่าที่ส่งคืนก็ตาม ระบบจะล้างข้อมูลผู้ใช้และแคช พาร์ติชันไม่ว่าคุณจะส่งคืนสำเร็จหรือล้มเหลว

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

แบรนด์ของอุปกรณ์

สุดท้าย ให้ใส่ข้อความมาตรฐานที่ส่วนท้ายของไฟล์ recovery_ui.cpp สำหรับฟังก์ชัน make_device() ที่สร้างและแสดงผลอินสแตนซ์ของคลาสอุปกรณ์

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

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

หลังจากแก้ไขไฟล์ recovery_ui.cpp เสร็จแล้ว ให้สร้างและลิงก์ไฟล์ดังกล่าวกับการกู้คืนในอุปกรณ์ ใน Android.mk ให้สร้างไลบรารีแบบคงที่ที่มีเฉพาะไฟล์ C++ นี้

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)

จากนั้นในการกำหนดค่าบอร์ดสำหรับอุปกรณ์นี้ ให้ระบุไลบรารีแบบคงที่เป็นค่าของ TARGET_RECOVERY_UI_LIB

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

# device-specific extensions to the recovery UI
TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis

รูปภาพ UI การกู้คืน

อินเทอร์เฟซผู้ใช้สำหรับการกู้คืนประกอบด้วยรูปภาพ ในอุดมคติ ผู้ใช้จะไม่โต้ตอบกับ UI เลย ในระหว่างการอัปเดตปกติ โทรศัพท์จะบูตเข้าสู่การกู้คืน เติมแถบความคืบหน้าในการติดตั้ง และบูตกลับเข้าสู่ระบบใหม่โดยไม่ต้องป้อนข้อมูลจากผู้ใช้ ในกรณีที่เกิดปัญหาการอัปเดตระบบ การดำเนินการของผู้ใช้ที่ทำได้มีเพียงการโทรหาฝ่ายดูแลลูกค้า

อินเทอร์เฟซแบบรูปภาพเท่านั้นช่วยลดความจำเป็นในการแปล อย่างไรก็ตาม ตั้งแต่ Android 5.0 เป็นต้นมา การอัปเดตสามารถแสดงสตริงข้อความ (เช่น "กำลังติดตั้งการอัปเดตระบบ...") พร้อมกับรูปภาพได้ โปรดดูรายละเอียดที่หัวข้อ ข้อความการกู้คืนที่แปลแล้ว

Android 5.0 ขึ้นไป

UI การกู้คืนของ Android 5.0 ขึ้นไปใช้รูปภาพหลัก 2 รูป ได้แก่ รูปภาพข้อผิดพลาด และภาพเคลื่อนไหวการติดตั้ง

รูปภาพที่แสดงระหว่างเกิดข้อผิดพลาด OTA

รูปที่ 1 icon_error.png

รูปภาพที่แสดงระหว่างการติดตั้ง OTA

รูปที่ 2 icon_installing.png

ภาพเคลื่อนไหวการติดตั้งแสดงเป็นรูปภาพ PNG เดียวที่มีเฟรมต่างๆ ของ ภาพเคลื่อนไหวที่สอดแทรกตามแถว (ซึ่งเป็นเหตุผลที่รูปที่ 2 ดูบีบ) เช่น สำหรับภาพเคลื่อนไหว 7 เฟรมขนาด 200x200 ให้สร้างรูปภาพเดียวขนาด 200x1400 โดยเฟรมแรกคือแถว 0, 7, 14, 21, ... เฟรมที่สองคือแถว 1, 8, 15, 22, ... เป็นต้น รูปภาพที่รวมกันจะมี ก้อนข้อความที่ระบุจำนวนเฟรมภาพเคลื่อนไหวและจำนวนเฟรมต่อวินาที (FPS) เครื่องมือนี้bootable/recovery/interlace-frames.pyจะรับชุดเฟรมอินพุต และรวมเข้ากับรูปภาพคอมโพสิตที่จำเป็นซึ่งใช้โดยการกู้คืน

รูปภาพเริ่มต้นมีความหนาแน่นที่แตกต่างกันและอยู่ใน bootable/recovery/res-$DENSITY/images (เช่น bootable/recovery/res-hdpi/images) หากต้องการใช้รูปภาพนิ่งระหว่างการติดตั้ง คุณเพียงแค่ต้องระบุรูปภาพ icon_installing.png และตั้งค่าจำนวนเฟรมใน ภาพเคลื่อนไหวเป็น 0 (ไอคอนข้อผิดพลาดจะไม่มีภาพเคลื่อนไหว แต่จะเป็นรูปภาพนิ่งเสมอ)

Android 4.x และเก่ากว่า

UI การกู้คืนของ Android 4.x และรุ่นก่อนหน้าใช้รูปภาพข้อผิดพลาด (แสดงด้านบน) และภาพเคลื่อนไหวกำลังติดตั้ง รวมถึงรูปภาพซ้อนทับหลายรูป ดังนี้

รูปภาพที่แสดงระหว่างการติดตั้ง OTA

รูปที่ 3 icon_installing.png

รูปภาพที่แสดงเป็น
ภาพซ้อนทับแรก

รูปที่ 4 icon-installing_overlay01.png

รูปภาพที่แสดงเป็นภาพซ้อนทับที่ 7

รูปที่ 5 icon_installing_overlay07.png

ในระหว่างการติดตั้ง ระบบจะสร้างการแสดงบนหน้าจอโดยการวาดรูปภาพ icon_installing.png จากนั้นวาดเฟรมภาพซ้อนทับเฟรมใดเฟรมหนึ่งไว้ด้านบนโดยมีการชดเชยที่เหมาะสม ในที่นี้ เราวาง กรอบสีแดงซ้อนทับเพื่อไฮไลต์ตำแหน่งที่วางซ้อนทับไว้เหนือรูปภาพฐาน

ภาพคอมโพสิตของ
การติดตั้งบวกกับการวางซ้อนครั้งแรก

รูปที่ 6 ติดตั้งเฟรมภาพเคลื่อนไหว 1 (icon_installing.png + icon_installing_overlay01.png)

ภาพคอมโพสิตของ
การติดตั้งบวกการวางซ้อนที่ 7

รูปที่ 7 ติดตั้งเฟรมภาพเคลื่อนไหว 7 (icon_installing.png + icon_installing_overlay07.png)

เฟรมถัดไปจะแสดงโดยการวาดเฉพาะรูปภาพซ้อนทับถัดไปบนสิ่งที่ มีอยู่แล้ว โดยจะไม่วาดรูปภาพฐานใหม่

ตัวแปรสมาชิกของคลาส ScreenRecoveryUI จะกำหนดจำนวนเฟรมในภาพเคลื่อนไหว ความเร็วที่ต้องการ และออฟเซ็ต x และ y ของภาพซ้อนทับ ที่สัมพันธ์กับฐาน เมื่อใช้ รูปภาพที่กำหนดเองแทนรูปภาพเริ่มต้น ให้ลบล้างเมธอด Init() ในคลาสย่อยเพื่อเปลี่ยนค่าเหล่านี้สำหรับรูปภาพที่กำหนดเอง (ดูรายละเอียดได้ที่ ScreenRecoveryUI) สคริปต์ bootable/recovery/make-overlay.py สามารถช่วยแปลงชุดเฟรมรูปภาพ เป็นรูปแบบ "รูปภาพฐาน + รูปภาพซ้อนทับ" ที่จำเป็นสำหรับการกู้คืน รวมถึงการคำนวณออฟเซ็ตที่ จำเป็น

รูปภาพเริ่มต้นจะอยู่ใน bootable/recovery/res/images หากต้องการใช้รูปภาพนิ่ง ในระหว่างการติดตั้ง คุณเพียงแค่ต้องระบุรูปภาพ icon_installing.png และตั้งค่าจำนวน เฟรมในภาพเคลื่อนไหวเป็น 0 (ไอคอนข้อผิดพลาดจะไม่มีภาพเคลื่อนไหว แต่จะเป็นรูปภาพนิ่งเสมอ)

ข้อความการกู้คืนที่แปลแล้ว

Android 5.x จะแสดงสตริงข้อความ (เช่น "กำลังติดตั้งการอัปเดตระบบ...") พร้อมกับ รูปภาพ เมื่อระบบหลักบูตเข้าสู่การกู้คืน ระบบจะส่งภาษาปัจจุบันของผู้ใช้เป็น ตัวเลือกบรรทัดคำสั่งไปยังการกู้คืน สำหรับการแสดงข้อความแต่ละข้อความ การกู้คืนจะรวมถึงรูปภาพ คอมโพสิตที่ 2 พร้อมสตริงข้อความที่เรนเดอร์ล่วงหน้าสำหรับข้อความนั้นๆ ในแต่ละภาษา

ตัวอย่างรูปภาพของสตริงข้อความการกู้คืน

รูปภาพของข้อความการกู้คืน

รูปที่ 8 ข้อความที่แปลแล้วสำหรับข้อความการกู้คืน

ข้อความการกู้คืนอาจแสดงข้อความต่อไปนี้

  • กำลังติดตั้งการอัปเดตระบบ...
  • ข้อผิดพลาด!
  • กำลังลบ… (เมื่อล้างข้อมูล/รีเซ็ตเป็นค่าเริ่มต้น)
  • ไม่มีคำสั่ง (เมื่อผู้ใช้บูตเข้าสู่การกู้คืนด้วยตนเอง)

แอป Android จะbootable/recovery/tools/recovery_l10n/แสดงการแปล ของข้อความและสร้างรูปภาพคอมโพสิต ดูรายละเอียดเกี่ยวกับการใช้แอปนี้ได้ใน ความคิดเห็นใน bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java

เมื่อผู้ใช้บูตเข้าสู่การกู้คืนด้วยตนเอง อาจไม่มีภาษาให้เลือกและไม่มีข้อความแสดง อย่าใช้ข้อความ SMS เป็นสิ่งสำคัญในกระบวนการกู้คืน

หมายเหตุ: อินเทอร์เฟซที่ซ่อนอยู่ซึ่งแสดงข้อความบันทึกและอนุญาตให้ผู้ใช้ เลือกการดำเนินการจากเมนูจะใช้ได้ในภาษาอังกฤษเท่านั้น

แถบความคืบหน้า

แถบความคืบหน้าจะปรากฏใต้รูปภาพ (หรือภาพเคลื่อนไหว) หลัก แถบความคืบหน้าสร้างขึ้นโดย การรวมรูปภาพอินพุต 2 รูป ซึ่งต้องมีขนาดเท่ากัน

แถบความคืบหน้าที่ว่างเปล่า

รูปที่ 9 progress_empty.png

แถบความคืบหน้าแบบเต็ม

รูปที่ 10 progress_fill.png

ปลายด้านซ้ายของรูปภาพเต็มจะแสดงอยู่ข้างปลายด้านขวาของรูปภาพ ว่างเพื่อสร้างแถบความคืบหน้า ตำแหน่งของขอบเขตระหว่างรูปภาพทั้ง 2 รูปจะเปลี่ยนไปเพื่อแสดงความคืบหน้า เช่น เมื่อใช้คู่รูปภาพอินพุตด้านบน แสดง:

แถบความคืบหน้าที่ 1%

รูปที่ 11 แถบความคืบหน้าที่ 1%>

แถบความคืบหน้าที่ 10%

รูปที่ 12 แถบความคืบหน้าที่ 10%

แถบความคืบหน้าที่ 50%

รูปที่ 13 แถบความคืบหน้าที่ 50%

คุณระบุรูปภาพเวอร์ชันเฉพาะอุปกรณ์ได้โดยวางไว้ใน (ในตัวอย่างนี้) device/yoyodyne/tardis/recovery/res/images ชื่อไฟล์ต้องตรงกับชื่อที่แสดงไว้ด้านบน เมื่อพบไฟล์ในไดเรกทอรีนั้น ระบบบิลด์จะใช้ไฟล์ดังกล่าวแทนรูปภาพเริ่มต้นที่เกี่ยวข้อง รองรับเฉพาะ PNG ในรูปแบบ RGB หรือ RGBA ที่มีความลึกของสี 8 บิต

หมายเหตุ: ใน Android 5.x หากการกู้คืนทราบภาษาและเป็นภาษาที่อ่านจากขวาไปซ้าย (RTL) (อาหรับ ฮีบรู ฯลฯ) แถบความคืบหน้าจะเติมจากขวาไปซ้าย

อุปกรณ์ที่ไม่มีหน้าจอ

อุปกรณ์ Android บางรุ่นไม่มีหน้าจอ หากอุปกรณ์เป็นเครื่องใช้แบบไม่มีจอแสดงผลหรือมี อินเทอร์เฟซแบบเสียงเท่านั้น คุณอาจต้องปรับแต่ง UI การกู้คืนเพิ่มเติม แทนที่จะสร้างคลาสย่อยของ ScreenRecoveryUI ให้สร้างคลาสย่อยของคลาสหลัก RecoveryUI โดยตรง

RecoveryUI มีเมธอดสำหรับการจัดการการดำเนินการ UI ระดับล่าง เช่น "สลับการแสดงผล" "อัปเดตแถบความคืบหน้า" "แสดงเมนู" "เปลี่ยนการเลือกเมนู" ฯลฯ คุณสามารถลบล้าง เมธอดเหล่านี้เพื่อจัดเตรียมอินเทอร์เฟซที่เหมาะสมสำหรับอุปกรณ์ของคุณ อุปกรณ์อาจมี LED ที่คุณใช้สีหรือรูปแบบการกะพริบต่างๆ เพื่อระบุสถานะ หรืออาจเล่นเสียงได้ (คุณอาจไม่ต้องการรองรับเมนูหรือโหมด "การแสดงข้อความ" เลยก็ได้ โดยคุณสามารถ ป้องกันการเข้าถึงด้วยการติดตั้งใช้งาน CheckKey() และ HandleMenuKey() ที่ไม่เคยสลับการแสดงผลหรือเลือกรายการเมนู ในกรณีนี้ เมธอด RecoveryUI หลายรายการที่คุณต้องระบุอาจเป็นเพียง Stubs ที่ว่างเปล่า)

ดูbootable/recovery/ui.hสำหรับการประกาศ RecoveryUI เพื่อดูว่าคุณต้องรองรับวิธีการใด บ้าง RecoveryUI เป็นแบบนามธรรม ซึ่งบางเมธอดเป็นแบบเสมือนจริงและต้องระบุโดยคลาสย่อย แต่มีโค้ดสำหรับการประมวลผลอินพุตคีย์ คุณสามารถลบล้างการทำงานนี้ได้เช่นกัน หากอุปกรณ์ไม่มีคีย์หรือต้องการประมวลผลคีย์ในลักษณะอื่น

Updater

คุณสามารถใช้โค้ดเฉพาะอุปกรณ์ในการติดตั้งแพ็กเกจการอัปเดตได้โดยระบุฟังก์ชันส่วนขยายของคุณเองที่เรียกใช้ได้จากภายในสคริปต์โปรแกรมอัปเดต ตัวอย่างฟังก์ชัน สำหรับอุปกรณ์ Tardis มีดังนี้

device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h>
#include <string.h>

#include "edify/expr.h"

ฟังก์ชันส่วนขยายทุกฟังก์ชันมีลายเซ็นเดียวกัน อาร์กิวเมนต์คือชื่อที่ใช้เรียกฟังก์ชัน คุกกี้ State* จำนวนอาร์กิวเมนต์ขาเข้า และอาร์เรย์ของตัวชี้ Expr* ที่แสดงถึงอาร์กิวเมนต์ ค่าที่ส่งคืนคือ Value* ที่จัดสรรใหม่

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

อาร์กิวเมนต์ของคุณจะไม่ได้รับการประเมินในเวลาที่เรียกใช้ฟังก์ชัน โดยตรรกะของฟังก์ชันจะกำหนดว่าอาร์กิวเมนต์ใดจะได้รับการประเมินและประเมินกี่ครั้ง ดังนั้น คุณจึงใช้ฟังก์ชันส่วนขยาย เพื่อใช้โครงสร้างการควบคุมของคุณเองได้ Call Evaluate() เพื่อประเมิน อาร์กิวเมนต์ Expr* โดยแสดงผลเป็น Value* หาก Evaluate() แสดงผลเป็น NULL คุณควรปล่อยทรัพยากรที่ถืออยู่และแสดงผลเป็น NULL ทันที (this propagates aborts up the edify stack) ไม่เช่นนั้น คุณจะเป็นเจ้าของค่าที่แสดงผลและ มีหน้าที่เรียกใช้ FreeValue() ในที่สุด

สมมติว่าฟังก์ชันต้องการอาร์กิวเมนต์ 2 รายการ ได้แก่ key ที่มีค่าเป็นสตริงและ image ที่มีค่าเป็น Blob คุณอาจอ่านอาร์กิวเมนต์ได้ดังนี้

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

การตรวจสอบ NULL และการปล่อยอาร์กิวเมนต์ที่ประเมินก่อนหน้านี้อาจเป็นเรื่องน่าเบื่อสำหรับอาร์กิวเมนต์หลายรายการ ReadValueArgs() ฟังก์ชันนี้จะช่วยให้การดำเนินการง่ายขึ้น คุณอาจเขียนโค้ดต่อไปนี้แทนโค้ด ด้านบนได้

   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() ไม่ได้ตรวจสอบประเภท ดังนั้นคุณต้องตรวจสอบที่นี่ การตรวจสอบด้วยคำสั่ง if เพียงคำสั่งเดียวจะสะดวกกว่า แต่จะทำให้ข้อความแสดงข้อผิดพลาดที่ได้เมื่อไม่สำเร็จมีความเฉพาะเจาะจงน้อยลง แต่ ReadValueArgs() จะจัดการการประเมิน อาร์กิวเมนต์แต่ละรายการและปล่อยอาร์กิวเมนต์ทั้งหมดที่ประเมินก่อนหน้านี้ (รวมถึงการตั้งค่าข้อความแสดงข้อผิดพลาดที่มีประโยชน์) หากการประเมินใดๆ ล้มเหลว คุณสามารถใช้ ReadValueVarArgs()ฟังก์ชันอำนวยความสะดวกในการประเมินอาร์กิวเมนต์ จำนวนตัวแปร (ซึ่งจะแสดงผลอาร์เรย์ของ Value*)

หลังจากประเมินอาร์กิวเมนต์แล้ว ให้ดำเนินการกับฟังก์ชัน

   // 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 ...

ค่าที่ส่งคืนต้องเป็นออบเจ็กต์ Value* โดยการเป็นเจ้าของออบเจ็กต์นี้จะส่งผ่านไปยัง ผู้เรียก ผู้เรียกจะรับสิทธิ์เป็นเจ้าของข้อมูลใดก็ตามที่ระบุโดย Value*ซึ่งก็คือ datamember

ในกรณีนี้ คุณต้องการส่งคืนค่าจริงหรือเท็จเพื่อระบุว่าสำเร็จ โปรดทราบว่า สตริงว่างเป็นเท็จ และสตริงอื่นๆ ทั้งหมดเป็นจริง คุณ ต้องจัดสรรออบเจ็กต์ Value ด้วยสำเนาสตริงค่าคงที่ที่จัดสรรด้วย malloc เพื่อส่งคืน เนื่องจาก ผู้เรียกจะfree() ทั้ง 2 รายการ อย่าลืมเรียกใช้ FreeValue() ในออบเจ็กต์ ที่คุณได้รับจากการประเมินอาร์กิวเมนต์

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

ฟังก์ชันอำนวยความสะดวก StringValue() จะห่อสตริงเป็นออบเจ็กต์ Value ใหม่ ใช้เพื่อเขียนโค้ดด้านบนให้กระชับยิ่งขึ้น

   FreeValue(key);
    FreeValue(image);

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

หากต้องการเชื่อมต่อฟังก์ชันกับตัวแปลภาษา Edify ให้ระบุฟังก์ชัน Register_foo โดยที่ foo คือชื่อของไลบรารีแบบคงที่ที่มี โค้ดนี้ เรียกใช้ RegisterFunction() เพื่อลงทะเบียนฟังก์ชันส่วนขยายแต่ละรายการ ตาม ธรรมเนียม ให้ตั้งชื่อฟังก์ชันเฉพาะอุปกรณ์ device.whatever เพื่อหลีกเลี่ยง การขัดแย้งกับฟังก์ชันในตัวที่จะเพิ่มในอนาคต

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

ตอนนี้คุณกำหนดค่า Makefile เพื่อสร้างไลบรารีแบบคงที่ด้วยโค้ดได้แล้ว (นี่คือ makefile เดียวกันที่ใช้ปรับแต่ง UI การกู้คืนในส่วนก่อนหน้า อุปกรณ์ของคุณอาจมีทั้ง ไลบรารีแบบคงที่ที่กำหนดไว้ที่นี่)

device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := recovery_updater.c
LOCAL_C_INCLUDES += bootable/recovery

ชื่อของไลบรารีแบบคงที่ต้องตรงกับชื่อของฟังก์ชัน Register_libname ที่อยู่ในไลบรารีนั้น

LOCAL_MODULE := librecovery_updater_tardis
include $(BUILD_STATIC_LIBRARY)

สุดท้าย ให้กำหนดค่าบิลด์ของการกู้คืนเพื่อดึงข้อมูลในคลัง เพิ่มไลบรารีลงใน TARGET_RECOVERY_UPDATER_LIBS (ซึ่งอาจมีหลายไลบรารี โดยระบบจะลงทะเบียนทั้งหมด) หากโค้ดของคุณขึ้นอยู่กับไลบรารีแบบคงที่อื่นๆ ที่ไม่ใช่ส่วนขยาย Edify (เช่น ไม่มีฟังก์ชัน Register_libname) คุณสามารถระบุฟังก์ชันเหล่านั้นใน TARGET_RECOVERY_UPDATER_EXTRA_LIBS เพื่อลิงก์กับโปรแกรมอัปเดตโดยไม่ต้องเรียกใช้ฟังก์ชันการลงทะเบียน (ที่ไม่มีอยู่) ของฟังก์ชันเหล่านั้น ตัวอย่างเช่น หากโค้ดเฉพาะอุปกรณ์ต้องการใช้ zlib เพื่อคลายการบีบอัดข้อมูล คุณจะต้องรวม libz ไว้ที่นี่

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 +=

ตอนนี้สคริปต์โปรแกรมอัปเดตในแพ็กเกจ OTA สามารถเรียกฟังก์ชันของคุณได้เหมือนกับฟังก์ชันอื่นๆ หากต้องการตั้งโปรแกรมอุปกรณ์ TARDIS ใหม่ สคริปต์การอัปเดตอาจมีข้อมูลต่อไปนี้ tardis.reprogram("the-key", package_extract_file("tardis-image.dat")) โดยใช้ ฟังก์ชัน package_extract_file() ในตัวเวอร์ชันที่มีอาร์กิวเมนต์เดียว ซึ่งจะแสดงเนื้อหาของไฟล์ที่แยกจากแพ็กเกจการอัปเดตเป็น Blob เพื่อสร้าง อาร์กิวเมนต์ที่ 2 ให้กับฟังก์ชันส่วนขยายใหม่

การสร้างแพ็กเกจ OTA

คอมโพเนนต์สุดท้ายคือการทำให้เครื่องมือสร้างแพ็กเกจ OTA รู้จักข้อมูลเฉพาะของอุปกรณ์ และปล่อยสคริปต์โปรแกรมอัปเดตที่มีการเรียกฟังก์ชันส่วนขยาย

ก่อนอื่น ให้ระบบบิลด์ทราบเกี่ยวกับ Blob ข้อมูลเฉพาะอุปกรณ์ สมมติว่าไฟล์ข้อมูล อยู่ใน device/yoyodyne/tardis/tardis.dat ให้ประกาศข้อมูลต่อไปนี้ใน AndroidBoard.mk ของอุปกรณ์

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

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

คุณยังใส่ไว้ใน Android.mk แทนได้ แต่จะต้องมีการตรวจสอบอุปกรณ์เนื่องจากระบบจะโหลดไฟล์ Android.mk ทั้งหมดในโครงสร้างไม่ว่าอุปกรณ์ใดจะได้รับการสร้างก็ตาม (หากโครงสร้างมีอุปกรณ์หลายเครื่อง คุณจะต้องเพิ่มไฟล์ tardis.dat เมื่อสร้างอุปกรณ์ 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

ไฟล์เหล่านี้เรียกว่าไฟล์วิทยุเนื่องจากเหตุผลทางประวัติศาสตร์ โดยอาจไม่เกี่ยวข้องกับ วิทยุของอุปกรณ์ (หากมี) ซึ่งเป็นเพียงก้อนข้อมูลทึบแสงที่ระบบบิลด์คัดลอกลงใน ไฟล์ .zip ของเป้าหมายที่เครื่องมือสร้าง OTA ใช้ เมื่อคุณทำการบิลด์ ระบบจะจัดเก็บ tardis.dat ไว้ใน target-files.zip เป็น RADIO/tardis.dat คุณเรียกใช้ add-radio-file ได้หลายครั้งเพื่อเพิ่มไฟล์ได้มากเท่าที่ต้องการ

โมดูล Python

หากต้องการขยายเครื่องมือเผยแพร่ ให้เขียนโมดูล Python (ต้องชื่อ releasetools.py) ที่เครื่องมือ สามารถเรียกใช้ได้หากมี ตัวอย่าง

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"));""")

ฟังก์ชันแยกต่างหากจะจัดการกรณีการสร้างแพ็กเกจ OTA แบบเพิ่ม สำหรับ ตัวอย่างนี้ สมมติว่าคุณต้องตั้งโปรแกรม TARDIS ใหม่เมื่อไฟล์ tardis.dat มีการเปลี่ยนแปลง ระหว่างการสร้าง 2 ครั้งเท่านั้น

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"));""")

ฟังก์ชันของโมดูล

คุณสามารถระบุฟังก์ชันต่อไปนี้ในโมดูล (ใช้เฉพาะฟังก์ชันที่คุณต้องการ)

FullOTA_Assertions()
เรียกใช้เมื่อเริ่มสร้าง OTA แบบเต็ม ซึ่งเป็นจุดที่เหมาะสำหรับการส่งคำยืนยัน เกี่ยวกับสถานะปัจจุบันของอุปกรณ์ อย่าส่งคำสั่งสคริปต์ที่ทำการเปลี่ยนแปลงใน อุปกรณ์
FullOTA_InstallBegin()
เรียกใช้หลังจากที่ข้อความทั้งหมดเกี่ยวกับการยืนยันสถานะอุปกรณ์ผ่านแล้ว แต่ก่อนที่จะมีการเปลี่ยนแปลง ใดๆ คุณสามารถส่งคำสั่งสำหรับการอัปเดตเฉพาะอุปกรณ์ที่ต้องเรียกใช้ก่อน การเปลี่ยนแปลงอื่นๆ ในอุปกรณ์
FullOTA_InstallEnd()
เรียกใช้เมื่อสิ้นสุดการสร้างสคริปต์ หลังจากที่ส่งคำสั่งสคริปต์เพื่ออัปเดตการบูตและ พาร์ติชันระบบแล้ว นอกจากนี้ คุณยังส่งคำสั่งเพิ่มเติมสำหรับการอัปเดตเฉพาะอุปกรณ์ได้ด้วย
IncrementalOTA_Assertions()
คล้ายกับ FullOTA_Assertions() แต่จะเรียกใช้เมื่อสร้างแพ็กเกจการอัปเดต แบบเพิ่มทีละรายการ
IncrementalOTA_VerifyBegin()
เรียกใช้หลังจากที่ข้อความยืนยันทั้งหมดเกี่ยวกับสถานะอุปกรณ์ผ่านแล้ว แต่ก่อนที่จะมีการเปลี่ยนแปลง ใดๆ คุณสามารถส่งคำสั่งสำหรับการอัปเดตเฉพาะอุปกรณ์ที่ต้องเรียกใช้ก่อนที่จะมีการเปลี่ยนแปลงสิ่งอื่นใดในอุปกรณ์ ได้
IncrementalOTA_VerifyEnd()
เรียกใช้เมื่อสิ้นสุดระยะการยืนยัน เมื่อสคริปต์ยืนยันเสร็จแล้วว่าไฟล์ที่จะแตะมีเนื้อหาเริ่มต้นตามที่คาดไว้ ณ จุดนี้ ยังไม่มีการเปลี่ยนแปลงใดๆ ใน อุปกรณ์ นอกจากนี้ คุณยังส่งโค้ดสำหรับการยืนยันเพิ่มเติมที่เฉพาะเจาะจงกับอุปกรณ์ได้ด้วย
IncrementalOTA_InstallBegin()
เรียกใช้หลังจากที่ยืนยันแล้วว่าไฟล์ที่จะแก้ไขมีสถานะก่อนตามที่คาดไว้ แต่ก่อนที่จะมีการเปลี่ยนแปลงใดๆ คุณสามารถส่งคำสั่งสำหรับการอัปเดตเฉพาะอุปกรณ์ที่ต้องเรียกใช้ก่อนที่จะมีการเปลี่ยนแปลงสิ่งอื่นใดในอุปกรณ์
IncrementalOTA_InstallEnd()
เช่นเดียวกับแพ็กเกจ OTA แบบเต็ม ฟังก์ชันนี้จะเรียกใช้ที่ส่วนท้ายของการสร้างสคริปต์ หลังจากที่ส่งคำสั่งสคริปต์เพื่ออัปเดตพาร์ติชันการบูตและระบบแล้ว นอกจากนี้ คุณยังส่งคำสั่งเพิ่มเติมสำหรับการอัปเดตเฉพาะอุปกรณ์ได้ด้วย

หมายเหตุ: หากอุปกรณ์แบตเตอรี่หมด การติดตั้ง OTA อาจเริ่มต้นใหม่ตั้งแต่ ต้น เตรียมพร้อมรับมือกับอุปกรณ์ที่คำสั่งเหล่านี้ได้ดำเนินการไปแล้ว ไม่ว่าจะทั้งหมดหรือบางส่วน

ส่งฟังก์ชันไปยังออบเจ็กต์ข้อมูล

ส่งฟังก์ชันไปยังออบเจ็กต์ข้อมูลเดียวที่มีรายการต่างๆ ที่เป็นประโยชน์

  • info.input_zip (OTA แบบเต็มเท่านั้น) ออบเจ็กต์ zipfile.ZipFile สำหรับ ไฟล์ .zip ของเป้าหมายอินพุต
  • info.source_zip (OTA แบบเพิ่มเท่านั้น) zipfile.ZipFile ออบเจ็กต์สำหรับ ไฟล์ .zip ของเป้าหมายต้นทาง (บิลด์ที่อยู่ในอุปกรณ์อยู่แล้วเมื่อมีการติดตั้งแพ็กเกจแบบเพิ่ม)
  • info.target_zip (OTA แบบเพิ่มเท่านั้น) ออบเจ็กต์ zipfile.ZipFile สำหรับ target-files .zip เป้าหมาย (บิลด์ที่แพ็กเกจแบบเพิ่มจะใส่ไว้ในอุปกรณ์)
  • info.output_zip กำลังสร้างแพ็กเกจ zipfile.ZipFile เปิดออบเจ็กต์ เพื่อเขียน ใช้ common.ZipWriteStr(info.output_zip, filename, data) เพื่อเพิ่มไฟล์ลงในแพ็กเกจ
  • info.script ออบเจ็กต์สคริปต์ที่คุณสามารถเพิ่มคำสั่งได้ โทร info.script.AppendExtra(script_text) เพื่อส่งออกข้อความไปยังสคริปต์ ตรวจสอบว่าข้อความเอาต์พุตลงท้ายด้วยเครื่องหมายอัฒภาคเพื่อไม่ให้ข้อความดังกล่าวไปรบกวนคำสั่งที่ปล่อยออกมา หลังจากนั้น

ดูรายละเอียดเกี่ยวกับออบเจ็กต์ข้อมูลได้ที่ เอกสารประกอบของ Python Software Foundation สำหรับไฟล์ ZIP

ระบุตำแหน่งโมดูล

ระบุตำแหน่งของสคริปต์ releasetools.py ของอุปกรณ์ในไฟล์ BoardConfig.mk ดังนี้

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

TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis

หากไม่ได้ตั้งค่า TARGET_RELEASETOOLS_EXTENSIONS ระบบจะใช้ค่าเริ่มต้นเป็นไดเรกทอรี $(TARGET_DEVICE_DIR)/../common (device/yoyodyne/common ในตัวอย่างนี้) เราขอแนะนำให้คุณกำหนดตำแหน่งของสคริปต์ releasetools.py อย่างชัดเจน เมื่อสร้างอุปกรณ์ Tardis สคริปต์ releasetools.py จะรวมอยู่ในไฟล์ target-files .zip (META/releasetools.py )

เมื่อคุณเรียกใช้เครื่องมือเผยแพร่ (ไม่ว่าจะเป็น img_from_target_files หรือ ota_from_target_files) สคริปต์ releasetools.py ในไฟล์ .zip ของ target-files จะได้รับ สิทธิ์เหนือสคริปต์จากโครงสร้างแหล่งที่มาของ Android หากมี นอกจากนี้ คุณยังระบุเส้นทางไปยังส่วนขยายเฉพาะอุปกรณ์อย่างชัดเจนได้ด้วยตัวเลือก -s (หรือ --device_specific) ซึ่งมีความสําคัญสูงสุด ซึ่งจะช่วยให้คุณ แก้ไขข้อผิดพลาดและทำการเปลี่ยนแปลงในส่วนขยาย releasetools รวมถึงใช้การเปลี่ยนแปลงเหล่านั้นกับ ไฟล์เป้าหมายเก่าได้

ตอนนี้เมื่อคุณเรียกใช้ ota_from_target_files ระบบจะเลือกโมดูลเฉพาะอุปกรณ์จากไฟล์ target_files .zip โดยอัตโนมัติและใช้เมื่อสร้างแพ็กเกจ 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

หรือจะระบุส่วนขยายเฉพาะอุปกรณ์เมื่อเรียกใช้ 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

หมายเหตุ: ดูรายการตัวเลือกทั้งหมดได้ที่ ota_from_target_filesความคิดเห็นใน build/make/tools/releasetools/ota_from_target_files

กลไกการไซด์โหลด

การกู้คืนมีกลไกการโหลดด้านข้างสำหรับการติดตั้งแพ็กเกจการอัปเดตด้วยตนเองโดยไม่ต้อง ดาวน์โหลดผ่านอากาศโดยระบบหลัก การไซด์โหลดมีประโยชน์สำหรับการแก้ไขข้อบกพร่องหรือทำการเปลี่ยนแปลงในอุปกรณ์ที่บูตระบบหลักไม่ได้

ในอดีต การโหลดแอปจากแหล่งที่ไม่รู้จักทำได้โดยการโหลดแพ็กเกจจากการ์ด SD ของอุปกรณ์ ในกรณีที่อุปกรณ์บูตไม่ขึ้น คุณสามารถใส่แพ็กเกจลงในการ์ด SD โดยใช้คอมพิวเตอร์เครื่องอื่น แล้วใส่การ์ด SD ลงในอุปกรณ์ เพื่อรองรับอุปกรณ์ Android ที่ไม่มี พื้นที่เก็บข้อมูลภายนอกที่ถอดออกได้ การกู้คืนจึงรองรับกลไกเพิ่มเติม 2 อย่างสำหรับการโหลดจากแหล่งที่ไม่รู้จัก การโหลดแพ็กเกจจากพาร์ติชันแคช และการโหลดผ่าน USB โดยใช้ adb

หากต้องการเรียกใช้กลไกการโหลดแอปจากแหล่งที่ไม่รู้จักแต่ละรายการ Device::InvokeMenuItem() method ของอุปกรณ์จะแสดงค่า BuiltinAction ต่อไปนี้ได้

  • APPLY_EXT โหลดแพ็กเกจการอัปเดตจากแหล่งที่ไม่รู้จักจากที่เก็บข้อมูลภายนอก (ไดเรกทอรี /sdcard ) ไฟล์ recovery.fstab ต้องกำหนดจุดต่อเชื่อม /sdcard ซึ่งใช้ในอุปกรณ์ที่จำลองการ์ด SD ด้วย Symlink ไปยัง /data (หรือกลไกที่คล้ายกัน) ไม่ได้ โดยปกติแล้ว /data จะไม่พร้อมใช้งานสำหรับการกู้คืนเนื่องจากอาจมีการเข้ารหัส UI การกู้คืนจะแสดงเมนูไฟล์ .zip ใน /sdcard และ อนุญาตให้ผู้ใช้เลือกไฟล์ได้ 1 ไฟล์
  • APPLY_CACHE คล้ายกับการโหลดแพ็กเกจจาก /sdcard ยกเว้นว่าจะใช้ ไดเรกทอรี /cache (ซึ่งพร้อมใช้งานสำหรับการกู้คืนเสมอ) แทน จากระบบปกติ /cache จะเขียนได้เฉพาะผู้ใช้ที่มีสิทธิ์ และหากอุปกรณ์บูตไม่ได้ ระบบจะเขียนไดเรกทอรี /cache ไม่ได้ เลย (ซึ่งทำให้กลไกนี้มีประโยชน์จำกัด)
  • APPLY_ADB_SIDELOAD อนุญาตให้ผู้ใช้ส่งแพ็กเกจไปยังอุปกรณ์ผ่านสาย USB และ เครื่องมือพัฒนา adb เมื่อเรียกใช้กลไกนี้ การกู้คืนจะเริ่มใช้ adbd daemon เวอร์ชันมินิของตัวเอง เพื่อให้ adb ในคอมพิวเตอร์โฮสต์ที่เชื่อมต่อสื่อสารกับ adbd daemon ได้ มินิเวอร์ชันนี้ รองรับเพียงคำสั่งเดียวคือ adb sideload filename ระบบจะส่งไฟล์ที่มีชื่อจากเครื่องโฮสต์ไปยังอุปกรณ์ จากนั้นอุปกรณ์จะยืนยันและ ติดตั้งไฟล์ดังกล่าวราวกับว่าไฟล์นั้นอยู่ในพื้นที่เก็บข้อมูลในเครื่อง

ข้อควรระวังบางประการมีดังนี้

  • รองรับเฉพาะการรับส่งผ่าน USB เท่านั้น
  • หากการกู้คืนเรียกใช้ adbd ตามปกติ (มักจะเป็นจริงสำหรับบิลด์ userdebug และ eng) ระบบจะปิด ขณะที่อุปกรณ์อยู่ในโหมดการติดตั้งด้านข้างด้วย adb และจะรีสตาร์ทเมื่อการติดตั้งด้านข้างด้วย adb รับแพ็กเกจเสร็จแล้ว ขณะอยู่ในโหมดการโหลดด้านข้างของ adb คำสั่ง adb อื่นๆ นอกเหนือจาก sideload จะไม่ทำงาน ( logcat, reboot, push, pull, shell ฯลฯ จะล้มเหลวทั้งหมด)
  • คุณออกจากโหมดการโหลดด้านข้างด้วย adb ในอุปกรณ์ไม่ได้ หากต้องการยกเลิก ให้ส่ง /dev/null (หรือสิ่งอื่นใดที่ไม่ใช่แพ็กเกจที่ถูกต้อง) เป็นแพ็กเกจ จากนั้นอุปกรณ์จะยืนยันแพ็กเกจไม่สำเร็จและหยุดขั้นตอนการติดตั้ง ระบบจะเรียกใช้เมธอด CheckKey() ของการติดตั้งใช้งาน RecoveryUI ต่อไปสำหรับการกดแป้น เพื่อให้คุณระบุลำดับการกดแป้นที่จะรีบูตอุปกรณ์และทำงานในโหมดการโหลดข้าง adb ได้