פורמט הקובץ APEX

פורמט הקונטיינר Android Pony EXpress (APEX) הושק ב-Android 10 והיא משמשת בתהליך ההתקנה של מערכת ברמה נמוכה יותר מודולים. הפורמט הזה מאפשר עדכונים של רכיבי מערכת שלא מתאימים למודל הסטנדרטי של Android. חלק מהרכיבים לדוגמה הם מקוריים שירותים וספריות, שכבות הפשטה של חומרה (HALs), סביבת זמן ריצה (ART) וספריות של כיתות.

המונח APEX יכול גם להפנות לקובץ APEX.

רקע

מערכת Android תומכת בעדכונים של מודולים שתואמים לאפליקציה הרגילה (לדוגמה, שירותים, פעילויות) באמצעות אפליקציות של מנהל התקנה (כמו באפליקציה של חנות Google Play), באמצעות מודל דומה לרכיבי מערכת הפעלה ברמה נמוכה יותר. יש את החסרונות הבאים:

  • לא ניתן להשתמש במודולים המבוססים על APK בשלב מוקדם של רצף ההפעלה. החבילה הוא המאגר המרכזי של מידע על אפליקציות, והוא יכול לשמש רק התחיל במנהל הפעילות, שמוכן בשלב מאוחר יותר של את תהליך האתחול.
  • פורמט ה-APK (במיוחד המניפסט) מיועד לאפליקציות ל-Android. המודולים של המערכת לא תמיד מתאימים.

עיצוב

בקטע הזה מתואר העיצוב הכללי של פורמט הקובץ APEX APEX Manager, שהוא שירות שמנהל קובצי APEX.

למידע נוסף על הסיבה לבחירת העיצוב הזה עבור APEX, ראו חלופות אפשריות במהלך הפיתוח של APEX.

פורמט APEX

זהו הפורמט של קובץ APEX.

פורמט הקובץ APEX

איור 1. פורמט הקובץ APEX

ברמה העליונה, קובץ APEX הוא קובץ ZIP שבו מאוחסנים הקבצים לא דחוס ובגבולות של 4 KB.

ארבעת הקבצים בקובץ APEX הם:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

הקובץ apex_manifest.json מכיל את שם החבילה והגרסה שלו, לזהות קובץ APEX. זהו ApexManifest מאגר נתונים זמני של פרוטוקול בפורמט JSON.

הקובץ AndroidManifest.xml מאפשר לקובץ ה-APEX להשתמש בכלים שקשורים ל-APK כגון ADB, PackageManager ואפליקציות של מנהל ההתקנה של חבילות (כמו חנות Play). לדוגמה, קובץ ה-APEX יכול להשתמש בכלי קיים כמו aapt כדי לבדוק מטא-נתונים בסיסיים מהקובץ. הקובץ מכיל את שם החבילה ואת פרטי הגרסה. המידע הזה זמין בדרך כלל גם apex_manifest.json

מומלץ להשתמש ב-apex_manifest.json מעל AndroidManifest.xml לקוד חדש וגם במערכות שעוסקות ב-APEX. ייתכן שהשדה AndroidManifest.xml מכיל עוד מידע לטירגוט שבו יכולים להשתמש הכלים הקיימים לפרסום אפליקציות.

apex_payload.img היא תמונה של מערכת קבצים מסוג ext4 שמגובה על ידי dm-verity. התמונה טעונה בזמן הריצה באמצעות מכשיר לולאה חוזרת. וספציפית, עץ הגיבוב (hash) בלוק המטא-נתונים נוצר באמצעות הספרייה libavb. המטען הייעודי (Payload) של מערכת הקבצים לא מנותח (כי התמונה צריכה להיות ניתנת לטעינה במקום זאת). הקבצים הרגילים הם נכללות בקובץ apex_payload.img.

apex_pubkey הוא המפתח הציבורי שמשמש לחתימה על תמונת מערכת הקבצים. בזמן הריצה, המפתח הזה מבטיח שה-APEX שהורדתם ייחתם עם אותה ישות שחותם על אותו APEX במחיצות המובנות.

