שאלות נפוצות

האם Google השתמשה ב-OTA A/B במכשירים כלשהם?

כן. השם השיווקי של עדכוני A/B הוא עדכונים ללא הפרעה. טלפונים מדגמי Pixel ו-Pixel XL מאוקטובר 2016 נשלחו עם A/B, וכל מכשירי Chromebook משתמשים באותו update_engine יישום של A/B. הטמעת קוד הפלטפורמה הנדרשת היא ציבורית ב-Android 7.1 ומעלה.

למה עדכוני OTA מסוג A/B טובים יותר?

עדכוני OTA מסוג A/B מספקים חוויית משתמש טובה יותר כשמבצעים עדכונים. התכונה הזו כבר הוכיחה את עצמה: נתונים מעדכוני אבטחה חודשיים מראים שבמאי 2017,‏ 95% מבעלי מכשירי Pixel הריצו את עדכון האבטחה האחרון אחרי חודש, לעומת 87% ממשתמשי Nexus. בנוסף, משתמשי Pixel מעדכנים מוקדם יותר ממשתמשי Nexus. אם העדכון של בלוקים נכשל במהלך עדכון OTA, המכשיר לא יפסיק לפעול. עד שקובץ האימג' החדש של המערכת יופעל בהצלחה, Android שומרת על היכולת לחזור לקובץ האימג' הקודם של המערכת.

מהו system_other?

אפליקציות מאוחסנות בקובצי APK, שהם למעשה ארכיוני ZIP. כל קובץ ‎ .apk מכיל קובץ ‎ .dex אחד או יותר עם בייטקוד נייד של Dalvik. קובץ ‎.odex (‎.dex מותאם) נמצא בנפרד מקובץ ה-APK ויכול להכיל קוד מכונה שספציפי למכשיר. אם קובץ ‎ .odex זמין, מערכת Android יכולה להריץ אפליקציות במהירויות של קומפילציה מראש בלי לחכות לקומפילציה של הקוד בכל פעם שהאפליקציה מופעלת. קובץ ‎.odex לא נחוץ בהכרח: מערכת Android יכולה להריץ את קוד ‎ .dex ישירות באמצעות פרשנות או קומפילציה בזמן ריצה (JIT), אבל קובץ ‎ .odex מספק את השילוב הטוב ביותר של מהירות הפעלה ומהירות זמן ריצה אם יש מקום.

דוגמה: בקובץ installed-files.txt מ-Nexus 6P עם Android 7.1, שגודל תמונת המערכת הכולל שלו הוא ‎2,628 MiB (‎2,755,792,836 בייט), פירוט הגורמים הכי משמעותיים לגודל הכולל של תמונת המערכת לפי סוג הקובץ הוא כדלקמן:

‎.odex ‫1391770312 בייטים ‫50.5%
‫.apk ‫846878259 בייטים 30.7%
‫.so (קוד Native ב-C/C++) ‫202162479 בייטים ‫7.3%
קובצי ‎.oat / תמונות ‎.art ‫163892188 בייטים ‫5.9%
גופנים ‫38952361 בייטים 1.4%
נתוני לוקאל של ICU ‫27468687 בייטים 0.9%

הנתונים האלה דומים גם במכשירים אחרים, כך שבמכשירי Nexus/Pixel, קובצי ‎.odex תופסים בערך חצי ממחיצת המערכת. המשמעות היא שיכולנו להמשיך להשתמש ב-ext4 אבל לכתוב את קובצי ה-odex למחיצה B במפעל ואז להעתיק אותם אל /data בהפעלה הראשונה. הנפח בפועל שמשמש עם ext4 A/B זהה לזה של SquashFS A/B, כי אם היינו משתמשים ב-SquashFS, היינו שולחים את קובצי ה-odex שעברו אופטימיזציה מראש ב-system_a במקום ב-system_b.

