עדכון OTA למכשירי A/B ללא מחיצות דינמיות

‫Android 10 תומך במחיצות דינמיות, מערכת חלוקת מחיצות במרחב המשתמש שיכולה ליצור, לשנות את הגודל ולמחוק מחיצות במהלך עדכונים דרך האוויר (OTA).

בדף הזה מוסבר איך לקוחות OTA משנים את הגודל של מחיצות דינמיות במהלך עדכון למכשירי A/B שהושקו ללא תמיכה במחיצות דינמיות, ואיך לקוחות OTA משדרגים ל-Android 10.

רקע

במהלך עדכון של מכשיר A/B לתמיכה במחיצות דינמיות, טבלת המחיצות GUID (GPT) במכשיר נשמרת, כך שאין מחיצת super במכשיר. המטא-נתונים מאוחסנים ב-system_a וב-system_b, אבל אפשר לשנות את ההגדרה הזו באמצעות שינוי של BOARD_SUPER_PARTITION_METADATA_DEVICE.

בכל אחד ממכשירי הבלוק יש שני משבצות מטא-נתונים. משתמשים רק במשבצת מטא-נתונים אחת בכל מכשיר בלוק. לדוגמה, מטא-נתונים 0 ב-system_a ומטא-נתונים 1 ב-system_b מתאימים למחיצות במשבצות A ו-B, בהתאמה. בזמן הריצה, לא משנה איזה מיקום מתעדכן.

בדף הזה, משבצות המטא-נתונים נקראות Metadata S (מקור) ו-Metadata T (יעד). באופן דומה, המחיצות נקראות system_s,‏ vendor_t וכן הלאה.

מידע נוסף על הגדרות של מערכת ה-build זמין במאמר שדרוג מכשירים.

מידע נוסף על השתייכות של מחיצות לקבוצות עדכון זמין במאמר שינויים בהגדרות של לוחות למכשירים חדשים.

דוגמה למטא-נתונים במכשיר:

  • התקן בלוקים פיזי system_a
    • מטא-נתונים 0
      • קבוצה foo_a
        • מחיצה לוגית (דינמית) system_a
        • מחיצה לוגית (דינמית) product_services_a
        • מחיצות אחרות שעודכנו על ידי Foo
      • קבוצה bar_a
        • מחיצה לוגית (דינמית) vendor_a
        • מחיצה לוגית (דינמית) product_a
        • מחיצות אחרות שעודכנו על ידי Bar
    • מטא-נתונים 1 (לא בשימוש)
  • התקן בלוקים פיזי system_b
    • מטא-נתונים 0 (לא בשימוש)
    • מטא-נתונים 1
      • Group foo_b
        • מחיצה לוגית (דינמית) system_b
        • מחיצה לוגית (דינמית) product_services_b
        • מחיצות אחרות שעודכנו על ידי Foo
      • Group bar_b
        • מחיצה לוגית (דינמית) vendor_b
        • מחיצה לוגית (דינמית) product_b
        • מחיצות אחרות שעודכנו על ידי Bar

אפשר להשתמש בכלי lpdump בקטע system/extras/partition_tools כדי להעביר את המטא-נתונים למכשיר. לדוגמה:

lpdump --slot 0 /dev/block/by-name/system_a
lpdump --slot 1 /dev/block/by-name/system_b

התאמת עדכון

במכשירים עם Android 9 וגרסאות קודמות, לקוח ה-OTA במכשיר לא תומך במיפוי מחיצות דינמיות לפני העדכון. נוצרת קבוצה נוספת של תיקונים כדי שאפשר יהיה להחיל את המיפוי ישירות על המחיצות הפיזיות הקיימות.

מחולל ה-OTA יוצר את קובץ ה-super.img הסופי שמכיל את התוכן של כל המחיצות הדינמיות, ואז מפצל את קובץ האימג' לכמה קובצי אימג' שתואמים לגדלים של התקני הבלוק הפיזיים שמתאימים למערכת, לספק וכו'. התמונות האלה נקראות super_system.img, super_vendor.img וכן הלאה. לקוח ה-OTA מחיל את התמונות האלה על המחיצות הפיזיות, ולא על המחיצות הלוגיות (הדינמיות).

