הטמעת עדכוני A/B

יצרני ציוד מקורי וספקי SoC שרוצים להטמיע עדכוני מערכת A/B חייבים לוודא את תוכנת האתחול שלהם מממשת את HAL מסוגboo_control ומעבירה את הפרמטרים הנכונים של הליבה.

הטמעת HAL של בקרת אתחול

תוכנת אתחול עם יכולות A/B חייבת להטמיע את מדד ה-HAL boot_control ב hardware/libhardware/include/hardware/boot_control.h. אפשר לבדוק הטמעות באמצעות system/extras/bootctl שימושי system/extras/tests/bootloader/.

בנוסף, צריך להטמיע את מכונת המצב שמוצגת בהמשך:

איור 1. מצב תוכנת אתחול

הגדרת הליבה

כדי להטמיע עדכוני מערכת A/B:

  1. בחרו בקפידה את סדרת תיקוני הליבה הבאה (אם יש צורך):
  2. מוודאים שהארגומנטים בשורת הפקודה של הליבה מכילים את הארגומנטים הנוספים הבאים:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... כאשר הערך <public-key-id> הוא המזהה של המפתח הציבורי שמשמש לאמת את חתימת טבלת האימות (פרטים נוספים זמינים במאמר dm-verity).
  3. מוסיפים את אישור .X509 שמכיל את המפתח הציבורי לאוסף המפתחות של המערכת:
    1. מעתיקים את אישור ה- .X509 בפורמט .der לרמה הבסיסית (root) של הספרייה kernel. אם אישור .X509 בפורמט קובץ .pem, צריך להשתמש בפקודת openssl הבאה כדי להמיר הפורמט .pem עד .der:
      opensl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. צריך ליצור את zImage כדי לכלול את האישור כחלק מאוסף המפתחות של המערכת. כדי לאמת,יש לבדוק את הערך procfs (נדרשת KEYS_CONFIG_DEBUG_PROC_KEYS להפעלה):
      angler:/# cat /proc/keys
      
      1c8a217e I------ 1 perm 1f010000 0 0 אסימטרית
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      אישור ה- .X509 מצביע על כך שהמפתח הציבורי נכלל בהצלחה באוסף המפתחות של המערכת (ההדגשה מציינת את מזהה המפתח הציבורי).
    3. מחליפים את המרחב המשותף ב-# ומעבירים אותו בתור <public-key-id> בשורת הפקודה בליבה (kernel). לדוגמה, להעביר Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f במקום <public-key-id>.

הגדרה של משתני build

תוכנת אתחול עם יכולות A/B חייבת לעמוד בקריטריונים הבאים של משתני build:

יש להגדיר ליעד A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \

      system \   vendor
    ומחיצות אחרות שעודכנו באמצעות update_engine (רדיו, תוכנת אתחול, etc.)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
לדוגמה, אפשר להיעזר ב /device/google/marlin/+/android-7.1.0_r1/device-common.mk. ניתן לבצע את שלב dex2oat לאחר ההתקנה (אבל לפני האתחול) המתואר ב- הידור.
מומלץ מאוד ליעד A/B
  • הגדרה של TARGET_NO_RECOVERY := true
  • הגדרה של BOARD_USES_RECOVERY_AS_BOOT := true
  • לא להגדיר BOARD_RECOVERYIMAGE_PARTITION_SIZE
לא ניתן להגדיר ליעד A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
אופציונלי לגרסאות build של ניפוי באגים PRODUCT_PACKAGES_DEBUG += update_engine_client

הגדרת מחיצות (משבצות)

במכשירי A/B לא נדרשת מחיצה לשחזור או מחיצת מטמון, כי מערכת Android כבר לא משתמשת מחיצות אלה. מחיצת הנתונים משמשת עכשיו לחבילת ה-OTA שהורדתם. הקוד של תמונת השחזור נמצא במחיצת האתחול. יש לתת שם לכל המחיצות המוגדרות כ-A/B-ed באופן הבא (המשבצות תמיד נקראות a, b וכן הלאה): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.

