หน้านี้อธิบายการเปลี่ยนแปลงที่เพิ่มลงใน AOSP เพื่อลดการเปลี่ยนแปลงไฟล์ที่ไม่จำเป็นระหว่างบิลด์ ผู้ติดตั้งใช้งานอุปกรณ์ที่ดูแลระบบบิลด์ของตนเองสามารถใช้ข้อมูลนี้เป็นแนวทาง ในการลดขนาดการอัปเดตผ่านอากาศ (OTA)
การอัปเดต OTA ของ Android บางครั้งมีไฟล์ที่เปลี่ยนแปลงซึ่งไม่สอดคล้องกับการเปลี่ยนแปลงโค้ด แต่เป็นอาร์ติแฟกต์ของระบบบิลด์ ซึ่งอาจเกิดขึ้นได้เมื่อโค้ดเดียวกันที่สร้างขึ้นในเวลาต่างๆ จากไดเรกทอรีต่างๆ หรือในเครื่องต่างๆ ทำให้เกิดไฟล์ที่เปลี่ยนแปลงจำนวนมาก ไฟล์ส่วนเกินดังกล่าวจะเพิ่มขนาดแพตช์ OTA และทำให้ยากต่อการระบุ โค้ดที่เปลี่ยนแปลง
AOSP มีการเปลี่ยนแปลงระบบบิลด์ที่ออกแบบมาเพื่อลดขนาดแพตช์ OTA เพื่อให้เนื้อหาของการอัปเดต OTA โปร่งใสมากขึ้น ระบบได้นำการเปลี่ยนแปลงไฟล์ที่ไม่จำเป็นระหว่างบิลด์ออกแล้ว และการอัปเดต OTA จะมีเฉพาะไฟล์ที่เกี่ยวข้องกับแพตช์เท่านั้น นอกจากนี้ AOSP ยังมี เครื่องมือ diff ของบิลด์ ซึ่งจะกรองการเปลี่ยนแปลงไฟล์ที่เกี่ยวข้องกับบิลด์ทั่วไป ออกเพื่อให้ diff ของไฟล์บิลด์สะอาดขึ้น และ เครื่องมือการแมปบล็อก ซึ่งช่วยให้คุณจัดสรรบล็อกได้อย่างสม่ำเสมอ
ระบบบิลด์สามารถสร้างแพตช์ขนาดใหญ่โดยไม่จำเป็นได้หลายวิธี เพื่อลดปัญหานี้ ใน Android 8.0 ขึ้นไป เราได้นำฟีเจอร์ใหม่มาใช้เพื่อลดขนาดแพตช์สำหรับ diff ของแต่ละ ไฟล์ การปรับปรุงที่ช่วยลดขนาดแพ็กเกจการอัปเดต OTA ได้แก่
-
การใช้ ZSTD ซึ่งเป็นอัลกอริทึมการบีบอัดแบบไม่สูญเสียข้อมูลอเนกประสงค์สำหรับรูปภาพแบบเต็ม
ในการอัปเดตอุปกรณ์ที่ไม่ใช่ A/B คุณสามารถปรับแต่ง ZSTD เพื่อให้อัตราส่วนการบีบอัดสูงขึ้นได้โดยการเพิ่มระดับการบีบอัด ระดับการบีบอัดจะตั้งค่าในระหว่างการสร้าง OTA
เวลา และตั้งค่าได้โดยส่งแฟล็ก
--vabc_compression_param=zstd,$COMPRESSION_LEVEL -
การเพิ่มขนาดหน้าต่างการบีบอัดที่ใช้ระหว่าง OTA คุณสามารถตั้งค่าขนาดหน้าต่างการบีบอัดสูงสุดได้โดยการปรับแต่งพารามิเตอร์บิลด์ในไฟล์
.mkของอุปกรณ์ ตัวแปรนี้ตั้งค่าเป็นPRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144 - การใช้การบีบอัดซ้ำ Puffin ซึ่งเป็นเครื่องมือแพตช์แบบดีเทอร์มินิสติกสำหรับสตรีม deflate ที่จัดการฟังก์ชันการบีบอัดและ diff สำหรับการสร้างการอัปเดต OTA แบบ A/B
-
การเปลี่ยนแปลงการใช้งานเครื่องมือสร้างเดลต้า เช่น วิธีใช้
bsdiffไลบรารีสำหรับการบีบอัดแพตช์ ใน Android 9 ขึ้นไป เครื่องมือbsdiffจะเลือกอัลกอริทึมการบีบอัดที่จะให้ ผลการบีบอัดที่ดีที่สุดสำหรับแพตช์ -
การปรับปรุง
update_engineส่งผลให้ใช้หน่วยความจำน้อยลงเมื่อใช้แพตช์สำหรับการอัปเดตอุปกรณ์แบบ A/B
ส่วนต่อไปนี้จะกล่าวถึงปัญหาต่างๆ ที่ส่งผลต่อขนาดการอัปเดต OTA, โซลูชัน, และตัวอย่างการติดตั้งใช้งานใน AOSP
ลำดับไฟล์
ปัญหา: ระบบไฟล์ไม่รับประกันลำดับไฟล์เมื่อมีการขอรายการไฟล์ในไดเรกทอรี
แม้ว่าโดยทั่วไปแล้วจะเหมือนกันสำหรับการชำระเงินเดียวกัน เครื่องมือต่างๆ เช่น
ls จะจัดเรียงผลลัพธ์ตามค่าเริ่มต้น แต่ฟังก์ชันไวลด์การ์ดที่ใช้โดยคำสั่งต่างๆ เช่น
as find and make จะไม่จัดเรียง คุณต้องจัดเรียง
เอาต์พุตก่อนใช้เครื่องมือเหล่านี้
โซลูชัน: เมื่อใช้เครื่องมือต่างๆ เช่น find และ
make กับฟังก์ชันไวลด์การ์ด ให้จัดเรียงเอาต์พุตของคำสั่งเหล่านี้ก่อนใช้งาน
เมื่อใช้ $(wildcard) หรือ $(shell find) ใน
Android.mk ไฟล์ ให้จัดเรียงไฟล์เหล่านั้นด้วย เครื่องมือบางอย่าง เช่น Java จะจัดเรียงอินพุต ดังนั้น
ก่อนที่จะจัดเรียงไฟล์ ให้ตรวจสอบว่าเครื่องมือที่คุณใช้อยู่ได้จัดเรียงไฟล์แล้ว
ตัวอย่าง: เราได้แก้ไขอินสแตนซ์จำนวนมากในระบบบิลด์หลักโดยใช้
มาโคร all-*-files-under ในตัว ซึ่งรวมถึง
all-cpp-files-under (เนื่องจากมีการกระจายคำจำกัดความหลายรายการใน Makefiles อื่นๆ)
ดูรายละเอียดได้ที่
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
ไดเรกทอรีบิลด์
ปัญหา: การเปลี่ยนไดเรกทอรีที่สร้างสิ่งต่างๆ อาจทำให้
ไบนารีแตกต่างกัน เส้นทางส่วนใหญ่ในบิลด์ Android เป็นเส้นทางแบบสัมพัทธ์ ดังนั้น
__FILE__ ใน C/C++ จึงไม่ใช่ปัญหา อย่างไรก็ตาม สัญลักษณ์การแก้ไขข้อบกพร่องจะเข้ารหัสชื่อเส้นทางแบบเต็ม
ตามค่าเริ่มต้น และระบบจะสร้าง .note.gnu.build-id จากการแฮชไบนารีที่ลบออกก่อนหน้านี้
ดังนั้นไฟล์นี้จะเปลี่ยนแปลงหากสัญลักษณ์การแก้ไขข้อบกพร่องเปลี่ยนแปลง
โซลูชัน: ตอนนี้ AOSP ทำให้เส้นทางการแก้ไขข้อบกพร่องเป็นแบบสัมพัทธ์ ดูรายละเอียดได้ที่ CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02
การประทับเวลา
ปัญหา: การประทับเวลาในเอาต์พุตบิลด์ทำให้เกิดการเปลี่ยนแปลงไฟล์ที่ไม่จำเป็น ซึ่งมักจะเกิดขึ้นในตำแหน่งต่อไปนี้
- มาโคร
__DATE__/__TIME__/__TIMESTAMP__ในโค้ด C หรือ C++ - การประทับเวลาที่ฝังอยู่ในไฟล์เก็บถาวรที่ใช้ zip
โซลูชัน/ตัวอย่าง: หากต้องการนำการประทับเวลาออกจากเอาต์พุตบิลด์ ให้ใช้ คำแนะนำด้านล่างใน __DATE__/__TIME__/__TIMESTAMP__ ใน C/C++. และ การประทับเวลาที่ฝังอยู่ในไฟล์เก็บถาวร
__DATE__/__TIME__/__TIMESTAMP__ ใน C/C++
มาโครเหล่านี้จะสร้างเอาต์พุตที่แตกต่างกันเสมอสำหรับบิลด์ต่างๆ ดังนั้นจึงไม่ควรใช้ ตัวเลือกบางอย่างสำหรับการนำมาโครเหล่านี้ออกมีดังนี้
- นำมาโครออก ดูตัวอย่างได้ที่ https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f
- หากต้องการระบุไบนารีที่ทำงานอยู่โดยไม่ซ้ำกัน ให้อ่าน build-id จากส่วนหัว ELF
-
หากต้องการทราบเวลาที่สร้างระบบปฏิบัติการ ให้อ่าน
ro.build.date(วิธีนี้ใช้ได้กับ ทุกอย่างยกเว้นบิลด์แบบเพิ่มส่วน ซึ่งอาจไม่อัปเดตวันที่นี้) ดูตัวอย่างได้ที่ https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84
การประทับเวลาที่ฝังอยู่ในไฟล์เก็บถาวร (zip, jar)
Android 7.0 แก้ไขปัญหาการประทับเวลาที่ฝังอยู่ในไฟล์เก็บถาวร zip โดยการเพิ่ม
-X ลงในการใช้คำสั่ง zip ทั้งหมด ซึ่งจะนำ UID/GID ของ
ผู้สร้างและการประทับเวลา Unix แบบขยายออกจากไฟล์ zip
เครื่องมือใหม่ ziptime (อยู่ใน
/platform/build/+/android17-release/tools/ziptime/) จะรีเซ็ตการประทับเวลาปกติในส่วนหัว zip ดูรายละเอียดได้ใน
ไฟล์ README
เครื่องมือ signapk จะตั้งค่าการประทับเวลาสำหรับไฟล์ APK ซึ่งอาจแตกต่างกันไปตาม
เขตเวลาของเซิร์ฟเวอร์ ดูรายละเอียดได้ที่ CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028
เครื่องมือ signapk จะตั้งค่าการประทับเวลาสำหรับไฟล์ APK ซึ่งอาจแตกต่างกันไปตาม
เขตเวลาของเซิร์ฟเวอร์ ดูรายละเอียดได้ที่ CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028
สตริงเวอร์ชัน
ปัญหา: สตริงเวอร์ชัน APK มักจะมี BUILD_NUMBER ต่อท้าย
เวอร์ชันที่ฮาร์ดโค้ด แม้ว่าจะไม่มีการเปลี่ยนแปลงอื่นๆ ใน APK แต่ APK
ก็ยังคงแตกต่างกัน
โซลูชัน: นำหมายเลขบิลด์ออกจากสตริงเวอร์ชัน APK
ตัวอย่าง:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
เปิดใช้การคำนวณ verity ในอุปกรณ์
หากเปิดใช้ dm-verity ในอุปกรณ์ เครื่องมือ OTA จะเลือกการกำหนดค่า verity โดยอัตโนมัติและ เปิดใช้การคำนวณ verity ในอุปกรณ์ ซึ่งจะช่วยให้คำนวณบล็อก verity ในอุปกรณ์ Android ได้แทนที่จะจัดเก็บเป็นไบต์ดิบในแพ็กเกจ OTA บล็อก verity สามารถใช้พื้นที่ ประมาณ 16 MB สำหรับพาร์ติชัน 2 GB
อย่างไรก็ตาม การคำนวณ verity ในอุปกรณ์อาจใช้เวลานาน โดยเฉพาะโค้ดการแก้ไขข้อผิดพลาดไปข้างหน้า (Forward
Error-Correction Code) อาจใช้เวลานาน ในอุปกรณ์ Pixel มักใช้เวลาไม่เกิน 10
นาที ส่วนในอุปกรณ์ระดับล่างอาจใช้เวลานานกว่านั้น หากต้องการปิดใช้การคำนวณ verity ในอุปกรณ์ แต่ยังคงเปิดใช้ dm-verity คุณสามารถทำได้โดยส่ง --disable_fec_computation ไปยังเครื่องมือ ota_from_target_files เมื่อสร้างการอัปเดต OTA แฟล็กนี้จะปิดใช้การคำนวณ verity ในอุปกรณ์ระหว่างการอัปเดต OTA
ซึ่งจะลดเวลาในการติดตั้ง OTA แต่เพิ่มขนาดแพ็กเกจ OTA หากอุปกรณ์ไม่ได้เปิดใช้ dm-verity
การส่งแฟล็กนี้จะไม่มีผล
เครื่องมือบิลด์ที่สอดคล้องกัน
ปัญหา: เครื่องมือที่สร้างไฟล์ที่ติดตั้งต้องสอดคล้องกัน (อินพุตที่กำหนด ควรสร้างเอาต์พุตเดียวกันเสมอ)
โซลูชัน/ตัวอย่าง: เราต้องทำการเปลี่ยนแปลงในเครื่องมือบิลด์ต่อไปนี้
- เครื่องมือสร้างไฟล์ NOTICE เราได้เปลี่ยนเครื่องมือสร้างไฟล์ NOTICE เพื่อสร้าง คอลเล็กชัน NOTICE ที่ทำซ้ำได้ ดูได้ที่ CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64
- Java Android Compiler Kit (Jack) เราต้องอัปเดต Toolchain ของ Jack เพื่อ จัดการการเปลี่ยนแปลงที่เกิดขึ้นเป็นครั้งคราวในการจัดลำดับตัวสร้าง เราได้เพิ่มตัวเข้าถึงแบบดีเทอร์มินิสติกสำหรับ ตัวสร้างลงใน Toolchain แล้ว: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b
- คอมไพเลอร์ ART AOT (dex2oat) ไบนารีของคอมไพเลอร์ ART ได้รับการอัปเดตที่ เพิ่มตัวเลือกในการสร้างรูปภาพแบบดีเทอร์มินิสติก: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9
-
ไฟล์ libpac.so (V8) ทุกบิลด์จะสร้างไฟล์ที่แตกต่างกัน
/system/lib/libpac.soเนื่องจากสแนปช็อต V8 เปลี่ยนไปสำหรับแต่ละบิลด์ โซลูชันคือการนำสแนปช็อตออก: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29 - ไฟล์ pre-dexopt (.odex) ของแอปพลิเคชัน ไฟล์ pre-dexopt (.odex) มีการเพิ่มพื้นที่ว่างที่ไม่ได้เริ่มต้นในระบบ 64 บิต เราได้แก้ไขปัญหานี้แล้ว: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029
ใช้เครื่องมือ diff ของบิลด์
ในกรณีที่ไม่สามารถนำการเปลี่ยนแปลงไฟล์ที่เกี่ยวข้องกับบิลด์ออกได้ AOSP มี
เครื่องมือ diff ของบิลด์
target_files_diff.py
สำหรับใช้ในการเปรียบเทียบแพ็กเกจไฟล์ 2 แพ็กเกจ เครื่องมือนี้จะทำการ diff แบบเรียกซ้ำระหว่าง 2
บิลด์ โดยไม่รวมการเปลี่ยนแปลงไฟล์ที่เกี่ยวข้องกับบิลด์ทั่วไป เช่น
- การเปลี่ยนแปลงที่คาดไว้ในเอาต์พุตบิลด์ (เช่น เนื่องจากการเปลี่ยนแปลงหมายเลขบิลด์)
- การเปลี่ยนแปลงเนื่องจากปัญหาที่ทราบในระบบบิลด์ปัจจุบัน
หากต้องการใช้เครื่องมือ diff ของบิลด์ ให้เรียกใช้คำสั่งต่อไปนี้
target_files_diff.py dir1 dir2
dir1 และ dir2 เป็นไดเรกทอรีฐานที่มีไฟล์เป้าหมายที่แยกออกมาสำหรับแต่ละบิลด์
จัดสรรบล็อกอย่างสม่ำเสมอ
สำหรับไฟล์ที่กำหนด แม้ว่าเนื้อหาจะยังคงเหมือนเดิมระหว่าง 2 บิลด์ แต่บล็อกจริง ที่เก็บข้อมูลอาจเปลี่ยนแปลงไป ด้วยเหตุนี้ ตัวอัปเดตจึงต้องทำการ I/O ที่ไม่จำเป็น เพื่อย้ายบล็อกไปมาสำหรับการอัปเดต OTA
ในการอัปเดต OTA แบบ Virtual A/B การทำ I/O ที่ไม่จำเป็นอาจเพิ่มพื้นที่เก็บข้อมูลที่จำเป็นในการจัดเก็บสแนปช็อตแบบคัดลอกเมื่อเขียน (Copy-on-Write) อย่างมาก ในการอัปเดต OTA แบบที่ไม่ใช่ A/B การย้ายบล็อกไปมาสำหรับการ อัปเดต OTA จะทำให้เวลาในการอัปเดตนานขึ้นเนื่องจากมีการทำ I/O มากขึ้นจากการย้ายบล็อก
เพื่อแก้ไขปัญหานี้ ใน Android 7.0 Google ได้ขยายเครื่องมือ make_ext4fs เพื่อให้
การจัดสรรบล็อกสอดคล้องกันในบิลด์ต่างๆ เครื่องมือ make_ext4fs รับ
แฟล็กที่ไม่บังคับ -d base_fs ซึ่งพยายามจัดสรรไฟล์ไปยังบล็อกเดียวกัน
เมื่อสร้างรูปภาพ ext4 คุณสามารถแยกไฟล์การแมปบล็อก (เช่น
ไฟล์การแมป base_fs) ออกจากไฟล์ zip ของไฟล์เป้าหมายของบิลด์ก่อนหน้า สำหรับแต่ละ
ext4 พาร์ติชัน จะมีไฟล์ .map ใน
IMAGES ไดเรกทอรี (เช่น IMAGES/system.map จะสอดคล้องกับ
system พาร์ติชัน) จากนั้นคุณสามารถเช็กอินและ
ระบุไฟล์ base_fs เหล่านี้ผ่าน PRODUCT_<partition>_BASE_FS_PATH ได้ ดังตัวอย่างนี้
PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map
แม้ว่าวิธีนี้จะไม่ช่วยลดขนาดแพ็กเกจ OTA โดยรวม แต่จะช่วยปรับปรุงประสิทธิภาพการอัปเดต OTA โดยลดปริมาณการทำ I/O สำหรับการอัปเดต Virtual A/B วิธีนี้จะช่วยลดพื้นที่เก็บข้อมูลที่จำเป็นในการใช้ OTA ลงอย่างมาก
หลีกเลี่ยงการอัปเดตแอป
นอกเหนือจากการลด diff ของบิลด์แล้ว คุณยังลดขนาดการอัปเดต OTA ได้ด้วยการยกเว้นการอัปเดต สำหรับแอปที่ได้รับการอัปเดตผ่าน App Store APK มักจะเป็นส่วนสำคัญของ พาร์ติชันต่างๆ ในอุปกรณ์ การรวมแอปเวอร์ชันล่าสุดที่ได้รับการอัปเดตโดย App Store ไว้ในการอัปเดต OTA อาจส่งผลต่อขนาดแพ็กเกจ OTA อย่างมาก และผู้ใช้ได้รับประโยชน์เพียงเล็กน้อย เมื่อผู้ใช้ได้รับแพ็กเกจ OTA ผู้ใช้อาจมีแอปที่อัปเดตแล้วหรือ เวอร์ชันที่ใหม่กว่านั้นอีกซึ่งได้รับจาก App Store โดยตรง