בדף הזה מתוארים שינויים שנוספו ל-AOSP כדי לצמצם את השינויים הלא נחוצים בקובצים בין גרסאות build. מפתחי מכשירים שמנהלים מערכות build משלהם יכולים להשתמש במידע הזה כמדריך לצמצום הגודל של העדכונים שלהם דרך האוויר (OTA).
לפעמים עדכוני OTA של Android מכילים קבצים ששונו ולא תואמים לשינויים בקוד. הם למעשה ארטיפקטים של מערכת build. מצב כזה יכול לקרות כשאותו קוד, שנוצר בזמנים שונים, מתיקיות שונות או במכונות שונות, יוצר מספר גדול של קבצים ששונו. קבצים עודפים כאלה מגדילים את הגודל של תיקון OTA, ומקשים על הזיהוי של הקוד שהשתנה.
כדי לשפר את השקיפות של התוכן של עדכון OTA, מערכת AOSP כוללת שינויים במערכת ה-build שנועדו לצמצם את הגודל של תיקוני OTA. הוסרו שינויים מיותרים בקבצים בין גרסאות build, ועדכוני OTA מכילים רק קבצים שקשורים לתיקונים. AOSP כולל גם כלי לבדיקת שינויים בגרסאות build, שמסנן שינויים נפוצים בקבצים שקשורים ל-build כדי לספק בדיקה נקייה יותר של שינויים בקבצים של build, וגם כלי למיפוי בלוקים, שעוזר לשמור על עקביות בהקצאת בלוקים.
מערכת build יכולה ליצור תיקונים גדולים מדי שלא נחוצים בכמה דרכים. כדי לצמצם את הבעיה, בגרסה Android 8.0 ואילך הוחלו תכונות חדשות שמפחיתות את גודל התיקון לכל קובץ. השיפורים שהקטינו את הגודל של חבילות העדכונים דרך OTA כוללים:
-
שימוש ב-ZSTD, אלגוריתם דחיסה ללא אובדן נתונים למטרות כלליות, לתמונות מלאות בעדכוני מכשיר שאינם A/B. אפשר להתאים אישית את ZSTD כדי לקבל יחסי דחיסה גבוהים יותר על ידי הגדלת רמת הדחיסה. רמת הדחיסה מוגדרת במהלך הזמן שבו מתבצע ה-OTA, וניתן להגדיר אותה על ידי העברת הדגל
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
. -
הגדלת גודל חלון הדחיסה שמשמש במהלך OTA. אפשר להגדיר את גודל חלון הדחיסה המקסימלי על ידי התאמה אישית של פרמטר ה-build בקובץ
.mk
של המכשיר. המשתנה הזה מוגדר בתורPRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
. - שימוש ב-Puffin, כלי לתיקון דטרמיניסטי של זרמים של deflate, שמטפל בפונקציות הדחיסה וההשוואה ליצירת עדכוני OTA של A/B.
-
שינויים בשימוש בכלי ליצירת דלתא, למשל אופן השימוש בספרייה
bsdiff
לדחיסת תיקוני באגים. ב-Android 9 ואילך, הכליbsdiff
בוחר את אלגוריתם הדחיסה שיניב את תוצאות הדחיסה הטובות ביותר לתיקון. -
שיפורים ב-
update_engine
הביאו לירידה בצריכת הזיכרון כשחלים תיקונים על עדכוני מכשירים בניסוי A/B.
בקטעים הבאים נדון בבעיות שונות שמשפיעות על גודל העדכונים דרך OTA, בפתרון שלהן ובדוגמאות להטמעה ב-AOSP.
סדר הקבצים
הבעיה: מערכת הקבצים לא מבטיחה סדר של קבצים כשמבקשים רשימה של קבצים בתיקייה, אבל בדרך כלל הסדר זהה לאותו תהליך אימות. כלים כמו ls
ממיינים את התוצאות כברירת מחדל, אבל פונקציית התו הכללי לחיפוש שמשמשת פקודות כמו find
ו-make
לא מבצעת מיון. לפני שמשתמשים בכלים האלה, צריך למיין את הפלט.
פתרון: כשמשתמשים בכלים כמו find
ו-make
עם פונקציית התו הכללי לחיפוש, צריך למיין את הפלט של הפקודות האלה לפני שמשתמשים בהן. כשמשתמשים ב-$(wildcard)
או ב-$(shell find)
בקבצים מסוג Android.mk
, צריך למיין אותם גם כן. כלים מסוימים, כמו Java, מבצעים מיון של הקלט, ולכן לפני שממיינים את הקבצים, צריך לוודא שהכלי שבו אתם משתמשים כבר לא עשה זאת.
דוגמאות: תיקנו הרבה מקרים במערכת הליבה ליצירת גרסאות build באמצעות המאקרו המובנה all-*-files-under
, שכולל את all-cpp-files-under
(כי כמה הגדרות הופצו בקובצי makefile אחרים).
פרטים נוספים זמינים במאמרים הבאים:
- 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
ספריית ה-build
הבעיה: שינוי הספרייה שבה מתבצע ה-build יכול לגרום לקבצים הבינאריים להיות שונים. רוב הנתיבים ב-build של Android הם נתיבים יחסיים, כך ש-__FILE__
ב-C/C++ לא מהווה בעיה. עם זאת, סמלי ניפוי הבאגים מקודדים את נתיב הנתונים המלא כברירת מחדל, וה-.note.gnu.build-id
נוצר מהצפנה של הקובץ הבינארי שנחסר ממנו תוכן מראש, כך שהוא ישתנה אם סמלי ניפוי הבאגים ישתנו.
הפתרון: עכשיו נתיבי ניפוי הבאגים ב-AOSP הם יחסיים. פרטים נוספים זמינים ב-CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02.
חותמות זמן
הבעיה: חותמות זמן בפלט ה-build גורמות לשינויים מיותרים בקובץ. סביר להניח שהמצב הזה יקרה במיקומים הבאים:
__DATE__/__TIME__/__TIMESTAMP__
מאקרו בקוד C או C++.- חותמות זמן שמוטמעות בארכיונים מבוססי-zip.
פתרונות/דוגמאות: כדי להסיר חותמות זמן מפלט ה-build, יש לפעול לפי ההוראות שמפורטות בהמשך בקטע __DATE__/__TIME__/__TIMESTAMP__ ב-C/C++ ובקטע חותמות זמן מוטמעות בארכיונים.
__DATE_/__TIME__/__TIMESTAMP__ ב-C/C++
המאקרואים האלה תמיד יוצרים תוצאות שונות לגרסאות build שונות, לכן אל תשתמשו בהם. ריכזנו כאן כמה אפשרויות להסרת המאקרו:
- להסיר אותם. לדוגמה, אפשר לעיין ב-https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- כדי לזהות באופן ייחודי את קובץ הבינארי שפועל, קוראים את מזהה ה-build מכותרת ה-ELF.
-
כדי לדעת מתי מערכת ההפעלה נוצרה, קוראים את
ro.build.date
(האפשרות הזו תקפה לכל הקבצים, מלבד גרסאות build מצטברות, שיכול להיות שהתאריך שלהן לא יתעדכן). לדוגמה, אפשר לעיין ב-https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84.
חותמות זמן מוטמעות בארכיונים (zip, jar)
ב-Android 7.0 תוקנה הבעיה של חותמות זמן מוטמעות בארכיונים בפורמט zip, על ידי הוספת -X
לכל השימושים בפקודה zip
. כך הוסרו מקובץ ה-zip ה-UID/GID של ה-builder וחותמת הזמן המורחבת של Unix.
כלי חדש, ziptime
(שנמצא ב-/platform/build/+/android16-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 עדיין תהיה שונה.
פתרון: מסירים את מספר ה-build ממחרושת גרסת ה-APK.
דוגמאות:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
הפעלת חישוב של אימות במכשיר
אם התכונה dm-verity מופעלת במכשיר, הכלים לעדכון OTA מזהים באופן אוטומטי את הגדרות האימות ומפעילים את החישוב של האימות במכשיר. כך אפשר לחשב בלוקים של אימות במכשירי Android, במקום לאחסן אותם כבייט גולמיים בחבילת ה-OTA. בלוקים של Verity יכולים להשתמש ב-16MB בערך למחיצה של 2GB.
עם זאת, חישוב האימות במכשיר עשוי להימשך זמן רב. באופן ספציפי, הקוד לתיקון שגיאות יכול להימשך זמן רב. במכשירי Pixel, התהליך נמשך עד 10 דקות. במכשירים בסיסיים, התהליך עשוי להימשך זמן רב יותר. אם רוצים להשבית את החישוב של אימות במכשיר, אבל עדיין להפעיל את dm-verity, אפשר להעביר את הערך --disable_fec_computation
לכלי ota_from_target_files
בזמן יצירת עדכון OTA. הדגל הזה משבית את חישוב האימות במכשיר במהלך עדכונים דרך OTA.
היא מקצרת את זמן ההתקנה דרך OTA, אבל מגדילה את גודל חבילת ה-OTA. אם ה-dm-verity לא מופעל במכשיר, העברת הדגל הזה לא תשפיע.
כלים עקביים ל-build
הבעיה: כלים שיוצרים קבצים מותקנים חייבים להיות עקביים (כל קלט נתון צריך תמיד לייצר את אותו פלט).
פתרונות/דוגמאות: נדרשו שינויים בכלי ה-build הבאים:
- הודעה ליוצר הקובץ. הכלי ליצירת קובצי NOTICE השתנה כדי ליצור אוספים של קובצי NOTICE שניתנים לשכפול. אפשר לעיין ב-CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack) נדרש עדכון של כלי הפיתוח של Jack כדי לטפל בשינויים מזדמנים בסדר היצירה של ה-constructor. נוספו ל-toolchain רכיבי גישה חד-משמעיים למאפיינים של קונסטרוקטור: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- מַעְבד AOT של ART (dex2oat). קובץ הבינארי של המהדר של ART קיבל עדכון שבו נוספה אפשרות ליצור קובץ אימג' דטרמיניסטית: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9.
-
הקובץ libpac.so (V8). בכל build נוצר קובץ
/system/lib/libpac.so
שונה, כי קובץ ה-snapshot של V8 משתנה בכל build. הפתרון היה להסיר את קובץ snapshot: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29. - קבצים של אפליקציות לפני דקס-אופטימיזציה (.odex). הקבצים מ-pre-dexopt (.odex) הכילו מילוי לא מוגדר מראש במערכות של 64 ביט. הבעיה תוקנה: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
שימוש בכלי build diff
במקרים שבהם אי אפשר למנוע שינויים בקבצים שקשורים ל-build, מערכת AOSP כוללת target_files_diff.py
, כלי להשוואה בין שתי חבילות קבצים. הכלי מבצע השוואה רפלקסיבית בין שני גרסאות build, לא כולל שינויים נפוצים בקובצי build, כמו
- שינויים צפויים בפלט של ה-build (לדוגמה, עקב שינוי במספר ה-build).
- שינויים עקב בעיות ידועות במערכת ה-build הנוכחית.
כדי להשתמש בכלי build diff, מריצים את הפקודה הבאה:
target_files_diff.py dir1 dir2
dir1
ו-dir2
הן ספריות בסיס שמכילות את קובצי היעד שחולצו לכל build.
שמירה על עקביות בהקצאת בלוקים
בקובץ נתון, התוכן שלו נשאר זהה בין שני גרסאות build, אבל יכול להיות שהבלוקים בפועל שמכילים את הנתונים השתנו. כתוצאה מכך, מנהל העדכונים צריך לבצע פעולות קלט/פלט מיותרות כדי להעביר את הבלוק לעדכון OTA.
בעדכון OTA וירטואלי של A/B, פעולות קלט/פלט מיותרות יכולות להגדיל באופן משמעותי את נפח האחסון הנדרש לשמירת קובץ ה-snapshot של 'העתקה בזמן כתיבה'. בעדכון OTA שאינו A/B, העברת הבלוק לעדכון OTA תורמת לזמן העדכון כי יש יותר קלט/פלט בגלל העברת הבלוק.
כדי לטפל בבעיה הזו, Google הרחיבה את הכלי make_ext4fs
ב-Android 7.0 כדי לשמור על עקביות בהקצאת בלוקים בין גרסאות build. הכלי make_ext4fs
מקבל את הדגל האופציונלי -d base_fs
, שמנסה להקצות קבצים לאותו בלוק בזמן יצירת קובץ האימג' ext4
. אפשר לחלץ את קובצי המיפוי של הבלוק (כמו קובצי המפה base_fs
) מקובץ ה-zip של קובצי היעד של build קודם. לכל מחיצה 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 על ידי צמצום כמות הקלט/פלט. בעדכוני A/B וירטואליים, היא מפחיתה באופן משמעותי את נפח האחסון הנדרש כדי להחיל את העדכון דרך OTA.
הימנעות משימוש בעדכונים של אפליקציות
בנוסף לצמצום ההבדלים בין גרסאות build, אפשר לצמצם את הגודל של עדכוני OTA על ידי החרגת עדכונים לאפליקציות שמקבלות עדכונים דרך חנויות אפליקציות. לעיתים קרובות, חבילות APK מהוות חלק משמעותי במחיצות השונות במכשיר. הכללתם של הגרסאות האחרונות של אפליקציות שמתעדכנות בחנויות האפליקציות בעדכון OTA עשויה להשפיע על הגודל של חבילות OTA, ולספק למשתמשים מעט תועלת. יכול להיות שכבר תהיה למשתמשים גרסה מעודכנת של האפליקציה, או גרסה חדשה יותר, שהם קיבלו ישירות מחנויות האפליקציות, עד שהם יקבלו את חבילת ה-OTA.