פורמט המכולה 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 שבו הקבצים מאוחסנים ללא דחיסה וממוקמים בגבולות של 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). לדוגמה, אפשר להשתמש בכלי קיים כמו 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.*
- שמור ל-APEX ב-AOSP. לא ייחודי לאף חברה או מכשיר.
com.<companyname>.*
- שמור לחברה. יכול להיות שנעשה בו שימוש בכמה מכשירים של אותה חברה.
com.<companyname>.<devicename>.*
- שמור לקובצי APEX ייחודיים למכשיר ספציפי (או לקבוצת משנה של מכשירים).
מנהל APEX
מנהל ה-APEX (או apexd
) הוא תהליך מקורי עצמאי שאחראי על אימות, התקנה והסרה של קובצי APEX. התהליך הזה מופעל ומוכן בשלב מוקדם ברצף האתחול. בדרך כלל קובצי APEX מותקנים מראש במכשיר בתיקייה /system/apex
. אם אין עדכונים זמינים, מנהל APEX משתמש בחבילות האלה כברירת מחדל.
רצף העדכון של APEX משתמש במחלקה PackageManager והוא מתבצע באופן הבא.
- קובץ APEX מורידים דרך אפליקציה להתקנת חבילות, דרך ADB או ממקור אחר.
- מנהל החבילות מתחיל את תהליך ההתקנה. כשמנהל החבילות מזהה שהקובץ הוא APEX, הוא מעביר את השליטה למנהל ה-APEX.
- מנהל ה-APEX מאמת את קובץ ה-APEX.
- אם קובץ ה-APEX מאומת, מסד הנתונים הפנימי של מנהל ה-APEX מתעדכן כדי לשקף את העובדה שקובץ ה-APEX יופעל באתחול הבא.
- מגיש בקשת ההתקנה מקבל שידור אחרי אימות החבילה.
- כדי להמשיך בהתקנה, צריך להפעיל מחדש את המערכת.
באתחול הבא, מנהל ה-APEX מתחיל, קורא את מסד הנתונים הפנימי ומבצע את הפעולות הבאות לכל קובץ APEX שמופיע ברשימה:
- מאמת את קובץ ה-APEX.
- יוצרת מכשיר loopback מקובץ ה-APEX.
- יוצר מכשיר בלוק של מיפוי מכשירים על גבי מכשיר הלולאה החוזרת.
- המערכת מבצעת Mount של מכשיר הבלוקים של מיפוי המכשירים לנתיב ייחודי (לדוגמה,
/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 v3. בתהליך הזה נעשה שימוש בשני מקשים שונים.
בצד המכשיר, מותקן מפתח ציבורי שמתאים למפתח הפרטי ששימש לחתימה על תיאור ה-vbmeta. מנהל ה-APEX משתמש במפתח הציבורי כדי לאמת קובצי APEX שמתבקשים להתקין. כל APEX צריך להיות חתום באמצעות מפתחות שונים, והאכיפה מתבצעת בזמן הבנייה ובזמן הריצה.
APEX במחיצות מובנות
קובצי APEX יכולים להיות ממוקמים במחיצות מובנות כמו /system
. המחיצה כבר נמצאת מעל dm-verity, ולכן קובצי ה-APEX נטענים ישירות מעל מכשיר הלולאה.
אם קובץ APEX קיים במחיצה מובנית, אפשר לעדכן אותו על ידי אספקת חבילת APEX עם אותו שם חבילה וקוד גרסה גדול או שווה. קובץ ה-APEX החדש מאוחסן ב-/data
, ובדומה לקובצי APK, הגרסה החדשה שהותקנה מסתירה את הגרסה שכבר נמצאת במחיצה המובנית. אבל בניגוד לקובצי APK, הגרסה החדשה של APEX שמותקנת מופעלת רק אחרי הפעלה מחדש.
דרישות ליבה
כדי לתמוך במודולים של APEX mainline במכשיר Android, נדרשות התכונות הבאות של ליבת Linux: מנהל ההתקן של loopback ו-dm-verity. מנהל ההתקן של ה-loopback מטעין את תמונת מערכת הקבצים במודול APEX, ו-dm-verity מאמת את מודול ה-APEX.
הביצועים של מנהל ההתקן של ה-loopback ושל dm-verity חשובים להשגת ביצועים טובים של המערכת כשמשתמשים במודולי APEX.
גרסאות ליבה נתמכות
מודולים מרכזיים של APEX נתמכים במכשירים עם ליבה מגרסה 4.4 ומעלה. במכשירים חדשים עם Android מגרסה 10 ומעלה צריך להשתמש בליבה מגרסה 4.9 ומעלה כדי לתמוך במודולי APEX.
תיקונים נדרשים בקרנל
תיקוני הליבה הנדרשים לתמיכה במודולי APEX כלולים בעץ המשותף של Android. כדי לקבל את תיקוני האבטחה לתמיכה ב-APEX, צריך להשתמש בגרסה האחרונה של עץ Android המשותף.
גרסת ליבה 4.4
הגרסה הזו נתמכת רק במכשירים שמשודרגים מ-Android 9 ל-Android 10 ורוצים לתמוך במודולי APEX. כדי לקבל את התיקונים הנדרשים, מומלץ לבצע מיזוג כלפי מטה מהענף 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)
- Revert "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 (ממשקי יישום בינאריים) הראשיים והמשניים של המכשיר. אם חבילת 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 בו-זמנית.
בין נתיבי ההרכבה, הנתיב שמתאים לגרסה האחרונה הוא /apex/<apex_name>
.
לקוחות יכולים להשתמש בנתיב של ה-bind-mount כדי לקרוא או להפעיל קבצים מ-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. לדוגמה, יכול להיות שהליבה נוצרה ללא CONFIG_BLK_DEV_LOOP=Y
, שהוא חיוני להרכבת תמונת מערכת הקבצים בתוך APEX.
חבילת APEX שטוחה היא חבילת APEX שנבנתה במיוחד ואפשר להפעיל אותה במכשירים עם ליבת מערכת הפעלה מדור קודם. קבצים ב-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 רגיל.
הגדרת ברירת המחדל היא Flattened APEX. המשמעות היא שכל חבילות ה-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 שאפשר לעדכן זמינה בדף Modular System Components.חבילות APEX של ספריות דינמיות משותפות. מכיוון שהערך
apexd
תמיד מפעיל את שתי הגרסאות של חבילות APEX כאלה (הגרסה שהותקנה מראש והגרסה המשודרגת), דחיסה שלהן לא מוסיפה ערך.
פורמט קובץ APEX דחוס
זהו הפורמט של קובץ APEX דחוס.
איור 2. פורמט קובץ APEX דחוס
ברמה העליונה, קובץ APEX דחוס הוא קובץ ZIP שמכיל את קובץ ה-APEX המקורי בצורה מנופחת עם רמת דחיסה של 9, ועם קבצים אחרים שמאוחסנים ללא דחיסה.
קובץ APEX מורכב מארבעה קבצים:
-
original_apex
: deflated with compression level of 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.
ב-Android.bp
, האפשרות לדחיסת קובץ APEX נקבעת על ידי המאפיין compressible
:
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
PRODUCT_COMPRESSED_APEX
דגל מוצר קובע אם תמונת מערכת שנבנתה ממקור חייבת להכיל קובצי APEX דחוסים.
כדי לבצע ניסויים מקומיים, אפשר להגדיר את OVERRIDE_PRODUCT_COMPRESSED_APEX=
לערך true
כדי לכפות על המערכת ליצור דחיסה של קובצי APEX.
לקובצי 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
נפרס לתוך/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
כדי לוודא שהוא תקף: במסגרת הבדיקות, המפתח הציבורי שכלול ב-/data/apex/decompressed/com.android.foo@37.apex
נבדק כדי לוודא שהוא זהה לזה שכלול ב-/system/apex/com.android.foo.capex
.apexd
- הקובץ
/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, שמטמיע את הפונקציות.
במקרים כאלה, לא מתבצעת הפניה אוטומטית לאפליקציות האלה.