האם 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,628MiB (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 בפקטורי (factory) ואז להעתיק אותם אל /data בהפעלה הראשונה. בפועל, נפח האחסון בשימוש ב-ext4 A/B זהה לזה של SquashFS A/B, בגלל שאם היינו משתמשים ב-SquashFS, היינו כוללים את קובצי .odex שעברו אופטימיזציה מראש ב-system_a במקום ב-system_b.
האם כשמעתיקים קובצי .odex ל-/data בעצם מאבדים שם את המקום שנחסך ב-/system?
לא בדיוק. ב-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-qpr1-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-qpr1-release/services/core/java/com/android/server/pm/PackageManagerService.java#7215). אחרי שהמערכת החדשה מופעלת, installd (frameworks/native/+/android16-qpr1-release/cmds/installd/dexopt.cpp#2422) יכול להסיר את קובצי ה-odex שהמערכת הישנה השתמשה בהם, וכך המכשיר חוזר למצב יציב שבו יש רק עותק אחד.
לכן, יכול להיות שב-/data יש שני עותקים של כל קובצי ה- .odex, אבל (א) זה זמני ו-(ב) זה קורה רק אם יש מספיק מקום פנוי ב-/data. רק בזמן עדכון יש שני עותקים – אחרת יש רק אחד. בנוסף, כחלק מתכונות החוסן הכלליות של ART, המערכת לעולם לא תמלא את /data בקובצי .odex (כי זו תהיה בעיה גם במערכת שאינה A/B).
האם הכתיבה וההעתקה לא מגבירות את השחיקה של צריבת ה-ROM (flash)?
רק חלק קטן מצריבת ה-ROM (flash) נכתב מחדש: עדכון מלא של מערכת Pixel כותב כ-2.3GiB. (האפליקציות עוברות גם הידור מחדש, אבל זה נכון גם לגבי אפליקציות שלא מבוססות על A/B). בעדכוני OTA מלאים מבוססי בלוקים נהגו לכתוב כמות דומה של נתונים, ולכן גם השחיקה של צריבת ה-ROM (flash) אמורה להיות דומה.
האם צריבת שתי מחיצות מערכת מגדילה את זמן הצריבה בפקטורי (factory)?
לא. גודל קובץ האימג' של המערכת ב-Pixel לא גדל (הוא פשוט חולק בין שתי מחיצות).
האם שמירת קובצי .odex ב-B לא גורמת להפעלה מחדש להיות איטית אחרי האיפוס לנתוני היצרן?
כן. אם השתמשתם במכשיר, קיבלתם עדכון OTA וביצעתם איפוס לנתוני היצרן, ההפעלה מחדש הראשונה תהיה איטית יותר ממה שהיא בדרך כלל (דקה ו-40 שניות לעומת 40 שניות ב-Pixel XL), כי קובצי ה-odex אבדו מ-B אחרי עדכון ה-OTA הראשון, ולכן אי אפשר להעתיק אותם אל /data. זה שיקול שצריך לקחת בחשבון.
איפוס לנתוני היצרן צריך להיות פעולה נדירה בהשוואה לאתחול רגיל, ולכן הזמן שנדרש לאיפוס פחות חשוב. (הבעיה הזו לא משפיעה על משתמשים או בודקים שמקבלים את המכשיר מהמפעל, כי במקרה כזה מחיצת B זמינה). כשמשתמשים בהידור JIT, זה אומר שלא צריך לקמפל מחדש הכול, אז זה לא נורא כמו שזה נשמע. אפשר גם לסמן אפליקציות ככאלה שנדרשת בשבילן הידור מראש באמצעות coreApp="true" במניפסט: (frameworks/base/+/android16-qpr1-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 תמיד נדרשו עדכונים מבוססי-בלוק, אבל לא בהכרח עדכוני 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, אם העדכון לא מתבצע, הוא לא משפיע על המערכת שפועלת כרגע. אפשר פשוט לנסות לבצע את העדכון שוב אחר כך.