פריסת המחיצות

ב-Android 10, מערכת הקבצים ברמה הבסיסית כבר לא כלולה ב-ramdisk.img, אלא מוזגת עם system.img (כלומר, system.img נוצר תמיד כאילו הוגדר BOARD_BUILD_SYSTEM_ROOT_IMAGE). במכשירים שהושקעו עם Android 10:

  • שימוש בפריסה של מחיצה של מערכת כ-root (המערכת אוכפת את הפריסה הזו באופן אוטומטי במהלך ה-build, ללא אפשרויות לשינוי ההתנהגות).
  • חובה להשתמש ב-ramdisk, שנדרש ל-dm-linear.
  • צריך להגדיר את BOARD_BUILD_SYSTEM_ROOT_IMAGE לערך false. ההגדרה הזו משמשת רק כדי להבדיל בין מכשירים שמשתמשים ב-ramdisk לבין מכשירים שלא משתמשים ב-ramdisk (ומטעינים את system.img ישירות במקום זאת).

המשמעות של הגדרת מערכת כ-root שונה בין Android 9 לבין Android 10. בתצורה של מערכת כ-root ב-Android 9, הערך של BOARD_BUILD_SYSTEM_ROOT_IMAGE מוגדר כ-true, כך שה-build יאילץ למזג את מערכת הקבצים ברמה הבסיסית (root) עם system.img, ואז יטען את system.img כמערכת הקבצים ברמה הבסיסית (rootfs). ההגדרה הזו חובה במכשירים שמריצים Android 9, אבל היא אופציונלית במכשירים שמשודרגים ל-Android 9 ובמכשירים עם גרסאות Android ישנות יותר. בהגדרה של מערכת Android 10 כ-root, תמיד מתבצע מיזוג של $TARGET_SYSTEM_OUT ו-$TARGET_ROOT_OUT ל-system.img. זוהי התנהגות ברירת המחדל בכל המכשירים עם Android 10.

ב-Android 10 נוספו שינויים נוספים כדי לתמוך במחיצות דינמיות – מערכת מחיצות במרחב המשתמש שמאפשרת ליצור מחיצות, לשנות את הגודל שלהן או למחוק אותן באמצעות עדכונים אוויריים (OTA). במסגרת השינוי הזה, לליבת Linux אין יותר אפשרות לטעון את מחיצה המערכת הלוגית במכשירים עם Android 10, ולכן הפעולה הזו מטופלת על ידי init בשלב הראשון.

בקטעים הבאים מתוארות הדרישות למערכת כ-root עבור עדכוני OTA למערכת בלבד, ומפורטות הנחיות לעדכון מכשירים לשימוש במערכת כ-root (כולל שינויים בפריסה של המחיצות ודרישות הליבה של dm-verity). פרטים על השינויים ב-ramdisk מופיעים במאמר מחיצות של Ramdisk.

מידע על עדכוני OTA למערכת בלבד

כדי לעדכן את system.img ו-product.img במהדורות Android באמצעות עדכוני OTA למערכת בלבד, בלי לשנות את המחיצות האחרות, צריך פריסת מחיצה של מערכת כ-root. כדי להפעיל עדכוני OTA למערכת בלבד, כל המכשירים עם Android מגרסה 10 חייבים להשתמש בפריסה של מחיצה כמערכת ברמה הבסיסית (root).

  • במכשירי A/B, שמרכבים את המחיצה system בתור rootfs, כבר נעשה שימוש ב-system-as-root ואין צורך בשינויים כדי לתמוך בעדכוני OTA למערכת.
  • במכשירים שאינם A/B, שמרכבים את המחיצה system ב-/system, צריך לעדכן את פריסת המחיצה כך שתהיה 'מערכת כשורש' כדי לתמוך בעדכוני OTA למערכת.

לפרטים על מכשירים עם סביבות A/B ומכשירים ללא סביבות A/B, אפשר לעיין במאמר עדכוני מערכת A/B (חלקים).

שימוש בשכבת-על של ספק (AOSP 14 ואילך)

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

כשהמכשיר מופעל, התהליך init משלים את הרכבת השלב הראשון וקורא את מאפייני ברירת המחדל. לאחר מכן, אם התנאים הבאים מתקיימים, מתבצע חיפוש ב-/product/vendor_overlay/<target_vendor_version> ומתקינים כל ספריית משנה בספריית המחיצה vendor המתאימה:

  • /vendor/<overlay_dir> קיים.
  • /product/vendor_overlay/<target_vendor_version>/<overlay_dir> יש את אותו הקשר קובץ כמו /vendor/<overlay_dir>.
  • ל-init מותר לטעון את הקובץ בהקשר של /vendor/<overlay_dir>.

הטמעת שכבת-על של ספק

מתקינים את קובצי שכבת-העל של הספקים ב-/product/vendor_overlay/<target_vendor_version>. הקבצים האלה מוטמעים במחיצה vendor כשהמכשיר מופעל, מחליפים קבצים באותו שם ומוסיפים קבצים חדשים. שכבת-העל של הספק לא יכולה להסיר קבצים מהמחיצה vendor.

