סקירה כללית על בדיקת A/B וירטואלית

עדכוני A/B וירטואליים הם מנגנון העדכון העיקרי של Android. עדכוני A/B וירטואליים מבוססים על עדכוני A/B מדור קודם (ראו עדכוני מערכת A/B) ועל עדכונים שהם לא A/B, שהוצאו משימוש בגרסה 15 כדי לצמצם את התקורה של העדכונים.

למעשה, אין ב-Virtual A/B משבצת נוספת למחיצות דינמיות. מידע נוסף על מחיצות דינמיות במקום זאת, הדלתא נכתבת לתמונת מצב, ואז מתבצע מיזוג שלה עם מחיצת הבסיס אחרי אישור של הפעלה מוצלחת. בבדיקות A/B וירטואליות נעשה שימוש בפורמט ספציפי של תמונת מצב ל-Android. אפשר לעיין במאמר בנושא פורמט COW לדחיסת קובצי snapshot, שמאפשר לדחוס קובצי snapshot ולצמצם את השימוש במקום בדיסק. ב-OTA מלא, גודל התמונה מצטמצם בכ-45% באמצעות דחיסה, וב-OTA מצטבר, גודל התמונה מצטמצם בכ-55%.

‫Android 12 מציע את האפשרות של דחיסת Virtual A/B לדחיסת מחיצות של תמונות מצב. היתרונות של בדיקות A/B וירטואליות

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

רקע וטרמינולוגיה

בקטע הזה מוגדרת הטרמינולוגיה ומתואר הטכנולוגיה שתומכת בבדיקות A/B וירטואליות. במהלך התקנת OTA, נתוני מערכת הפעלה חדשים נכתבים למקום החדש שלהם במחיצות הפיזיות, או למכשיר COW ספציפי ל-Android. אחרי שמפעילים מחדש את המכשיר, נתוני המחיצה הדינמית משולבים בחזרה במכשיר הבסיסי באמצעות השדים dm-user ו-snapuserd. התהליך הזה מתרחש כולו במרחב המשתמש.

Device-mapper

‫Device-mapper היא שכבת בלוק וירטואלית של Linux שמשמשת לעיתים קרובות ב-Android. עם מחיצות דינמיות, מחיצות כמו /system הן אוסף של מכשירים בשכבות:

  • בתחתית הערימה נמצאת מחיצת העל הפיזית (לדוגמה, /dev/block/by-name/super).
  • באמצע יש dm-linearמכשיר, שמציין אילו בלוקים במחיצת העל יוצרים את המחיצה הדינמית הנתונה. הערך הזה מופיע כ-/dev/block/mapper/system_[a|b] במכשיר A/B, או כ-/dev/block/mapper/system במכשיר שאינו A/B.
  • בחלק העליון נמצא מכשיר dm-verity, שנוצר עבור מחיצות מאומתות. המכשיר הזה מאמת שחסימות במכשיר dm-linear חתומות בצורה נכונה. הוא מופיע כ-/dev/block/mapper/system-verity והוא המקור של נקודת הטעינה /system.

איור 1 מציג את המערך מתחת לנקודת הטעינה /system.

הצבת מחיצות זו על גבי זו מתחת למערכת

איור 1. הערימה מתחת לנקודת הטעינה ‎ /system

תמונות מצב דחוסות

ב-Android 12 ומעלה, דרישות המקום במחיצת /data יכולות להיות גבוהות. כדי לעמוד בדרישות המקום הגבוהות יותר במחיצת /data, אפשר להפעיל צילומי מצב דחוסים ב-build.

תמונות מצב דחוסות של A/B וירטואליות מבוססות על הרכיבים הבאים שזמינים ב-Android מגרסה 12 ואילך:

  • dm-user, מודול ליבה שדומה ל-FUSE ומאפשר למרחב המשתמש להטמיע התקני בלוקים.
  • snapuserd, דמון במרחב המשתמש להטמעה של פורמט חדש של תמונת מצב.

הרכיבים האלה מאפשרים את הדחיסה. בקטעים הבאים מפורטים השינויים הנוספים שצריך לבצע כדי להטמיע את היכולות של תמונות מצב דחוסות: פורמט COW לתמונות מצב דחוסות,‏ dm-user ו-snapuserd.

פורמט COW לתמונות מצב דחוסות