הנחיות למתן שמות ל-APEX

כדי למנוע התנגשויות בין שמות של APEX חדשים ככל שהפלטפורמה מתקדמת, יש לפעול לפי ההנחיות הבאות למתן שמות:

  • com.android.*
    • שמור ל-AOSP APEX. לא ייחודי לשום חברה או מכשיר.
  • com.<companyname>.*
    • שמור לחברה. עשוי להיות בשימוש במספר מכשירים מתוך האפליקציה בחברה.
  • com.<companyname>.<devicename>.*
    • שמור ל-APEX ייחודיים למכשיר ספציפי (או לקבוצת משנה של מכשירים).

מנהל APEX

מנהל APEX (או apexd) הוא תהליך נייטיב עצמאי שאחראי על אימות, התקנה והסרה של קובצי APEX. התהליך הזה הושק מוכן בשלב מוקדם ברצף האתחול. בדרך כלל קובצי APEX מותקנים מראש במכשיר מתחת ל-/system/apex. כברירת מחדל, מנהל APEX משתמש אם אין עדכונים זמינים.

רצף העדכונים של APEX משתמש מחלקה PackageManager והוא מופיע בהמשך.

  1. מתבצעת הורדה של קובץ APEX דרך אפליקציה של מנהל התקנה של חבילה, ADB או מקור.
  2. מנהל החבילות מתחיל את תהליך ההתקנה. כשאנחנו מכירים הקובץ הוא APEX, מנהל החבילות מעביר את השליטה ל-APEX מנהל.
  3. מנהל APEX מאמת את קובץ ה-APEX.
  4. אם קובץ APEX מאומת, מסד הנתונים הפנימי של מנהל APEX מעודכן כדי לשקף שקובץ APEX מופעל בהפעלה הבאה.
  5. מבקש ההתקנה מקבל שידור לאחר שהחבילה הצליחה אימות.
  6. כדי להמשיך בהתקנה, צריך להפעיל מחדש את המערכת.
  7. באתחול הבא, מנהל APEX מתחיל, קורא את מסד הנתונים הפנימי ו את הפרטים הבאים לגבי כל קובץ APEX

    1. מאמתת את קובץ APEX.
    2. יצירה של מכשיר לולאה חוזרת מקובץ ה-APEX.
    3. יצירת מכשיר בלוק של מיפוי מכשיר על גבי המכשיר הלולאה החוזרת.
    4. טוענת את התקן הבלוק של מיפוי המכשיר לנתיב ייחודי (לדוגמה, /apex/name@ver).

כשטוענים את כל קובצי ה-APEX שרשומים במסד הנתונים הפנימי, קובץ ה-APEX מספק שירות binder לרכיבי מערכת אחרים כדי לשלוח שאילתות מידע על קובצי ה-APEX המותקנים. לדוגמה, המערכת השנייה רכיבים יכולים לשלוח שאילתה על רשימת קובצי APEX המותקנים במכשיר או לשלוח שאילתה הנתיב המדויק שבו נטען APEX ספציפי, כדי שאפשר יהיה לגשת לקבצים.

קובצי APEX הם קובצי APK

קובצי APEX הם קובצי APK תקינים כי הם חתומים בארכיוני ZIP (באמצעות סכמת חתימת APK) שמכילה קובץ AndroidManifest.xml. כך אפשר להשתמש ב-APEX לשימוש בתשתית של קובצי APK, כמו אפליקציה של מנהל התקנה של חבילה, כלי החתימה, ומנהל החבילות.

הקובץ AndroidManifest.xml שנמצא בקובץ APEX הוא מינימלי, וכולל חבילה name, versionCode ו-targetSdkVersion, minSdkVersion אופציונלי, ו-maxSdkVersion לטירגוט מדויק יותר. המידע הזה מאפשר ל-APEX קבצים שיישלחו דרך ערוצים קיימים, כמו אפליקציות של מנהל ההתקנה של חבילות ADB.

סוגי הקבצים הנתמכים

פורמט APEX תומך בסוגי הקבצים הבאים:

  • libs משותפים מותאמים
  • קובצי הפעלה נייטיב
  • קובצי JAR
  • קובצי נתונים
  • קובצי תצורה

