עדכוני מערכת A/B, הידועים גם בשם עדכונים חלקים, מבטיחים שמערכת אתחול מתאימה נשארת בדיסק במהלך עדכון אוויר (OTA) . גישה זו מפחיתה את הסבירות של מכשיר לא פעיל לאחר עדכון, מה שאומר פחות החלפות מכשיר והחזרות מכשירים במרכזי תיקון ואחריות. מערכות הפעלה אחרות בדרגה מסחרית כגון ChromeOS משתמשות גם הן בעדכוני A/B בהצלחה.
למידע נוסף על עדכוני מערכת A/B וכיצד הם פועלים, ראה בחירת מחיצה (חריצים) .
עדכוני מערכת A/B מספקים את היתרונות הבאים:
- עדכוני OTA יכולים להתרחש בזמן שהמערכת פועלת, מבלי להפריע למשתמש. משתמשים יכולים להמשיך להשתמש במכשירים שלהם במהלך OTA - זמן ההשבתה היחיד במהלך עדכון הוא כאשר המכשיר מאתחל במחיצת הדיסק המעודכנת.
- לאחר עדכון, אתחול מחדש לא אורך יותר מאתחול רגיל.
- אם OTA לא יחול (לדוגמה, בגלל הבזק רע), המשתמש לא יושפע. המשתמש ימשיך להפעיל את מערכת ההפעלה הישנה, והלקוח חופשי לנסות מחדש את העדכון.
- אם מוחל עדכון OTA אך לא מצליח לאתחל, המכשיר יאתחל בחזרה למחיצה הישנה וישאר שמיש. הלקוח חופשי לנסות מחדש את העדכון.
- שגיאות כלשהן (כגון שגיאות קלט/פלט) משפיעות רק על ערכת המחיצה שאינה בשימוש וניתן לנסות שוב. שגיאות כאלה הופכות פחות בסבירות מכיוון שעומס הקלט/פלט נמוך בכוונה כדי למנוע פגיעה בחוויית המשתמש.
- ניתן להזרים עדכונים למכשירי A/B, מה שמסיר את הצורך להוריד את החבילה לפני התקנתה. סטרימינג פירושה שאין צורך שלמשתמש יהיה מספיק מקום פנוי כדי לאחסן את חבילת העדכון ב-
/data
או/cache
. - מחיצת המטמון אינה משמשת עוד לאחסון חבילות עדכוני OTA, כך שאין צורך להבטיח שמחיצת המטמון גדולה מספיק עבור עדכונים עתידיים.
- dm-verity מבטיחה שהתקן יאתחל תמונה לא פגומה. אם מכשיר לא מאתחל עקב בעיה ב-OTA או dm-verity, המכשיר יכול לאתחל מחדש לתוך תמונה ישנה. ( אתחול מאומת של אנדרואיד אינו דורש עדכוני A/B).
על עדכוני מערכת A/B
עדכוני A/B דורשים שינויים הן ללקוח והן למערכת. עם זאת, שרת החבילות של OTA לא צריך לדרוש שינויים: חבילות עדכון עדיין מוגשות באמצעות HTTPS. עבור מכשירים המשתמשים בתשתית OTA של גוגל, כל שינויי המערכת הם ב-AOSP, וקוד הלקוח מסופק על ידי שירותי Google Play. יצרני ציוד מקורי שאינם משתמשים בתשתית OTA של גוגל יוכלו לעשות שימוש חוזר בקוד מערכת ה-AOSP אך יצטרכו לספק ללקוח שלהם.
עבור יצרני OEM המספקים ללקוח שלהם, הלקוח צריך:
- להחליט מתי לקחת עדכון. מכיוון שעדכוני A/B מתרחשים ברקע, הם כבר לא ביוזמת המשתמש. כדי למנוע הפרעה למשתמשים, מומלץ לתזמן עדכונים כאשר המכשיר נמצא במצב תחזוקה סרק, כגון לילה, וב-Wi-Fi. עם זאת, הלקוח שלך יכול להשתמש בכל יוריסטיקה שתרצה.
- בדוק עם שרתי חבילת ה-OTA שלך וקבע אם עדכון זמין. זה אמור להיות זהה לרוב לקוד הלקוח הקיים שלך, פרט לכך שתרצה לאותת שהמכשיר תומך ב-A/B. (הלקוח של גוגל כולל גם כפתור בדוק עכשיו כדי שמשתמשים יוכלו לבדוק את העדכון האחרון).
- התקשר ל-
update_engine
עם כתובת ה-HTTPS עבור חבילת העדכון שלך, בהנחה שאחת זמינה.update_engine
יעדכן את הבלוקים הגולמיים במחיצה שאינה בשימוש כעת בזמן שהוא מזרים את חבילת העדכון. - דווח על הצלחות או כשלים בהתקנה לשרתים שלך, בהתבסס על קוד התוצאה
update_engine
. אם העדכון מוחל בהצלחה,update_engine
יגיד לטוען האתחול לאתחל למערכת ההפעלה החדשה באתחול הבא. טוען האתחול יחזור למערכת ההפעלה הישנה אם מערכת ההפעלה החדשה לא תצליח לאתחל, כך שלא נדרשת עבודה מהלקוח. אם העדכון נכשל, הלקוח צריך להחליט מתי (ואם) לנסות שוב, על סמך קוד השגיאה המפורט. לדוגמה, לקוח טוב יכול לזהות שחבילת OTA חלקית ("שונה") נכשלת ולנסות במקום זאת חבילת OTA מלאה.
לחלופין, הלקוח יכול:
- הצג הודעה המבקשת מהמשתמש לאתחל. אם ברצונך ליישם מדיניות שבה מעודדים את המשתמש לעדכן באופן שוטף, ניתן להוסיף הודעה זו ללקוח שלך. אם הלקוח לא יבקש מהמשתמשים, המשתמשים יקבלו את העדכון בפעם הבאה שהם מאתחלים בכל מקרה. (ללקוח של גוגל יש עיכוב שניתן להגדרה לפי עדכון).
- הצג הודעה שאומרת למשתמשים אם הם עלו לגרסת מערכת הפעלה חדשה או שציפו לעשות זאת אך חזרו לגרסת מערכת ההפעלה הישנה. (הלקוח של גוגל בדרך כלל לא עושה את זה.)
בצד המערכת, עדכוני מערכת A/B משפיעים על הדברים הבאים:
- בחירת מחיצות (חריצים), הדמון
update_engine
ואינטראקציות עם טוען האתחול (מתואר להלן) - תהליך בנייה ויצירת חבילות עדכוני OTA (מתואר ביישום עדכוני A/B )
בחירת מחיצה (חריצים)
עדכוני מערכת A/B משתמשים בשתי קבוצות של מחיצות המכונות חריצים (בדרך כלל חריץ A וחריץ B). המערכת פועלת מהחריץ הנוכחי בעוד שהמערכת הפועלת אינה ניגשת למחיצות בחריץ שאינו בשימוש במהלך פעולה רגילה. גישה זו הופכת את העדכונים לעמידים בפני תקלות על ידי שמירה על המשבצת שאינה בשימוש כחלופה: אם מתרחשת שגיאה במהלך עדכון או מיד לאחריו, המערכת יכולה לחזור אחורה למשבצת הישנה ולהמשיך להחזיק במערכת תקינה. כדי להשיג מטרה זו, אין לעדכן אף מחיצה המשמשת את המשבצת הנוכחית כחלק מעדכון OTA (כולל מחיצות שיש להן עותק אחד בלבד).
לכל חריץ יש תכונה ניתנת לאתחול הקובעת אם החריץ מכיל מערכת נכונה ממנה המכשיר יכול לאתחל. המשבצת הנוכחית ניתנת לאתחול כאשר המערכת פועלת, אך ייתכן שלחריץ השני יש גרסה ישנה (עדיין נכונה) של המערכת, גרסה חדשה יותר או נתונים לא חוקיים. לא משנה מה המשבצת הנוכחית , יש חריץ אחד שהוא המשבצת הפעילה (זה שמטען האתחול יאתחל באתחול הבא) או המשבצת המועדפת .
לכל משבצת יש גם תכונה מוצלחת שנקבעה על ידי מרחב המשתמש, שרלוונטית רק אם המשבצת גם ניתנת לאתחול. חריץ מוצלח אמור להיות מסוגל לאתחל, להפעיל ולעדכן את עצמו. חריץ ניתן לאתחול שלא סומן כמוצלח (לאחר שבוצעו מספר ניסיונות לאתחול ממנו) צריך להיות מסומן כלא ניתן לאתחול על ידי טוען האתחול, כולל שינוי המשבצת הפעילה למשבצת ניתנת לאתחול אחר (בדרך כלל למשבצת שפועלת מיד לפני ניסיון האתחול לתוך החדש והפעיל). הפרטים הספציפיים של הממשק מוגדרים ב- boot_control.h
.
עדכן את דמון המנוע
עדכוני מערכת A/B משתמשים בדמון רקע בשם update_engine
כדי להכין את המערכת לאתחול לגרסה חדשה ומעודכנת. הדמון הזה יכול לבצע את הפעולות הבאות:
- קרא ממחיצות ה-A/B של החריץ הנוכחי וכתוב את כל הנתונים למחיצות ה-A/B של החריץ שאינן בשימוש לפי הוראות חבילת OTA.
- התקשר לממשק
boot_control
בזרימת עבודה מוגדרת מראש. - הפעל תוכנית לאחר התקנה מהמחיצה החדשה לאחר כתיבת כל מחיצות החריצים שאינן בשימוש, לפי הוראות חבילת OTA. (לפרטים, ראה לאחר ההתקנה ).
מכיוון שהדמון update_engine
אינו מעורב בתהליך האתחול עצמו, הוא מוגבל במה שהוא יכול לעשות במהלך עדכון על ידי המדיניות והתכונות של SELinux במשבצת הנוכחית (לא ניתן לעדכן מדיניות ותכונות כאלה עד שהמערכת תאתחל לתוך גרסה חדשה). כדי לשמור על מערכת חזקה, תהליך העדכון לא אמור לשנות את טבלת המחיצות, את התוכן של המחיצות בחריץ הנוכחי, או את התוכן של מחיצות שאינן A/B שלא ניתן למחוק עם איפוס להגדרות היצרן.
עדכן את מקור המנוע
מקור update_engine
נמצא ב- system/update_engine
. קבצי ה-A/B OTA dexopt מחולקים בין installd
למנהל חבילות:
-
frameworks/native/cmds/installd/
ota* כולל את הסקריפט שלאחר ההתקנה, את הקובץ הבינארי עבור chroot, את השיבוט המותקן שקורא ל-dex2oat, את הסקריפט post-OTA move-artifacts ואת קובץ ה-rc עבור הסקריפט להזיז. -
frameworks/base/services/core/java/com/android/server/pm/OtaDexoptService.java
(בתוספתOtaDexoptShellCommand
) הוא מנהל החבילות שמכין פקודות dex2oat עבור יישומים.
לדוגמא עובדת, עיין ב- /device/google/marlin/device-common.mk
.
עדכון יומני מנוע
עבור מהדורות אנדרואיד 8.x ומעלה, ניתן למצוא את יומני update_engine
ב- logcat
ובדוח הבאגים. כדי להפוך את יומני update_engine
לזמינים במערכת הקבצים, תקן את השינויים הבאים ב-build שלך:
שינויים אלה שומרים עותק של יומן update_engine
העדכני ביותר ב- /data/misc/update_engine_log/update_engine. YEAR - TIME
. בנוסף ליומן הנוכחי, חמשת היומנים העדכניים ביותר נשמרים תחת /data/misc/update_engine_log/
. משתמשים עם מזהה קבוצת היומן יוכלו לגשת ליומני מערכת הקבצים.
אינטראקציות עם טוען אתחול
ה- boot_control
HAL משמש את update_engine
(ואולי דמונים אחרים) כדי להנחות את טוען האתחול ממה לאתחל. תרחישים נפוצים לדוגמה והמצבים הקשורים אליהם כוללים את הדברים הבאים:
- מקרה רגיל : המערכת פועלת מהחריץ הנוכחי שלה, או חריץ A או B. לא הוחלו עדכונים עד כה. החריץ הנוכחי של המערכת ניתן לאתחול, מוצלח והחריץ הפעיל.
- עדכון מתבצע : המערכת פועלת מחריץ B, כך שחריץ B הוא החריץ הניתן לאתחול, המוצלח והפעיל. חריץ A סומן כלא ניתן לאתחול מכיוון שהתוכן של חריץ A מתעדכן אך עדיין לא הושלם. אתחול במצב זה אמור להמשיך באתחול מחריץ B.
- העדכון הוחל, אתחול מחדש בהמתנה : המערכת פועלת מחריץ B, חריץ B ניתן לאתחול ומוצלח, אך חריץ A סומן כפעיל (ולכן מסומן כניתן לאתחול). חריץ A עדיין לא מסומן כמוצלח ומספר מסוים של ניסיונות לאתחל ממשבץ A צריך להיעשות על ידי טוען האתחול.
- המערכת מופעלת מחדש לעדכון חדש : המערכת פועלת ממשבץ A בפעם הראשונה, חריץ B עדיין ניתן לאתחול ומוצלח בעוד חריץ A ניתן לאתחול בלבד, ועדיין פעיל אך לא מוצלח. דמון מרחב משתמש,
update_verifier
, אמור לסמן את חריץ A כמוצלח לאחר ביצוע בדיקות מסוימות.
תמיכה בעדכוני סטרימינג
למכשירי המשתמש לא תמיד יש מספיק מקום ב- /data
כדי להוריד את חבילת העדכון. מכיוון שגם יצרני OEM וגם משתמשים לא רוצים לבזבז מקום על מחיצת /cache
, חלק מהמשתמשים עוברים ללא עדכונים מכיוון שלמכשיר אין היכן לאחסן את חבילת העדכון. כדי לטפל בבעיה זו, אנדרואיד 8.0 הוסיפה תמיכה בהזרמת עדכוני A/B שכותבים בלוקים ישירות למחיצת B תוך כדי הורדתם, ללא צורך לאחסן את הבלוקים ב- /data
. עדכוני A/B בזרימה אינם זקוקים כמעט לאחסון זמני ודורשים מספיק אחסון עבור כ-100 KiB של מטא נתונים.
כדי להפעיל עדכוני סטרימינג באנדרואיד 7.1, בחר את התיקונים הבאים:
- אפשר לבטל בקשה לפתרון פרוקסי
- תקן סיום העברה תוך פתרון פרוקסי
- הוסף בדיקת יחידה עבור TerminateTransfer בין טווחים
- נקה את ה-RetryTimeoutCallback()
התיקונים הללו נדרשים כדי לתמוך בהזרמת עדכוני A/B באנדרואיד 7.1 ואילך, בין אם משתמשים בשירותי Google Mobile (GMS) או בכל לקוח עדכונים אחר.
חיים של עדכון A/B
תהליך העדכון מתחיל כאשר חבילת OTA (המכונה בקוד מטען ) זמינה להורדה. מדיניות במכשיר עשויה לדחות את הורדת המטען והיישום בהתבסס על רמת הסוללה, פעילות המשתמש, מצב הטעינה או מדיניות אחרת. בנוסף, מכיוון שהעדכון פועל ברקע, ייתכן שהמשתמשים לא ידעו שעדכון מתבצע. כל זה אומר שתהליך העדכון עלול להיות מופסק בכל נקודה עקב מדיניות, אתחולים לא צפויים או פעולות משתמש.
לחלופין, מטא נתונים בחבילת OTA עצמה מציינים שניתן להזרים את העדכון; אותה חבילה יכולה לשמש גם להתקנה ללא סטרימינג. השרת עשוי להשתמש במטא נתונים כדי לומר ללקוח שהוא זורם כך שהלקוח ימסור את ה-OTA ל- update_engine
בצורה נכונה. יצרני התקנים עם שרת ולקוח משלהם יכולים לאפשר עדכוני סטרימינג על ידי הבטחת השרת מזהה שהעדכון זורם (או מניח שכל העדכונים זורמים) והלקוח מבצע את הקריאה הנכונה ל- update_engine
לסטרימינג. יצרנים יכולים להשתמש בעובדה שהחבילה היא מגרסה הסטרימינג כדי לשלוח דגל ללקוח כדי להפעיל מסירה לצד המסגרת כזרימה.
לאחר שמטען זמין, תהליך העדכון הוא כדלקמן:
שלב | פעילויות |
---|---|
1 | החריץ הנוכחי (או "חריץ המקור") מסומן כמוצלח (אם עדיין לא סומן) עם markBootSuccessful() . |
2 | המשבצת הלא בשימוש (או "חריץ היעד") מסומן כלא ניתן לאתחול על ידי קריאה לפונקציה setSlotAsUnbootable() . המשבצת הנוכחית מסומנת תמיד כמוצלחת בתחילת העדכון כדי למנוע מהמטען האתחול ליפול בחזרה למשבצת שאינה בשימוש, שבקרוב יהיו נתונים לא חוקיים. אם המערכת הגיעה לנקודה שבה היא יכולה להתחיל להחיל עדכון, החריץ הנוכחי מסומן כמוצלח גם אם רכיבים עיקריים אחרים מקולקלים (כגון ממשק המשתמש בלולאת קריסה) מכיוון שניתן לדחוף תוכנה חדשה כדי לתקן אותם בעיות.מטען העדכון הוא כתם אטום עם ההוראות לעדכון לגרסה החדשה. מטען העדכון מורכב מהדברים הבאים:
|
3 | הורדת המטא נתונים של המטען. |
4 | עבור כל פעולה שהוגדרה במטא-נתונים, לפי הסדר, הנתונים המשויכים (אם קיימים) יורדים לזיכרון, הפעולה מופעלת והזיכרון המשויך מושלך. |
5 | כל המחיצות נקראות מחדש ומאומתות מול ה-hash הצפוי. |
6 | השלב שלאחר ההתקנה (אם קיים) מופעל. במקרה של שגיאה במהלך ביצוע כל שלב, העדכון נכשל ומתנסה מחדש עם אולי מטען אחר. אם כל השלבים עד כה הצליחו, העדכון מצליח והשלב האחרון מבוצע. |
7 | החריץ שאינו בשימוש מסומן כפעיל על ידי קריאה ל- setActiveBootSlot() . סימון החריץ שאינו בשימוש כפעיל אינו אומר שהוא יסתיים את האתחול. טוען האתחול (או המערכת עצמה) יכול להחליף את המשבצת הפעילה בחזרה אם הוא לא קורא מצב מוצלח. |
8 | לאחר התקנה (מתואר להלן) כרוכה בהפעלת תוכנית מגרסת "העדכון החדש" בעודה פועלת בגרסה הישנה. אם הוא מוגדר בחבילת OTA, שלב זה הוא חובה והתוכנית חייבת לחזור עם קוד יציאה 0 ; אחרת, העדכון נכשל. | 9 | לאחר שהמערכת מאתחלת בהצלחה מספיק רחוק לתוך המשבצת החדשה ותסיים את הבדיקות שלאחר האתחול, המשבצת הנוכחית (לשעבר "חריץ היעד") מסומנת כמוצלחת על ידי קריאה ל- markBootSuccessful() . |
לאחר התקנה
עבור כל מחיצה שבה מוגדר שלב לאחר ההתקנה, update_engine
מעלה את המחיצה החדשה למיקום מסוים ומבצע את התוכנית שצוינה ב-OTA ביחס למחיצה המותקנת. לדוגמה, אם התוכנית שלאחר ההתקנה מוגדרת כ- usr/bin/postinstall
במחיצת המערכת, מחיצה זו מהחריץ שאינו בשימוש תותקן במיקום קבוע (כגון /postinstall_mount
) וה- /postinstall_mount/usr/bin/postinstall
הפקודה /postinstall_mount/usr/bin/postinstall
מבוצעת.
כדי שאחרי ההתקנה תצליח, על הקרנל הישן להיות מסוגל:
- התקן את הפורמט החדש של מערכת הקבצים . סוג מערכת הקבצים לא יכול להשתנות אלא אם כן יש תמיכה עבורו בקרנל הישן, כולל פרטים כגון אלגוריתם הדחיסה המשמש אם משתמשים במערכת קבצים דחוסה (כלומר SquashFS).
- הבן את פורמט התוכנית לאחר ההתקנה של המחיצה החדשה . אם משתמשים בתבנית בינארית ניתנת להפעלה וניתנת לקישור (ELF), היא אמורה להיות תואמת לליבה הישנה (למשל תוכנית חדשה של 64 סיביות הפועלת על ליבה ישנה של 32 סיביות אם הארכיטקטורה עברה מ-32 ל-64 סיביות בנייה). אלא אם הטוען (
ld
) מקבל הוראה להשתמש בנתיבים אחרים או לבנות בינארי סטטי, ספריות ייטענו מתמונת המערכת הישנה ולא מהחדשה.
לדוגמה, אתה יכול להשתמש בסקריפט מעטפת כתוכנית לאחר ההתקנה המתפרשת על ידי הבינארי של המעטפת של המערכת הישנה עם #!
סמן בחלק העליון), ולאחר מכן הגדר נתיבי ספרייה מהסביבה החדשה להפעלת תוכנית בינארית מורכבת יותר לאחר התקנה. לחלופין, תוכל להריץ את השלב שלאחר ההתקנה ממחיצה קטנה יותר ייעודית כדי לאפשר עדכון של פורמט מערכת הקבצים במחיצת המערכת הראשית מבלי שייגרמו לבעיות תאימות לאחור או עדכוני צעד; זה יאפשר למשתמשים לעדכן ישירות לגרסה העדכנית ביותר מתמונת יצרן.
התוכנית החדשה שלאחר ההתקנה מוגבלת על ידי מדיניות SELinux המוגדרת במערכת הישנה. ככזה, השלב שלאחר ההתקנה מתאים לביצוע משימות הנדרשות על ידי עיצוב במכשיר נתון או משימות אחרות במאמץ הטוב ביותר (כלומר עדכון הקושחה או מאתחול התומך A/B, הכנת עותקים של מסדי נתונים לגרסה החדשה וכו'. ). השלב שלאחר ההתקנה אינו מתאים לתיקוני באגים חד פעמיים לפני אתחול מחדש הדורשים הרשאות בלתי צפויות.
התוכנית שנבחרה לאחר ההתקנה פועלת בהקשר postinstall
של SELinux. כל הקבצים במחיצה המורכבת החדשה יתויגו עם postinstall_file
, ללא קשר לתכונות שלהם לאחר אתחול מחדש באותה מערכת חדשה. שינויים בתכונות SELinux במערכת החדשה לא ישפיעו על השלב שלאחר ההתקנה. אם התוכנית שלאחר ההתקנה צריכה הרשאות נוספות, יש להוסיף אותן להקשר שלאחר ההתקנה.
לאחר אתחול מחדש
לאחר אתחול מחדש, update_verifier
מפעיל את בדיקת השלמות באמצעות dm-verity. בדיקה זו מתחילה לפני zygote כדי למנוע משירותי Java לבצע שינויים בלתי הפיכים שימנעו חזרה בטוחה. במהלך תהליך זה, טוען האתחול והקרנל עשויים גם להפעיל אתחול מחדש אם אתחול מאומת או dm-verity מזהים שחיתות כלשהי. לאחר השלמת הבדיקה, update_verifier
מסמן שהאתחול מוצלח.
update_verifier
יקרא רק את הבלוקים הרשומים ב- /data/ota_package/care_map.txt
care_map.txt, הנכלל בחבילת A/B OTA בעת שימוש בקוד AOSP. לקוח עדכון המערכת של Java, כגון GmsCore, מחלץ care_map.txt
, מגדיר את הרשאת הגישה לפני אתחול המכשיר ומוחק את הקובץ שחולץ לאחר שהמערכת מאתחלת בהצלחה בגרסה החדשה.