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

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

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

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

ตั้งแต่ 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 ซึ่งเป็นทางเลือก จุดเชื่อมต่อทั้งหมดในตัวอย่างนี้จะต้องถูกกำหนด (อุปกรณ์อาจเพิ่มพาร์ติชันเพิ่มเติมด้วย) ระบบไฟล์ที่รองรับมีห้าประเภท:

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

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

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

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

แอนิเมชั่นการบูต

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

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

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

UI การกู้คืน

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

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

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

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 ของอุปกรณ์ของคุณ ตัวอย่างนี้ถือว่าอุปกรณ์ ควานหา มีหน้าจอ ดังนั้นคุณสามารถสืบทอดจากการใช้งาน ScreenRecoveryUI ในตัว (ดูคำแนะนำสำหรับ อุปกรณ์ที่ไม่มีหน้าจอ ) ฟังก์ชันเดียวในการปรับแต่งจาก ScreenRecoveryUI คือ CheckKey() ซึ่งทำหน้าที่จัดการคีย์อะซิงโครนัสเริ่มต้น:

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

ค่าคงที่ที่สำคัญ

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

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

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

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

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 การกู้คืนหน้าจอ

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

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

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

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

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

จัดการเมนูคีย์

จากนั้น ให้จัดเตรียมฟังก์ชัน 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 . ย้ายไฮไลท์เมนูไปยังรายการก่อนหน้า
  • kไฮไลท์ดาวน์ ย้ายไฮไลท์เมนูไปยังรายการถัดไป
  • kInurgeItem เรียกใช้รายการที่ไฮไลต์ในปัจจุบัน
  • 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
        }
        ...
    }

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

เรียกใช้เมนูรายการ

จากนั้น ให้จัดเตรียมเมธอด InvokeMenuItem() ที่แมปตำแหน่งจำนวนเต็มในอาร์เรย์ของรายการที่ GetMenuItems() ส่งคืนไปยังการดำเนินการ สำหรับอาร์เรย์ของรายการในตัวอย่างควานหา ให้ใช้:

   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 มีค่าต่อไปนี้:

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

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

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

ทำอุปกรณ์

สุดท้าย ให้รวมตัวอย่างบางส่วนไว้ที่ส่วนท้ายของไฟล์ recovery_ui.cpp สำหรับฟังก์ชัน make_device() ที่สร้างและส่งคืนอินสแตนซ์ของคลาส 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 และใหม่กว่าใช้อิมเมจหลักสองอิมเมจ: อิมเมจ ข้อผิดพลาด และภาพเคลื่อนไหว การติดตั้ง

ภาพที่แสดงระหว่างข้อผิดพลาด ota

รูปที่ 1. icon_error.png

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

รูปที่ 2. icon_installing.png

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

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

รูปที่ 5. icon_installing_overlay07.png

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

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

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

ภาพรวมของการติดตั้งบวกกับภาพซ้อนทับที่เจ็ด

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

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

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

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

ข้อความการกู้คืนที่แปลเป็นภาษาท้องถิ่น

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

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

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

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

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

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

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

เมื่อผู้ใช้บูตเข้าสู่การกู้คืนด้วยตนเอง โลแคลอาจไม่พร้อมใช้งานและไม่มีข้อความปรากฏขึ้น อย่าทำให้ข้อความมีความสำคัญต่อกระบวนการกู้คืน

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

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

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

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

รูปที่ 9.progress_empty.png

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

รูปที่ 10.progress_fill.png

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

แถบความคืบหน้าที่ 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 หลายวิธีที่คุณต้องระบุอาจเป็นเพียงส่วนที่ว่างเปล่า)

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

ตัวอัพเดต

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

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

สมมติว่าฟังก์ชันต้องการอาร์กิวเมนต์ 2 รายการ ได้แก่ คีย์ ค่าสตริง และ อิมเมจ ค่าหยด คุณสามารถอ่านข้อโต้แย้งเช่นนี้:

   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* —โดยเฉพาะสมาชิกข้อมูล

