אתם יכולים להשתמש בפורמט הקובץ APEX כדי לארוז ולהתקין מודולים ברמה נמוכה יותר של מערכת ההפעלה Android. היא מאפשרת ליצור ולהתקין באופן עצמאי רכיבים כמו שירותים וספריות מקוריים, יישומי HAL, קושחה, קובצי תצורה וכו'.
מערכת ה-Build מתקינה באופן אוטומטי את קובצי ה-APEX של הספק במחיצה /vendor
ומפעילה אותם בזמן הריצה באמצעות apexd
, בדיוק כמו קובצי APEX במחיצות אחרות.
תרחישים לדוגמה
מודולריזציה של תמונות ספקים
חבילות APEX מאפשרות לאגד באופן טבעי ולחלק למודולים יישומי תכונות בתמונות של ספקים.
כשיוצרים תמונות של ספקים כשילוב של קובצי APEX של ספקים שנבנו בנפרד, יצרני מכשירים יכולים לבחור בקלות את ההטמעות הספציפיות של הספקים שהם רוצים במכשיר שלהם. יצרנים יכולים אפילו ליצור חבילת APEX חדשה של ספק אם אף אחת מחבילות ה-APEX שסופקו לא מתאימה לצרכים שלהם, או אם יש להם חומרה מותאמת אישית חדשה לגמרי.
לדוגמה, יצרן ציוד מקורי יכול לבחור להרכיב את המכשיר שלו עם APEX של הטמעת Wi-Fi ב-AOSP, עם APEX של הטמעת Bluetooth ב-SoC ועם APEX של הטמעת טלפוניה מותאמת אישית של יצרן הציוד המקורי.
בלי רכיבי APEX של ספקים, הטמעה עם כל כך הרבה תלות בין רכיבי הספקים דורשת תיאום ומעקב קפדניים. על ידי אריזת כל הרכיבים (כולל קובצי תצורה וספריות נוספות) ב-APEX עם ממשקים מוגדרים בבירור בכל נקודה של תקשורת בין תכונות, הרכיבים השונים הופכים להחלפה.
איטרציה של מפתח
חבילות APEX של ספקים עוזרות למפתחים לבצע איטרציות מהר יותר במהלך פיתוח מודולים של ספקים. הן עושות זאת על ידי איגוד של יישום תכונה שלמה, כמו HAL של Wi-Fi, בתוך חבילת APEX של ספק. לאחר מכן, המפתחים יכולים לבנות ולדחוף בנפרד את ספק ה-APEX כדי לבדוק שינויים, במקום לבנות מחדש את כל תמונת הספק.
השינוי הזה מפשט ומקצר את מחזור האיטרציה של מפתחים שעובדים בעיקר בתחום תכונה אחד ורוצים לבצע איטרציה רק בתחום התכונה הזה.
הקיבוץ הטבעי של אזור תכונות לתוך APEX גם מפשט את התהליך של בנייה, דחיפה ובדיקה של שינויים באזור התכונות הזה. לדוגמה, התקנה מחדש של APEX מעדכנת אוטומטית כל ספרייה או קובצי הגדרה שכלולים ב-APEX.
בנוסף, כשמאגדים תחום תכונות ב-APEX, קל יותר לבצע ניפוי באגים או לחזור למצב הקודם כשמזהים התנהגות לא תקינה של המכשיר. לדוגמה, אם הטלפוניה לא פועלת בצורה טובה בגרסה חדשה, המפתחים יכולים לנסות להתקין במכשיר APEX ישן יותר של הטלפוניה (בלי צורך בהפעלה של גרסה מלאה) ולבדוק אם הפעולה התקינה חוזרת.
תהליך עבודה לדוגמה:
# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w
# Test the device.
... testing ...
# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...
# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...
דוגמאות
יסודות
בדף הראשי בנושא פורמט קובץ APEX מופיע מידע כללי על APEX, כולל דרישות המכשיר, פרטים על פורמט הקובץ ושלבי ההתקנה.
ב-Android.bp
, הגדרת המאפיין vendor: true
הופכת מודול APEX ל-APEX של ספק.
apex {
..
vendor: true,
..
}
קבצים בינאריים וספריות משותפות
חבילת APEX כוללת יחסי תלות טרנזיטיביים בתוך מטען ה-APEX, אלא אם יש להם ממשקים יציבים.
ממשקי Native יציבים ליחסי תלות של ספקי APEX כוללים cc_library
עם stubs
וספריות LLNDK. התלויות האלה לא נכללות באריזה, והתלויות מתועדות במניפסט של APEX. קובץ המניפסט עובר עיבוד על ידי linkerconfig
כדי שהתלויות החיצוניות המקוריות יהיו זמינות בזמן הריצה.
בקטע הקוד הבא, קובץ ה-APEX מכיל גם את הקובץ הבינארי (my_service
) וגם את התלויות הלא יציבות שלו (קבצי *.so
).
apex {
..
vendor: true,
binaries: ["my_service"],
..
}
בקטע הקוד הבא, קובץ ה-APEX מכיל את הספרייה המשותפת
my_standalone_lib
ואת כל יחסי התלות הלא יציבים שלה (כפי שמתואר למעלה).
apex {
..
vendor: true,
native_shared_libs: ["my_standalone_lib"],
..
}
הקטנת APEX
יכול להיות ש-APEX יגדל כי הוא כולל תלות לא יציבה. מומלץ להשתמש בקישור סטטי. אפשר לקשר באופן סטטי ספריות נפוצות כמו libc++.so
ו-libbase.so
לקבצים בינאריים של HAL. אפשרות נוספת היא ליצור תלות כדי לספק ממשק יציב. התלות לא תצורף לחבילת ה-APEX.
הטמעות של HAL
כדי להגדיר הטמעה של HAL, צריך לספק את הקבצים הבינאריים והספריות המתאימים בתוך ספק APEX, בדומה לדוגמאות הבאות:
כדי להקיף את ההטמעה של HAL, צריך לציין ב-APEX גם את כל קטעי ה-VINTF הרלוונטיים ואת סקריפטים של init.
VINTF fragments
אפשר להציג רכיבי VINTF מ-APEX של ספק כשהרכיבים נמצאים ב-etc/vintf
של ה-APEX.
משתמשים במאפיין prebuilts
כדי להטמיע את קטעי ה-VINTF ב-APEX.
apex {
..
vendor: true,
prebuilts: ["fragment.xml"],
..
}
prebuilt_etc {
name: "fragment.xml",
src: "fragment.xml",
sub_dir: "vintf",
}
Query APIs
כשמוסיפים פרגמנטים של VINTF ל-APEX, משתמשים בממשקי libbinder_ndk
API כדי לקבל את המיפויים של ממשקי HAL ושמות APEX.
-
AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default")
:true
אם מופע ה-HAL מוגדר ב-APEX. -
AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...)
: מקבל את שם ה-APEX שמגדיר את מופע ה-HAL. -
AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...)
: משמש לפתיחת HAL של העברה.
סקריפטים של init
חבילות APEX יכולות לכלול סקריפטים של init בשתי דרכים: (א) קובץ טקסט מוכן מראש בתוך מטען ה-APEX, או (ב) סקריפט init רגיל ב-/vendor/etc
. אפשר להגדיר את שניהם לאותו APEX.
סקריפט Init ב-APEX:
prebuilt_etc {
name: "myinit.rc",
src: "myinit.rc"
}
apex {
..
vendor: true,
prebuilts: ["myinit.rc"],
..
}
סקריפטים של init ב-APEX של ספקים יכולים לכלול הגדרות של service
והוראות של on <property or event>
.
מוודאים שהגדרת service
מפנה לקובץ בינארי באותו APEX.
לדוגמה, com.android.foo
APEX עשוי להגדיר שירות בשם foo-service
.
on foo-service /apex/com.android.foo/bin/foo
...
חשוב להיזהר כשמשתמשים בהנחיות on
. מכיוון שסקריפטים של init ב-APEXes מנותחים ומופעלים אחרי שה-APEXes מופעלים, אי אפשר להשתמש בחלק מהאירועים או המאפיינים. משתמשים ב-apex.all.ready=true
כדי להפעיל פעולות מוקדם ככל האפשר.
Bootstrap APEXes יכולים להשתמש ב-on init
, אבל לא ב-on early-init
.
קושחה
דוגמה:
משבצים קושחה ב-APEX של ספק באמצעות סוג המודול prebuilt_firmware
, באופן הבא.
prebuilt_firmware {
name: "my.bin",
src: "path_to_prebuilt_firmware",
vendor: true,
}
apex {
..
vendor: true,
prebuilts: ["my.bin"], // installed inside APEX as /etc/firmware/my.bin
..
}
מודולים של prebuilt_firmware
מותקנים בספרייה <apex name>/etc/firmware
של APEX. ueventd
scans /apex/*/etc/firmware
directories to
find firmware modules.
ה-file_contexts
של ה-APEX צריך לתייג כראוי את כל רכיבי ה-payload של הקושחה כדי להבטיח ש-ueventd
יוכל לגשת לקבצים האלה בזמן הריצה. בדרך כלל התווית vendor_file
מספיקה. לדוגמה:
(/.*)? u:object_r:vendor_file:s0
מודולים של ליבת מערכת ההפעלה
כדי להטמיע מודולים של ליבת מערכת ההפעלה ב-APEX של ספק כמודולים מוכנים מראש, פועלים לפי השלבים הבאים.
prebuilt_etc {
name: "my.ko",
src: "my.ko",
vendor: true,
sub_dir: "modules"
}
apex {
..
vendor: true,
prebuilts: ["my.ko"], // installed inside APEX as /etc/modules/my.ko
..
}
השדה file_contexts
של APEX צריך לתייג בצורה נכונה את כל רשומות המטען הייעודי (payload) של מודול הליבה. לדוגמה:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
צריך להתקין מודולים של ליבת המערכת באופן מפורש. בדוגמה הבאה מוצג סקריפט init במחיצת הספק, שמראה התקנה באמצעות insmod
:
my_init.rc
:
on early-boot
insmod /apex/myapex/etc/modules/my.ko
..
חבילות של שכבות-על בזמן ריצה (RRO)
דוגמה:
הטמעת שכבות-על של משאבים בזמן ריצה ב-APEX של ספק באמצעות המאפיין rros
.
runtime_resource_overlay {
name: "my_rro",
soc_specific: true,
}
apex {
..
vendor: true,
rros: ["my_rro"], // installed inside APEX as /overlay/my_rro.apk
..
}
קבצים אחרים של הגדרות
מודולי APEX של ספקים תומכים בקובצי הגדרה שונים אחרים שנמצאים בדרך כלל במחיצת הספק כקובצי prebuilt בתוך מודולי APEX של ספקים, ונוספים עוד קבצים כאלה.
דוגמאות:
- קובצי XML של הצהרת תכונות
- תכונת החיישנים כוללת קובצי XML כרכיבים מוכנים מראש ב-APEX של ספק HAL של חיישן
- קבצי תצורה של קלט
- הגדרות מסך מגע כרכיבים מוכנים מראש ב-APEX של ספק עם הגדרה בלבד
אתחול של קובצי APEX של ספקים
חלק משירותי HAL, כמו keymint
, צריכים להיות זמינים לפני שמפעילים את חבילות ה-APEX. בדרך כלל, קובצי ה-HAL האלה מוגדרים ב-early_hal
בהגדרת השירות שלהם בסקריפט ההפעלה. דוגמה נוספת היא מחלקת animation
, שבדרך כלל מתחילה לפני אירוע post-fs-data
. כששירות HAL מוקדם כזה נארז ב-APEX של הספק, צריך להוסיף את ה-APEX "vendorBootstrap": true
למניפסט ה-APEX שלו כדי שאפשר יהיה להפעיל אותו בשלב מוקדם יותר. שימו לב שאפשר להפעיל bootstrap APEXes רק מהמיקום המובנה כמו /vendor/apex
, ולא מ-/data/apex
.
מאפייני מערכת
אלה מאפייני המערכת שהמסגרת קוראת כדי לתמוך ב-APEX של ספקים:
-
input_device.config_file.apex=<apex name>
– אם ההגדרה הזו מופעלת, המערכת מחפשת את קובצי התצורה של הקלט (*.idc
,*.kl
ו-*.kcm
) בספרייה/etc/usr
של ה-APEX. -
ro.vulkan.apex=<apex name>
– אם הערך מוגדר, מנהל ההתקן של Vulkan נטען מ-APEX. מכיוון שמנהל ההתקן של Vulkan משמש ל-HAL מוקדמים, צריך ליצור את Bootstrap APEX של APEX ולהגדיר את מרחב השמות של המקשר כך שיהיה גלוי.
מגדירים את מאפייני המערכת בסקריפטים של init באמצעות הפקודה setprop
.
תכונות נוספות
בחירת APEX בזמן האתחול
דוגמה:
אפשר להפעיל את ספקי ה-APEX במהלך האתחול.
אם מציינים שם קובץ באמצעות מאפיין המערכת ro.vendor.apex.<apex name>
, רק ה-APEX שתואם לשם הקובץ מופעל עבור <apex name>
הספציפי.
המערכת מתעלמת מ-APEX עם <apex name>
(הוא לא מופעל) אם מאפיין המערכת הזה מוגדר ל-none
. אתם יכולים להשתמש בתכונה הזו כדי להתקין כמה עותקים של APEX עם אותו שם. אם יש כמה גרסאות של אותו APEX, הן צריכות לחלוק את אותו מפתח.
תרחישים לדוגמה:
- התקנה של 3 גרסאות של ספק ה-APEX של ה-HAL של ה-Wi-Fi: צוותי QA יכולים להריץ בדיקות ידניות או אוטומטיות באמצעות גרסה אחת, ואז להפעיל מחדש לגרסה אחרת ולהריץ מחדש את הבדיקות, ואז להשוות את התוצאות הסופיות.
- התקנה של 2 גרסאות של ספק ה-HAL של המצלמה APEX, הגרסה הנוכחית והגרסה הניסיונית: משתמשי Dogfood יכולים להשתמש בגרסה הניסיונית בלי להוריד ולהתקין קובץ נוסף, כך שהם יכולים לחזור בקלות לגרסה הקודמת.
במהלך האתחול, apexd
מחפש sysprops בפורמט ספציפי כדי להפעיל את גרסת ה-APEX הנכונה.
הפורמטים הנדרשים למפתח המאפיין הם:
- Bootconfig
- המאפיין משמש להגדרת ערך ברירת המחדל ב-
BoardConfig.mk
. androidboot.vendor.apex.<apex name>
- המאפיין משמש להגדרת ערך ברירת המחדל ב-
- מאפיין מערכת מתמשך
- משמש לשינוי ערך ברירת המחדל שמוגדר במכשיר שכבר הופעל.
- אם יש ערך, הוא מחליף את הערך של bootconfig.
persist.vendor.apex.<apex name>
ערך המאפיין צריך להיות שם הקובץ של ה-APEX שרוצים להפעיל, או none
כדי להשבית את ה-APEX.
// Default version.
apex {
name: "com.oem.camera.hal.my_apex_default",
vendor: true,
..
}
// Non-default version.
apex {
name: "com.oem.camera.hal.my_apex_experimental",
vendor: true,
..
}
צריך גם להגדיר את גרסת ברירת המחדל באמצעות bootconfig ב-BoardConfig.mk
:
# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default
אחרי שהמכשיר מופעל, משנים את הגרסה שהופעלה על ידי הגדרת sysprop מתמשך:
$ adb root;
$ adb shell setprop \
persist.vendor.apex.com.oem.camera.hal \
com.oem.camera.hal.my_apex_experimental;
$ adb reboot;
אם המכשיר תומך בעדכון של bootconfig אחרי ההפעלה (למשל באמצעות פקודות fastboot
oem
), שינוי המאפיין bootconfig עבור APEX שהותקן בכמה מקומות משנה גם את הגרסה שמופעלת בהפעלה.
במכשירי הפניה וירטואליים שמבוססים על Cuttlefish, אפשר להשתמש בפקודה --extra_bootconfig_args
כדי להגדיר את המאפיין bootconfig ישירות במהלך ההפעלה. לדוגמה:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";