האם Google השתמשה בעדכוני OTA מסוג A/B במכשירים כלשהם?
כן. השם השיווקי של עדכוני A/B הוא עדכונים חלקים. טלפונים מדגמי Pixel ו-Pixel XL שנשלחו החל מאוקטובר 2016 כוללים את התכונה A/B, וכל מכשירי Chromebook משתמשים באותה הטמעה של A/B ב-update_engine
. הטמעת קוד הפלטפורמה הנדרשת זמינה ב-Android מגרסה 7.1 ואילך.
למה בדיקות A/B של OTA עדיפות?
עדכוני 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, שגודל קובץ האימג' הכולל של המערכת הוא 2628MiB (2755792836 בייטים), פירוט הקבצים שתורמים הכי הרבה לגודל קובץ האימג' הכולל של המערכת לפי סוג הקובץ הוא:
.odex | 1391770312 בייטים | 50.5% |
.apk | 846878259 בייטים | 30.7% |
.so (קוד C/C++ מקורי) | 202162479 בייטים | 7.3% |
קובצי oat/תמונות art | 163892188 בייטים | 5.9% |
גופנים | 38952361 בייטים | 1.4% |
נתוני icu locale | 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 .קטנים יותר מהידור מראש (כי בקודם מתבצעת אופטימיזציה רק לקוד קריטי לביצועים).
פרטים על אפשרויות הכוונון הזמינות ליצרני ציוד מקורי מופיעים במאמר הגדרת ART.
האם אין שני עותקים של קובצי odex . ב-/data?
זה קצת יותר מסובך… אחרי שתמונה המערכת החדשה נכתבת, הגרסה החדשה של dex2oat מופעלת על קובצי ה-dex. החדשים כדי ליצור את קובצי ה-odex. החדשים. התהליך הזה מתבצע בזמן שהמערכת הישנה עדיין פועלת, כך שקובצי ה-odex הישנים והחדשים נמצאים ב-/data
בו-זמנית.
הקוד ב-OtaDexoptService (frameworks/base/+/main/services/core/java/com/android/server/pm/OtaDexoptService.java
) קורא ל-getAvailableSpace
לפני אופטימיזציה של כל חבילה כדי למנוע מילוי יתר של /data
. הערך available עדיין שמרני: זהו נפח האחסון שנותר לפני שמגיעים לסף הרגיל של נפח אחסון נמוך במערכת (נמדד גם כאחוז וגם כמספר בייטים). לכן, אם /data
מלא, לא יהיו שני עותקים של כל קובץ .odex. לאותו קוד יש גם את הערך BULK_DELETE_THRESHOLD: אם המכשיר מתקרב למצב שבו הוא ממלא את כל המקום הזמין (כפי שמתואר למעלה), קבצי ה-odex .ששייכים לאפליקציות שלא בשימוש יוסרו. זהו מקרה נוסף בלי שני עותקים של כל קובץ .odex.
במקרה הגרוע ביותר שבו /data
מלא לגמרי, העדכון ממתין עד שהמכשיר יופעל מחדש במערכת החדשה ולא יהיה צורך יותר בקובצי ה-odex של המערכת הישנה. PackageManager מטפל בזה: (frameworks/base/+/main/services/core/java/com/android/server/pm/PackageManagerService.java#7215
). אחרי שהמערכת החדשה מופעלת בהצלחה, installd
(frameworks/native/+/main/cmds/installd/dexopt.cpp#2422
) יכול להסיר את קובצי ה-odex . שבהם המערכת הישנה השתמשה, ולהחזיר את המכשיר למצב יציב שבו יש רק עותק אחד.
לכן, יכול להיות ש-/data
מכיל שתי עותקים של כל קובצי ה-odex ., אבל (א) זה זמני ו (ב) זה קורה רק אם יש לכם מספיק מקום פנוי ב-/data
. מלבד במהלך עדכון, יש עותק אחד בלבד. בנוסף, כחלק מתכונות העמידות הכלליות של ART, הוא אף פעם לא ימלא את /data
בקובצי odex . (כי זו תהיה בעיה גם במערכת שאינה מערכת A/B).
האם כל הכתיבה/ההעתקה הזו לא מגדילה את שחיקה של הפלאש?
רק חלק קטן מהזיכרון מסוג Flash נכתב מחדש: עדכון מערכת מלא ל-Pixel גורם לכתיבה של כ-2.3GB. (גם האפליקציות עוברות הידור מחדש, אבל זה נכון גם לגבי בדיקות שלא כוללות ניסוי A/B). באופן מסורתי, עדכוני OTA מלאים שמבוססים על בלוקים כתבו כמות דומה של נתונים, כך ששיעורי הבלאי של זיכרון הפלאש אמורים להיות דומים.
האם איחוי של שני מחיצות מערכת מאריך את זמן האיחוי במפעל?
לא. גודל קובץ האימג' של המערכת לא השתנה ב-Pixel (הוא פשוט חולק את המרחב לשני מחיצות).
האם שמירת קבצים מסוג odex .ב-B לא גורמת להאטה של הפעלה מחדש אחרי איפוס לנתוני היצרן?
כן. אם השתמשתם במכשיר, קיבלתם עדכון OTA וביצעתם איפוס לנתוני היצרן, ההפעלה מחדש הראשונה תהיה איטית יותר מאשר במקרה אחר (1 דקה ו-40 שניות לעומת 40 שניות ב-Pixel XL), כי קובצי ה-odex .יאבדו מ-B אחרי עדכון ה-OTA הראשון, ולכן לא ניתן יהיה להעתיק אותם אל /data
. זה המחיר שצריך לשלם.
איפוס לנתוני היצרן הוא פעולה נדירה בהשוואה לאתחול רגיל, ולכן משך הזמן הנדרש לביצועה פחות חשוב. (הדבר לא משפיע על משתמשים או על בודקים שקיבלו את המכשיר שלהם מהמפעל, כי במקרה כזה המחיצה B זמינה). השימוש במהדר JIT מאפשר לנו לא לאסוף מחדש את הכול, כך שהמצב לא גרוע כמו שנדמה. אפשר גם לסמן אפליקציות שדורשות הידור מראש באמצעות coreApp="true"
במניפסט: (frameworks/base/+/main/packages/SystemUI/AndroidManifest.xml#23
). האפשרות הזו נמצאת כרגע בשימוש ב-system_server
כי אסור לה להשתמש ב-JIT מטעמי אבטחה.
האם שמירת קובצי odex .ב- /data במקום ב- /system גורמת להאטה של ההפעלה מחדש אחרי עדכון OTA?
לא. כפי שהוסבר למעלה, ה-dex2oat החדש פועל בזמן שתמונת המערכת הישנה עדיין פועלת, כדי ליצור את הקבצים הנדרשים למערכת החדשה. העדכון נחשב לזמין רק אחרי שהשלמתם את העבודה הזו.
האם אפשר (צריך) לשלוח מכשיר A/B בנפח 32GB? 16GiB? 8GiB?
32GB פועלים היטב, כפי שהוכח ב-Pixel, ו-320MB מתוך 16GB הם הפחתה של 2%. באופן דומה, 320MiB מתוך 8GiB הם הפחתה של 4%. ברור ש-A/B לא תהיה הבחירה המומלצת במכשירים עם 4GB, כי 320MB של תקורה הם כמעט 10% מכלל נפח האחסון הזמין.
האם AVB2.0 מחייב עדכוני OTA מסוג A/B?
לא. תמיד נדרשו עדכונים מבוססי-בלוקים ב-Verified Boot, אבל לא בהכרח עדכוני A/B.
האם עדכוני OTA מסוג A/B דורשים AVB2.0?
לא.
האם עדכוני OTA מסוג A/B מפריעים להגנה מפני החזרה למצב הקודם של AVB2.0?
לא. יש כאן בלבול מסוים כי אם מערכת A/B לא מצליחה להפעיל את קובץ האימג' החדש של המערכת, היא תעבור באופן אוטומטי (אחרי מספר ניסיונות חוזרים שנקבע על ידי מנהל האתחול) לקובץ האימג' "הקודם" של המערכת. עם זאת, הנקודה החשובה כאן היא ש'קודם' במסגרת בדיקת A/B הוא למעשה עדיין קובץ האימג' ה'נוכחי' של המערכת. ברגע שהמכשיר מפעיל בהצלחה תמונה חדשה, ההגנה מפני חזרה לאחור נכנסת לתוקף ומוודאת שלא תוכלו לחזור לאחור. עם זאת, עד שתפעילו בהצלחה את התמונה החדשה, ההגנה מפני חזרה לאחור לא תתייחס אליה כאל קובץ האימג' המערכת הנוכחי.
אם מתקינים עדכון בזמן שהמערכת פועלת, האם זה לא איטי?
בעדכונים שאינם A/B, המטרה היא להתקין את העדכון מהר ככל האפשר, כי המשתמש ממתין ולא יכול להשתמש במכשיר בזמן החלת העדכון. בעדכוני A/B, המצב הפוך: המטרה היא להשפיע על המשתמש כמה שפחות, כי הוא עדיין משתמש במכשיר, ולכן העדכון מתבצע באיטיות מכוונת. באמצעות לוגיקה בלקוח של עדכון המערכת ב-Java (ב-Google זהו GmsCore, חבילת הליבה ש-GMS מספקת), Android גם מנסה לבחור זמן שבו המשתמשים לא משתמשים במכשירים בכלל. הפלטפורמה תומכת בהשהיה או בהמשך של העדכון, והלקוח יכול להשתמש בכך כדי להשהות את העדכון אם המשתמש מתחיל להשתמש במכשיר, ולהמשיך אותו כשהמכשיר חוזר למצב חוסר פעילות.
יש שני שלבים בתהליך העדכון האווירי, שמוצגים בבירור בממשק המשתמש בתור שלב 1 מתוך 2 ו-שלב 2 מתוך 2 מתחת לסרגל ההתקדמות. שלב 1 מתאים לכתיבת בלוקים של נתונים, ואילו שלב 2 הוא הידור מראש של קובצי ה-dex. שני השלבים האלה שונים מאוד מבחינת ההשפעה על הביצועים. השלב הראשון הוא קלט/פלט פשוט. הצורך במשאבים (זיכרון RAM, מעבד, קלט/פלט) הוא קטן, כי מדובר רק בהעתקה איטית של בלוקים.
בשלב השני מופעל dex2oat כדי לבצע הידור מראש של קובץ האימג' החדש של המערכת. ברור שהדרישות של ה-IDE הזה פחות ברורות, כי הוא מקמפל אפליקציות אמיתיות. כמובן שדרושה הרבה יותר עבודה כדי לקמפל אפליקציה גדולה ומורכבת מאשר אפליקציה קטנה ופשוטה. לעומת זאת, בשלב 1 אין בלוקים בדיסק שגדולים או מורכבים יותר מאחרים.
התהליך דומה למקרה שבו Google Play מתקין עדכון לאפליקציה ברקע לפני שהוא מציג את ההתראה 5 אפליקציות עודכנו, כפי שהיה נהוג במשך שנים.
מה קורה אם משתמש מחכה לעדכון?
ההטמעה הנוכחית ב-GmsCore לא מבדילה בין עדכונים ברקע לבין עדכונים שמשתמשים מפעילים, אבל יכול להיות שהיא תבדיל ביניהם בעתיד. אם המשתמש ביקש במפורש להתקין את העדכון או שהוא צופה במסך ההתקדמות של העדכון, נעדיף את העבודה על העדכון בהנחה שהוא מחכה שהיא תסתיים.
מה קורה אם לא ניתן להחיל עדכון?
בעדכונים שאינם A/B, אם עדכון מסוים לא הצליח לחול, בדרך כלל המשתמש נשאר עם מכשיר שלא ניתן להשתמש בו. היוצא מן הכלל היחיד היה אם התקלה התרחשה עוד לפני שהאפליקציה התחילה לפעול (למשל, כי לא ניתן היה לאמת את החבילה). בעדכוני A/B, כשל בהטמעת עדכון לא משפיע על המערכת שפועלת כרגע. אפשר פשוט לנסות שוב מאוחר יותר.