זה לא אומר ש-APEX יכול לעדכן את כל סוגי הקבצים האלה. האם הקובץ תלוי בפלטפורמה ובמידת היציבות של ההגדרות הממשקים של סוגי הקבצים.

אפשרויות חתימה

קובצי APEX חתומים בשתי דרכים. קודם כל, apex_payload.img (באופן ספציפי, קובץ ה-vbmeta שמצורף לקובץ apex_payload.img) חתום באמצעות מפתח. לאחר מכן, כל ה-APEX נחתם באמצעות סכמת חתימת APK גרסה 3 נעשה שימוש בשני מפתחות שונים בתהליך הזה.

בצד המכשיר, מפתח ציבורי שתואם למפתח הפרטי שמשמש לחתימה מתאר vbmeta מותקן. מנהל APEX משתמש במפתח הציבורי כדי לאמת APEX שהתבקשו להתקין. יש לחתום על כל APEX באמצעות מפתחות שונים, ונאכפים גם בזמן ה-build וגם בזמן הריצה.

APEX במחיצות מובנות

קובצי APEX יכולים להיות ממוקמים במחיצות מובנות כמו /system. המחיצה כבר מעל dm-verity, כך שקובצי ה-APEX נטענים ישירות במכשיר הלולאה החוזרת.

אם קיים APEX במחיצה מובנית, אפשר לעדכן את ה-APEX על ידי לספק חבילת APEX עם אותו שם חבילה וערך גדול מ- או שווה ל- לקוד גרסה. קובץ APEX החדש מאוחסן ב-/data, ובדומה ל-APK, הגרסה החדשה שהותקנה מאכלסת את הגרסה שכבר קיימת מחיצה. אבל שלא כמו ב-APKs, הגרסה החדשה שהותקנה של APEX הופעל לאחר האתחול.

דרישות ליבה (kernel)

כדי לתמוך במודולים של השורה הראשית של APEX במכשיר Android, בגרסאות הבאות של Linux נדרשות תכונות ליבה: מנהל התקן הלולאה החוזרת ו-dm-verity. הלולאה החוזרת מנהל התקן טוען את התמונה של מערכת הקבצים במודול APEX ו-dm-verity מאמת מודול APEX.

הביצועים של מנהל הלולאה החוזרת וה-dm-verity חשובים להשגת ביצועי מערכת טובים בעת השימוש במודולים של APEX.

גרסאות ליבה נתמכות

במודולים הראשיים של APEX יש תמיכה במכשירים עם גרסאות ליבה (kernel) 4.4 או גבוהה יותר. מכשירים חדשים שמושקים עם Android מגרסה 10 ואילך חייבים להשתמש בליבה (kernel) 4.9 ואילך כדי לתמוך במודולים של APEX.

תיקונים נדרשים בליבה (kernel)

תיקוני הליבה (kernel) הנדרשים לתמיכה במודולים של APEX נכללים עץ נפוץ של Android. כדי לקבל את התיקונים לתמיכה ב-APEX, צריך להשתמש בגרסה האחרונה של העץ המשותף של Android.

גרסת ליבה 4.4

הגרסה הזו נתמכת רק במכשירים ששודרגו מ-Android 9 Android 10 ורוצים לתמוך במודולים של APEX. כדי לקבל את תיקונים נדרשים, מיזוג כלפי מטה מההסתעפות android-4.4 מומלץ. בהמשך מופיעה רשימה של התיקונים הנפרדים הנדרשים לגרסת הליבה 4.4.

  • UPSTREAM: לולאה: הוספת ioctl לשינוי גודל בלוק לוגי (4.4)
  • BACKPORT: בלוק/לולאה: הגדר hw_sectors (4.4)
  • UPSTREAM: לולאה: הוספת LOOP_SET_BLOCK_SIZE ב-ioctl תואם (4.4)
  • Android: mnt: תיקון next_descendent (4.4)
  • Android: mnt: טעינה מחדש צריכה להיעשות לעבדים של עבדים (4.4)
  • ANDROID: mnt: הפצת מחדש תקינה (4.4)
  • חזרה לגרסה הקודמת של 'ANDROID: dm verity: הוספת גודל מינימלי לשליפה מראש' (4.4)
  • UPSTREAM: לולאה: שחרור מטמון אם משנים את קיזוז או block_size (4.4)