ในกรณีนี้ คุณต้องการส่งกลับค่าจริงหรือเท็จเพื่อบ่งชี้ความสำเร็จ จำหลักการที่ว่าสตริงว่างเป็น เท็จ และสตริงอื่นๆ ทั้งหมดเป็น จริง คุณต้อง malloc อ็อบเจ็กต์ Value พร้อมด้วยสำเนา malloc ของสตริงคงที่เพื่อส่งคืน เนื่องจากผู้เรียกจะ free() ทั้งสองอย่าง อย่าลืมเรียก 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 (ซึ่งอาจมีหลายห้องสมุด โดยทั้งหมดได้รับการลงทะเบียนแล้ว) หากโค้ดของคุณขึ้นอยู่กับไลบรารีสแตติกอื่นๆ ที่ไม่ได้แก้ไขส่วนขยายด้วยตนเอง (เช่น ไลบรารีเหล่านั้นไม่มีฟังก์ชัน 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.reprogram("the-key", package_extract_file("tardis-image.dat")) สิ่งนี้ใช้เวอร์ชันอาร์กิวเมนต์เดียวของฟังก์ชันในตัว package_extract_file() ซึ่งส่งคืนเนื้อหาของไฟล์ที่แยกจากแพ็คเกจการอัปเดตเป็นหยดเพื่อสร้างอาร์กิวเมนต์ที่สองให้กับฟังก์ชันส่วนขยายใหม่

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

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

ขั้นแรก ให้ระบบบิลด์ทราบเกี่ยวกับหยดข้อมูลเฉพาะอุปกรณ์ สมมติว่าไฟล์ข้อมูลของคุณอยู่ใน 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 ได้หลายครั้งเพื่อเพิ่มไฟล์ได้มากเท่าที่คุณต้องการ

โมดูลหลาม

หากต้องการขยายเครื่องมือ release ให้เขียนโมดูล 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 มีการเปลี่ยนแปลงระหว่างสองบิลด์เท่านั้น

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

สำหรับรายละเอียดเกี่ยวกับออบเจ็กต์ info โปรดดู เอกสารประกอบ 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 อย่างชัดเจน เมื่อสร้างอุปกรณ์ควานหา สคริปต์ releasetools.py จะรวมอยู่ในไฟล์เป้าหมาย .zip ( META/releasetools.py )

เมื่อคุณเรียกใช้เครื่องมือ release (เช่น img_from_target_files หรือ ota_from_target_files ) สคริปต์ releasetools.py ในไฟล์เป้าหมาย .zip หากมีอยู่ จะถูกเลือกใช้มากกว่าสคริปต์จากแผนผังต้นทางของ 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

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

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

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

ในการเรียกใช้กลไก sideload แต่ละตัว Device::InvokeMenuItem() สามารถส่งคืนค่าของ builtinaction ต่อไปนี้:

  • Apply_ext sideload แพ็คเกจอัปเดตจากที่เก็บข้อมูลภายนอก ( /sdcard ) การกู้คืนของคุณ fstab ต้องกำหนดจุดเมานต์ /sdcard สิ่งนี้ไม่สามารถใช้งานได้บนอุปกรณ์ที่เลียนแบบการ์ด SD ด้วย Symlink to /data (หรือกลไกที่คล้ายกัน) โดยทั่วไป /data จะไม่สามารถกู้คืนได้เนื่องจากอาจมีการเข้ารหัส การกู้คืน UI จะแสดงเมนูของไฟล์. zip ใน /sdcard และอนุญาตให้ผู้ใช้เลือกไฟล์
  • appl_cache คล้ายกับการโหลดแพ็คเกจจาก /sdcard ยกเว้นว่าไดเรกทอรี /cache ( ซึ่ง ใช้งานได้เสมอเพื่อการกู้คืน) จะใช้แทน จากระบบปกติ /cache สามารถเขียนได้โดยผู้ใช้ที่ได้รับการยกเว้นเท่านั้นและหากอุปกรณ์ไม่สามารถบูตไดเร็กทอรี /cache ไม่สามารถเขียนได้เลย (ซึ่งทำให้กลไกของยูทิลิตี้ จำกัด นี้)
  • Apply_ADB_SIDELOAD อนุญาตให้ผู้ใช้ส่งแพ็คเกจไปยังอุปกรณ์ผ่านสายเคเบิล USB และเครื่องมือพัฒนา ADB เมื่อกลไกนี้ถูกเรียกใช้การกู้คืนจะเริ่มต้น Mini ของตัวเอง ADBD daemon เพื่อให้ ADB บนคอมพิวเตอร์โฮสต์ที่เชื่อมต่อคุยกับมัน รุ่นมินินี้รองรับคำสั่งเดียวเท่านั้น: adb sideload filename ไฟล์ชื่อถูกส่งจากเครื่องโฮสต์ไปยังอุปกรณ์ซึ่งจะตรวจสอบและติดตั้งราวกับว่ามันอยู่ในที่เก็บข้อมูลในท้องถิ่น

คำเตือนสองสามข้อ:

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