מערכת השחזור כוללת כמה ווקים להוספת קוד ספציפי למכשיר, כדי שעדכוני OTA יוכלו לעדכן גם חלקים במכשיר שאינם מערכת Android (למשל, מעבד הרדיו או התדר הבסיסי).
בקטעים ובדוגמאות הבאים מוסבר איך לבצע התאמה אישית של מכשיר tardis שמיוצר על ידי הספק yoyodyne.
מפת מחיצה
החל מגרסה 2.3 של Android, הפלטפורמה תומכת במכשירי פלאש מסוג eMMc ובמערכת הקבצים ext4 שפועלת במכשירים האלה. הוא תומך גם בהתקני זיכרון Flash מסוג Memory Technology Device (MTD) ובמערכת הקבצים yaffs2 מגרסאות ישנות יותר.
קובץ המפה של המחיצות מצוין על ידי TARGET_RECOVERY_FSTAB. הקובץ הזה משמש גם את הקובץ הבינארי של התאוששות וגם את הכלים ליצירת חבילות. אפשר לציין את שם קובץ המיפוי בקטע TARGET_RECOVERY_FSTAB בקובץ BoardConfig.mk.
קובץ לדוגמה של מפת מחיצה עשוי להיראות כך:
device/yoyodyne/tardis/recovery.fstab
# mount point fstype device [device2] [options (3.0+ only)] /sdcard vfat /dev/block/mmcblk0p1 /dev/block/mmcblk0 /cache yaffs2 cache /misc mtd misc /boot mtd boot /recovery emmc /dev/block/platform/s3c-sdhci.0/by-name/recovery /system ext4 /dev/block/platform/s3c-sdhci.0/by-name/system length=-4096 /data ext4 /dev/block/platform/s3c-sdhci.0/by-name/userdata
מלבד /sdcard
, שהיא אופציונלית, צריך להגדיר את כל נקודות הטעינה בדוגמה הזו (אפשר גם להוסיף למכשירים עוד מחיצות). יש חמישה סוגי מערכת קבצים נתמכים:
- yaffs2
-
מערכת קבצים מסוג yaffs2 על גבי מכשיר MTD מסוג flash. 'device' חייב להיות השם של מחיצת ה-MTD, והוא חייב להופיע ב-
/proc/mtd
. - mtd
-
מחיצה MTD גולמית, המשמשת למחיצות שניתן להפעיל אותן, כמו הפעלה ושחזור. MTD לא מצורף בפועל, אבל נקודת הקישור משמשת כמפתח לאיתור המחיצה. 'device' חייב להיות השם של מחיצת ה-MTD ב-
/proc/mtd
. - ext4
- מערכת קבצים מסוג ext4 על גבי מכשיר פלאש מסוג eMMc. 'device' חייב להיות הנתיב של מכשיר החסימה.
- emmc
- מכשיר בלוק גולמי של eMMc, המשמש למחיצות שניתן להפעיל אותן, כמו אתחול ושחזור. בדומה לסוג mtd, אף פעם לא מתבצעת טעינה של eMMc, אבל מחרוזת נקודת הטעינה משמשת לאיתור המכשיר בטבלה.
- vfat
-
מערכת קבצים מסוג FAT שמוטמעת בהתקן בלוקים, בדרך כלל לאחסון חיצוני כמו כרטיס SD. device הוא מכשיר הבלוק, ו-device2 הוא מכשיר בלוק שני שהמערכת מנסה לטעון אם הטעינה של המכשיר הראשי נכשלת (לצורך תאימות לכרטיסי SD שעשויים להיות או לא להיות בפורמט עם טבלת מחיצות).
כל המחיצות צריכות להיות מותקנות בספריית השורש (כלומר, הערך של נקודת הטעינה חייב להתחיל בקו נטוי ולא לכלול קווים נטויים אחרים). ההגבלה הזו חלה רק על טעינה של מערכות קבצים במצב שחזור. במערכת הראשית אפשר לטעון אותן בכל מקום. הספריות
/boot
,/recovery
ו-/misc
צריכות להיות מסוג 'גולמי' (mtd או emmc), בעוד שהספריות/system
,/data
,/cache
ו-/sdcard
(אם הן זמינות) צריכות להיות מסוג 'מערכת קבצים' (yaffs2, ext4 או vfat).
החל מגרסה 3.0 של Android, נוספו לקובץ recovery.fstab שדה אופציונלי נוסף, options. נכון לעכשיו, האפשרות היחידה שמוגדרת היא length , שמאפשרת לציין במפורש את אורך המחיצה. האורך הזה משמש בפורמט מחדש של המחיצה (למשל, למחיצה userdata במהלך מחיקה של נתונים או איפוס להגדרות המקוריות, או למחיצה המערכת במהלך התקנה של חבילת OTA מלאה). אם ערך האורך הוא שלילי, הגודל לפורמט מחושב על ידי הוספת ערך האורך לגודל המחיצה האמיתי. לדוגמה, אם מגדירים את הערך 'length=-16384', המשמעות היא שה-16k האחרונים של המחיצה הזו לא יימחקו כשהמחיצה תפורמט מחדש. כך אפשר להשתמש בתכונות כמו הצפנה של מחיצה של נתוני משתמש (המטא-נתונים של ההצפנה מאוחסנים בסוף המחיצה, אסור להחליף אותם).
הערה: השדות device2 ו-options הם אופציונליים, והם יוצרים אי-בהירות בניתוח. אם הערך בשדה הרביעי בשורה מתחיל בתווית '/', הוא נחשב כערך של device2. אם הערך לא מתחיל בתווית '/', הוא נחשב כערך של השדה options.
אנימציית אתחול
יצרני המכשירים יכולים להתאים אישית את האנימציה שמוצגת כשמכשיר Android מופעל. לשם כך, יוצרים קובץ zip שמאורגן וממוקם בהתאם למפרטים בפורמט bootanimation.
במכשירי Android Things, אפשר להעלות את הקובץ המכווץ במסוף Android Things כדי שהתמונות ייכללו במוצר שנבחר.
הערה: התמונות האלה צריכות לעמוד בהנחיות למותג Android. להנחיות בנושא מותגים, אפשר לעיין בקטע Android ב-Partner Marketing Hub.
ממשק המשתמש של Recovery
כדי לתמוך במכשירים עם חומרה שונה (לחצנים פיזיים, נוריות LED, מסכים וכו'), אתם יכולים להתאים אישית את ממשק השחזור כדי להציג את הסטטוס ולגשת לתכונות המוסתרות של כל מכשיר, שמופעל באופן ידני.
המטרה היא ליצור ספרייה סטטית קטנה עם כמה אובייקטים של C++ כדי לספק את הפונקציונליות הספציפית למכשיר. הקובץ bootable/recovery/default_device.cpp
משמש כברירת מחדל, והוא נקודת התחלה טובה להעתקה כשכותבים גרסה של הקובץ הזה למכשיר.
הערה: יכול להיות שתופיע כאן ההודעה No Command. כדי להחליף בין הטקסט, לוחצים לחיצה ארוכה על לחצן ההפעלה ועל הלחצן להגברת עוצמת הקול. אם במכשיר שלכם אין את שני הלחצנים, לוחצים לחיצה ארוכה על כל לחצן כדי להחליף בין הטקסט.
device/yoyodyne/tardis/recovery/recovery_ui.cpp
#include <linux/input.h> #include "common.h" #include "device.h" #include "screen_ui.h"
פונקציות של כותרות ופריטים
בכיתה Device נדרשות פונקציות להחזרת כותרות ופריטים שמופיעים בתפריט השחזור המוסתר. הכותרות מתארות איך להשתמש בתפריט (כלומר, אילו פקדים יש לשנות או לבחור כדי לבחור את הפריט המודגש).
static const char* HEADERS[] = { "Volume up/down to move highlight;", "power button to select.", "", NULL }; static const char* ITEMS[] = {"reboot system now", "apply update from ADB", "wipe data/factory reset", "wipe cache partition", NULL };
הערה: שורות ארוכות נחתכות (לא מחולקות לשורות), לכן חשוב לזכור את רוחב המסך של המכשיר.
התאמה אישית של CheckKey
בשלב הבא מגדירים את ההטמעה של RecoveryUI במכשיר. בדוגמה הזו, אנחנו מניחים שלמכשיר tardis יש מסך, כך שאפשר לרשת מההטמעה המובנית של ScreenRecoveryUI (ראו הוראות למכשירים ללא מסך). הפונקציה היחידה שצריך להתאים אישית מ-ScreenRecoveryUI היא CheckKey()
, שמבצעת את הטיפול הראשוני במפתחות אסינכררוניים:
class TardisUI : public ScreenRecoveryUI { public: virtual KeyAction CheckKey(int key) { if (key == KEY_HOME) { return TOGGLE; } return ENQUEUE; } };
קבועים של מפתחות
הקבועים KEY_* מוגדרים בקובץ linux/input.h
.הפונקציה CheckKey()
נקראת ללא קשר למה שקורה בשאר תהליך השחזור: כשהתפריט מושבת, כשהוא מופעל, במהלך התקנת החבילה, במהלך מחיקה של נתוני המשתמש וכו'. היא יכולה להחזיר אחת מארבע קבועות:
- TOGGLE. הפעלה או השבתה של תצוגת התפריט ו/או יומן הטקסט
- REBOOT. מפעילים מחדש את המכשיר באופן מיידי
- IGNORE. התעלמות מלחיצת המקש הזו
- ENQUEUE. הוספת לחיצת המקש הזו לתור לשימוש בו באופן סינכרוני (כלומר, על ידי מערכת תפריט השחזור אם המסך מופעל)
CheckKey()
נקראת בכל פעם שאחר אירוע key-down מתרחש אירוע key-up לאותו מקש. (הרצף של האירועים A-down B-down B-up A-up גורם רק להפעלה של CheckKey(B)
). CheckKey()
יכול לקרוא ל-IsKeyPressed()
כדי לבדוק אם מקשים אחרים מוחזקים. (בסדרת האירועים המרכזיים שלמעלה, אם CheckKey(B)
יקרא ל-IsKeyPressed(A)
, הפונקציה תחזיר את הערך true).
CheckKey()
יכול לשמור מצב בכיתה שלו. הדבר יכול להיות שימושי לזיהוי של רצפי מפתחות. בדוגמה הזו מוצגת הגדרה מורכבת יותר: כדי להפעיל או לכבות את המסך, לוחצים לחיצה ארוכה על לחצן ההפעלה ואז על לחצן הגברת עוצמת הקול. כדי להפעיל מחדש את המכשיר באופן מיידי, לוחצים על לחצן ההפעלה חמש פעמים ברציפות (בלי ללחוץ על לחצנים אחרים בינתיים):
class TardisUI : public ScreenRecoveryUI { private: int consecutive_power_keys; public: TardisUI() : consecutive_power_keys(0) {} virtual KeyAction CheckKey(int key) { if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) { return TOGGLE; } if (key == KEY_POWER) { ++consecutive_power_keys; if (consecutive_power_keys >= 5) { return REBOOT; } } else { consecutive_power_keys = 0; } return ENQUEUE; } };
ScreenRecoveryUI
כשמשתמשים בתמונות משלכם (סמל שגיאה, אנימציית התקנה, סרגל התקדמות) עם ScreenRecoveryUI, אפשר להגדיר את המשתנה animation_fps
כדי לשלוט במהירות של האנימציות בפריימים לשנייה (FPS).
הערה: הסקריפט הנוכחי של interlace-frames.py
מאפשר לאחסן את המידע של animation_fps
בתמונה עצמה. בגרסאות קודמות של Android היה צריך להגדיר את animation_fps
בעצמכם.
כדי להגדיר את המשתנה animation_fps
, צריך לשנות את ברירת המחדל של הפונקציה ScreenRecoveryUI::Init()
בתת-הסוג. מגדירים את הערך ומפעילים את הפונקציה parent Init()
כדי להשלים את תהליך האתחול. ערך ברירת המחדל (20FPS) תואם לתמונות ברירת המחדל לשחזור. כשמשתמשים בתמונות האלה, אין צורך לספק פונקציית Init()
. פרטים על התמונות זמינים במאמר תמונות של ממשק המשתמש לשחזור.
סוג המכשיר
אחרי שמטמיעים את RecoveryUI, מגדירים את סוג המכשיר (סוג משנה של סוג המכשיר המובנה). היא צריכה ליצור מופע יחיד של כיתה ממשק המשתמש ולהחזיר אותו מהפונקציה GetUI()
:
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
השיטה StartRecovery()
נקראת בתחילת תהליך השחזור, אחרי שממשק המשתמש מופעל ואחרי שהארגומנטים נסרקו, אבל לפני שמתבצעת פעולה כלשהי. הטמעת ברירת המחדל לא מבצעת שום פעולה, לכן אין צורך לספק אותה בתת-הסוג אם אין לכם מה לעשות:
void StartRecovery() { // ... do something tardis-specific here, if needed .... }
הוספה וניהול של תפריט שחזור
המערכת קוראת לשתי שיטות כדי לקבל את רשימת שורות הכותרת ואת רשימת הפריטים. בהטמעה הזו, הפונקציה מחזירה את המערכים הסטטיים שהוגדרו בחלק העליון של הקובץ:
const char* const* GetMenuHeaders() { return HEADERS; } const char* const* GetMenuItems() { return ITEMS; }
HandleMenuKey
בשלב הבא, נותנים פונקציית HandleMenuKey()
, שמקבלת לחיצה על מקש והצגת התפריט הנוכחית, ומחליטה איזו פעולה לבצע:
int HandleMenuKey(int key, int visible) { if (visible) { switch (key) { case KEY_VOLUMEDOWN: return kHighlightDown; case KEY_VOLUMEUP: return kHighlightUp; case KEY_POWER: return kInvokeItem; } } return kNoAction; }
השיטה מקבלת קוד מפתח (שעבר עיבוד והוספה לתור על ידי השיטה CheckKey()
של אובייקט ממשק המשתמש) ואת המצב הנוכחי של הרשאות הגישה של יומן התפריט/הטקסט. הערך המוחזר הוא מספר שלם. אם הערך הוא 0 או גבוה ממנו, הוא נחשב כמיקום של פריט בתפריט, שמופעל באופן מיידי (ראו את השיטה InvokeMenuItem()
בהמשך). אחרת, הוא יכול להיות אחד מהקבועים המוגדרים מראש הבאים:
- kHighlightUp. העברת ההדגשה בתפריט לפריט הקודם
- kHighlightDown. העברת ההדגשה בתפריט לפריט הבא
- kInvokeItem. הפעלת הפריט המודגש הנוכחי
- kNoAction. לא לבצע שום פעולה עם הקשה על המקש הזה
כפי שרואים מהארגומנט visible, הפונקציה HandleMenuKey()
נקראת גם אם התפריט לא גלוי. בניגוד ל-CheckKey()
, הפונקציה הזו לא נקראת בזמן שהשחזור מבצע פעולה כלשהי, כמו מחיקת נתונים או התקנת חבילה. היא נקראת רק כשהשחזור לא פעיל וממתין לקלט.
מנגנונים של כדורי עקיבה
אם במכשיר יש מנגנון קלט שדומה לטרקר-בול (יוצר אירועי קלט מסוג EV_REL
וקוד REL_Y), תהליך השחזור יוצר לחיצות על המקשים KEY_UP ו-KEY_DOWN בכל פעם ש
מכשיר הקלט שדומה לטרקר-בול מדווח על תנועה בציר Y. כל מה שצריך לעשות הוא למפות את האירועים KEY_UP ו-KEY_DOWN לפעולות בתפריט. המיפוי הזה לא מתבצע ב-CheckKey()
, ולכן אי אפשר להשתמש בתנועות של גלגל העכבר כטריגרים להפעלה מחדש או להפעלה/כיבוי של המסך.
מקשי צירוף
כדי לבדוק אם מקשים מוחזקים כמקשי שינוי, צריך להפעיל את השיטה IsKeyPressed()
של אובייקט ממשק המשתמש שלכם. לדוגמה, במכשירים מסוימים, לחיצה על Alt-W במצב שחזור תגרום למחיקת הנתונים, גם אם התפריט גלוי וגם אם לא. אפשר להטמיע את הקוד כך:
int HandleMenuKey(int key, int visible) { if (ui->IsKeyPressed(KEY_LEFTALT) && key == KEY_W) { return 2; // position of the "wipe data" item in the menu } ... }
הערה: אם הערך של visible הוא false, אין טעם להחזיר את הערכים המיוחדים שמפעילים שינויים בתפריט (העברת ההדגשה, הפעלת הפריט המודגש), כי המשתמש לא יכול לראות את ההדגשה. עם זאת, אפשר להחזיר את הערכים אם רוצים.
InvokeMenuItem
בשלב הבא, מספקים שיטה InvokeMenuItem()
שממפה מיקומי מספרים שלמים במערך הפריטים שמוחזרים על ידי GetMenuItems()
לפעולות. במערך הפריטים בדוגמה של tardis, משתמשים בקוד:
BuiltinAction InvokeMenuItem(int menu_position) { switch (menu_position) { case 0: return REBOOT; case 1: return APPLY_ADB_SIDELOAD; case 2: return WIPE_DATA; case 3: return WIPE_CACHE; default: return NO_ACTION; } }
השיטה הזו יכולה להחזיר כל חבר במערך הערכים של BuiltinAction כדי להורות למערכת לבצע את הפעולה הזו (או את החבר NO_ACTION אם רוצים שהמערכת לא תעשה כלום). כאן אפשר להוסיף פונקציונליות נוספת לשחזור מעבר לזו שמובנית במערכת: מוסיפים פריט לתפריט, מריצים אותו כאן כשמפעילים את פריט התפריט הזה ומחזירים את הערך NO_ACTION כדי שהמערכת לא תבצע פעולה נוספת.
השדה BuiltinAction מכיל את הערכים הבאים:
- NO_ACTION. לא לעשות דבר.
- REBOOT. יוצאים ממצב שחזור ומפעילים מחדש את המכשיר באופן רגיל.
- APPLY_EXT, APPLY_CACHE, APPLY_ADB_SIDELOAD. התקנה של חבילת עדכון ממקומות שונים. פרטים נוספים זמינים במאמר התקנה ידנית.
- WIPE_CACHE. צריך לפרמט מחדש רק את מחיצת המטמון. לא נדרש אישור כי הפעולה הזו לא מסוכנת.
- WIPE_DATA. צריך לפרמט מחדש את המחיצות של נתוני המשתמש והמטמון, שנקראות גם איפוס לנתוני היצרן. המשתמש יתבקש לאשר את הפעולה הזו לפני שהוא יוכל להמשיך.
השיטה האחרונה, WipeData()
, היא אופציונלית והיא נקראת בכל פעם שמתחילים פעולת מחיקה של נתונים (מהשחזור דרך התפריט או כשהמשתמש בוחר לבצע איפוס להגדרות המקוריות מהמערכת הראשית). המערכת קוראת לשיטה הזו לפני שמוחקים את נתוני המשתמש ואת המחיצות של המטמון. אם נתוני המשתמשים נשמרים במכשיר במקום אחר מלבד שני המחיצות האלה, צריך למחוק אותם כאן. צריך להחזיר 0 כדי לציין הצלחה וערך אחר כדי לציין כשל, אבל בשלב זה המערכת מתעלמת מערך ההחזרה. נתוני המשתמש ומחיצות המטמון נמחקים, גם אם הפונקציה מחזירה ערך של הצלחה וגם אם היא מחזירה ערך של כישלון.
int WipeData() { // ... do something tardis-specific here, if needed .... return 0; }
יצרן המכשיר
לבסוף, מוסיפים קוד לדוגמה בסוף הקובץ recovery_ui.cpp לפונקציה make_device()
שיוצרת מופע של הכיתה Device ומחזירה אותו:
class TardisDevice : public Device { // ... all the above methods ... }; Device* make_device() { return new TardisDevice(); }
פיתוח וקישור לשחזור המכשיר
אחרי שמסיימים את הקובץ recovery_ui.cpp, צריך ליצור אותו ולקשר אותו לשחזור במכשיר. בקובץ Android.mk, יוצרים ספרייה סטטית שמכילה רק את קובץ ה-C++ הזה:
device/yoyodyne/tardis/recovery/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_C_INCLUDES += bootable/recovery LOCAL_SRC_FILES := recovery_ui.cpp # should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk LOCAL_MODULE := librecovery_ui_tardis include $(BUILD_STATIC_LIBRARY)
לאחר מכן, בהגדרות הלוח של המכשיר הזה, מציינים את הספרייה הסטטית בתור הערך של TARGET_RECOVERY_UI_LIB.
device/yoyodyne/tardis/BoardConfig.mk [...] # device-specific extensions to the recovery UI TARGET_RECOVERY_UI_LIB := librecovery_ui_tardis
תמונות של ממשק המשתמש של התכונה 'שחזור'
ממשק המשתמש של התהליך כולל תמונות. באופן אידיאלי, המשתמשים לא צריכים לבצע אף פעולה בממשק המשתמש: במהלך עדכון רגיל, הטלפון מופעל במצב שחזור, סרגל ההתקדמות של ההתקנה מתמלא והטלפון מופעל מחדש במערכת החדשה ללא קלט מהמשתמש. במקרה של בעיה בעדכון המערכת, הפעולה היחידה שהמשתמש יכול לבצע היא להתקשר לשירות הלקוחות.
ממשק של תמונות בלבד מבטל את הצורך בתרגום. עם זאת, החל מגרסה 5.0 של Android, יכול להיות שיוצג יחד עם התמונה גם מחרוזת טקסט (למשל 'מתקין את עדכון המערכת…'). פרטים נוספים זמינים במאמר טקסט שחזור מותאם.
Android מגרסה 5.0 ואילך
בממשק המשתמש של תהליך השחזור ב-Android מגרסה 5.0 ואילך מוצגות שתי תמונות עיקריות: התמונה של השגיאה והאנימציה של ההתקנה.
![]() איור 1. icon_error.png |
![]() איור 2. icon_installing.png |
אנימציית ההתקנה מיוצגת כתמונה יחידה בפורמט PNG, עם פריימים שונים של האנימציה שמקושרים בשורה (לכן איור 2 נראה מרוחק). לדוגמה, כדי ליצור אנימציה של שבעה פריימים בגודל 200x200, יוצרים תמונה אחת בגודל 200x1400 שבה הפריים הראשון הוא השורות 0, 7, 14, 21 וכו', הפריים השני הוא השורות 1, 8, 15, 22 וכו' וכן הלאה. התמונה המשולבת כוללת מקטע טקסט שמציין את מספר הפריימים באנימציה ואת מספר הפריימים לשנייה (FPS). הכלי bootable/recovery/interlace-frames.py
לוקח קבוצה של פריימים של קלט ומחבר אותם לתמונה המשולבת הנדרשת לצורך השחזור.
תמונות ברירת המחדל זמינות בצפיפויות שונות וממוקמות ב-bootable/recovery/res-$DENSITY/images
(למשל,
bootable/recovery/res-hdpi/images
). כדי להשתמש בתמונה סטטית במהלך ההתקנה, צריך רק לספק את התמונה icon_installing.png ולהגדיר את מספר המסגרות באנימציה כ-0 (סמל השגיאה לא מונפש, הוא תמיד תמונה סטטית).
Android 4.x וגרסאות קודמות
בממשק המשתמש של תהליך השחזור ב-Android 4.x וגרסאות מוקדמות יותר מוצגת התמונה error (שמוצגת למעלה) והאנימציה installing, וכן כמה תמונות שכבת-על:
![]() איור 3. icon_installing.png |
![]() איור 4. icon-installing_overlay01.png |
![]() איור 5. icon_installing_overlay07.png |
במהלך ההתקנה, התצוגה במסך נוצרת על ידי ציור התמונה icon_installing.png, ואז ציור אחד מסגרות שכבת-העל מעליו במרווח המתאים. כאן, תיבה אדומה מוטמעת כדי להדגיש את המיקום שבו שכבת-העל ממוקמת מעל לתמונה הבסיסית:
![]() איור 6. התקנת מסגרת אנימציה 1 (icon_installing.png + icon_installing_overlay01.png) |
![]() איור 7. מתבצעת התקנה של מסגרת האנימציה 7 (icon_installing.png + icon_installing_overlay07.png) |
כדי להציג את המסגרות הבאות, רק תמונה שכבת-העל הבאה נמשכת מעל מה שכבר נמצא שם. התמונה הבסיסית לא נמשכת מחדש.
מספר הפריימים באנימציה, המהירות הרצויה וההיסטים ב-x וב-y של שכבת-העל ביחס לבסיס מוגדרים על ידי משתני חברים של הכיתה ScreenRecoveryUI. כשמשתמשים בתמונות בהתאמה אישית במקום בתמונות ברירת המחדל, צריך לשנות את הערכים האלה בתמונות בהתאמה אישית באמצעות שינוי השיטה Init()
בתת-הסוג (פרטים נוספים זמינים במאמר ScreenRecoveryUI). הסקריפט bootable/recovery/make-overlay.py
יכול לעזור בהמרה של קבוצת מסגרות תמונה לפורמט 'תמונה בסיסית + תמונות שכבת-על' שנדרש לשחזור, כולל חישוב ההיסטים הנדרשים.
תמונות ברירת המחדל נמצאות בתיקייה bootable/recovery/res/images
. כדי להשתמש בתמונה סטטית במהלך ההתקנה, צריך רק לספק את התמונה icon_installing.png ולהגדיר את מספר הפריים באנימציה ל-0 (סמל השגיאה לא מונפש, הוא תמיד תמונה סטטית).
טקסט שחזור מותאם לשוק המקומי
ב-Android 5.x מוצגת מחרוזת טקסט (למשל, "Installing system update…") יחד עם התמונה. כשהמערכת הראשית מופעל לתהליך שחזור, היא מעבירה את האזור הנוכחי של המשתמש כאפשרות בשורת הפקודה לתהליך השחזור. לכל הודעה שמוצגת, התהליך כולל תמונה מורכבת שנייה עם מחרוזות טקסט שעבר עיבוד מראש עבור ההודעה הזו בכל אזור גיאוגרפי.
תמונה לדוגמה של מחרוזות טקסט לשחזור:

איור 8. טקסט מותאם לשוק המקומי להודעות שחזור
בהודעת הטקסט לשחזור יכולות להופיע ההודעות הבאות:
- מתבצעת התקנה של עדכון מערכת…
- שגיאה!
- מתבצעת מחיקה… (כשמבצעים מחיקה של נתונים או איפוס להגדרות המקוריות)
- ללא פקודה (כשמשתמש מפעיל את המכשיר למצב שחזור באופן ידני)
אפליקציית Android ב-bootable/recovery/tools/recovery_l10n/
מבצעת עיבוד (רנדור) של הגרסאות המקומיות של ההודעה ויוצרת את התמונה המשולבת. לפרטים על השימוש באפליקציה הזו, אפשר לעיין בתגובות שב-bootable/recovery/tools/recovery_l10n/src/com/android/recovery_l10n/Main.java
.
כשמשתמש מפעיל את המכשיר למצב שחזור באופן ידני, יכול להיות שהשפה לא תהיה זמינה ולא יוצג טקסט. אל תתייחסו להודעות הטקסט כאל מידע קריטי לתהליך השחזור.
הערה: הממשק המוסתר שבו מוצגות הודעות ביומן ומאפשר למשתמש לבחור פעולות מהתפריט זמין רק באנגלית.
סרגי התקדמות
סרגי התקדמות יכולים להופיע מתחת לתמונה הראשית (או לאנימציה). סרגל ההתקדמות נוצר על ידי שילוב של שתי תמונות קלט, שצריכות להיות באותו גודל:

איור 9. progress_empty.png

איור 10. progress_fill.png
הקצה השמאלי של התמונה המלאה מוצג לצד הקצה הימני של התמונה הריקה כדי ליצור את סרגל ההתקדמות. המיקום של הגבול בין שתי התמונות משתנה כדי לציין את ההתקדמות. לדוגמה, עם זוגות התמונות של הקלט שלמעלה, יוצגו:

איור 11. סרגל התקדמות ב-1%>

איור 12. סרגל התקדמות ב-10%

איור 13. סרגל התקדמות ב-50%
כדי לספק גרסאות ספציפיות למכשיר של התמונות האלה, אפשר להציב אותן בתיקייה (בדוגמה הזו) device/yoyodyne/tardis/recovery/res/images
. שמות הקבצים חייבים להיות זהים לשמות שמפורטים למעלה. כשהמערכת מאתרת קובץ בספרייה הזו, היא משתמשת בו במקום בתמונת ברירת המחדל המתאימה. המערכת תומכת רק בקובצי PNG בפורמט RGB או RGBA עם עומק צבע של 8 ביט.
הערה: ב-Android 5.x, אם השפה המקומית מוכרת לתהליך השחזור והיא שפה שנכתבת מימין לשמאל (RTL) (ערבית, עברית וכו'), סרגל ההתקדמות יאוכלס מימין לשמאל.
מכשירים ללא מסכים
לא כל מכשירי Android כוללים מסכים. אם המכשיר הוא מכשיר ללא צג או שיש לו ממשק אודיו בלבד, יכול להיות שתצטרכו לבצע התאמה אישית נרחבת יותר של ממשק המשתמש לשחזור. במקום ליצור Subclass של ScreenRecoveryUI, יוצרים Subclass ישירות של הכיתה ההורה RecoveryUI.
ל-RecoveryUI יש שיטות לטיפול בפעולות ממשק משתמש ברמה נמוכה יותר, כמו 'החלפת מצב המסך', 'עדכון סרגל ההתקדמות', 'הצגת התפריט', 'שינוי הבחירה בתפריט' וכו'. אפשר לשנות את השיטות האלה כדי לספק ממשק מתאים למכשיר. יכול להיות שיש במכשיר נוריות LED שבהן אפשר להשתמש בצבעים שונים או בדפוסי הבהוב שונים כדי לציין את הסטטוס, או שאולי אפשר להפעיל אודיו. (יכול להיות שאתם לא רוצים לתמוך בתפריט או במצב 'תצוגת טקסט' בכלל. תוכלו למנוע גישה אליהם באמצעות הטמעות של CheckKey()
ו-HandleMenuKey()
שלא מפעילות את התצוגה אף פעם או בוחרות פריט תפריט אף פעם. במקרה כזה, הרבה מהשיטות של RecoveryUI שצריך לספק יכולות להיות פשוט stubs ריקים).
כדי לראות אילו שיטות עליכם לתמוך בהן, תוכלו לעיין בהצהרה על RecoveryUI בקובץ bootable/recovery/ui.h
. RecoveryUI הוא אבסטרקטי – חלק מהשיטות הן וירטואליות טהורות וצריך לספק אותן על ידי תת-כיתות – אבל הוא מכיל את הקוד לעיבוד של קלט מפתחות. אפשר גם לשנות את ההגדרה הזו אם אין במכשיר מפתחות או אם רוצים לעבד אותם בצורה שונה.
מעדכן
כדי להשתמש בקוד ספציפי למכשיר בהתקנה של חבילת העדכון, צריך לספק פונקציות תוסף משלכם שאפשר להפעיל מתוך סקריפט העדכון. הנה דוגמה לפונקציה של מכשיר tardis:
device/yoyodyne/tardis/recovery/recovery_updater.c
#include <stdlib.h> #include <string.h> #include "edify/expr.h"
לכל פונקציית תוסף יש אותה חתימה. הארגומנטים הם השם שבו הפונקציה נקראת, קובץ cookie מסוג State*
, מספר הארגומנטים הנכנסים ומערך של מצביעים מסוג Expr*
שמייצגים את הארגומנטים. הערך המוחזר הוא Value*
שהוקצה מחדש.
Value* ReprogramTardisFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); }
הארגומנטים לא נבדקו בזמן הקריאה לפונקציה – הלוגיקה של הפונקציה קובעת אילו מהם נבדקים וכמה פעמים. כך תוכלו להשתמש בפונקציות ההרחבה כדי להטמיע מבני בקרה משלכם. Call Evaluate()
כדי להעריך ארגומנט Expr*
ולהחזיר Value*
. אם הפונקציה Evaluate()
מחזירה את הערך NULL, צריך לפנות את כל המשאבים ששמורים ולחזור מיד עם הערך NULL (הפעולה הזו מפיצה את הביטולים ב-stack של edify). אחרת, אתם הבעלים של הערך המוחזר ועליך לקרוא ל-FreeValue()
עליו בסופו של דבר.
נניח שהפונקציה צריכה שני ארגומנטים: key עם ערך מחרוזת ו-image עם ערך blob. אפשר לקרוא את הארגומנטים כך:
Value* key = EvaluateValue(state, argv[0]); if (key == NULL) { return NULL; } if (key->type != VAL_STRING) { ErrorAbort(state, "first arg to %s() must be string", name); FreeValue(key); return NULL; } Value* image = EvaluateValue(state, argv[1]); if (image == NULL) { FreeValue(key); // must always free Value objects return NULL; } if (image->type != VAL_BLOB) { ErrorAbort(state, "second arg to %s() must be blob", name); FreeValue(key); FreeValue(image) return NULL; }
בדיקה של NULL וביטול הקצאה של ארגומנטים ששויכו בעבר יכולה להיות משעממת כשיש כמה ארגומנטים. הפונקציה ReadValueArgs()
יכולה לעזור לכם לעשות זאת. במקום הקוד שלמעלה, אפשר היה לכתוב את זה:
Value* key; Value* image; if (ReadValueArgs(state, argv, 2, &key, &image) != 0) { return NULL; // ReadValueArgs() will have set the error message } if (key->type != VAL_STRING || image->type != VAL_BLOB) { ErrorAbort(state, "arguments to %s() have wrong type", name); FreeValue(key); FreeValue(image) return NULL; }
ReadValueArgs()
לא מבצע בדיקת סוגים, לכן צריך לבצע את הבדיקה הזו כאן. קל יותר לבצע את הבדיקה באמצעות משפט if אחד, אבל התוצאה היא הודעת שגיאה פחות ספציפית במקרה של כשל. עם זאת, ReadValueArgs()
מטפלת בהערכה של כל ארגומנט ובשחרור כל הארגומנטים שהועברו להערכה בעבר (וגם בהגדרת הודעת שגיאה מועילה) אם אחת מההערכות נכשלת. אפשר להשתמש בפונקציית הנוחות ReadValueVarArgs()
כדי להעריך מספר משתנה של ארגומנטים (היא מחזירה מערך של Value*
).
אחרי הערכת הארגומנטים, מבצעים את הפעולה של הפונקציה:
// key->data is a NUL-terminated string // image->data and image->size define a block of binary data // // ... some device-specific magic here to // reprogram the tardis using those two values ...
הערך המוחזר חייב להיות אובייקט Value*
. הבעלות על האובייקט הזה תועבר למבצע הקריאה החוזרת. מבצע הקריאה החוזרת מקבל בעלות על כל הנתונים ש-Value*
מפנה אליהם, ובמיוחד על ה-datamember.
במקרה הזה, רוצים להחזיר ערך true או false כדי לציין הצלחה. חשוב לזכור את המוסכמה שמחרוזת ריקה היא false וכל המחרוזות האחרות הן true. צריך להקצות (malloc) אובייקט Value עם עותק של המחרוזת הקבועה שהוקצה באמצעות malloc כדי להחזיר אותו, כי מבצע הקריאה free()
את שניהם. אל תשכחו להפעיל את FreeValue()
על האובייקטים שקיבלתם על ידי הערכת הארגומנטים!
FreeValue(key); FreeValue(image); Value* result = malloc(sizeof(Value)); result->type = VAL_STRING; result->data = strdup(successful ? "t" : ""); result->size = strlen(result->data); return result; }
פונקציית הנוחות StringValue()
עוטפת מחרוזת באובייקט Value חדש.
אפשר להשתמש באפשרות הזו כדי לכתוב את הקוד שלמעלה בצורה תמציתית יותר:
FreeValue(key); FreeValue(image); return StringValue(strdup(successful ? "t" : "")); }
כדי לקשר פונקציות למפרש edify, מספקים את הפונקציה Register_foo
, כאשר foo הוא השם של הספרייה הסטטית שמכילה את הקוד הזה. קוראים לפונקציה RegisterFunction()
כדי לרשום כל פונקציית תוסף. לפי המוסכמה, פונקציות ספציפיות למכשיר צריכות להיקרא device.whatever
כדי למנוע התנגשויות עם פונקציות מובנות שתתווספו בעתיד.
void Register_librecovery_updater_tardis() { RegisterFunction("tardis.reprogram", ReprogramTardisFn); }
עכשיו אפשר להגדיר את קובץ ה-makefile כדי ליצור ספרייה סטטית עם הקוד שלכם. (זהו אותו קובץ makefile ששימש להתאמה אישית של ממשק המשתמש של התאוששות בקטע הקודם. יכול להיות שבמכשיר מוגדרות כאן שתי הספריות הסטטיות).
device/yoyodyne/tardis/recovery/Android.mk
include $(CLEAR_VARS) LOCAL_SRC_FILES := recovery_updater.c LOCAL_C_INCLUDES += bootable/recovery
השם של הספרייה הסטטית חייב להתאים לשם של הפונקציה Register_libname
שמכילה אותה.
LOCAL_MODULE := librecovery_updater_tardis include $(BUILD_STATIC_LIBRARY)
לבסוף, מגדירים את build השחזור כדי שהוא ימשוך את הספרייה. מוסיפים את הספרייה ל-TARGET_RECOVERY_UPDATER_LIBS (שיכולה להכיל כמה ספריות, והן כולן נרשמות).
אם הקוד שלכם תלוי בספריות סטטיות אחרות שאינן בעצמן תוספים של edify (כלומר,
אין להן פונקציית Register_libname
), אפשר לרשום אותן ב-TARGET_RECOVERY_UPDATER_EXTRA_LIBS כדי לקשר אותן ל-updater בלי לקרוא לפונקציית הרישום (הלא קיימת) שלהן. לדוגמה, אם בקוד הספציפי למכשיר רוצים להשתמש ב-zlib כדי לבצע דחיסה לאחור של נתונים, צריך לכלול את libz כאן.
device/yoyodyne/tardis/BoardConfig.mk
[...] # add device-specific extensions to the updater binary TARGET_RECOVERY_UPDATER_LIBS += librecovery_updater_tardis TARGET_RECOVERY_UPDATER_EXTRA_LIBS +=
סקריפטים של העדכון בחבילת ה-OTA יכולים עכשיו להפעיל את הפונקציה שלכם כמו כל פונקציה אחרת. כדי לתכנת מחדש את מכשיר ה-TARDIS, סקריפט העדכון עשוי להכיל את הערך: tardis.reprogram("the-key", package_extract_file("tardis-image.dat"))
. הפונקציה הזו משתמשת בגרסה עם ארגומנט יחיד של הפונקציה המובנית package_extract_file()
, שמחזירה את תוכן הקובץ שחולץ מחבילת העדכון כ-blob כדי ליצור את הארגומנט השני לפונקציית התוסף החדשה.
יצירת חבילות OTA
הרכיב האחרון הוא הוספת הנתונים הספציפיים למכשיר שלכם לכלים ליצירת חבילות OTA, והפעלת סקריפטים של עדכונים שכוללים קריאות לפונקציות של התוספים.
קודם כול, צריך להודיע למערכת ה-build על blob של נתונים ספציפי למכשיר. בהנחה שקובץ הנתונים נמצא ב-device/yoyodyne/tardis/tardis.dat
, מגדירים את הקוד הבא בקובץ AndroidBoard.mk של המכשיר:
device/yoyodyne/tardis/AndroidBoard.mk
[...] $(call add-radio-file,tardis.dat)
אפשר גם להוסיף אותו לקובץ Android.mk, אבל במקרה כזה צריך להגן עליו באמצעות בדיקת מכשיר, כי כל קובצי Android.mk בעץ נטענים ללא קשר למכשיר שנוצר. (אם העץ כולל כמה מכשירים, צריך להוסיף את הקובץ tardis.dat רק כשמפתחים את מכשיר tardis).
device/yoyodyne/tardis/Android.mk
[...] # an alternative to specifying it in AndroidBoard.mk ifeq (($TARGET_DEVICE),tardis) $(call add-radio-file,tardis.dat) endif
הקבצים האלה נקראים 'קובצי רדיו' מסיבות היסטוריות, אבל יכול להיות שאין להם שום קשר לרדיו במכשיר (אם יש כזה). הם פשוט גושים אטומים של נתונים שמערכת ה-build מעתיקה לקובצי ה-zip של היעד שבהם משתמשים הכלים ליצירת OTA. כשמבצעים build, הקובץ tardis.dat נשמר בקובץ target-files.zip בתור RADIO/tardis.dat
. אפשר להפעיל את הפונקציה add-radio-file
כמה פעמים שרוצים כדי להוסיף כמה קבצים שרוצים.
מודול Python
כדי להרחיב את כלי הפיתוח, כותבים מודול Python (השם שלו חייב להיות releasetools.py) שהכלים יוכלו להפעיל אם הוא קיים. דוגמה:
device/yoyodyne/tardis/releasetools.py
import common def FullOTA_InstallEnd(info): # copy the data into the package. tardis_dat = info.input_zip.read("RADIO/tardis.dat") common.ZipWriteStr(info.output_zip, "tardis.dat", tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
פונקציה נפרדת מטפלת במקרה של יצירת חבילת OTA מצטברת. לדוגמה, נניח שצריך לתכנת מחדש את ה-tardis רק כשהקובץ tardis.dat השתנה בין שני גרסאות build.
def IncrementalOTA_InstallEnd(info): # copy the data into the package. source_tardis_dat = info.source_zip.read("RADIO/tardis.dat") target_tardis_dat = info.target_zip.read("RADIO/tardis.dat") if source_tardis_dat == target_tardis_dat: # tardis.dat is unchanged from previous build; no # need to reprogram it return # include the new tardis.dat in the OTA package common.ZipWriteStr(info.output_zip, "tardis.dat", target_tardis_dat) # emit the script code to install this data on the device info.script.AppendExtra( """tardis.reprogram("the-key", package_extract_file("tardis.dat"));""")
פונקציות של מודולים
אפשר לספק את הפונקציות הבאות במודול (מטמיעים רק את אלה שנחוצים).
FullOTA_Assertions()
- הקריאה מתבצעת לקראת תחילת היצירה של עדכון OTA מלא. זהו מקום טוב להנפיק טענות נכוֹנוּת (assertions) לגבי המצב הנוכחי של המכשיר. אסור להפעיל פקודות סקריפט שמבצעות שינויים במכשיר.
FullOTA_InstallBegin()
- הקריאה מתבצעת אחרי שכל טענות הנכוֹנוּת לגבי מצב המכשיר עוברות, אבל לפני שבוצעו שינויים. אפשר לשלוח פקודות לעדכונים ספציפיים למכשיר, שצריכים לפעול לפני שמשנים משהו אחר במכשיר.
FullOTA_InstallEnd()
- הפונקציה נקראת בסוף יצירת הסקריפט, אחרי שהפקודות של הסקריפט לעדכון המחיצות של האתחול והמערכת הופקו. אפשר גם לשלוח פקודות נוספות לעדכונים ספציפיים למכשיר.
IncrementalOTA_Assertions()
-
דומה ל-
FullOTA_Assertions()
, אבל נקראת כשיוצרים חבילת עדכון מצטבר. IncrementalOTA_VerifyBegin()
- הקריאה מתבצעת אחרי שכל טענות הנכוֹנוּת לגבי מצב המכשיר עוברות, אבל לפני שבוצעו שינויים. אפשר לשלוח פקודות לעדכונים ספציפיים למכשיר, שצריכים לפעול לפני ששינויים אחרים במכשיר יתבצעו.
IncrementalOTA_VerifyEnd()
- הפונקציה הזו נקראת בסוף שלב האימות, אחרי שהסקריפט סיים לאשר שהקבצים שהוא עומד לגעת בהם מכילים את התוכן ההתחלתי הצפוי. בשלב הזה לא בוצע שינוי כלשהו במכשיר. אפשר גם להפיק קוד לאימותים נוספים ספציפיים למכשיר.
IncrementalOTA_InstallBegin()
- הפונקציה נקראת אחרי שמוודאים שהקבצים שצריך לתקן נמצאים במצב before הצפוי, אבל לפני שמבצעים שינויים. אפשר לשלוח פקודות לעדכונים ספציפיים למכשיר, שצריכים לפעול לפני שמשנים משהו אחר במכשיר.
IncrementalOTA_InstallEnd()
- בדומה למקבילה שלו בחבילת OTA מלאה, הפונקציה הזו נקראת בסוף היצירה של הסקריפט, אחרי שהפקודות של הסקריפט לעדכון המחיצות של האתחול והמערכת הופקו. אפשר גם לשלוח פקודות נוספות לעדכונים ספציפיים למכשיר.
הערה: אם המכשיר ינותק מהחשמל, ייתכן שההתקנה דרך OTA תתחיל מחדש מההתחלה. חשוב להיות מוכנים למקרה שכבר הופעלו פקודות כאלה במכשירים, באופן מלא או חלקי.
העברת פונקציות לאובייקטים של מידע
העברת פונקציות לאובייקט מידע יחיד שמכיל פריטים מועילים שונים:
-
info.input_zip. (עדכוני OTA מלאים בלבד) האובייקט
zipfile.ZipFile
של קובץ ה-zip של יעדי הקלט. -
info.source_zip. (עדכוני OTA מצטברים בלבד) האובייקט
zipfile.ZipFile
של קובץ ה-zip של קובצי היעד של המקור (הגרסה המאוחדת שכבר נמצאת במכשיר בזמן התקנת החבילה המצטברת). -
info.target_zip. (עדכוני OTA מצטברים בלבד) האובייקט
zipfile.ZipFile
של קובץ ה-zip של קובצי היעד (הגרסה המאוחדת שהחבילה המצטברת מטמיעה במכשיר). -
info.output_zip. החבילה נוצרת. אובייקט
zipfile.ZipFile
נפתח לכתיבה. משתמשים ב-common.ZipWriteStr(info.output_zip, filename, data) כדי להוסיף קובץ לחבילה. -
info.script. אובייקט סקריפט שאפשר לצרף אליו פקודות. קוראים ל-
info.script.AppendExtra(script_text)
כדי להפיק טקסט בתסריט. חשוב לוודא שטקסט הפלט מסתיים בנקודתיים, כדי שלא יתנגש בפקודות שיופקו לאחר מכן.
פרטים על אובייקט המידע מופיעים במסמכי העזרה של Python Software Foundation לארכיונים מסוג ZIP.
ציון מיקום המודול
מציינים את המיקום של הסקריפט releasetools.py של המכשיר בקובץ BoardConfig.mk:
device/yoyodyne/tardis/BoardConfig.mk
[...] TARGET_RELEASETOOLS_EXTENSIONS := device/yoyodyne/tardis
אם לא מגדירים את TARGET_RELEASETOOLS_EXTENSIONS, ברירת המחדל היא הספרייה $(TARGET_DEVICE_DIR)/../common
(device/yoyodyne/common
בדוגמה הזו). מומלץ להגדיר במפורש את המיקום של הסקריפט releasetools.py.
כשמפתחים את מכשיר Tardis, הסקריפט releasetools.py נכלל בקובץ ה-zip של קובצי היעד (META/releasetools.py
).
כשמריצים את כלי ההשקה (img_from_target_files
או ota_from_target_files
), עדיף להשתמש בסקריפט releasetools.py בקובץ ה-zip של קובצי היעד, אם הוא קיים, במקום בסקריפט מ-Android source tree. אפשר גם לציין באופן מפורש את הנתיב לתוספים הספציפיים למכשיר באמצעות האפשרות -s
(או --device_specific
), שהיא בעלת העדיפות הגבוהה ביותר. כך תוכלו לתקן שגיאות ולבצע שינויים בתוספים של releasetools, ולהחיל את השינויים האלה על קובצי יעד ישנים.
עכשיו, כשמריצים את ota_from_target_files
, המערכת תזהה באופן אוטומטי את המודול הספציפי למכשיר מקובץ ה-zip target_files ותשתמש בו בזמן יצירת חבילות OTA:
./build/make/tools/releasetools/ota_from_target_files \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
לחלופין, אפשר לציין תוספים ספציפיים למכשיר כשמריצים את הפקודה ota_from_target_files
.
./build/make/tools/releasetools/ota_from_target_files \
-s device/yoyodyne/tardis \
-i PREVIOUS-tardis-target_files.zip \
dist_output/tardis-target_files.zip \
incremental_ota_update.zip
הערה: רשימת האפשרויות המלאה מופיעה בתגובות ota_from_target_files
בקטע build/make/tools/releasetools/ota_from_target_files
.
מנגנון להתקנה ממקור לא ידוע
בתהליך השחזור יש מנגנון העלאה צדדית להתקנה ידנית של חבילת עדכון, בלי להוריד אותה באוויר באמצעות המערכת הראשית. העלאה צדדית שימושית לניפוי באגים או לביצוע שינויים במכשירים שבהם אי אפשר להפעיל את המערכת הראשית.
בעבר, הטעינה מצד הלקוח בוצעה באמצעות טעינת חבילות מכרטיס ה-SD של המכשיר. במקרה של מכשיר שלא מופעל, אפשר להעביר את החבילה לכרטיס ה-SD באמצעות מחשב אחר, ואז להכניס את כרטיס ה-SD למכשיר. כדי להתאים למכשירי Android ללא אחסון חיצוני נשלף, התכונה 'שחזור' תומכת בשני מנגנונים נוספים להתקנה ממקור לא ידוע: טעינת חבילות מהמחיצה של המטמון וטעינתן דרך USB באמצעות adb.
כדי להפעיל כל מנגנון טעינה צדדית, שיטת Device::InvokeMenuItem()
של המכשיר יכולה להחזיר את הערכים הבאים של BuiltinAction:
-
APPLY_EXT. התקנה צדדית של חבילת עדכון מאחסון חיצוני (ספריית
/sdcard
). צריך להגדיר את נקודת הטעינה/sdcard
בקובץ recovery.fstab. אי אפשר להשתמש בזה במכשירים שמחקים כרטיס SD באמצעות קישור סימלי ל-/data
(או מנגנון דומה כלשהו). בדרך כלל לא ניתן לשחזר את/data
כי הוא עשוי להיות מוצפן. בממשק המשתמש של השחזור מוצג תפריט של קובצי zip ב-/sdcard
, ומשתמשים יכולים לבחור אחד מהם. -
APPLY_CACHE. בדומה לטעינה של חבילה מ-
/sdcard
, אלא שבמקום זאת נעשה שימוש בספרייה/cache
(שתמיד זמינה לשחזור). במערכת הרגילה, רק משתמשים עם הרשאות יכולים לכתוב ב-/cache
, ואם המכשיר לא ניתן לאתחול, אי אפשר לכתוב בכלל בספרייה/cache
(כך שהשימוש במנגנון הזה מוגבל). -
APPLY_ADB_SIDELOAD. מאפשרת למשתמש לשלוח חבילה למכשיר באמצעות כבל USB וכלי הפיתוח adb. כשהמנגנון הזה מופעל, תהליך השחזור מפעיל גרסה משלו של הדימון adbd כדי לאפשר ל-adb במחשב מארח מחובר לתקשר איתו. בגרסה המינימלית הזו יש תמיכה רק בפקודה אחת:
adb sideload filename
. הקובץ בעל השם נשלח מהמכונה המארחת למכשיר, שמאמת אותו ומתקין אותו כאילו היה באחסון המקומי.
כמה הערות:
- יש תמיכה רק בהעברה באמצעות USB.
-
אם תהליך adbd פועל כרגיל בתהליך השחזור (בדרך כלל זה נכון לגרסאות build של userdebug ו-eng), הוא ייכבה בזמן שהמכשיר נמצא במצב adb sideload, ויופעל מחדש כש-adb sideload יסיים לקבל חבילה. במצב sideload של adb, אף פקודת adb מלבד
sideload
לא פועלת (logcat
,reboot
,push
,pull
,shell
וכו' נכשלות). -
אי אפשר לצאת ממצב הטעינה הצדדית של adb במכשיר. כדי לבטל את ההתקנה, אפשר לשלוח את הערך
/dev/null
(או כל דבר אחר שאינו חבילה תקינה) בתור החבילה, ואז המכשיר לא יוכל לאמת אותה ויפסיק את תהליך ההתקנה. שיטתCheckKey()
של הטמעת RecoveryUI תמשיך להפעיל לחיצות על מקשים, כך שתוכלו לספק רצף מקשים שמפעיל מחדש את המכשיר ועובד במצב sideload של adb.