האם העתקה של קובצי ‎ .odex אל /data אומרת שהמקום שנחסך ב-‎ /system אובד ב-‎ /data?

לא בדיוק. ב-Pixel, רוב הנפח שתופסים קובצי ‎ .odex הוא של אפליקציות, שבדרך כלל קיימות ב-/data. האפליקציות האלה מקבלות עדכונים מ-Google Play, כך שקבצי ה-‎ .apk ו-‎ .odex בתמונת המערכת לא נמצאים בשימוש במשך רוב חיי המכשיר. אפשר להחריג קבצים כאלה לחלוטין ולהחליף אותם בקובצי ‎ .odex קטנים שמבוססים על פרופיל, כשהמשתמש משתמש בפועל בכל אפליקציה (כך שלא נדרש מקום לאפליקציות שהמשתמש לא משתמש בהן). פרטים נוספים זמינים בהרצאה The Evolution of Art (התפתחות האומנות) בכנס Google I/O 2016.

קשה להשוות בין המערכות מכמה סיבות עיקריות:

  • קבצי ה-odex של אפליקציות שעודכנו על ידי Google Play תמיד נמצאים ב-/data ברגע שהן מקבלות את העדכון הראשון.
  • אפליקציות שהמשתמש לא מפעיל לא צריכות קובץ ‎ .odex בכלל.
  • קומפילציה מבוססת-פרופיל יוצרת קובצי ‎ .odex קטנים יותר מקומפילציה מראש (כי היא מבצעת אופטימיזציה רק לקוד שחשוב לביצועים).

פרטים על אפשרויות ההתאמה שזמינות ליצרני ציוד מקורי (OEM) מופיעים במאמר הגדרת ART.

האם אין שני עותקים של קובצי ‎ .odex בתיקייה ‎ /data?

התהליך קצת יותר מורכב… אחרי שקובץ תמונת המערכת החדש נכתב, הגרסה החדשה של dex2oat מופעלת על קובצי ה-dex החדשים כדי ליצור את קובצי ה-odex החדשים. הפעולה הזו מתרחשת בזמן שהמערכת הישנה עדיין פועלת, כך שגם קובצי ה-odex הישנים וגם קובצי ה-odex החדשים נמצאים ב-/data בו-זמנית.

הקוד ב-OtaDexoptService ‏ (frameworks/base/+/android16-release/services/core/java/com/android/server/pm/OtaDexoptService.java) קורא ל-getAvailableSpace לפני האופטימיזציה של כל חבילה כדי למנוע מילוי יתר /data. שימו לב שהנתון זמין שמופיע כאן עדיין שמרני: זהו נפח האחסון שנותר לפני שמגיעים לסף הרגיל של המערכת לנפח אחסון נמוך (נמדד כאחוז וכמספר בייטים). לכן, אם נפח האחסון של /data מלא, לא יהיו שני עותקים של כל קובץ ‎ .odex. באותו קוד יש גם BULK_DELETE_THRESHOLD: אם המכשיר מתקרב למילוי המקום הזמין (כפי שתואר), קובצי ה-odex ששייכים לאפליקציות שלא נמצאות בשימוש יוסרו. זהו מקרה נוסף שבו אין שני עותקים של כל קובץ ‎ .odex.