לקובצי שכבת-העל של הספקים צריך להיות אותו הקשר קובץ כמו של קובצי היעד שהם מחליפים במחיצה vendor. כברירת מחדל, לקבצים בספרייה /product/vendor_overlay/<target_vendor_version> יש את ההקשר vendor_file. אם יש אי-התאמות בהקשר של קבצים בין קובצי שכבת-העל של הספק לבין הקבצים שהם מחליפים, צריך לציין זאת במדיניות האבטחה הספציפית למכשיר. ההקשר של הקובץ מוגדר ברמת הספרייה. אם הקשר הקובץ של ספריית שכבת-על של ספק לא תואם לספריית היעד, והקשר הקובץ הנכון לא צוין במדיניות האבטחה הספציפית למכשיר, ספריית שכבת-העל של הספק לא תתווסף לספריית היעד.

כדי להשתמש בשכבת-על של ספק, צריך להגדיר את OverlayFS בליבה באמצעות CONFIG_OVERLAY_FS=y. בנוסף, צריך למזג את הליבה מהליבה המשותפת 4.4 ואילך, או לתקן אותה באמצעות "overlayfs: override_creds=off option bypass creator_cred".

דוגמה להטמעה של שכבת-על של ספק

כאן מוצגת הטמעה של שכבת-על של ספק שמצפה על הספריות /vendor/lib/*,‏ /vendor/etc/* ו-/vendor/app/*.

  1. מוסיפים קובצי ספקים מוכנים מראש בקטע device/<vendor>/<target>/vendor_overlay/<target_vendor_version>/:

    device/google/device/vendor_overlay/28/lib/libfoo.so
    device/google/device/vendor_overlay/28/lib/libbar.so
    device/google/device/vendor_overlay/28/etc/baz.xml
    device/google/device/vendor_overlay/28/app/qux.apk
  2. מתקינים את קובצי הספק שנוצרו מראש ב-product/vendor_overlay ב-device/google/device/device.mk:

    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,device/google/device/vendor_overlay,$(TARGET_COPY_OUT_PRODUCT)/vendor_overlay)
  3. מגדירים את ההקשרים של הקבצים אם לקבצים של מחיצה היעד vendor יש הקשרים שונים מ-vendor_file. מכיוון ש-/vendor/lib/* משתמש בהקשר של vendor_file, הדוגמה הזו לא כוללת את הספרייה הזו.

    מוסיפים את הפרטים הבאים ל-device/google/device-sepolicy/private/file_contexts:

    /(product|system/product)/vendor_overlay/[0-9]+/etc(/.*)?   u:object_r:vendor_configs_file:s0
    /(product|system/product)/vendor_overlay/[0-9]+/app(/.*)?   u:object_r:vendor_app_file:s0
  4. מאפשרים לתהליך init לטעון את שכבת-העל של הספק בהקשרים של קבצים שאינם vendor_file. מכיוון שלתהליך init כבר יש הרשאה לטעון את ההקשר vendor_file, בדוגמה הזו לא מוגדרת המדיניות עבור vendor_file.

    מוסיפים את הפרטים הבאים ל-device/google/device-sepolicy/public/init.te:

    allow init vendor_configs_file:dir mounton;
    allow init vendor_app_file:dir mounton;

אימות שכבת-העל של הספק

כדי לאמת את ההגדרה של שכבת-העל של הספק, מוסיפים קבצים ב-/product/vendor_overlay/<target_vendor_version>/<overlay_dir> ובודקים אם הקבצים מוטמעים מעל הקבצים ב-/vendor/<overlay_dir>.

לגרסאות build של userdebug יש מודול בדיקה ל-Atest:

$ atest -v fs_mgr_vendor_overlay_test

עדכון ל-system-as-root

כדי לעדכן מכשירי A/B לשימוש ב-system-as-root, צריך לעדכן את הסכימה של חלוקת המחיצות של boot.img ו-system.img, להגדיר את dm-verity ולהסיר את כל יחסי התלות של האתחול בתיקיות השורש הספציפיות למכשיר.

עדכון המחיצות

בניגוד למכשירי A/B שבהם /boot משמש כמחיצה לשחזור, במכשירים שאינם A/B צריך להפריד את המחיצה /recovery כי אין להם מחיצה של חריץ חלופי (לדוגמה, מ-boot_a ל-boot_b). אם המחיצה /recovery תוסר במכשיר שאינו A/B ותתווסף לתוכנית A/B, מצב השחזור עלול להיפגע במהלך עדכון כושל למחיצה /boot. לכן, במכשירים ללא תמיכה ב-A/B, מחיצה /recovery חייבת להיות מחיצה נפרדת מ-/boot. המשמעות היא שתמונת השחזור ממשיכה להתעדכן באופן מושהה (כלומר, כמו במכשירים עם Android מגרסה 8.1.0 ואילך).

בטבלה הבאה מפורטים ההבדלים בין מחיצות התמונות במכשירים שאינם A/B לפני ואחרי Android 9.

תמונה דיסק זיכרון (עד גרסה 9) מערכת כ-root (אחרי 9)
boot.img מכיל ליבה ו-ramdisk.img:
ramdisk.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/ (mount point)
    - vendor/ (mount point)
    - odm/ (mount point)
    ...
מכיל ליבה רגילה לאתחול בלבד.
recovery.img מכיל ליבה של שחזור ו-ramdisk.img של שחזור.
system.img מכיל את הפרטים הבאים:
system.img
  -/
    - bin/
    - etc
    - vendor -> /vendor
    - ...
מכיל את התוכן הממוזג של system.img ו-ramdisk.img המקוריים:
system.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/
      - bin/
      - etc/
      - vendor -> /vendor
      - ...
    - vendor/ (mount point)
    - odm/ (mount point)
    ...

המחיצות עצמן לא משתנות. גם ב-ramdisk וגם ב-system-as-root נעשה שימוש בסכימת המחיצות הבאה:

  • /boot
  • /system
  • /system
  • /recovery
  • /vendor וכו'

הגדרת dm-verity

ב-system-as-root, הליבה צריכה לטעון את system.img מתחת ל-/ (נקודת הטעינה) באמצעות dm-verity. AOSP תומך בהטמעות הבאות של dm-verity עבור system.img.

vboot 1.0

ב-vboot 1.0, הליבה צריכה לנתח מטא-נתונים ספציפיים ל-Android ב-/system, ולאחר מכן להמיר אותם לפרמטרים של dm-verity כדי להגדיר את dm-verity (נדרשים תיקוני הליבה האלה). בדוגמה הבאה מוצגות הגדרות שקשורות ל-dm-verity למערכת כ-root בשורת הפקודה של הליבה:

ro root=/dev/dm-0 rootwait skip_initramfs init=/init
dm="system none ro,0 1 android-verity /dev/sda34"
veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f

vboot 2.0

ב-vboot 2.0 (AVB), תוכנת האתחול צריכה לשלב את external/avb/libavb, שמנתחת את התיאור של hashtree עבור /system, ממירה אותו ל-dm-verity params ובסופו של דבר מעבירה את הפרמטרים לליבה דרך שורת הפקודה של הליבה. (התיאורים של עץ הגיבוב של /system יכולים להיות ב-/vbmeta או ב-/system עצמו).

כדי להשתמש ב-vboot 2.0 נדרשים תיקוני הליבה הבאים:

בדוגמה הבאה מוצגות הגדרות שקשורות ל-dm-verity למערכת כ-root בשורת הפקודה של הליבה:

ro root=/dev/dm-0 rootwait  skip_initramfs init=/init

dm="1 vroot none ro 1,0 5159992 verity 1
PARTUUID=00000016-0000-0000-0000-000000000000
PARTUUID=00000016-0000-0000-0000-000000000000 4096 4096 644999 644999
sha1 d80b4a8be3b58a8ab86fad1b498640892d4843a2
8d08feed2f55c418fb63447fec0d32b1b107e42c 10 restart_on_corruption
ignore_zero_blocks use_fec_from_device
PARTUUID=00000016-0000-0000-0000-000000000000 fec_roots 2 fec_blocks
650080 fec_start 650080"

שימוש בתיקיות ברמה הבסיסית (root) ספציפיות למכשיר

כשמשתמשים ב-system-as-root, אחרי שקובץ האימג' המערכת הגנרי (GSI) מופעל במכשיר (ולפני שמריצים את הבדיקות של Vendor Test Suite), כל התיקיות ברמה הבסיסית (root) שנוספו באמצעות BOARD_ROOT_EXTRA_FOLDERS נמחקות כי כל התוכן של ספריית הבסיס הוחלף ב-GSI של system-as-root. אם יש תלות בתיקיות הבסיס הספציפיות למכשיר (לדוגמה, הן משמשות כנקודות קישור), הסרת התיקיות האלה עלולה לגרום לכך שלא תהיה אפשרות להפעיל את המכשיר.

כדי למנוע את הבעיה הזו, אל תשתמשו ב-BOARD_ROOT_EXTRA_FOLDERS כדי להוסיף תיקיות ברמה הבסיסית ספציפיות למכשיר. אם צריך לציין נקודות עגינה ספציפיות למכשיר, משתמשים ב-/mnt/vendor/<mount point> (נוסף ברשימות השינויים האלה). אפשר לציין את נקודות הטעינה הספציפיות לספק ישירות גם בעץ המכשיר fstab (לטעינה בשלב הראשון) וגם בקובץ /vendor/etc/fstab.{ro.hardware} בלי הגדרה נוספת (כי fs_mgr יוצר אותן באופן אוטומטי ב-/mnt/vendor/*).