גרסאות ליבה 4.9/4.14/4.19

כדי לקבל את התיקונים הנדרשים לגרסאות ליבה 4.9/4.14/4.19, יש להקטין את המיזוג מ- ההסתעפות android-common.

האפשרויות הנדרשות להגדרת ליבה (kernel)

הרשימה הבאה מציגה את דרישות ההגדרה הבסיסית לתמיכה מודולים של APEX שהושקו ב-Android 10. הפריטים המסומנים בכוכבית (*) הן דרישות קיימות מ-Android מגרסה 9 ומטה.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

דרישות לגבי פרמטרים של שורת הפקודה בליבה (Kernel)

כדי לתמוך ב-APEX, צריך לוודא שהפרמטרים של שורת הפקודה בליבה (kernel) עומדים בדרישות הבאות דרישות:

  • אין להגדיר את loop.max_loop
  • הערך של loop.max_part חייב להיות 8<=

בניית APEX

בקטע הזה מוסבר איך לבנות APEX באמצעות מערכת ה-build של Android. הדוגמה הבאה היא של Android.bp ל-APEX בשם apex.test.

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

דוגמה ל-apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

דוגמה ל-file_contexts:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

סוגי קבצים ומיקומים ב-APEX

סוג הקובץ מיקום ב-APEX
ספריות משותפות /lib וגם /lib64 (/lib/arm עבור זרוע מתורגמת ב-x86)
קובצי הפעלה /bin
ספריות Java /javalib
מוכנים מראש /etc

יחסי תלות עוברים

קובצי APEX כוללים אוטומטית יחסי תלות טרנזקטיביים של Libs משותפים מקוריים או קובצי הפעלה. לדוגמה, אם libFoo תלוי ב-libBar, שני ה-libs נכללים רק כאשר libFoo רשום בנכס native_shared_libs.

טיפול בכמה ממשקי ABI

התקנת הנכס native_shared_libs גם לחשבון הראשי וגם לחשבון המשני וממשקים בינאריים של אפליקציות (ABI) של המכשיר. אם ספק APEX מטרגט מכשירים עם ממשק ABI יחיד (כלומר, 32 ביט בלבד או 64 ביט בלבד), רק ספריות עם של ממשק ה-ABI המתאים.

התקנת הנכס binaries רק עבור ה-ABI הראשי של המכשיר בהמשך:

  • אם המכשיר הוא בגרסת 32 ביט בלבד, רק הווריאנט הבינארי של 32 ביט הוא מותקנת.
  • אם המכשיר הוא בגרסת 64 ביט בלבד, רק הווריאנט של 64 ביט של הקוד הבינארי מותקנת.

כדי להוסיף שליטה פרטנית בממשקי ה-ABI של הספריות והקבצים הבינאריים המקוריים, להשתמש multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] נכסים.

  • first: תואם לממשק ה-ABI הראשי של המכשיר. זוהי ברירת המחדל עבור בינאריים.
  • lib32: תואם ל-ABI של 32 ביט של המכשיר, אם הוא נתמך.
  • lib64: תואם ל-ABI של המכשיר בגרסת 64 ביט, שהוא נתמך.
  • prefer32: תואם ל-ABI של 32 ביט של המכשיר, אם נתמך. אם אין תמיכה ב-ABI של 32 ביט, והוא תואם ל-ABI של 64 ביט.
  • both: תואם לשני ממשקי ה-ABI. זוהי ברירת המחדל עבור native_shared_libraries

הנכסים java, libraries ו-prebuilts עוברים ניתוח ABI.

הדוגמה הזו מיועדת למכשיר שתומך ב-32/64 ולא בהעדפה 32:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

חתימת vbmeta

