ระบบการกู้คืนมีฮุกหลายรายการสําหรับการแทรกโค้ดเฉพาะอุปกรณ์เพื่อให้การอัปเดต 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 ดิบ ใช้สำหรับพาร์ติชันที่บูตได้ เช่น บูตและการกู้คืน ระบบจะไม่มาสก์ eMMc จริง แต่จะใช้สตรีงจุดมาสก์เพื่อค้นหาอุปกรณ์ในตาราง เช่นเดียวกับประเภท mtd
- vfat
-
ระบบไฟล์ FAT บนอุปกรณ์บล็อก ซึ่งปกติจะใช้สำหรับพื้นที่เก็บข้อมูลภายนอก เช่น การ์ด SD โดยที่ device คืออุปกรณ์บล็อก และ device2 คืออุปกรณ์บล็อกที่ 2 ที่ระบบพยายามต่อเชื่อมหากการต่อเชื่อมอุปกรณ์หลักไม่สำเร็จ (เพื่อให้ใช้งานร่วมกับการ์ด SD ได้ ซึ่งอาจฟอร์แมตด้วยตารางพาร์ติชันหรือไม่ก็ได้)
ต้องเมานต์พาร์ติชันทั้งหมดในไดเรกทอรีรูท (กล่าวคือ ค่าจุดเมานต์ต้องขึ้นต้นด้วยเครื่องหมายทับและไม่มีเครื่องหมายทับอื่นๆ) ข้อจำกัดนี้มีผลกับการต่อเชื่อมระบบไฟล์ในการกู้คืนเท่านั้น ระบบหลักจะต่อเชื่อมระบบไฟล์ที่ใดก็ได้ ไดเรกทอรี
/boot
,/recovery
และ/misc
ควรเป็นประเภทไฟล์ RAW (mtd หรือ emmc) ส่วนไดเรกทอรี/system
,/data
,/cache
และ/sdcard
(หากมี) ควรเป็นประเภทระบบไฟล์ (yaffs2, ext4 หรือ vfat)
ตั้งแต่ Android 3.0 ไฟล์ recovery.fstab จะมีช่องเพิ่มเติมที่ไม่บังคับ ซึ่งก็คือ options ปัจจุบันตัวเลือกที่กําหนดไว้เพียงอย่างเดียวคือ length ซึ่งช่วยให้คุณระบุความยาวของพาร์ติชันได้อย่างชัดเจน ระบบจะใช้ความยาวนี้เมื่อฟอร์แมตพาร์ติชันอีกครั้ง (เช่น สําหรับพาร์ติชัน userdata ระหว่างการล้างข้อมูล/การรีเซ็ตเป็นค่าเริ่มต้น หรือสําหรับพาร์ติชันระบบระหว่างการติดตั้งแพ็กเกจ OTA แบบเต็ม) หากค่าความยาวเป็นค่าลบ ระบบจะเพิ่มค่าความยาวลงในขนาดพาร์ติชันจริงเพื่อหาขนาดที่จะจัดรูปแบบ ตัวอย่างเช่น การตั้งค่า "length=-16384" หมายความว่าระบบจะไม่เขียนทับ 16, 000 ไบต์สุดท้ายของพาร์ติชันนั้นเมื่อมีการฟอร์แมตพาร์ติชันนั้นอีกครั้ง ซึ่งรองรับฟีเจอร์ต่างๆ เช่น การเข้ารหัสพาร์ติชัน userdata (ที่เก็บข้อมูลเมตาการเข้ารหัสไว้ที่ส่วนท้ายของพาร์ติชันที่ไม่ควรเขียนทับ)
หมายเหตุ: ช่อง device2 และ options เป็นช่องที่ไม่บังคับ ซึ่งอาจทําให้แยกวิเคราะห์ได้ยาก หากรายการในช่องที่ 4 ในบรรทัดขึ้นต้นด้วยอักขระ "/" ระบบจะถือว่ารายการนั้นเป็นรายการ device2 หากรายการไม่ได้ขึ้นต้นด้วยอักขระ "/" ระบบจะถือว่ารายการนั้นเป็นช่อง options
ภาพเคลื่อนไหวขณะเปิดเครื่อง
ผู้ผลิตอุปกรณ์สามารถปรับแต่งภาพเคลื่อนไหวที่แสดงเมื่ออุปกรณ์ Android กำลังบูตได้ โดยสร้างไฟล์ .zip ที่จัดระเบียบและจัดวางตามข้อกำหนดในรูปแบบภาพเคลื่อนไหวในระบบปฏิบัติการ
สำหรับอุปกรณ์ Android Things คุณอาจอัปโหลดไฟล์ ZIP ในคอนโซล Android Things เพื่อให้รูปภาพรวมอยู่ในผลิตภัณฑ์ที่เลือก
หมายเหตุ: รูปภาพเหล่านี้ต้องเป็นไปตามหลักเกณฑ์การใช้แบรนด์ Android ดูหลักเกณฑ์การใช้แบรนด์ได้ที่ส่วน Android ของฮับการตลาดของพาร์ทเนอร์
UI การกู้คืน
เพื่อรองรับอุปกรณ์ที่มีฮาร์ดแวร์ที่แตกต่างกัน (ปุ่มจริง LED หน้าจอ ฯลฯ) คุณสามารถปรับแต่งอินเทอร์เฟซการกู้คืนเพื่อแสดงสถานะและเข้าถึงฟีเจอร์ที่ซ่อนอยู่ซึ่งดำเนินการด้วยตนเองสำหรับอุปกรณ์แต่ละเครื่อง
เป้าหมายของคุณคือการสร้างไลบรารีแบบคงที่ขนาดเล็กที่มีออบเจ็กต์ C++ 2 รายการเพื่อให้ฟังก์ชันการทำงานเฉพาะอุปกรณ์ ระบบจะใช้ไฟล์ 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 มีหน้าจอ คุณจึงรับค่าจากการใช้งาน 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()
จะเรียกใช้ไม่ว่าการกู้คืนจะดำเนินการอย่างไรก็ตาม เช่น เมื่อปิดเมนู เมื่อเปิดเมนู ระหว่างการติดตั้งแพ็กเกจ ระหว่างการลบข้อมูลผู้ใช้ ฯลฯ โดยสามารถแสดงผลค่าคงที่อย่างใดอย่างหนึ่งต่อไปนี้
- เปิด/ปิด สลับการแสดงเมนูและ/หรือบันทึกข้อความเป็นเปิดหรือปิด
- รีบูต รีบูตอุปกรณ์ทันที
- IGNORE ละเว้นการกดแป้นพิมพ์นี้
- จัดคิว จัดคิวการกดแป้นพิมพ์นี้เพื่อใช้แบบซิงค์ (เช่น โดยระบบเมนูการกู้คืนหากเปิดใช้การแสดงผล)
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()
แม้ว่าเมนูจะมองไม่เห็นก็ตาม ตามที่อาร์กิวเมนต์ visible บอกเป็นนัย ซึ่งต่างจาก 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 เพื่อบอกให้ระบบดำเนินการ (หรือสมาชิก NO_ACTION หากต้องการให้ระบบไม่ดําเนินการใดๆ) นี่เป็นตําแหน่งที่จะระบุฟังก์ชันการกู้คืนเพิ่มเติมนอกเหนือจากที่มีอยู่ในระบบ เช่น เพิ่มรายการสําหรับฟังก์ชันนี้ในเมนู เรียกใช้ฟังก์ชันที่นี่เมื่อมีการเรียกใช้รายการเมนูนั้น และแสดงผลลัพธ์เป็น NO_ACTION เพื่อให้ระบบไม่ดําเนินการใดๆ
BuiltinAction มีค่าต่อไปนี้
- NO_ACTION ไม่ดำเนินการใดๆ
- รีบูต ออกจากการกู้คืนและรีบูตอุปกรณ์ตามปกติ
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD ติดตั้งแพ็กเกจอัปเดตจากแหล่งต่างๆ โปรดดูรายละเอียดที่หัวข้อการโหลดจากแหล่งที่ไม่รู้จัก
- WIPE_CACHE ฟอร์แมตพาร์ติชันแคชใหม่เท่านั้น ไม่ต้องยืนยันเนื่องจากการดำเนินการนี้ค่อนข้างไม่เป็นอันตราย
- WIPE_DATA ฟอร์แมตพาร์ติชันข้อมูลผู้ใช้และแคชใหม่ หรือที่เรียกว่า "รีเซ็ตข้อมูลเป็นค่าเริ่มต้น" ระบบจะขอให้ผู้ใช้ยืนยันการดำเนินการนี้ก่อนดำเนินการต่อ
วิธีการสุดท้าย WipeData()
เป็นตัวเลือกและจะเรียกใช้เมื่อเริ่มการลบข้อมูล (จากการกู้คืนผ่านเมนูหรือเมื่อผู้ใช้เลือกรีเซ็ตข้อมูลเป็นค่าเริ่มต้นจากระบบหลัก) ระบบจะเรียกใช้เมธอดนี้ก่อนที่จะล้างข้อมูลผู้ใช้และพาร์ติชันแคช หากอุปกรณ์จัดเก็บข้อมูลผู้ใช้ไว้ที่อื่นนอกเหนือจากพาร์ติชัน 2 รายการดังกล่าว คุณควรลบข้อมูลที่นี่ คุณควรแสดงผล 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 แล้ว ให้ลิงก์ไฟล์กับ Recovery ในอุปกรณ์ ใน 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 รูป ได้แก่ รูปข้อผิดพลาดและภาพเคลื่อนไหวการติดตั้ง
![]() รูปที่ 1 icon_error.png |
![]() รูปที่ 2 icon_installing.png |
ภาพเคลื่อนไหวการติดตั้งจะแสดงเป็นภาพ PNG รูปเดียวที่มีเฟรมต่างๆ ของภาพเคลื่อนไหวสลับกันตามแถว (ซึ่งเป็นเหตุผลที่รูปภาพ 2 ดูเหมือนถูกบีบอัด) เช่น สำหรับภาพเคลื่อนไหว 7 เฟรมขนาด 200x200 ให้สร้างรูปภาพขนาด 200x1400 รูปเดียว โดยเฟรมแรกคือแถวที่ 0, 7, 14, 21, ... เฟรมที่ 2 คือแถวที่ 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 และเวอร์ชันก่อนหน้าใช้รูปภาพข้อผิดพลาด (แสดงด้านบน) และภาพเคลื่อนไหวกำลังติดตั้ง รวมถึงภาพวางซ้อนหลายภาพ ดังนี้
![]() รูปที่ 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
เมื่อผู้ใช้บูตเข้าสู่การกู้คืนด้วยตนเอง ภาษาที่เลือกไว้อาจไม่พร้อมใช้งานและไม่มีข้อความแสดง SMS ดังกล่าวไม่ใช่สิ่งสําคัญต่อกระบวนการกู้คืน
หมายเหตุ: อินเทอร์เฟซที่ซ่อนอยู่ซึ่งแสดงข้อความบันทึกและอนุญาตให้ผู้ใช้เลือกการดำเนินการจากเมนูมีให้บริการเป็นภาษาอังกฤษเท่านั้น
แถบความคืบหน้า
แถบความคืบหน้าจะปรากฏใต้รูปภาพหลัก (หรือภาพเคลื่อนไหว) แถบความคืบหน้าสร้างขึ้นโดยการรวมรูปภาพอินพุต 2 รูปเข้าด้วยกัน ซึ่งต้องมีขนาดเท่ากัน

