מערכת השחזור כוללת כמה נקודות חיבור להוספת קוד ספציפי למכשיר, כך שעדכונים דרך האוויר (OTA) יכולים לעדכן גם חלקים במכשיר שאינם מערכת Android (למשל, פס הבסיס או מעבד הרדיו).
בדוגמאות ובקטעים הבאים מוסבר איך להתאים אישית את מכשיר tardis שמיוצר על ידי הספק yoyodyne.
מפת מחיצות
החל מ-Android 2.3, הפלטפורמה תומכת במכשירי eMMc flash ובמערכת הקבצים ext4 שפועלת במכשירים האלה. הוא תומך גם בהתקני פלאש 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. device חייב להיות השם של מחיצת ה-MTD
והוא חייב להופיע ב-
/proc/mtd. - mtd
-
מחיצת MTD גולמית, שמשמשת למחיצות שאפשר להפעיל מהן, כמו boot ו-recovery. ה-MTD לא מותקן בפועל, אבל נקודת הטעינה משמשת כמפתח לאיתור המחיצה. device
חייב להיות השם של מחיצת ה-MTD ב-
/proc/mtd. - ext4
- מערכת קבצים ext4 על גבי מכשיר פלאש eMMC. 'device' חייב להיות הנתיב של מכשיר הבלוק.
- emmc
- מכשיר בלוקים eMMc גולמי, שמשמש למחיצות שאפשר להפעיל מהן, כמו boot ו-recovery. בדומה לסוג mtd, eMMc אף פעם לא נטען בפועל, אבל מחרוזת נקודת הטעינה משמשת לאיתור המכשיר בטבלה.
- vfat
-
מערכת קבצים מסוג FAT מעל התקן בלוקים, בדרך כלל לאחסון חיצוני כמו כרטיס SD. המכשיר הוא מכשיר הבלוקים; device2 הוא מכשיר בלוקים שני שהמערכת מנסה לטעון אם טעינת המכשיר הראשי נכשלת (לצורך תאימות לכרטיסי SD שאולי פורמטו עם טבלת מחיצות ואולי לא).
כל המחיצות צריכות להיות מותקנות בספריית הבסיס (כלומר, ערך נקודת ההרכבה חייב להתחיל בלוכסן ולא לכלול לוכסנים אחרים). ההגבלה הזו חלה רק על טעינה של מערכות קבצים בשחזור. המערכת הראשית יכולה לטעון אותן בכל מקום. הספריות
/boot,/recoveryו-/miscצריכות להיות מסוגים גולמיים (mtd או emmc), ואילו הספריות/system,/data,/cacheו-/sdcard(אם הן זמינות) צריכות להיות מסוגים של מערכת קבצים (yaffs2, ext4 או vfat).
החל מ-Android 3.0, הקובץ recovery.fstab כולל שדה אופציונלי נוסף, options. נכון לעכשיו, האפשרות היחידה שמוגדרת היא length , שמאפשרת לציין במפורש את אורך המחיצה. האורך הזה משמש כשמפרמטים מחדש את המחיצה (למשל, למחיצת נתוני המשתמש במהלך מחיקת נתונים או איפוס להגדרות המקוריות, או למחיצת המערכת במהלך התקנה של חבילת OTA מלאה). אם ערך האורך שלילי, הגודל לעיצוב נקבע על ידי הוספת ערך האורך לגודל האמיתי של המחיצה. לדוגמה, ההגדרה length=-16384 אומרת ש-16k האחרונים של המחיצה לא יימחקו כשמפרמטים מחדש את המחיצה. התמיכה הזו מאפשרת להשתמש בתכונות כמו הצפנה של מחיצת נתוני המשתמש (שבה מאוחסנים מטא-נתוני ההצפנה בסוף המחיצה, שאסור להחליף).
הערה: השדות device2 ו-options הם אופציונליים, ולכן יכולה להיווצר אי בהירות בניתוח. אם הערך בשדה הרביעי בשורה מתחיל בתו '/' , הוא נחשב לערך device2. אם הערך לא מתחיל בתו '/' , הוא נחשב לשדה options.
אנימציית האתחול
יצרני מכשירים יכולים להתאים אישית את האנימציה שמוצגת כשמכשיר Android מופעל. כדי לעשות זאת, יוצרים קובץ ZIP מאורגן וממוקם בהתאם למפרט שמופיע בקטע פורמט של אנימציית הפעלה.
במכשירי Android Things, אפשר להעלות את הקובץ הדחוס במסוף Android Things כדי שהתמונות ייכללו במוצר שנבחר.
הערה: התמונות האלה צריכות לעמוד בהנחיות המותג של Android. הנחיות בנושא מותג זמינות בקטע Android ב-Partner Marketing Hub.
ממשק משתמש לשחזור
כדי לתמוך במכשירים עם חומרה שונה (כפתורים פיזיים, נוריות LED, מסכים וכו'), אפשר להתאים אישית את ממשק השחזור כדי להציג את הסטטוס ולגשת לתכונות המוסתרות שמופעלות באופן ידני לכל מכשיר.
המטרה שלך היא ליצור ספרייה סטטית קטנה עם כמה אובייקטים של C++ כדי לספק את הפונקציונליות הספציפית למכשיר. קובץ bootable/recovery/default_device.cpp משמש כברירת מחדל, והוא נקודת התחלה טובה להעתקה כשכותבים גרסה של הקובץ הזה למכשיר.
הערה: יכול להיות שתופיע כאן ההודעה אין פקודה. כדי להחליף בין טקסט לדיבור, לוחצים לחיצה ארוכה על לחצן ההפעלה ולוחצים על הלחצן להגברת עוצמת הקול. אם במכשיר אין את שני הלחצנים, לוחצים לחיצה ארוכה על אחד מהלחצנים כדי להחליף את הטקסט.
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. הפעלה מחדש מיידית של המכשיר
- התעלמות. התעלמות מהקשה על המקש
- ENQUEUE. הוספת הלחיצה על המקש הזה לתור כדי שהיא תנוצל באופן סינכרוני (כלומר, על ידי מערכת תפריט השחזור אם התצוגה מופעלת)
CheckKey() מופעל בכל פעם שאירוע של לחיצה על מקש מלווה באירוע של הרמת האצבע מהמקש, עבור אותו מקש. (רצף האירועים 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() כדי להשלים את האתחול. ערך ברירת המחדל (20 FPS)
תואם לתמונות השחזור שמוגדרות כברירת מחדל. כשמשתמשים בתמונות האלה לא צריך לציין
פונקציה Init(). פרטים על תמונות זמינים במאמר בנושא תמונות של ממשק משתמש לשחזור.
סוג המכשיר
אחרי שמטמיעים את RecoveryUI, מגדירים את מחלקת המכשיר (subclassed מתוך מחלקת המכשיר המובנית). היא צריכה ליצור מופע יחיד של מחלקת ממשק המשתמש ולהחזיר אותו מהפונקציה GetUI():
class TardisDevice : public Device { private: TardisUI* ui; public: TardisDevice() : ui(new TardisUI) { } RecoveryUI* GetUI() { return ui; }
StartRecovery
ה-method 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. לא לבצע שום פעולה כשמקשים על המקש הזה
כפי שניתן להבין מהארגומנט הגלוי, הפונקציה 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
לאחר מכן, מציינים method 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
תמונות של ממשק המשתמש לשחזור
ממשק המשתמש של השחזור מורכב מתמונות. במצב אידיאלי, המשתמשים לא צריכים לבצע פעולות בממשק המשתמש: במהלך עדכון רגיל, הטלפון מפעיל את מצב השחזור, ממלא את סרגל התקדמות ההתקנה, ומפעיל מחדש את המערכת החדשה ללא קלט מהמשתמש. במקרה של בעיה בעדכון המערכת, הפעולה היחידה שהמשתמש יכול לבצע היא להתקשר למוקד שירות הלקוחות.
ממשק שמבוסס על תמונות בלבד מייתר את הצורך בלוקליזציה. עם זאת, החל מ-Android 5.0, העדכון יכול להציג מחרוזת טקסט (למשל, 'מתבצעת התקנה של עדכון מערכת...') לצד התמונה. פרטים נוספים זמינים במאמר בנושא טקסט שחזור מותאם לשפה.
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 ובגרסאות קודמות נעשה שימוש בתמונת השגיאה (שמוצגת למעלה) ובאנימציה של ההתקנה, בנוסף לכמה תמונות שכבת-על:
|
איור 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 מוצגת מחרוזת טקסט (למשל, 'מתבצעת התקנה של עדכון מערכת...') לצד התמונה. כשהמערכת הראשית מופעלת במצב שחזור, היא מעבירה את הלוקאל הנוכחי של המשתמש כאפשרות בשורת הפקודה לשחזור. לכל הודעה שמוצגת, השחזור כולל תמונה מורכבת שנייה עם מחרוזות טקסט שעברו רינדור מראש עבור ההודעה הזו בכל לוקאל.
תמונה לדוגמה של מחרוזות טקסט לשחזור:
איור 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. שמות הקבצים צריכים להיות זהים לאלה שמופיעים למעלה. אם נמצא קובץ בספרייה הזו, מערכת build משתמשת בו במקום בתמונת ברירת המחדל התואמת. המערכת תומכת רק בקובצי PNG בפורמט RGB או RGBA עם עומק צבע של 8 ביט.
הערה: ב-Android 5.x, אם הלוקאל ידוע למצב שחזור והוא שפה שנקראת מימין לשמאל (RTL) (ערבית, עברית וכו'), סרגל ההתקדמות מתמלא מימין לשמאל.
מכשירים ללא מסכים
לא לכל מכשירי Android יש מסכים. אם המכשיר שלכם הוא מכשיר ללא מסך או שיש לו ממשק אודיו בלבד, יכול להיות שתצטרכו לבצע התאמה אישית נרחבת יותר של ממשק המשתמש לשחזור. במקום ליצור מחלקת משנה של ScreenRecoveryUI, יוצרים מחלקת משנה של מחלקת האב שלה, RecoveryUI, ישירות.
ל-RecoveryUI יש שיטות לטיפול בפעולות בממשק משתמש ברמה נמוכה יותר, כמו 'החלפת התצוגה', 'עדכון סרגל ההתקדמות', 'הצגת התפריט', 'שינוי הבחירה בתפריט' וכו'. אתם יכולים לבטל את השיטות האלה כדי לספק ממשק מתאים למכשיר שלכם. יכול להיות שלמכשיר יש נוריות LED שבהן אפשר להשתמש בצבעים שונים או בדפוסי הבהוב כדי לציין מצב, או שאפשר להפעיל אודיו. (יכול להיות שלא תרצו לתמוך בתפריט או במצב 'הצגת טקסט' בכלל. תוכלו למנוע גישה אליהם באמצעות הטמעות של CheckKey() ושל HandleMenuKey() שלא מפעילות את התצוגה או בוחרות פריט בתפריט). במקרה כזה, הרבה מהשיטות של RecoveryUI שצריך לספק יכולות להיות פשוט stub ריק).
במאמר bootable/recovery/ui.h מפורטת ההצהרה של RecoveryUI, שכוללת את השיטות שחובה לתמוך בהן. RecoveryUI הוא מופשט – חלק מהשיטות הן וירטואליות טהורות וחייבות להיות מסופקות על ידי מחלקות משנה – אבל הוא מכיל את הקוד לעיבוד של קלט מפתח. אפשר גם לשנות את זה, אם למכשיר שלכם אין מקשים או אם אתם רוצים לעבד אותם בצורה שונה.
מעדכן
אתם יכולים להשתמש בקוד ספציפי למכשיר בהתקנה של חבילת העדכון על ידי מתן פונקציות הרחבה משלכם שאפשר להפעיל מתוך סקריפט העדכון. הנה דוגמה לפונקציה של מכשיר טארדיס:
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 באופן מיידי (הפעולה הזו
מעבירה את הביטולים במעלה מחסנית edify). אחרת, אתם מקבלים בעלות על הערך שמוחזר ואחראים בסופו של דבר לקרוא ל-FreeValue() לגביו.
נניח שהפונקציה צריכה שני ארגומנטים: מפתח עם ערך מחרוזת ו-תמונה עם ערך 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* מקבלת בעלות על כל הנתונים שאליהם מצביע Value* – במיוחד על datamember.
במקרה הזה, רוצים להחזיר ערך מסוג true או false כדי לציין הצלחה. חשוב לזכור את המוסכמה שלפיה מחרוזת ריקה היא false וכל שאר המחרוזות הן true. צריך להקצות זיכרון לאובייקט 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)
לבסוף, מגדירים את בניית השחזור כדי למשוך את הספרייה. מוסיפים את הספרייה אל TARGET_RECOVERY_UPDATER_LIBS (יכול להיות שהיא תכיל כמה ספריות, וכולן יירשמו).
אם הקוד שלכם תלוי בספריות סטטיות אחרות שלא מהוות בעצמן תוספי edify (כלומר,
אין להם פונקציה Register_libname), אפשר לפרט אותם ב-TARGET_RECOVERY_UPDATER_EXTRA_LIBS כדי לקשר אותם לכלי העדכון בלי להפעיל את פונקציית הרישום שלהם (שלא קיימת). לדוגמה, אם הקוד הספציפי למכשיר רוצה להשתמש ב-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.reprogram("the-key", package_extract_file("tardis-image.dat")) . הפונקציה משתמשת בגרסה עם ארגומנט יחיד של הפונקציה המובנית package_extract_file(), שמחזירה את התוכן של קובץ שחולץ מחבילת העדכון כ-blob, כדי ליצור את הארגומנט השני של פונקציית התוסף החדשה.
יצירת חבילת OTA
המרכיב האחרון הוא לוודא שכלי יצירת חבילות ה-OTA יודעים על הנתונים הספציפיים למכשיר שלכם, וליצור סקריפטים של עדכון שכוללים קריאות לפונקציות של התוסף.
קודם כל, צריך לגרום למערכת build לדעת על בלוב נתונים ספציפי למכשיר. בהנחה שקובץ הנתונים נמצא ב-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
הם נקראים קובצי רדיו מסיבות היסטוריות, אבל יכול להיות שאין להם קשר לרדיו של המכשיר (אם יש כזה). הם פשוט נתונים אטומים שהמערכת מעתיקה לקובץ ה-ZIP של קובצי היעד שמשמש את כלי יצירת ה-OTA. כשמבצעים בנייה, הקובץ 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 השתנה בין שני מבנים.
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 מלא. כאן אפשר להוציא הצהרות לגבי המצב הנוכחי של המכשיר. אסור להפעיל פקודות סקריפט שמבצעות שינויים במכשיר.
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 נכלל בקובץ target-files
.zip (META/releasetools.py ).
כשמריצים את כלי השחרור (img_from_target_files או ota_from_target_files), הסקריפט releasetools.py בקובץ ה-zip של קובצי היעד, אם הוא קיים, מקבל עדיפות על פני הסקריפט מעץ המקור של Android. אפשר גם לציין באופן מפורש את הנתיב לתוספים הספציפיים למכשיר באמצעות האפשרות -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.
מנגנון התקנה בשיטה חלופית
לשחזור יש מנגנון sideloading להתקנה ידנית של חבילת עדכון בלי להוריד אותה דרך האוויר על ידי המערכת הראשית. התקנה צדדית שימושית לניפוי באגים או לביצוע שינויים במכשירים שבהם אי אפשר להפעיל את המערכת הראשית.
בעבר, התקנה צדדית בוצעה באמצעות טעינת חבילות מכרטיס ה-SD של המכשיר. במקרה של מכשיר שלא ניתן לאתחל, אפשר להעביר את החבילה לכרטיס ה-SD באמצעות מחשב אחר, ואז להכניס את כרטיס ה-SD למכשיר. כדי להתאים למכשירי Android בלי אחסון חיצוני נשלף, השחזור תומך בשני מנגנונים נוספים להתקנה ממקור לא ידוע: טעינת חבילות ממחיצת המטמון וטעינתן דרך USB באמצעות adb.
כדי להפעיל כל מנגנון של העברה צדדית, השיטה Device::InvokeMenuItem() של המכשיר יכולה להחזיר את הערכים הבאים של BuiltinAction:
-
APPLY_EXT. התקנת חבילת עדכון מאחסון חיצוני (ספרייה
/sdcard). בקובץ recovery.fstab צריך להגדיר את נקודת הטעינה/sdcard. אי אפשר להשתמש בזה במכשירים שמבצעים אמולציה של כרטיס SD עם קישור סמלי ל-/data(או למנגנון דומה כלשהו). בדרך כלל אי אפשר לשחזר את/dataכי הוא עשוי להיות מוצפן. בממשק השחזור מוצג תפריט של קובצי .zip ב-/sdcardוהמשתמש יכול לבחור אחד מהם. -
APPLY_CACHE. בדומה לטעינת חבילה מ-
/sdcard, אבל במקום זאת נעשה שימוש בספרייה/cache(שתמיד זמינה לשחזור). במערכת הרגילה, רק למשתמשים עם הרשאות יש אפשרות לכתוב ב-/cache, ואם אי אפשר לאתחל את המכשיר, אי אפשר לכתוב בספרייה/cacheבכלל (מה שהופך את המנגנון הזה למוגבל). -
APPLY_ADB_SIDELOAD. מאפשר למשתמש לשלוח חבילה למכשיר באמצעות כבל USB וכלי הפיתוח adb. כשמפעילים את המנגנון הזה, השחזור מתחיל להפעיל גרסה מיניאטורית משלו של שד ה-adbd כדי לאפשר ל-adb במחשב מארח מחובר לתקשר איתו. בגרסה המינימלית הזו יש תמיכה רק בפקודה אחת:
adb sideload filename. הקובץ עם השם נשלח מהמחשב המארח למכשיר, ואז המכשיר מאמת אותו ומתקין אותו בדיוק כמו שהוא היה מאוחסן באחסון המקומי.
כמה הערות:
- יש תמיכה רק בהעברה באמצעות USB.
-
אם השחזור מפעיל את adbd כרגיל (בדרך כלל נכון לגבי גרסאות userdebug ו-eng), הוא יושבת בזמן שהמכשיר נמצא במצב adb sideload ויופעל מחדש כש-adb sideload יסיים לקבל חבילה. בזמן שמכשיר נמצא במצב העברה צדדית של adb, אף פקודת adb לא פועלת (
sideloadהיא הפקודה היחידה שפועלת, ופקודות כמוlogcat,reboot, push, pull, shellוכו' נכשלות). -
אי אפשר לצאת ממצב adb sideload במכשיר. כדי לבטל את ההתקנה, אפשר לשלוח את
/dev/null(או כל דבר אחר שלא נחשב לחבילה תקינה) בתור החבילה, ואז המכשיר לא יצליח לאמת אותה ויפסיק את תהליך ההתקנה. השיטהCheckKey()של הטמעת RecoveryUI תמשיך להיקרא ללחיצות על מקשים, כך שתוכלו לספק רצף מקשים שמפעיל מחדש את המכשיר ופועל במצב adb sideload.