יש לחתום על כל APEX באמצעות מפתחות שונים. כשצריך מפתח חדש, יוצרים זוג מפתחות ציבורי-פרטי ויוצרים מודול apex_key. אפשר להשתמש בנכס key כדי לחתום על APEX באמצעות המפתח. המפתח הציבורי נכלל באופן אוטומטי APEX בשם avb_pubkey

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

בדוגמה שלמעלה, השם של המפתח הציבורי (foo) הופך למזהה של מקש. המזהה של המפתח שמשמש לחתימה על APEX כתוב ב-APEX. בזמן הריצה, המערכת של apexd מאמתת את APEX באמצעות מפתח ציבורי עם אותו מזהה במכשיר.

חתימת APEX

לחתום על APEX באותה דרך שחותמים על חבילות APK. לחתום פעמיים על APEX; פעם אחת בשביל מיני מערכת קבצים (קובץ אחד (apex_payload.img)) ופעם אחת לכל הקובץ.

כדי לחתום על APEX ברמת הקובץ, צריך להגדיר את המאפיין certificate באחד שלוש הדרכים הבאות:

  • לא מוגדר: אם לא הוגדר ערך, ה-APEX נחתם עם האישור שממוקם PRODUCT_DEFAULT_DEV_CERTIFICATE. אם לא הוגדר דגל, ברירת המחדל של הנתיב היא אל build/target/product/security/testkey.
  • <name>: ה-APEX חתום באמצעות האישור <name> באותו אותו לספרייה בתור PRODUCT_DEFAULT_DEV_CERTIFICATE.
  • :<name>: ה-APEX חתום באמצעות האישור שמוגדר על ידי מודול סונג בשם <name>. אפשר להגדיר את מודול האישור כך: עוקבים.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

התקנת APEX

כדי להתקין APEX, צריך להשתמש ב-ADB.

adb install apex_file_name
adb reboot

אם הערך של supportsRebootlessUpdate הוא true ב-apex_manifest.json וגם APEX המותקן כרגע אינו בשימוש (למשל, כל שירות שהוא מכיל הופסקה), ניתן להתקין APEX חדש ללא הפעלה מחדש עם סימון --force-non-staged.

adb install --force-non-staged apex_file_name

שימוש ב-APEX

לאחר ההפעלה מחדש, ה-APEX נטען ב-/apex/<apex_name>@<version> אפשר לטעון כמה גרסאות של אותו APEX בו-זמנית. מבין נתיבי הטעינה, הנתיב שתואם לגרסה האחרונה הוא מוטמע ב-/apex/<apex_name>.

לקוחות יכולים להשתמש בנתיב המחובר לקישור כדי לקרוא או להפעיל קבצים מ-APEX.

בדרך כלל משתמשים ב-APEX כך:

  1. OEM (יצרן ציוד מקורי) או ODM טוען מראש APEX מתחת ל-/system/apex כשהמכשיר נשלח.
  2. הגישה לקבצים ב-APEX מתבצעת דרך הנתיב /apex/<apex_name>/.
  3. כשמתקינים גרסה מעודכנת של APEX ב-/data/apex, הנתיב מצביע ל-APEX החדש לאחר האתחול.

עדכון שירות עם APEX

כדי לעדכן שירות באמצעות APEX:

  1. סימון השירות במחיצת המערכת כניתן לעדכון. הוספת האפשרות updatable להגדרת השירות.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. יוצרים קובץ .rc חדש לשירות המעודכן. שימוש באפשרות override כדי להגדיר מחדש את השירות הקיים.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

אפשר להגדיר את הגדרות השירות רק בקובץ .rc של APEX. Action (פעולה) טריגרים לא נתמכים ב-APEX.

אם שירות שסומן כניתן לעדכון מתחיל לפני הפעלת ה-APEX, ההפעלה מתעכבת עד להשלמת ההפעלה של APEX.

הגדרת המערכת לתמיכה בעדכוני APEX

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

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

או פשוט

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

APEX שטוח