รูปที่ 9 progress_empty.png

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

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

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

รูปที่ 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 เป็นนามธรรม - บางเมธอดเป็นเมธอดเสมือนล้วนๆ และต้องระบุโดยคลาสย่อย - แต่ก็มีโค้ดสำหรับประมวลผลอินพุตสำคัญ คุณลบล้างการตั้งค่าดังกล่าวได้ด้วยหากอุปกรณ์ไม่มีคีย์หรือคุณต้องการประมวลผลคีย์ในลักษณะอื่น
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 ทันที (การดำเนินการนี้จะส่งต่อการยกเลิกไปยังกองซ้อน edify) ไม่เช่นนั้น คุณจะเป็นเจ้าของค่าที่แสดงผลและมีหน้าที่รับผิดชอบในการเรียกใช้ FreeValue()
ในท้ายที่สุด
สมมติว่าฟังก์ชันต้องการอาร์กิวเมนต์ 2 รายการ ได้แก่ คีย์ที่มีค่าสตริงและรูปภาพที่มีค่า 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*
นี้ โดยเฉพาะสมาชิกข้อมูล
ในกรณีนี้ คุณต้องการแสดงผลค่า "จริง" หรือ "เท็จ" เพื่อระบุว่าสําเร็จ โปรดทราบว่ารูปแบบสตริงว่างคือ false และสตริงอื่นๆ ทั้งหมดคือ true คุณต้อง malloc ออบเจ็กต์ 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()
จะรวมสตริงไว้ในออบเจ็กต์ค่าใหม่
ใช้เพื่อเขียนโค้ดด้านบนให้กระชับยิ่งขึ้น
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 เพื่อสร้างไลบรารีแบบคงที่ด้วยโค้ดได้แล้ว (ไฟล์นี้เหมือนกับไฟล์ make ที่ใช้ปรับแต่ง 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
สำหรับไฟล์เป้าหมาย .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 จะรวมอยู่ในไฟล์ .zip ของไฟล์เป้าหมาย (META/releasetools.py
)
เมื่อคุณเรียกใช้เครื่องมือสำหรับเผยแพร่ (img_from_target_files
หรือ ota_from_target_files
) ให้ใช้สคริปต์ releasetools.py ในไฟล์เป้าหมาย .zip (หากมี) แทนสคริปต์จากซอร์สทรีของ Android นอกจากนี้ คุณยังระบุเส้นทางไปยังส่วนขยายเฉพาะอุปกรณ์ได้อย่างชัดเจนด้วยตัวเลือก -s
(หรือ --device_specific
) ซึ่งจะมีลำดับความสำคัญสูงสุด ซึ่งจะช่วยให้คุณแก้ไขข้อผิดพลาดและทําการเปลี่ยนแปลงในส่วนขยายของ Releasetools รวมถึงใช้การเปลี่ยนแปลงเหล่านั้นกับไฟล์เป้าหมายเก่าได้
ตอนนี้เมื่อคุณเรียกใช้ ota_from_target_files
ระบบจะเลือกโมดูลเฉพาะอุปกรณ์จากไฟล์ .zip ของ target_files โดยอัตโนมัติและใช้โมดูลดังกล่าวเมื่อสร้างแพ็กเกจ 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) ระบบจะปิด adbd ขณะอุปกรณ์อยู่ในโหมดการโหลดจากด้านข้างของ adb และจะรีสตาร์ทเมื่อการโหลดจากด้านข้างของ adb ได้รับแพ็กเกจเสร็จแล้ว เมื่ออยู่ในโหมดการโหลดจากด้านข้างของ adb จะไม่มีคำสั่ง adb อื่นที่ทำงานได้นอกจาก
sideload
(logcat
,reboot
,push
,pull
,shell
ฯลฯ ทั้งหมดใช้งานไม่ได้) -
คุณออกจากโหมดการโหลดจากด้านข้างของ adb ในอุปกรณ์ไม่ได้ หากต้องการยกเลิก ให้ส่ง
/dev/null
(หรือสิ่งอื่นใดที่ไม่ใช่แพ็กเกจที่ถูกต้อง) เป็นแพ็กเกจ แล้วอุปกรณ์จะยืนยันไม่ได้และหยุดกระบวนการติดตั้ง ระบบจะเรียกใช้เมธอดCheckKey()
ของการใช้งาน RecoveryUI ต่อไปสำหรับการกดแป้น เพื่อให้คุณระบุลำดับแป้นพิมพ์ที่จะรีบูตอุปกรณ์และทำงานในโหมดการโหลดจากด้านข้างของ adb ได้