פורמט הקונטיינר Android Pony EXpress (APEX) הוצג ב-Android 10, והוא משמש בתהליך ההתקנה של מודולים ברמה נמוכה יותר במערכת. הפורמט הזה מאפשר לעדכן רכיבי מערכת שלא מתאימים למודל האפליקציות הרגיל של Android. דוגמאות לרכיבים הם ספריות ושירותים מקומיים, שכבות הפשטה של חומרה (HAL), סביבת זמן ריצה (ART) וספריות של כיתות.
המונח 'APEX' יכול להתייחס גם לקובץ APEX.
רקע
מערכת Android תומכת בעדכונים של מודולים שתואמים למודל האפליקציה הרגיל (לדוגמה, שירותים, פעילויות) באמצעות אפליקציות להתקנת חבילות (כמו אפליקציית חנות Google Play). עם זאת, לשימוש במודל דומה לרכיבי מערכת הפעלה ברמה נמוכה יותר יש את החסרונות הבאים:
- לא ניתן להשתמש במודולים שמבוססים על קובצי APK בשלב מוקדם של רצף האתחול. מנהל החבילות הוא המאגר המרכזי של מידע על אפליקציות, וניתן להפעיל אותו רק ממנהל הפעילויות, שמגיע למצב מוכן בשלב מאוחר יותר של תהליך האתחול.
- פורמט ה-APK (במיוחד המניפסט) מיועד לאפליקציות ל-Android, ומודוללי מערכת לא תמיד מתאימים לו.
עיצוב
בקטע הזה מתוארים העיצוב הכללי של פורמט הקובץ APEX ושל מנהל APEX, שהוא שירות שמנהל קובצי APEX.
למידע נוסף על הסיבות לבחירה בתכנון הזה ל-APEX, ראו חלופות שנשקלו במהלך הפיתוח של APEX.
פורמט APEX
זהו הפורמט של קובץ APEX.
איור 1. פורמט קובץ APEX
ברמה העליונה, קובץ APEX הוא קובץ ZIP שבו הקבצים מאוחסנים ללא דחיסה וממוקמים בגבולות של 4KB.
ארבעת הקבצים בקובץ APEX הם:
apex_manifest.json
AndroidManifest.xml
apex_payload.img
apex_pubkey
קובץ apex_manifest.json
מכיל את שם החבילה ואת הגרסה שלה, שמזהים קובץ APEX. זהו מאגר פרוטוקול של ApexManifest
בפורמט JSON.
הקובץ AndroidManifest.xml
מאפשר לקובץ ה-APEX להשתמש בתשתית ובכלים שקשורים ל-APK, כמו ADB, PackageManager ואפליקציות להתקנת חבילות (כמו Play Store). לדוגמה, קובץ APEX יכול להשתמש בכלי קיים כמו aapt
כדי לבדוק מטא-נתונים בסיסיים מהקובץ. הקובץ מכיל את שם החבילה ואת פרטי הגרסה. בדרך כלל, המידע הזה זמין גם ב-apex_manifest.json
.
מומלץ להשתמש ב-apex_manifest.json
במקום ב-AndroidManifest.xml
בקוד ובמערכות חדשים שקשורים ל-APEX. AndroidManifest.xml
עשוי להכיל מידע נוסף על טירגוט שאפשר להשתמש בו בכלים הקיימים לפרסום אפליקציות.
apex_payload.img
הוא קובץ אימג' של מערכת קבצים מסוג ext4 שמגודר על ידי dm-verity. האימג' מותקן בסביבת זמן הריצה באמצעות מכשיר לולאה חוזרת (loopback). באופן ספציפי, עץ הגיבוב וחסימת המטא-נתונים נוצרים באמצעות הספרייה libavb
. מטען העבודה של מערכת הקבצים לא מנותח (כי התמונה צריכה להיות ניתנת לטעינה במקום). קבצים רגילים נכללים בקובץ apex_payload.img
.
apex_pubkey
הוא המפתח הציבורי שמשמש לחתימה על קובץ האימג' של מערכת הקבצים. במהלך זמן הריצה, המפתח הזה מבטיח שה-APEX שהורדתם נחתם על ידי אותה ישות שחותמת על אותו APEX במחיצות המובנות.
הנחיות למתן שמות ב-APEX
כדי למנוע התנגשויות בשמות בין מודולים חדשים של APEX עם התקדמות הפלטפורמה, מומלץ לפעול לפי ההנחיות הבאות לגבי מתן שמות:
com.android.*
- שמורה ל-APEXes של AOSP. הוא לא ייחודי לאף חברה או מכשיר.
com.<companyname>.*
- שמור לחברה. יכול להיות שהחשבון הזה משמש כמה מכשירים של החברה הזו.
com.<companyname>.<devicename>.*
- שמורות למזהי APEX ייחודיים למכשיר ספציפי (או לקבוצת משנה של מכשירים).
מנהל APEX
מנהל ה-APEX (או apexd
) הוא תהליך מקורי עצמאי שאחראי על אימות, התקנה והסרה של קובצי APEX. התהליך הזה מופעל ומוכין בשלב מוקדם ברצף האתחול. קובצי APEX מותקנים בדרך כלל מראש במכשיר בקטע /system/apex
. אם אין עדכונים זמינים, ברירת המחדל של מנהל APEX היא להשתמש בחבילות האלה.
רצף העדכון של APEX משתמש בClass PackageManager, והוא מפורט בהמשך.
- קובץ APEX מוריד באמצעות אפליקציית התקנת חבילות, ADB או מקור אחר.
- מנהל החבילות מתחיל את תהליך ההתקנה. כשמנהל החבילות מזהה שהקובץ הוא APEX, הוא מעביר את השליטה למנהל APEX.
- מנהל ה-APEX מאמת את קובץ ה-APEX.
- אם קובץ ה-APEX מאומת, מסד הנתונים הפנימי של מנהל ה-APEX מתעדכן כך שיציג שהקובץ הופעל בהפעלה הבאה.
- מבצע ההתקנה מקבל שידור לאחר אימות החבילה.
- כדי להמשיך את ההתקנה, צריך להפעיל מחדש את המערכת.
בהפעלה הבאה, מנהל APEX מתחיל, קורא את מסד הנתונים הפנימי ומבצע את הפעולות הבאות לכל קובץ APEX שמופיע ברשימה:
- אימות קובץ ה-APEX.
- יצירת מכשיר לולאה חוזרת מקובץ APEX.
- יצירת מכשיר בלוק של ממפאי מכשירים מעל מכשיר הלולאה החוזרת.
- הכלי מחבר את מכשיר ה-block של ממפאי המכשירים לנתיב ייחודי (לדוגמה,
/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 תומך בסוגי הקבצים הבאים:
- ספריות משותפות מקוריות
- קובצי הפעלה מקומיים
- קובצי JAR
- קובצי נתונים
- קובצי תצורה
עם זאת, זה לא אומר ש-APEX יכול לעדכן את כל סוגי הקבצים האלה. היכולת לעדכן סוג קובץ תלויה בפלטפורמה וביציבות של ההגדרות של ממשקי ה-API לסוגי הקבצים.
אפשרויות חתימה
קבצי APEX חותמים בשתי דרכים. קודם כול, קובץ apex_payload.img
(במיוחד, מתאר ה-vbmeta שמצורף ל-apex_payload.img
) נחתם באמצעות מפתח.
לאחר מכן, כל קובץ ה-APEX נחתם באמצעות APK signature scheme v3. בתהליך הזה נעשה שימוש בשני מפתחות שונים.
בצד המכשיר, מותקן מפתח ציבורי שתואם למפתח הפרטי שמשמש לחתימה על מתאר ה-vbmeta. מנהל ה-APEX משתמש במפתח הציבורי כדי לאמת את ה-APEXes שביקשו להתקין. כל קובץ APEX צריך להיות חתום במפתחות שונים, והאכיפה מתבצעת גם בזמן ה-build וגם בזמן הריצה.
APEX במחיצות מובנות
קובצי APEX יכולים להיות ממוקמים במחיצות מובנות כמו /system
. המחיצה כבר מעל dm-verity, כך שקובצי APEX מותקנים ישירות מעל מכשיר הלולאה החוזרת.
אם יש APEX במחיצה מובנית, אפשר לעדכן את ה-APEX על ידי הצגת חבילת APEX עם אותו שם חבילה וקוד גרסה שגדול מקוד הגרסה הקיים או שווה לו. קובץ ה-APEX החדש מאוחסן ב-/data
, וכמו בחבילות APK, הגרסה החדשה שמותקנת מצללת על הגרסה שכבר נמצאת במחיצה המובנית. אבל בניגוד לחבילות APK, הגרסה החדשה של APEX שמותקנת מופעלת רק אחרי הפעלה מחדש.
דרישות הליבה
כדי לתמוך במודולים של APEX במכשיר Android, נדרשות התכונות הבאות של ליבה של Linux: מנהל ההתקן של הלולאה החוזרת (loopback) ו-dm-verity. מנהל ה-loopback מחבר את קובץ האימג' של מערכת הקבצים למודול APEX, ו-dm-verity מאמת את מודול ה-APEX.
הביצועים של מנהל ה-loopback ושל dm-verity חשובים להשגת ביצועים טובים של המערכת כשמשתמשים במודולים של APEX.
גרסאות הליבה הנתמכות
מודולים של APEX mainline נתמכים במכשירים עם גרסאות ליבה 4.4 ואילך. במכשירים חדשים שמושקעים עם Android מגרסה 10 ואילך, צריך להשתמש בליבה בגרסה 4.9 ואילך כדי לתמוך במודולים של APEX.
תיקוני הליבה הנדרשים
התיקונים הנדרשים לליבה (kernel) לתמיכה במודולים של APEX כלולים בעץ המשותף של Android. כדי לקבל את התיקונים שתומכים ב-APEX, צריך להשתמש בגרסה העדכנית ביותר של עץ Android המשותף.
גרסת ליבה 4.4
הגרסה הזו נתמכת רק במכשירים ששודרגו מ-Android 9 ל-Android 10 ורוצים לתמוך במודולים של APEX. מומלץ מאוד לבצע מיזוג לאחור (down-merge) מההסתעפות android-4.4
כדי לקבל את התיקונים הנדרשים. בהמשך מופיעה רשימה של התיקונים הנפרדים הנדרשים לליבה בגרסה 4.4.
- UPSTREAM: loop: add ioctl for changing logical block size (4.4)
- BACKPORT: block/loop: set hw_sectors (4.4)
- UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
- ANDROID: mnt: Fix next_descendent (4.4)
- ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
- ANDROID: mnt: Propagate remount correctly (4.4)
- החזרה לגרסה הקודמת של 'ANDROID: dm verity: add minimum prefetch size' (4.4)
- UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)
גרסאות ליבה 4.9/4.14/4.19
כדי לקבל את התיקונים הנדרשים לגרסאות הליבה 4.9/4.14/4.19, מבצעים מיזוג לאחור מההסתעפות android-common
.
אפשרויות הגדרה נדרשות של הליבה
ברשימה הבאה מפורטות דרישות ההגדרה הבסיסיות לתמיכה במודולים של 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
דרישות לפרמטרים של שורת הפקודה של הליבה
כדי לתמוך ב-APEX, צריך לוודא שהפרמטרים של שורת הפקודה של הליבה עומדים בדרישות הבאות:
- אסור להגדיר את
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 עבור arm מתורגם ב-x86) |
קובצי הפעלה | /bin |
ספריות Java | /javalib |
חבילות מוכנות מראש | /etc |
יחסי תלות טרנסיטיביים
קובצי APEX כוללים באופן אוטומטי יחסי תלות טרנזיטיביים של ספריות שיתופיות מקומיות או קובצי הפעלה. לדוגמה, אם libFoo
תלויה ב-libBar
, שתי הספריות נכללות כשרק libFoo
מופיע בנכס native_shared_libs
.
טיפול במספר ממשקי ABI
מתקינים את המאפיין native_shared_libs
גם לממשקי ה-ABI הראשיים וגם לממשקי ה-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 pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_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 חתום על ידי האישור שמוגדר על ידי המודול של Soong שנקרא<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 בו-זמנית.
מבין נתיבי הטעינה, הנתיב שתואם לגרסה האחרונה מצורף באמצעות קישור (bind) אל /apex/<apex_name>
.
לקוחות יכולים להשתמש בנתיב הטעינה המצורף כדי לקרוא או להריץ קבצים מ-APEX.
בדרך כלל משתמשים ב-APEX באופן הבא:
- יצרן ציוד מקורי (OEM) או יצרן ציוד מקורי בהתאמה אישית (ODM) טוען מראש קובץ APEX בקטע
/system/apex
כשהמכשיר נשלח. - הגישה לקבצים ב-APEX מתבצעת דרך הנתיב
/apex/<apex_name>/
. - כשגרסה מעודכנת של APEX מותקנת ב-
/data/apex
, הנתיב מפנה ל-APEX החדש אחרי ההפעלה מחדש.
עדכון שירות באמצעות APEX
כדי לעדכן שירות באמצעות APEX:
מסמנים את השירות במחיצה של המערכת כשירות שניתן לעדכון. מוסיפים את האפשרות
updatable
להגדרת השירות./system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
יוצרים קובץ
.rc
חדש לשירות המעודכן. משתמשים באפשרותoverride
כדי להגדיר מחדש את השירות הקיים./apex/my.apex/etc/init.rc: service myservice /apex/my.apex/bin/myservice class core user system ... override
אפשר להגדיר הגדרות שירות רק בקובץ .rc
של APEX. אין תמיכה בטריגרים של פעולות ב-Apex.
אם שירות מסומן כמתעדכן והוא מתחיל לפעול לפני שה-APEXes מופעלים, ההפעלה מתעכבת עד שההפעלה של ה-APEXes תושלם.
הגדרת המערכת לתמיכה בעדכוני APEX
כדי לתמוך בעדכוני קבצים של APEX, מגדירים את מאפיין המערכת הבא ל-true
.
<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 שטוח
במכשירים מדור קודם, לפעמים אי אפשר או לא מעשי לעדכן את הליבה הישנה כך שתתמוך באופן מלא ב-APEX. לדוגמה, יכול להיות שהליבת ה-kernel נוצרה ללא CONFIG_BLK_DEV_LOOP=Y
, שחשובה מאוד לטעינה של קובץ האימג' של מערכת הקבצים בתוך APEX.
Flattened APEX הוא APEX שנוצר במיוחד ואפשר להפעיל אותו במכשירים עם ליבה מדור קודם. קבצים ב-APEX שטוח מותקנים ישירות בספרייה מתחת למחיצה המובנית. לדוגמה, lib/libFoo.so
ב-APEX שטוח מותקן ב-/system/apex/my.apex/lib/libFoo.so
.my.apex
הפעלת APEX שטוח לא כוללת את מכשיר הלולאה. כל הספרייה /system/apex/my.apex
מוצמדת ישירות ל-/apex/name@ver
.
לא ניתן לעדכן פונקציות APEX שטוחות על ידי הורדת גרסאות מעודכנות שלהן מהרשת, כי לא ניתן לשטח את הפונקציות APEX שהורדתם. אפשר לעדכן קצוות APEX שטוחים רק באמצעות OTA רגיל.
Flattened APEX היא הגדרת ברירת המחדל. המשמעות היא שכל קובצי ה-APEX מוצגים כקובצי flat כברירת מחדל, אלא אם מגדירים במפורש את המכשיר ליצירת קובצי APEX לא שטוחים כדי לתמוך בעדכוני APEX (כפי שמוסבר למעלה).
אי אפשר לשלב במכשיר בין מודעות APEX שטוחות למודעות APEX לא שטוחות. כל ה-APEX במכשיר חייבים להיות לא שטוחים או שטוחים.
זה חשוב במיוחד כששולחים חבילות APEX מוגדרות מראש בחתימה מראש לפרויקטים כמו Mainline. גם קוד APEX שלא נחתם מראש (כלומר, נוצר מהמקור) צריך להיות לא שטוח וחתום באמצעות מפתחות מתאימים. המכשיר צריך לרשת מ-updatable_apex.mk
, כפי שמוסבר במאמר עדכון שירות באמצעות APEX.
מודעות APEX דחוסות
ב-Android 12 ואילך יש דחיסה של APEX כדי לצמצם את ההשפעה על נפח האחסון של חבילות APEX שניתן לעדכן. אחרי שמתקינים עדכון ל-APEX, הגרסה המותקנת מראש לא בשימוש יותר, אבל היא עדיין תופסת את אותו נפח אחסון. המרחב המשותף הזה לא יהיה זמין.
כדי לצמצם את ההשפעה על נפח האחסון, דחיסת APEX משתמשת בקבוצה דחוסה מאוד של קובצי APEX במחיצות לקריאה בלבד (כמו המחיצה /system
). ב-Android מגרסה 12 ואילך נעשה שימוש באלגוריתם דחיסת zip של DEFLATE.
דחיסת הנתונים לא מבצעת אופטימיזציה לפריטים הבאים:
רכיבי APEX של Bootstrap שצריך לטעון מוקדם מאוד בסדרת האתחול.
מודולים של APEX שלא ניתן לעדכן. כדאי להשתמש בדחיסת נתונים רק אם מותקנת גרסה מעודכנת של APEX במחיצה
/data
. רשימה מלאה של רכיבי APEX שניתנים לעדכון זמינה בדף רכיבי מערכת מודולריים.ספריות APEX משותפות דינמיות. מכיוון ש-
apexd
תמיד מפעיל את שתי הגרסאות של מודולי 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
.
יש כמה פרמטרים שקשורים לדחיסת APEX שזמינים במערכת ה-build.
ב-Android.bp
, היכולת לדחוס קובץ APEX נקבע על ידי המאפיין compressible
:
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
דגל המוצר PRODUCT_COMPRESSED_APEX
קובע אם קובץ אימג' של מערכת שנוצר ממקור חייב לכלול קובצי APEX דחוסים.
כדי לבצע ניסויים מקומיים, אפשר לאלץ גרסה זמנית לדחוס קובצי APEX על ידי הגדרת OVERRIDE_PRODUCT_COMPRESSED_APEX=
לערך true
.
לקובצי APEX דחוסים שנוצרו על ידי מערכת ה-build יש את הסיומת .capex
.
התוסף מאפשר להבחין בקלות בין גרסאות דחוסות לבין גרסאות לא דחוסות של קובץ APEX.
אלגוריתמי דחיסה נתמכים
ב-Android 12 יש תמיכה רק בדחיסת deflate-zip.
הפעלת קובץ APEX דחוס במהלך האתחול
כדי להפעיל קובץ APEX דחוס, קובץ original_apex
שבתוכו צריך לעבור תהליך של דחיסה לאחור (decompression) ולעבור לספרייה /data/apex/decompressed
. קובץ ה-APEX שנוצר לאחר הפירוק יהיה מקושר באופן קבוע לספרייה /data/apex/active
.
הדוגמה הבאה היא המחשה לתהליך שמתואר למעלה.
אפשר להתייחס ל-/system/apex/com.android.foo.capex
כקובץ APEX דחוס שמופעל, עם versionCode 37.
- הקובץ
original_apex
בתוך/system/apex/com.android.foo.capex
decompresses ל-/data/apex/decompressed/com.android.foo@37.apex
. - הפקודה
restorecon /data/apex/decompressed/com.android.foo@37.apex
מבוצעת כדי לוודא שיש לה תווית SELinux נכונה. - בדיקות האימות מתבצעות ב-
/data/apex/decompressed/com.android.foo@37.apex
כדי לוודא שהוא תקף:apexd
בודק את המפתח הציבורי שצורף ל-/data/apex/decompressed/com.android.foo@37.apex
כדי לוודא שהוא זהה למפתח שצורף ל-/system/apex/com.android.foo.capex
. - לקובץ
/data/apex/decompressed/com.android.foo@37.apex
יש קישור קשיח לספרייה/data/apex/active/com.android.foo@37.apex
. - הלוגיקה הרגילה להפעלה של קבצי 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.
במקרה של עדכון OTA מסוג A/B, apexd
מנסה לבצע דחיסה ברקע כחלק מתהליך ה-OTA לאחר ההתקנה. אם הדחיסה לא מצליחה, apexd
מבצעת את הדחיסה במהלך האתחול שבו מוחלים את העדכון ב-OTA.
חלופות שנשקלו במהלך הפיתוח של APEX
ריכזנו כאן כמה אפשרויות ש-AOSP שקלה כשעיצבה את הפורמט של קובץ APEX, ומסבירים למה הן נכללו או לא נכללו.
מערכות רגילות לניהול חבילות
להפצות Linux יש מערכות ניהול חבילות כמו dpkg
ו-rpm
, שהן חזקות, מפותחות ויעילות. עם זאת, הן לא אומצו ב-APEX כי הן לא יכולות להגן על החבילות אחרי ההתקנה. האימות מתבצע רק במהלך התקנת החבילות.
תוקפים יכולים לשבור את תקינות החבילות המותקנות בלי שיבחינו בכך. זוהי נסיגה ב-Android, שבה כל רכיבי המערכת מאוחסנים במערכות קבצים לקריאה בלבד, שהשלמות שלהן מוגנת על ידי dm-verity לכל קלט/פלט. כל פגיעה ברכיבי המערכת חייבת להיות אסורה או לאפשר זיהוי, כדי שהמכשיר יוכל לסרב להפעלה אם הוא נפרץ.
dm-crypt לצורך תקינות
הקבצים בקונטיינר APEX מגיעים ממחיצות מובנות (לדוגמה, המחיצה /system
) שמוגנות על ידי dm-verity, שבהן אסור לשנות את הקבצים גם אחרי שמחברות את המחיצות. כדי לספק אותה רמת אבטחה לקבצים, כל הקבצים ב-APEX מאוחסנים בתמונת מערכת קבצים שמשויכת לעץ גיבוב ולתיאור vbmeta. בלי dm-verity, APEX במחיצה /data
חשוף לשינויים לא מכוונים שמתבצעים אחרי האימות וההתקנה שלו.
למעשה, המחיצה /data
מוגנת גם על ידי שכבות הצפנה כמו dm-crypt. אמנם הדבר מספק רמה מסוימת של הגנה מפני פגיעה, אבל המטרה העיקרית שלו היא פרטיות, ולא תקינות. כשתוקף מקבל גישה למחיצה /data
, אי אפשר להוסיף הגנה נוספת, וגם כאן מדובר בנסיגה בהשוואה למצב שבו כל רכיב המערכת נמצא במחיצה /system
.
עץ הגיבוב בקובץ 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, שמטמיע את הפונקציות.
במקרים כאלה, האפליקציות האלה לא מועברות לכתובת אחרת.