מכיוון שלקוח ה-OTA לא יודע איך למפות מחיצות דינמיות, כל השלבים שאחרי ההתקנה מושבתים אוטומטית עבור המחיצות האלה כשחבילת העדכון נוצרת. פרטים נוספים מופיעים במאמר בנושא הגדרת הפעולות אחרי ההתקנה.

תהליך העדכון זהה לזה שב-Android 9.

לפני העדכון:

ro.boot.dynamic_partitions=
ro.boot.dynamic_partitions_retrofit=

אחרי העדכון:

ro.boot.dynamic_partitions=true
ro.boot.dynamic_partitions_retrofit=true

עדכונים עתידיים אחרי התאמה

אחרי עדכון ה-retrofit, לקוח ה-OTA מתעדכן כדי לעבוד עם מחיצות דינמיות. ההיקפים של מחיצות המקור אף פעם לא חופפים למחיצות פיזיות של היעד.

תהליך העדכון באמצעות חבילת עדכון רגילה

  1. מאתחלים את המטא-נתונים של המחיצה super.
    1. יצירת מטא-נתונים חדשים M ממטא-נתונים S (מטא-נתוני המקור). לדוגמה, אם במטא-נתונים S נעשה שימוש ב-‎[system_s, vendor_s, product_s] כמכשירי בלוק, אז במטא-נתונים החדשים M נעשה שימוש ב-‎[system_t, vendor_t, product_t] כמכשירי בלוק. כל הקבוצות והמחיצות נמחקות ב-M.
    2. מוסיפים קבוצות יעד ומחיצות בהתאם לשדה dynamic_partition_metadata במניפסט העדכון. אפשר לראות את הגודל של כל מחיצה ב-new_partition_info.
    3. כותבים M במטא-נתונים T.
    4. ממפים את המחיצות שנוספו ב-device mapper כניתנות לכתיבה.
  2. מחילים את העדכון על המכשירים החסומים.
    1. במידת הצורך, ממפים את מחיצות המקור בממפה המכשיר כקריאה בלבד. הפעולה הזו נדרשת להעלאה צדדית כי מחיצות המקור לא ממופות לפני העדכון.
    2. החלת עדכון מלא או עדכון דלתא על כל מכשירי הבלוק בחריץ היעד.
    3. מפעילים את המחיצות כדי להריץ את הסקריפט אחרי ההתקנה, ואז מבטלים את הפעלת המחיצות.
  3. מבטלים את המיפוי של מחיצות היעד.

תהליך העדכון באמצעות חבילת עדכון רטרו

אם חבילת העדכון של Retrofit מופעלת במכשיר שכבר מופעלות בו מחיצות דינמיות, לקוח ה-OTA מפעיל את קובץ super.img במכשירי בלוק ישירות. תהליך העדכון דומה לעדכון רטרופיט. פרטים נוספים זמינים במאמר בנושא התאמת עדכון.

לדוגמה, נניח את הפרטים הבאים:

  • חריץ A הוא החריץ הפעיל.
  • system_a מכיל את המטא-נתונים הפעילים במשבצת 0.
  • האפליקציות system_a,‏ vendor_a וגם product_a משמשות כמכשירים לחסימה.

כשלקוח ה-OTA מקבל חבילת עדכון של retrofit, הוא מחיל את super_system.img על system_b פיזי, super_vendor.img על vendor_b פיזי, ואת super_product.img על product_b פיזי. מכשיר הבלוק הפיזי system_b מכיל את המטא-נתונים הנכונים למיפוי הלוגי system_b,‏ vendor_b ו-product_b בזמן האתחול.

יצירת חבילות עדכון

עדכון OTA מצטבר