במכשירים מדור קודם, לפעמים לא ניתן או לא ניתן לעדכן את הגרסה הקודמת ליבה (kernel) לתמיכה מלאה ב-APEX. לדוגמה, יכול להיות שהליבה נוצרה, בלי CONFIG_BLK_DEV_LOOP=Y, שהוא חיוני לטעינה של מערכת הקבצים תמונה בתוך APEX.

APEX שטוחה הוא APEX שנוצר במיוחד וניתן להפעיל אותו במכשירים עם ליבה (kernel) מדור קודם. קבצים ב-APEX שטוח מותקנים ישירות בספרייה במחיצה המובנית. לדוגמה, lib/libFoo.so בקובץ APEX שטוח my.apex מותקנת אצל /system/apex/my.apex/lib/libFoo.so.

הפעלה של APEX שטוח לא מעורבת במכשיר הלולאה. כל הספרייה /system/apex/my.apex מחוברת ישירות אל /apex/name@ver.

לא ניתן לעדכן גרסאות APEX שטוחות על ידי הורדת גרסאות מעודכנות של APEX מהרשת כי לא ניתן ליישר את ה-APEX שהורדו. אפשר לעדכן אישורי APEX שטוחים רק באמצעות OTA רגיל.

APEX שטוחה היא תצורת ברירת המחדל. המשמעות היא שכל כברירת מחדל, מזהי APEX מוחלטים, אלא אם מגדירים באופן מפורש את המכשיר כדי לבנות APEX לא שטוחים כדי לתמוך בעדכוני APEX (כפי שהוסבר למעלה).

לא ניתן לשלב במכשיר APEX שטוח ולא שטוח נתמך. נקודות APEX במכשיר צריכות להיות לא שטוחות או ללא שטוחה. הדבר חשוב במיוחד כאשר משלוח APEX חתום מראש נוצר מראש עבור כמו Mainline. APEX שאינם חתומים מראש (כלומר, נוצרו המקור) צריך להיות ללא שטוח ולחתום באמצעות מפתחות מתאימים. המכשיר צריך לקבל בירושה מupdatable_apex.mk, כמו שמוסבר עדכון שירות עם APEX.

APEX דחוס

ב-Android 12 ואילך יש דחיסת APEX עבור צמצום ההשפעה על האחסון של חבילות APEX שניתן לעדכן. לאחר עדכון של APEX מותקנת, למרות שהגרסה שהותקנה מראש כבר לא בשימוש, היא עדיין תופסת כמות זהה של מקום. המרחב המשותף הזה עדיין לא זמין.

דחיסת APEX ממזערת את השפעת האחסון הזו על ידי שימוש בקבוצה דחוסה מאוד של קובצי APEX במחיצות לקריאה בלבד (כמו המחיצה /system). במכשירי Android מגרסה 12 ואילך משתמשים באלגוריתם DEFLATE לדחיסת ZIP.

דחיסה לא מספקת אופטימיזציה לפריטים הבאים:

  • רצועות אתחול APEX שנדרשות לטעינה בשלב מוקדם מאוד באתחול ברצף.

  • קובצי APEX שלא ניתנים לעדכון. דחיסה מועילה רק אם מותקנת גרסה מעודכנת של APEX במחיצה /data. רשימה מלאה של שרתי APEX שניתנים לעדכון זמינה רכיבים למערכות מודולריות הדף הזה.

  • libs משותפים דינמיים APEX. מכיוון ש-apexd תמיד מפעיל את שתי הגרסאות של כאלה של APEX (שהותקנו מראש ושודרגו), דחיסת הנתונים שלהם לא מוסיפה ערך.

פורמט קובץ APEX דחוס

זהו הפורמט של קובץ APEX דחוס.

בתרשים מוצג הפורמט של קובץ APEX דחוס

איור 2. פורמט קובץ APEX דחוס

ברמה העליונה, קובץ APEX דחוס הוא קובץ ZIP שמכיל את הקובץ המקורי קובץ apex בפורמט ללא פילטר עם רמת דחיסה של 9, ועם קבצים אחרים מאוחסנים במצב לא דחוס.

ארבעה קבצים מכילים קובץ APEX:

  • original_apex: ללא ניקוז עם רמת דחיסה של 9 זהו קובץ APEX המקורי, הלא דחוס.
  • apex_manifest.pb: שמור בלבד
  • AndroidManifest.xml: שמור בלבד
  • apex_pubkey: שמור בלבד