מטמון

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

התאוששות

אחסון ה-RAM לשחזור נמצא עכשיו בקובץ boot.img. בכניסה אל שחזור, תוכנת האתחול לא יכולה להפעיל את האפשרות skip_initramfs בשורת הפקודה של הליבה.

בעדכוני A/B שאינם מסוג A/B, מחיצת השחזור מכילה את הקוד המשמש להחלת העדכונים. בדיקת A/B העדכונים מוחלים על ידי update_engine שפועל בקובץ האימג' של המערכת שמופעלת באופן רגיל. עדיין אפשר להשתמש במצב שחזור כדי לבצע איפוס לנתוני היצרן והתקנה ממקור לא ידוע של עדכון חבילות (ממנה הגיע השם 'שחזור'). הקוד והנתונים של מצב שחזור מאוחסן במחיצת האתחול הרגילה ב-ramdisk; כדי לאתחל בקובץ האימג' של המערכת, תוכנת האתחול מורה לליבה (kernel) לדלג על ה-ramdisk (אחרת המכשיר נכנס למצב התאוששות במצב תצוגה. מצב השחזור הוא קטן (וחלק גדול ממנו כבר היה במחיצת האתחול), לכן המחיצה לא גדלה.

Fstab

הארגומנט slotselect חייב להיות בשורה של ה-A/B. מחיצות. לדוגמה:

<path-to-block-device>/ספק  /ספק ext4 ro
pending,verify=<path-to-block-device>/metadata,slotselect

אין לתת שם למחיצה vendor. במקום זאת, יש לחלק את vendor_a או האפליקציה vendor_b תיבחר ותותקן בנקודת הטעינה /vendor.

ארגומנטים של משבצת ליבה

צריך להעביר את הסיומת של יחידת המשבצת הנוכחית דרך צומת של עץ מכשיר ספציפי (DT) (/firmware/android/slot_suffix) או באמצעות שורת הפקודה ליבה (kernel) או ארגומנטbooconfig ,androidboot.slot_suffix.

כברירת מחדל, במסגרת אתחול מהיר (fastboot) מהבהב החריץ הנוכחי במכשיר A/B. אם גם חבילת העדכון מכיל תמונות עבור החריץ השני, הלא עדכני, גם אתחול מהיר מהבהב את התמונות האלה. האפשרויות הזמינות כוללות:

  • --slot SLOT. ביטול של התנהגות ברירת המחדל וביצוע אתחול מהיר כדי להבהב את החריץ שמועבר בתור ארגומנט.
  • --set-active [SLOT]. הגדרת המשבצת כפעילה. אם אין ארגומנט אופציונלי מוגדר, החריץ הנוכחי מוגדר כפעיל.
  • fastboot --help קבלת פרטים על פקודות.

אם תוכנת האתחול מוסיפה אתחול מהיר (fastboot), הוא אמור לתמוך בפקודה set_active <slot> שמגדיר את המשבצת הפעילה הנוכחית למשבצת הנתונה (זו חייב לנקות גם את הסימון שאינו ניתן לאתחול עבור החריץ הזה ולאפס את ספירת הניסיונות החוזרים לברירת המחדל ). תוכנת האתחול צריכה לתמוך גם במשתנים הבאים:

  • has-slot:<partition-base-name-without-suffix>. הפונקציה מחזירה yes אם מחיצה תומכת בחריצים, אחרת "לא".
  • current-slot הפונקציה מחזירה את הסיומת של יחידת הקיבולת שתבוצע אחרי ההפעלה שלה.
  • slot-count. הפונקציה מחזירה מספר שלם שמייצג את מספר המשבצות הזמינות. כרגע יש תמיכה בשני משבצות, ולכן הערך הזה הוא 2.
  • slot-successful:<slot-suffix>. הפונקציה מחזירה 'yes'. אם המשבצת הנתונה מסומן כ'אתחול מוצלח', 'no' אחרת.
  • slot-unbootable:<slot-suffix>. הפונקציה מחזירה 'yes' אם המשבצת הנתונה מסומנת כלא ניתן לאתחול, "no" אחרת.
  • slot-retry-count:<slot-suffix>. מספר הניסיונות החוזרים שנותרו לניסיון לאתחל את החריץ הנתון.

כדי להציג את כל המשתנים, צריך להריץ את fastboot getvar all

יצירה של חבילות OTA

בכלים של חבילות OTA פועלים לפי אותן פקודות כמו פקודות למכשירים שאינם מסוג A/B. צריך ליצור את הקובץ target_files.zip על ידי הגדרה של משתני ה-build ליעד A/B. הכלים של חבילת OTA מזהים באופן אוטומטי וליצור חבילות בפורמט למעדכן A/B.

לדוגמה:

  • כדי ליצור OTA מלא:
    ./build/make/tools/releasetools/ota_from_target_files \
        Dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • כדי ליצור OTA מצטבר:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        Dist_output/tardis-target_files.zip \
        count_ota_update.zip
    

הגדרת מחיצות

ה-update_engine יכול לעדכן כל צמד של מחיצות A/B שמוגדרות באותו דיסק. לזוג מחיצות יש קידומת משותפת (כמו system או boot) וסיומת לכל משבצת (למשל _a). רשימת המחיצות שעבורן המטען הייעודי (Payload) מחולל מגדיר עדכון שמוגדר על ידי משתנה להפוך AB_OTA_PARTITIONS.

לדוגמה, אם זוג מחיצות bootloader_a ו- booloader_b נכללות (_a ו-_b הן משבצת הזמן סיומות), אפשר לעדכן את המחיצות האלה על ידי ציון המחיצות הבאות במוצר או בלוח תצורה:

AB_OTA_PARTITIONS := \
  אתחול \
  מערכת \
  תוכנת אתחול

אסור לשנות את כל המחיצות שעודכנו על ידי update_engine על ידי שאר המחיצות המערכת. במהלך עדכונים מצטברים או דלתא, הנתונים הבינאריים מהמשבצת הנוכחית הם ששימש ליצירת הנתונים במשבצת החדשה. כל שינוי עלול לגרום לנתוני יחידת הקיבולת (Slot) החדשים נכשלים באימות במהלך תהליך העדכון, ולכן הם נכשלים בעדכון.

הגדרת ההתקנה לאחר ההתקנה

ניתן להגדיר את השלב לאחר ההתקנה באופן שונה עבור כל מחיצה מעודכנת באמצעות קבוצת צמדי מפתח/ערך. כדי להפעיל תוכנית שנמצאת בכתובת /system/usr/bin/postinst תמונה, לציין את הנתיב היחסי לשורש של מערכת הקבצים במחיצת המערכת.

לדוגמה, usr/bin/postinst הוא system/usr/bin/postinst (אם לא באמצעות אחסון זיכרון RAM). בנוסף, מציינים את סוג מערכת הקבצים שרוצים להעביר שיחת מערכת אחת (mount(2)). צריך להוסיף את הערכים הבאים למוצר או למכשיר .mk קבצים (אם רלוונטי):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

הידור אפליקציות

אפשר לבצע הידור של אפליקציות ברקע לפני ההפעלה מחדש באמצעות תמונת המערכת החדשה. כדי להדר ברקע, מוסיפים את קטעי הקוד הבאים לתצורת המכשיר של המוצר (בקטע device.mk של המוצר):

  1. צריך לכלול את הרכיבים המקוריים ב-build כדי לוודא שסקריפט ההידור והקבצים הבינאריים שעברה הידור וכלול בתמונת המערכת.
      # חבילת dexopt OTA של A/B
      PRODUCT_PACKAGES += otapreopt_script
    
  2. חבר את סקריפט ההידור אל update_engine, כך שרץ אחרי ההתקנה.
      # חיבור OTA ל-dexopt update_engine
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

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