פורמט הקונטיינר 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
. המטען הייעודי (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 משתמש בClass PackageManager, והוא מתואר בהמשך.
- קובץ APEX מוריד באמצעות אפליקציית התקנת חבילות, ADB או מקור אחר.
- מנהל החבילות מתחיל את תהליך ההתקנה. כשמנהל החבילות מזהה שהקובץ הוא APEX, הוא מעביר את השליטה למנהל APEX.
- מנהל ה-APEX מאמת את קובץ ה-APEX.
- אם קובץ ה-APEX מאומת, מסד הנתונים הפנימי של מנהל ה-APEX מתעדכן כך שיציג שהקובץ הופעל בהפעלה הבאה.
- מבצע ההתקנה מקבל שידור לאחר אימות החבילה.
- כדי להמשיך בהתקנה, צריך להפעיל מחדש את המערכת.
בהפעלה הבאה, מנהל APEX מתחיל, קורא את מסד הנתונים הפנימי ומבצע את הפעולות הבאות בכל קובץ APEX שמופיע:
- אימות קובץ ה-APEX.
- יצירת מכשיר לולאה חוזרת מקובץ APEX.
- יצירת מכשיר בלוק של מיפוי מכשיר מעל מכשיר הלולאה החוזרת.
- טוענת את מכשיר הבלוק של מיפוי המכשיר לנתיב ייחודי (לדוגמה,
/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 יכול לעדכן את כל סוגי הקבצים האלה. האפשרות לעדכן סוג קובץ תלויה בפלטפורמה ובמידת היציבות של ההגדרות של הממשקים לסוגי הקבצים.
אפשרויות חתימה
קבצי APEX חותמים בשתי דרכים. קודם כול, קובץ apex_payload.img
(במיוחד, מתאר ה-vbmeta שמצורף ל-apex_payload.img
) נחתם באמצעות מפתח.
לאחר מכן, כל קובץ ה-APEX נחתם באמצעות APK signature scheme v3. בתהליך הזה משתמשים בשני מפתחות שונים.
בצד המכשיר, מותקן מפתח ציבורי שתואם למפתח הפרטי שמשמש לחתימה על מתאר ה-vbmeta. המנהל APEX משתמש במפתח הציבורי כדי לאמת APEX שמבקשים להתקין. כל קובץ APEX צריך להיות חתום במפתחות שונים, והאכיפה מתבצעת גם בזמן ה-build וגם בזמן הריצה.
APEX במחיצות מובנות
קובצי APEX יכולים להימצא במחיצות מובנות כמו /system
. המחיצה כבר ב-dm-verity, כך שקובצי ה-APEX נטענים ישירות במכשיר הלולאה החוזרת.
אם APEX נמצא במחיצה מובנית, אפשר לעדכן את ה-APEX על-ידי מתן חבילת APEX עם אותו שם חבילה וקוד הגרסה גדול או שווה לו. הגרסה החדשה של APEX מאוחסן ב-/data
, ובדומה ל-APK, הגרסה החדשה שהותקנה משמיצה את הגרסה שכבר קיימת במחיצה המובנית. אבל בניגוד ל-APKs, הגרסה החדשה של APEX מופעלת רק לאחר הפעלה מחדש.
דרישות הליבה
כדי לתמוך במודולים של השורה הראשית של APEX במכשיר Android, נדרשות תכונות הליבה הבאות של Linux: מנהל התקן הלולאה החוזרת ו-dm-verity. מנהל ה-loopback מחבר את קובץ האימג' של מערכת הקבצים למודול APEX, ו-dm-verity מאמת את מודול ה-APEX.
הביצועים של מנהל התקן הלולאה החוזרת וה-dm-verity חשובים להשגת ביצועי מערכת טובים כשמשתמשים במודולים של APEX.
גרסאות ליבה נתמכות
מודולים של APEX mainline נתמכים במכשירים עם גרסאות ליבה 4.4 ואילך. כדי לתמוך במודולים של APEX, מכשירים חדשים שמושקים עם Android מגרסה 10 ואילך חייבים להשתמש בליבה (kernel) 4.9 ואילך.
תיקונים נדרשים בליבה (kernel)
תיקוני הליבה הנדרשים לתמיכה במודולים של APEX כלולים בעץ הנפוץ של Android. כדי לקבל את התיקונים לתמיכה ב-APEX, צריך להשתמש בגרסה האחרונה של העץ המשותף של Android.
גרסת ליבה (kernel) 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: לולאה: שחרור מטמון אם משנים את קיזוז או 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
דרישות לפרמטרים של שורת הפקודה של הליבה
כדי לתמוך ב-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 עבור
זרוע מתורגמת ב-x86) |
קובצי הפעלה | /bin |
ספריות Java | /javalib |
מוכנים מראש | /etc |
יחסי תלות עוברים
קובצי APEX כוללים אוטומטית יחסי תלות טרנזיטיביים של קובצי lib או קובצי הפעלה משותפים מקוריים. לדוגמה, אם libFoo
תלוי ב-libBar
, שני libs נכללים כאשר רק 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 פעמיים: פעם אחת למערכת הקבצים Mini (הקובץ 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. אין תמיכה בטריגרי פעולה ב-Apexes.
אם שירות מסומן כמתעדכן והוא מתחיל לפעול לפני שה-APEXes מופעלים, ההפעלה מתעכבת עד שההפעלה של ה-APEXes תושלם.
הגדרת המערכת לתמיכה בעדכוני 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 שטוח
במכשירים מדור קודם, לפעמים אי אפשר או לא אפשרי לעדכן את הליבה הישנה כדי לתמוך באופן מלא ב-APEX. לדוגמה, יכול להיות שהליבת ה-kernel נוצרה ללא CONFIG_BLK_DEV_LOOP=Y
, שחשובה מאוד לטעינה של קובץ האימג' של מערכת הקבצים בתוך APEX.
Flattened APEX הוא APEX שנוצר במיוחד ואפשר להפעיל אותו במכשירים עם ליבה מדור קודם. קבצים ב-APEX שטוח מותקנים ישירות בספרייה מתחת למחיצה המובנית. לדוגמה, lib/libFoo.so
ב-APEX שטוח מותקן ב-/system/apex/my.apex/lib/libFoo.so
.
הפעלה של APEX שטוח לא מעורבת במכשיר הלולאה. כל הספרייה /system/apex/my.apex
מחוברת ישירות ל-/apex/name@ver
.
אי אפשר לעדכן שרתי APEX שטוחים על ידי הורדת גרסאות מעודכנות של ה-APEXes מהרשת כי לא ניתן ליישר אותם. אפשר לעדכן קצוות APEX שטוחים רק באמצעות עדכון OTA רגיל.
Flattened APEX היא הגדרת ברירת המחדל. המשמעות היא שכל קובצי ה-APEX מוצגים כקובצי flat כברירת מחדל, אלא אם מגדירים במפורש את המכשיר ליצירת קובצי APEX לא מוצגים כקובצי flat כדי לתמוך בעדכוני 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 שניתנים לעדכון זמינה בדף רכיבי מערכת מודולריים.libs משותפים דינמיים 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 דחוסים.
כדי לבצע ניסויים מקומיים, אפשר לאלץ 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.
- הקובץ
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.
במקרה של עדכון A/B OTA, apexd
מנסה לבטל את הדחיסה ברקע כחלק מתרחיש OTA לאחר ההתקנה. אם ביטול הדחיסה נכשל, הפקודה apexd
מבצעת את פתיחת הדחיסה במהלך ההפעלה שמחילה את עדכון ה-OTA.
חלופות שנשקלו במהלך הפיתוח של APEX
ריכזנו כאן כמה אפשרויות ש-AOSP שקלה כשעיצבה את פורמט הקובץ APEX, ומסבירים למה הן נכללו או לא נכללו.
מערכות לניהול חבילות רגילות
להפצות Linux יש מערכות ניהול חבילות כמו dpkg
ו-rpm
, שהן חזקות, מפותחות ויעילות. עם זאת, הם לא עברו ל-APEX כי הם לא יכולים להגן על החבילות אחרי ההתקנה. האימות מתבצע רק כאשר מתקינים חבילות.
תוקפים יכולים לשבור את תקינות החבילות המותקנות בלי שיבחינו בכך. זוהי רגרסיה ב-Android, שבה כל רכיבי המערכת אוחסנו במערכות קבצים לקריאה בלבד שהתקינות שלהן מוגנת באמצעות dm-verity לכל קלט/פלט. כל פגיעה ברכיבי המערכת חייבת להיות אסורה או לאפשר זיהוי, כדי שהמכשיר יוכל לסרב להפעלה אם הוא נפרץ.
dm-crypt לתקינות
הקבצים בקונטיינר APEX מגיעים ממחיצות מובנות (לדוגמה, המחיצה /system
) שמוגנות על ידי dm-verity, וכל שינוי בקבצים אסור גם אחרי שמחברות את המחיצות. כדי לספק את אותה רמת אבטחה לקבצים, כל הקבצים ב-APEX מאוחסנים בתמונה של מערכת קבצים שמותאמת לעץ גיבוב (hash) ולמתאר 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, שמטמיע את הפונקציות.
במקרים כאלה, האפליקציות האלה לא מועברות לכתובת אחרת.