הקבצים apex_manifest.pb, AndroidManifest.xml ו-apex_pubkey עותקים של הקבצים התואמים שלהם ב-original_apex.

בניית APEX דחוס

ניתן ליצור APEX דחוס באמצעות הכלי apex_compression_tool.py שנמצא בכתובת system/apex/tools.

במערכת ה-build יש כמה פרמטרים שקשורים לדחיסת APEX.

ב-Android.bp אפשר לקבוע אם קובץ APEX ניתן לדחיסה נשלט על ידי מאפיין compressible:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

דגל PRODUCT_COMPRESSED_APEX קובע אם תמונת מערכת נוצרה המקור חייב להכיל קובצי APEX דחוסים.

בניסויים מקומיים אפשר לאלץ build לדחוס קובצי APEX באמצעות הגדרה OVERRIDE_PRODUCT_COMPRESSED_APEX= עד true.

לקובצי APEX דחוסים שנוצרו על ידי מערכת ה-build יש את התוסף .capex. התוסף מאפשר להבחין בקלות בין נתונים דחוסים ולא דחוסים של קובץ APEX.

אלגוריתמים נתמכים לדחיסה

ב-Android 12 יש תמיכה רק בדחיסת נתונים מסוג deflate-zip.

הפעלה של קובץ APEX דחוס במהלך האתחול

לפני שניתן להפעיל APEX דחוס, הוא מכיל את הקובץ original_apex דחוסה בספרייה /data/apex/decompressed. התוצאה של קובץ APEX דחוס מקושר לספרייה /data/apex/active.

הדוגמה הבאה ממחישה את התהליך שמתואר למעלה.

/system/apex/com.android.foo.capex מוגדר כ-APEX דחוס מופעלת, עם versionCode 37.

  1. הקובץ original_apex בתוך /system/apex/com.android.foo.capex הוא מפרקים ל/data/apex/decompressed/com.android.foo@37.apex.
  2. ביצוע של restorecon /data/apex/decompressed/com.android.foo@37.apex מוודאים שיש לו תווית SELinux נכונה.
  3. בדיקות אימות מבוצעות בתאריך /data/apex/decompressed/com.android.foo@37.apex כדי לוודא את התוקף שלו: apexd בודק את המפתח הציבורי בחבילה /data/apex/decompressed/com.android.foo@37.apex כדי לאמת שהוא שווה לזו שמופיעה בחבילה ב-/system/apex/com.android.foo.capex.
  4. קיים קישור קשיח אל הקובץ /data/apex/decompressed/com.android.foo@37.apex הספרייה /data/apex/active/com.android.foo@37.apex.
  5. לוגיקת ההפעלה הרגילה לקובצי APEX לא דחוסים מתבצעת /data/apex/active/com.android.foo@37.apex

אינטראקציה עם OTA

לקובצי APEX דחוסים יש השלכות על העברת OTA ועל האפליקציה. מאז עדכון OTA עשוי להכיל קובץ APEX דחוס עם רמת גרסה גבוהה יותר מאשר במכשיר פעיל במכשיר, יש לשריין כמות מסוימת של שטח פנוי לפני הפעלה מחדש של המכשיר כדי להחיל עדכון OTA.

כדי לתמוך במערכת OTA, apexd חושף את שני ממשקי ה-API הבאים של binder:

  • calculateSizeForCompressedApex – מחשבת את הגודל הנדרש לפתיחת דחיסה קובצי APEX בחבילת OTA. אפשר להשתמש בהרשאה הזו כדי לוודא שלמכשיר יש מספיק מקום כדי להוריד OTA.
  • reserveSpaceForCompressedApex - שומר מקום בדיסק לשימוש עתידי על ידי apexd לביטול הדחיסה של קובצי APEX דחוסים בתוך חבילת ה-OTA.