כשיוצרים עדכוני OTA מצטברים למכשירים ששודרגו, העדכונים תלויים בהגדרות PRODUCT_USE_DYNAMIC_PARTITIONS ו-PRODUCT_RETROFIT_DYNAMIC_PARTITIONS בגרסת הבסיס.

  • אם בגרסת הבסיס לא מוגדרים המשתנים, זהו עדכון של התאמה מחדש. חבילת העדכון מכילה את הקובץ split super.img ומשביתה את השלב שאחרי ההתקנה.
  • אם בבניית הבסיס מוגדרים המשתנים, זה זהה לעדכון רגיל עם מחיצות דינמיות. חבילת העדכון מכילה את התמונות למחיצות לוגיות (דינמיות). אפשר להפעיל את שלב ההתקנה.

Full OTA

שתי חבילות OTA מלאות נוצרות למכשירים ששודרגו.

  • $(PRODUCT)-ota-retrofit-$(TAG).zip תמיד מכיל פיצול של super.img ומשבית את השלב שאחרי ההתקנה לצורך התאמה של עדכון.
    • הוא נוצר עם ארגומנט נוסף --retrofit_dynamic_partitions לסקריפט ota_from_target_files.
    • אפשר להחיל אותו על כל הגרסאות.
  • $(PRODUCT)-ota-$(TAG).zip מכיל תמונות לוגיות לעדכונים עתידיים.
    • ההגדרה הזו חלה רק על גרסאות build שבהן מופעלות מחיצות דינמיות. בהמשך מפורטות הוראות לאכיפת ההגדרה הזו.

דחיית עדכון שלא מתאים לבנייה ישנה

חבילת ה-OTA המלאה הרגילה חלה רק על גרסאות build שמופעלות בהן מחיצות דינמיות. אם שרת ה-OTA מוגדר בצורה שגויה והוא דוחף את החבילות האלה למכשירים עם Android 9 או גרסה מוקדמת יותר, המכשירים לא יפעלו. לקוח ה-OTA ב-Android 9 ובגרסאות נמוכות יותר לא יכול להבדיל בין חבילת OTA של retrofit לבין חבילת OTA מלאה רגילה, ולכן הלקוח לא ידחה את החבילה המלאה.

כדי למנוע מהמכשיר לקבל את חבילת ה-OTA המלאה, אפשר לדרוש שלב אחרי ההתקנה כדי לבדוק את הגדרת המכשיר הקיימת. לדוגמה:

device/device_name/dynamic_partitions/check_dynamic_partitions

#!/system/bin/sh
DP_PROPERTY_NAME="ro.boot.dynamic_partitions"
DP_RETROFIT_PROPERTY_NAME="ro.boot.dynamic_partitions_retrofit"

DP_PROPERTY=$(getprop ${DP_PROPERTY_NAME})
DP_RETROFIT_PROPERTY=$(getprop ${DP_RETROFIT_PROPERTY_NAME})

if [ "${DP_PROPERTY}" != "true" ] || [ "${DP_RETROFIT_PROPERTY}" != "true" ] ; then
    echo "Error: applied non-retrofit update on build without dynamic" \
         "partitions."
    echo "${DP_PROPERTY_NAME}=${DP_PROPERTY}"
    echo "${DP_RETROFIT_PROPERTY_NAME}=${DP_RETROFIT_PROPERTY}"
    exit 1
fi

device/device_name/dynamic_partitions/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= check_dynamic_partitions
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := check_dynamic_partitions
LOCAL_PRODUCT_MODULE := true
include $(BUILD_PREBUILT)

device/device_name/device.mk

PRODUCT_PACKAGES += check_dynamic_partitions

# OPTIONAL=false so that the error in check_dynamic_partitions will be
# propagated to OTA client.
AB_OTA_POSTINSTALL_CONFIG += \
    RUN_POSTINSTALL_product=true \
    POSTINSTALL_PATH_product=bin/check_dynamic_partitions \
    FILESYSTEM_TYPE_product=ext4 \
    POSTINSTALL_OPTIONAL_product=false \

כשחבילת ה-OTA הרגילה מוחלת על מכשיר שלא מופעלות בו מחיצות דינמיות, לקוח ה-OTA מפעיל את check_dynamic_partitions כשלב אחרי ההתקנה ודוחה את העדכון.