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

ระบบการกู้คืนมี 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;
        }
    }

เมธอดนี้จะแสดงสมาชิกใดก็ได้ของ BuiltinAction enum เพื่อบอกให้ระบบดำเนินการนั้น (หรือสมาชิก 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 หลายรายการที่คุณต้องระบุอาจเป็นเพียง สตับที่ว่างเปล่า)

ดู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 จะเผยแพร่การยกเลิกขึ้นไปในสแต็ก Edify) ไม่เช่นนั้น คุณจะเป็นเจ้าของค่าที่แสดงผลและ มีหน้าที่เรียกใช้ 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

ในกรณีนี้ คุณต้องการส่งคืนค่าจริงหรือเท็จเพื่อระบุว่าสำเร็จ โปรดทราบว่าตาม ธรรมเนียมแล้ว สตริงว่างคือ false และสตริงอื่นๆ ทั้งหมดคือ true คุณ ต้องจัดสรรออบเจ็กต์ 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 ของ target-files ที่เครื่องมือสร้าง 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 ออบเจ็กต์สคริปต์ที่คุณสามารถเพิ่มคำสั่งได้ Call 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 รวมถึงใช้การเปลี่ยนแปลงเหล่านั้นกับ target-files เก่าได้

ตอนนี้เมื่อคุณเรียกใช้ 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() ของอุปกรณ์ จะแสดงค่า BuiltinAction ต่อไปนี้ได้

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

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

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