במקרה הגרוע ביותר שבו /data מלא לגמרי, העדכון ימתין עד שהמכשיר יופעל מחדש במערכת החדשה ולא יהיה צורך יותר בקובצי ה-odex של המערכת הישנה. ‫PackageManager מטפל בזה: (frameworks/base/+/android16-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215). אחרי שהמערכת החדשה מופעלת בהצלחה, installd (frameworks/native/+/android16-release/cmds/installd/dexopt.cpp#2422) יכול להסיר את קובצי ה-odex ששימשו את המערכת הישנה, וכך המכשיר חוזר למצב יציב שבו יש רק עותק אחד.

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

האם כל הכתיבה וההעתקה האלה מגדילות את הבלאי של ה-Flash?

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

האם צריבת שתי מחיצות מערכת מגדילה את זמן הצריבה במפעל?

לא. גודל תמונת המערכת ב-Pixel לא גדל (הוא פשוט חולק בין שתי מחיצות).

האם שמירת קובצי ‎ .odex ב-B לא גורמת לאתחול מחדש אחרי איפוס לנתוני היצרן להיות איטי?

כן. אם השתמשתם במכשיר, קיבלתם עדכון OTA וביצעתם איפוס לנתוני היצרן, האתחול הראשון יהיה איטי יותר ממה שהוא בדרך כלל (דקה ו-40 שניות לעומת 40 שניות ב-Pixel XL), כי קובצי ה-odex אבדו מ-B אחרי עדכון ה-OTA הראשון, ולכן אי אפשר להעתיק אותם אל /data. זהו המחיר שמשלמים.

איפוס לנתוני היצרן צריך להיות פעולה נדירה בהשוואה לאתחול רגיל, ולכן הזמן שנדרש לאיפוס פחות חשוב. (הבעיה הזו לא משפיעה על משתמשים או בודקים שמקבלים את המכשיר מהמפעל, כי במקרה כזה מחיצת B זמינה). השימוש בקומפיילר JIT אומר שלא צריך לקמפל מחדש הכול, אז זה לא נורא כמו שזה נשמע. אפשר גם לסמן אפליקציות ככאלה שנדרשת עבורן קומפילציה מראש באמצעות coreApp="true" במניפסט: (frameworks/base/+/android16-release/packages/SystemUI/AndroidManifest.xml#23). כרגע נעשה בזה שימוש ב-system_server כי אסור להשתמש ב-JIT מטעמי אבטחה.

האם השארת קובצי ‎ .odex בתיקייה ‎ /data במקום בתיקייה ‎ /system מאטה את ההפעלה מחדש אחרי עדכון OTA?

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

האם כדאי לשלוח מכשיר A/B בנפח 32GiB? ‫16GiB? ‫8GiB?

‫32GiB הוא ערך טוב כי הוא הוכח ב-Pixel, ו-320MiB מתוך 16GiB מייצג הפחתה של 2%. באופן דומה, 320MiB מתוך 8GiB הם צמצום של 4%. ברור ש-A/B לא יהיה הבחירה המומלצת במכשירים עם 4GiB, כי התקורה של 320MiB היא כמעט 10% מהשטח הכולל שזמין.

האם AVB2.0 דורש עדכוני OTA מסוג A/B?

לא. ב-Android Verified Boot תמיד נדרשו עדכונים מבוססי-בלוק, אבל לא בהכרח עדכוני A/B.

האם נדרש AVB2.0 כדי לבצע עדכוני OTA של בדיקות A/B?

לא.

האם עדכוני OTA מסוג A/B פוגעים בהגנה מפני רולבק לגרסה קודמת של AVB2.0?

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

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

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

במהלך עדכון OTA יש שני שלבים, שמוצגים בבירור בממשק המשתמש כשלב 1 מתוך 2 ושלב 2 מתוך 2 מתחת לסרגל ההתקדמות. שלב 1 מתאים לכתיבת בלוקי הנתונים, בעוד ששלב 2 הוא קומפילציה מראש של קובצי ה-dex. שני השלבים האלה שונים מאוד מבחינת ההשפעה על הביצועים. השלב הראשון הוא קלט/פלט פשוט. הפעולה הזו לא דורשת הרבה משאבים (RAM, ‏ CPU, ‏ I/O) כי היא רק מעתיקה בלוקים לאט.

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

התהליך דומה למצב שבו Google Play מתקין עדכון לאפליקציה ברקע לפני שהוא מציג את ההתראה 5 אפליקציות עודכנו, כמו שקורה כבר שנים.

מה קורה אם משתמש באמת מחכה לעדכון?

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

מה קורה אם העדכון לא מוחל?

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