קוד ספציפי למכשיר

מערכת השחזור כוללת כמה נקודות חיבור להוספת קוד ספציפי למכשיר, כך שעדכונים דרך האוויר (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 משמש כברירת מחדל, והוא נקודת התחלה טובה להעתקה כשכותבים גרסה של הקובץ הזה למכשיר.

הערה: יכול להיות שתופיע כאן ההודעה 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 (הפעלה מחדש). הפעלה מחדש מיידית של המכשיר
  • התעלמות. התעלמות מהקשה על המקש
  • 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

השיטה 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

לאחר מכן, צריך לספק שיטת 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 enum כדי להורות למערכת לבצע את הפעולה הזו (או את החבר 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 ואילך יש שני תמונות עיקריות: תמונת השגיאה ואנימציית ההתקנה.

תמונה שמוצגת במהלך שגיאת OTA

איור 1. icon_error.png

תמונה שמוצגת במהלך התקנת OTA

איור 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 ובגרסאות קודמות נעשה שימוש בתמונת השגיאה (שמוצגת למעלה) ובאנימציה של ההתקנה, בנוסף לכמה תמונות שכבת-על:

תמונה שמוצגת במהלך התקנת OTA

איור 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

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

סרגל התקדמות ב-1%

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

סרגל התקדמות ב-10%

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

סרגל התקדמות ב-50%

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

כדי לספק גרסאות ספציפיות למכשיר של התמונות האלה, צריך למקם אותן בתיקייה (בדוגמה הזו) device/yoyodyne/tardis/recovery/res/images. שמות הקבצים חייבים להיות זהים לשמות שצוינו למעלה. כשמערכת הבנייה מוצאת קובץ בספרייה הזו, היא משתמשת בו במקום בתמונת ברירת המחדל התואמת. המערכת תומכת רק בקובצי PNG בפורמט RGB או RGBA עם עומק צבע של 8 ביט.

הערה: ב-Android 5.x, אם הלוקאל ידוע למצב השחזור והוא שפה שנכתבת מימין לשמאל (RTL) (ערבית, עברית וכו'), סרגל ההתקדמות מתמלא מימין לשמאל.

מכשירים ללא מסכים

לא לכל מכשירי Android יש מסכים. אם המכשיר שלכם הוא מכשיר ללא ראש או שיש לו ממשק אודיו בלבד, יכול להיות שתצטרכו לבצע התאמה אישית נרחבת יותר של ממשק המשתמש לשחזור. במקום ליצור מחלקת משנה של ScreenRecoveryUI, יוצרים מחלקת משנה של מחלקת האב שלה, RecoveryUI, ישירות.

ל-RecoveryUI יש שיטות לטיפול בפעולות בממשק משתמש ברמה נמוכה יותר, כמו 'החלפת התצוגה', 'עדכון סרגל ההתקדמות', 'הצגת התפריט', 'שינוי הבחירה בתפריט' וכו'. אתם יכולים לבטל את השיטות האלה כדי לספק ממשק מתאים למכשיר שלכם. יכול להיות שלמכשיר יש נוריות LED שבהן אפשר להשתמש בצבעים שונים או בדפוסי הבהוב כדי לציין את המצב, או שאפשר להפעיל אודיו. (יכול להיות שלא תרצו לתמוך בתפריט או במצב 'הצגת טקסט' בכלל. תוכלו למנוע גישה אליהם באמצעות הטמעות של CheckKey() ושל HandleMenuKey() שלא יפעילו את התצוגה או יבחרו פריט בתפריט). במקרה כזה, הרבה מהשיטות של RecoveryUI שצריך לספק יכולות להיות פשוט stub ריק.)

אפשר לעיין בהצהרה של RecoveryUI בכתובת bootable/recovery/ui.h כדי לראות אילו שיטות אתם צריכים לתמוך בהן. ‫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 עם עותק של מחרוזת הקבועה שהוקצה לה זיכרון באמצעות 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 כדי לקשר אותם לכלי העדכון בלי להפעיל את פונקציית הרישום שלהם (שלא קיימת). לדוגמה, אם הקוד הספציפי למכשיר רוצה להשתמש ב-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 כך שתכיר blob של נתונים שספציפי למכשיר. בהנחה שקובץ הנתונים נמצא ב-device/yoyodyne/tardis/tardis.dat, צריך להצהיר על הדברים הבאים ב-AndroidBoard.mk של המכשיר:

device/yoyodyne/tardis/AndroidBoard.mk
  [...]

$(call add-radio-file,tardis.dat)

אפשר גם להוסיף אותו לקובץ Android.mk, אבל אז צריך להוסיף בדיקה של המכשיר, כי כל קובצי Android.mk ב-tree נטענים בלי קשר למכשיר שנבנה. (אם העץ כולל כמה מכשירים, אתם רוצים להוסיף את הקובץ 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. כשמבצעים 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 מלא. כאן אפשר להוציא הצהרות לגבי המצב הנוכחי של המכשיר. אל תשלחו פקודות סקריפט שמבצעות שינויים במכשיר.
FullOTA_InstallBegin()
הפונקציה נקראת אחרי שכל הטענות לגבי מצב המכשיר עברו, אבל לפני שבוצעו שינויים כלשהם. אתם יכולים להפעיל פקודות לעדכונים ספציפיים למכשיר שצריכים לפעול לפני שמשנים משהו אחר במכשיר.
FullOTA_InstallEnd()
מופעל בסוף יצירת הסקריפט, אחרי שפקודות הסקריפט לעדכון מחיצות האתחול והמערכת הופקו. אפשר גם להפעיל פקודות נוספות כדי לבצע עדכונים ספציפיים למכשיר.
IncrementalOTA_Assertions()
דומה ל-FullOTA_Assertions() אבל מופעל כשיוצרים חבילת עדכון מצטבר.
IncrementalOTA_VerifyBegin()
הפונקציה נקראת אחרי שכל הטענות לגבי מצב המכשיר עברו בהצלחה, אבל לפני שבוצעו שינויים כלשהם. אפשר להפעיל פקודות לעדכונים ספציפיים למכשיר שצריכים לפעול לפני שמשנים משהו אחר במכשיר.
IncrementalOTA_VerifyEnd()
הפונקציה נקראת בסוף שלב האימות, אחרי שהסקריפט סיים לאשר שהקובצים שהוא עומד לגעת בהם מכילים את תוכן ההתחלה הצפוי. בשלב הזה לא בוצעו שינויים במכשיר. אפשר גם להנפיק קוד לאימותים נוספים שספציפיים למכשיר.
IncrementalOTA_InstallBegin()
Called after files to be patched have been verified as having the expected before state but before any changes have been made. אפשר להנפיק פקודות לעדכונים ספציפיים למכשיר שצריכים לפעול לפני שמשנים משהו אחר במכשיר.
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.

מנגנון התקנה בשיטה חלופית

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

בעבר, התקנה צדדית בוצעה באמצעות טעינת חבילות מכרטיס ה-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 sideload פעיל, אי אפשר להשתמש בפקודות adb אחרות מלבד sideload ( הפקודות logcat,‏ reboot,‏ push,‏ pull,‏ shell וכו' לא פועלות).
  • אי אפשר לצאת ממצב adb sideload במכשיר. כדי לבטל את ההתקנה, אפשר לשלוח /dev/null (או כל דבר אחר שלא נחשב לחבילה תקינה) בתור החבילה. המכשיר לא יצליח לאמת אותה ויפסיק את תהליך ההתקנה. הקריאה לשיטה CheckKey() של ההטמעה RecoveryUI תימשך עבור הקשות על מקשים, כך שתוכלו לספק רצף מקשים שמפעיל מחדש את המכשיר ופועל במצב adb sideload.