ב-Android מגרסה 12 ואילך, תמונות מצב דחוסות משתמשות בפורמט COW ספציפי ל-Android. פורמט ה-COW מכיל מטא-נתונים לגבי ה-OTA, ויש בו מאגרי נתונים נפרדים שמכילים פעולות COW ונתונים חדשים של מערכת ההפעלה. לעומת פורמט תמונת המצב של הליבה שמאפשר רק פעולות החלפה (החלפת בלוק X בתמונת הבסיס בתוכן של בלוק Y בתמונת המצב), פורמט ה-COW של תמונות מצב דחוסות ב-Android הוא מפורט יותר ותומך בפעולות הבאות:

  • העתקה: צריך להחליף את בלוק X במכשיר הבסיס בבלוק Y במכשיר הבסיס.
  • Replace: התוכן של בלוק X במכשיר הבסיסי צריך להיות מוחלף בתוכן של בלוק Y בתמונת המצב. כל אחד מהבלוקים האלה מכווץ בפורמט gz.
  • 0: חסימה של X במכשיר הבסיס צריכה להיות מוחלפת באפסים.
  • XOR: מכשיר ה-COW מאחסן בייטים דחוסים של XOR בין בלוק X לבין בלוק Y. (זמין ב-Android מגרסה 13 ואילך).

עדכוני OTA מלאים כוללים רק פעולות של replace ו-zero. עדכוני OTA מצטברים יכולים לכלול גם פעולות העתקה.

פריסת התמונה המלאה בדיסק נראית כך:

פורמט פרה

איור 2. פורמט COW של Android בכונן

dm-user

מודול הליבה dm-user מאפשר ל-userspace ליישם מכשירי בלוק של מיפוי מכשירים. רשומה בטבלה dm-user יוצרת מכשיר שונה בקטע /dev/dm-user/<control-name>. תהליך userspace יכול לבצע סקר במכשיר כדי לקבל בקשות קריאה וכתיבה מהליבה. לכל בקשה יש מאגר נתונים זמני שמשויך למרחב המשתמש, שאפשר לאכלס אותו (לקריאה) או להפיץ אותו (לכתיבה).

מודול הליבה dm-user מספק ממשק חדש שגלוי למשתמשים בליבה, שלא נכלל ב-code base של הליבה ב-kernel.org. עד אז, Google שומרת לעצמה את הזכות לשנות את הממשק של dm-user ב-Android.

snapuserd

רכיב snapuserd בסביבת המשתמש של dm-user מטמיע דחיסה וירטואלית של בדיקות A/B. ‫Snapuserd הוא daemon של מרחב משתמש שאחראי על כתיבה וקריאה של מכשירי COW ב-Android. כל פעולות הקלט והפלט של התמונה צריכות לעבור דרך השירות הזה. במהלך התקנת OTA, נתוני מערכת הפעלה חדשים נכתבים לתמונת המצב על ידי snapuserd (עם דחיסה). כאן מתבצע גם הניתוח של המטא-נתונים והפירוק של נתוני בלוק חדשים.

דחיסת XOR

במכשירים עם Android מגרסה 13 ומעלה, התכונה XOR compression (דחיסת XOR) מופעלת כברירת מחדל ומאפשרת ליצור תמונות מצב של מרחב המשתמש כדי לאחסן בייטים דחוסים של XOR בין בלוקים ישנים לבלוקים חדשים. כשמעדכנים בדיקת A/B וירטואלית ומשנים רק כמה בייטים בבלוק, תוכנית האחסון של דחיסת XOR תופסת פחות מקום מתוכנית האחסון שמוגדרת כברירת מחדל, כי תמונות המצב לא מאחסנות 4K בייטים מלאים. הקטנת הגודל של תמונת המצב אפשרית כי נתוני XOR מכילים הרבה אפסים וקל יותר לדחוס אותם מאשר נתוני בלוק גולמיים. במכשירי Pixel, דחיסת XOR מקטינה את גודל התמונה ב-25% עד 40%.

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

מיזוג תמונות מצב

במכשירים עם Android מגרסה 13 ומעלה, תהליכי תמונת המצב ומיזוג תמונת המצב בדחיסת Virtual A/B מתבצעים על ידי רכיב snapuserd במרחב המשתמש. במכשירים שמשדרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את התכונה הזו. פרטים נוספים מופיעים במאמר בנושא מיזוג של מרחבי משתמשים.

