ספק APEX

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

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

תרחישים לדוגמה

מודולריזציה של קובצי אימג' של ספקים

באמצעות APEXes אפשר לארוז ולפרוס באופן טבעי את הטמעות התכונות בתמונות של ספקים.

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

לדוגמה, יצרן ציוד מקורי יכול לבחור ליצור את המכשיר שלו באמצעות ההטמעה של AOSP ל-Wi-Fi, ההטמעה של SoC ל-Bluetooth וההטמעה של APEX לטלפון בהתאמה אישית של יצרן הציוד המקורי.

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

איטרציה למפתחים

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

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

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

הקיפול של אזור תכונות ל-APEX גם מפשט את ניפוי הבאגים או החזרה למצב הקודם כשמתגלה התנהגות לא תקינה במכשיר. לדוגמה, אם הטלפוניה פועלת בצורה לא טובה בגרסת build חדשה, המפתחים יכולים לנסות להתקין במכשיר גרסה ישנה יותר של 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, אלא אם יש להם ממשקים יציבים.

ממשקים מקומיים יציבים ליחסי תלות של ספקים ב-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 ולהתחיל סקריפטים.

קטעי VINTF

אפשר להציג קטעי 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",
}

ממשקי API לשאילתות

כשמקפידים להוסיף קטעי VINTF ל-APEX, משתמשים בממשקי ה-API של libbinder_ndk כדי לקבל את המיפויים של ממשקי 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.

סקריפט איפוס ב-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. מכיוון שסקריפטים ההתחלתיים ב-APEX מנותחים ומופעלים אחרי הפעלת APEX, אי אפשר להשתמש בחלק מהאירועים והמאפיינים. יש להשתמש ב-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 סורק את הספריות /apex/*/etc/firmware כדי למצוא מודולים של קושחת.

ה-file_contexts של ה-APEX צריך לתייג כראוי את כל הרשומות של עומסי העבודה של הקושחה כדי לוודא של-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 צריך לתייג כראוי את כל הרשומות של עומסי העבודה של מודול הליבה. לדוגמה:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

צריך להתקין מודולים של ליבה באופן מפורש. דוגמה לסקריפט האתחול במחיצת הספק מראה התקנה דרך insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

שכבות-על של משאבים בזמן ריצה

דוגמה:

מטמיעים שכבות-על של משאבים בזמן ריצה ב-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 של ספקים תומכים בקובצי תצורה שונים אחרים שבדרך כלל נמצאים במחיצה של הספק כניתנים מראש בתוך APEX של הספק, ועוד מתווספים עוד קבצים.

דוגמאות:

Bootstrap Vendor APEXes

חלק משירותי HAL, כמו keymint, אמורים להיות זמינים לפני הפעלת הרשתות הווירטואליות ברמה העליונה. בדרך כלל, ב-HALs האלה מוגדר early_hal בהגדרת השירות שלהם בסקריפט ה-init. דוגמה נוספת היא המחלקה animation, שמתחילה בדרך כלל לפני האירוע post-fs-data. כששירות HAL מוקדם כזה ארוז ב-APEX של הספק, צריך להפוך את הקצה "vendorBootstrap": true למניפסט ה-APEX שלו כדי שאפשר יהיה להפעיל אותו מוקדם יותר. שימו לב שאפשר להפעיל את ה-APEX של ה-bootstrap רק מהמיקום שנוצר מראש, כמו /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. מאחר שמנהלי HAL מוקדמים משתמשים ב-Vulkan driver, צריך ליצור את ה-APEX Bootstrap APEX ולהגדיר את מרחב השמות של ה-linker כך שיהיה גלוי.

מגדירים את מאפייני המערכת ב-init Script באמצעות הפקודה setprop.

תכונות פיתוח נוספות

בחירת APEX בזמן האתחול

דוגמה:

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

תרחישים לדוגמה:

  • התקנה של 3 גרסאות של APEX של WiFi HAL Vendor: צוותי בקרת איכות יכולים להריץ בדיקה ידנית או אוטומטית באמצעות גרסה אחת, ואז להפעיל מחדש בגרסה אחרת ולהריץ מחדש את הבדיקות, ולאחר מכן להשוות בין התוצאות הסופיות.
  • התקנה של 2 גרסאות של APEX של ספק ה-HAL של המצלמה, גרסה נוכחית וגרסה ניסיונית: משתמשים בתוכנית Dogfooding יכולים להשתמש בגרסה הניסיונית בלי להוריד ולהתקין קובץ נוסף, כך שהם יכולים לחזור לגרסה הקודמת בקלות.

במהלך האתחול, apexd מחפש sysprops בפורמט ספציפי כדי להפעיל את גרסת APEX המתאימה.

הפורמטים הנדרשים למפתח המאפיין הם:

  • Bootconfig
    • משמש להגדרת ערך ברירת המחדל, ב-BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • Persistent sysprop
    • משמש לשינוי ערך ברירת המחדל, שמוגדר במכשיר שכבר הופעל.
    • המדיניות הזו משנה את הערך startconfig, אם יש כזה.
    • persist.vendor.apex.<apex name>

הערך של המאפיין צריך להיות שם הקובץ של ה-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;

אם המכשיר תומך בעדכון אתחול תצורת האתחול לאחר הבהוב (למשל באמצעות פקודות fastboot oem), שינוי מאפיין האתחול ל-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";