במקרה של עדכון A/B OTA, apexd ינסה לבטל דחיסה ברקע כחלק מתרחיש OTA לאחר ההתקנה. אם ביטול הדחיסה נכשל, apexd מבצע את פתיחת הדחיסה במהלך האתחול שמחילה את OTA

אילו חלופות עשויות להיות התוצאה של פיתוח APEX

לפניכם כמה אפשרויות שמערכת AOSP השתמשה בהן במהלך העיצוב של קובץ APEX ולמה הן נכללו או לא נכללו.

מערכות לניהול חבילות רגילות

להפצות Linux יש מערכות לניהול חבילות כמו dpkg ו-rpm, עוצמתיים, בוגרים וחזקים. עם זאת, הם לא מתאימות ל-APEX מכיוון שהן לא יכולות להגן על החבילות לאחר בתהליך ההתקנה. האימות מתבצע רק כאשר מתקינים חבילות. תוקפים יכולים לשבור את התקינות של החבילות המותקנות, מבלי להבחין בהן. הדבר רגרסיה ב-Android שבה כל רכיבי המערכת אוחסנו במצב קריאה בלבד של מערכות קבצים שהתקינות שלהן מוגנת באמצעות dm-verity לכל קלט/פלט (I/O). כלשהו פגיעה ברכיבי המערכת חייבת להיות אסורה או להיות גלויה, שהמכשיר יכול לסרב לבצע את ההפעלה אם הוא נפרץ.

dm-crypt לתקינות

הקבצים בקונטיינר APEX הם ממחיצות מובנות (לדוגמה, /system) שמוגנות באמצעות dm-verity, כאשר כל שינוי של הקבצים אסורים גם לאחר טעינת המחיצות. כדי לספק את אותה רמת אבטחה לקבצים, כל הקבצים ב-APEX מאוחסנים בקובץ קובץ אימג' שמתאים לעץ גיבוב ולמתאר vbmeta. ללא dm-verity, APEX במחיצה /data חשוף לגורמים לא רצויים שמבוצעים לאחר שהוא מאומת והתקין.

למעשה, המחיצה /data מוגנת גם באמצעות שכבות הצפנה כמו dm-crypt. למרות שהפעולה הזו מספקת רמה מסוימת של הגנה מפני פגיעה, המטרה העיקרית היא פרטיות, ולא שלמות. כשתוקף מקבל גישה המחיצה /data, לא ניתנת הגנה נוספת, ושוב, רגרסיה בהשוואה לכל רכיב מערכת שנמצא במחיצה /system. עץ הגיבוב (hash) שבתוך קובץ APEX יחד עם dm-verity מספק רמת הגנה על התוכן.

הפניה אוטומטית של נתיבים מ- /system אל /apex

ניתן לגשת לקבצים של רכיבי מערכת שנארזים ב-APEX דרך נתיבים חדשים כמו /apex/<name>/lib/libfoo.so כשהקבצים היו חלק מ/system קטגוריה, הם היו נגישים דרך נתיבים כמו /system/lib/libfoo.so. א' של קובץ APEX (קובצי APEX אחרים או הפלטפורמה) חייבים להשתמש . יכול להיות שתצטרכו לעדכן קוד קיים בגלל שינוי הנתיב.

למרות שאחת הדרכים להימנע משינוי הנתיב היא להציג את תוכן הקובץ בשכבת-על קובץ APEX למחיצה /system והצוות של Android החליט לא ליצור שכבת-על קבצים במחיצה /system, כי זה יכול להשפיע על הביצועים כמו מספר הקבצים בשכבת-על (כנראה אפילו מוערם אחד אחרי השני) עלייה.

אפשרות נוספת הייתה לפרוץ פונקציות גישה לקבצים כמו open, stat ו readlink, כך שנתיבים המתחילים ב-/system הופנו אל הנתיבים התואמים בתוך /apex. צוות Android מחק את האפשרות הזו כי לא ניתן לשנות את כל הפונקציות שמקבלות נתיבים. לדוגמה, אפליקציות מסוימות מקשרות באופן סטטי ל-Bionic, שם האפליקציה שמטמיעה את הפונקציות. במקרים כאלה, האפליקציות האלה לא יופנו לכתובת אחרת.