התהליך של דחיסת בדיקות A/B וירטואליות מתואר בהמשך:

  1. המסגרת מטמיעה את המחיצה /system ממכשיר dm-verity, שנמצא מעל מכשיר dm-user. המשמעות היא שכל פעולת קלט/פלט ממערכת הקבצים הבסיסית מנותבת אל dm-user.
  2. dm-user מעביר את הקלט/פלט לדימון snapuserd במרחב המשתמשים, שמטפל בבקשת הקלט/פלט.
  3. בסיום פעולת המיזוג, המסגרת מתכווצת dm-verity מעל dm-linear (system_base) ו-dm-user מוסר.

תהליך דחיסה וירטואלית של A/B

איור 3. תהליך הדחיסה של בדיקת A/B וירטואלית

אפשר להפריע לתהליך המיזוג של התמונות. אם המכשיר מופעל מחדש במהלך תהליך המיזוג, תהליך המיזוג ימשיך אחרי ההפעלה מחדש.

העברות של התחלת הפעלה

כשמבצעים אתחול עם תמונות מצב דחוסות, תהליך האתחול בשלב הראשון צריך להתחיל snapuserd כדי לטעון מחיצות. הבעיה היא: כשקובץ sepolicy נטען ונאכף, קובץ snapuserd מוצב בהקשר שגוי, והבקשות לקריאה שלו נכשלות, עם דחיות של selinux.

כדי לפתור את הבעיה, snapuserd עובר שינוי בהתאם ל-init, באופן הבא:

  1. השלב הראשון init מפעיל את snapuserd מ-ramdisk ושומר בו מתאר קובץ פתוח במשתנה סביבה.
  2. בשלב הראשון init מעביר את מערכת הקבצים הבסיסית למחיצת המערכת, ואז מפעיל את העותק של המערכת של init.
  3. עותק המערכת של init קורא את מדיניות האבטחה המשולבת למחרוזת.
  4. Init מפעיל את mlock() בכל הדפים שמגובים על ידי ext4. לאחר מכן, הוא משבית את כל הטבלאות של device-mapper במכשירים של תמונת מצב, ומפסיק את snapuserd. אחרי זה אסור לקרוא ממחיצות, כי פעולה כזו גורמת לקיפאון.
  5. באמצעות המתאר הפתוח של העותק של snapuserd ב-ramdisk, ‏ init ה-daemon מופעל מחדש עם הקשר ה-selinux הנכון. טבלאות של מיפוי מכשירים למכשירים של תמונות מצב מופעלות מחדש.
  6. הפונקציה Init מפעילה את munlockall() – אפשר לבצע שוב קלט/פלט.

שימוש במרחב

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

השפעה של גודל non-A/B A/B בדיקת A/B וירטואלית Virtual A/B (compressed)
תמונת המפעל המקורית ‫4.5GB סופר (תמונה של 3.8G + 700M שמורים)1 ‫9GB סופר (‎3.8G + 700M שמורים, לשני חריצים) ‫4.5GB סופר (תמונה בנפח 3.8GB + 700MB שמורים) ‫4.5GB סופר (תמונה בנפח 3.8GB + 700MB שמורים)
מחיצות סטטיות אחרות /cache ללא ללא ללא
נפח אחסון נוסף במהלך OTA (הנפח מוחזר אחרי החלת ה-OTA) ‫1.4GB ב-‎ /data 0 ‫3.8GB2 ב-‎ /data ‫2.1GB2 ב-‎ /data
נפח האחסון הכולל שנדרש להחלת OTA ‫5.9GB3 (סופר ונתונים) ‫9GB (סופר) ‫8.3GB3 (super and data) ‫6.6GB3 (סופר ונתונים)

1 מציין פריסה משוערת שמבוססת על מיפוי Pixel.

2 המערכת מניחה שקובץ אימג' של המערכת החדש זהה בגודלו למקור.

3דרישת המקום היא זמנית עד להפעלה מחדש.

‫Android 11 Virtual A/B

ב-Android 11, מערכת Virtual A/B כתבה למחיצה דינמית באמצעות פורמט Kernel COW. בסופו של דבר, הפורמט הזה הוצא משימוש כי הוא לא תומך בדחיסה.

‫Android 12 Virtual A/B

ב-Android 12, הדחיסה נתמכת בפורמט COW ספציפי ל-Android. בגרסה הזו של Virtual A/B נדרש תרגום של ה-COW הספציפי ל-Android לפורמט Kernel COW. בסופו של דבר, היא הוחלפה ב-Android 13, שבו הוסרה ההסתמכות על פורמט Kernel COW וגם על dm-snapshot.

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