ב-Android 11, המחיצה product
מנותקת מהמחיצות system
ו-vendor
. במסגרת השינויים האלה, עכשיו אפשר לשלוט בגישה של המחיצה product
לממשקים מקומיים ולממשקי Java (בדומה לאופן שבו אכיפת הממשק פועלת במחיצות vendor
).
אכיפת ממשקים מקומיים
כדי להפעיל את אכיפת הממשק המקומי, מגדירים את PRODUCT_PRODUCT_VNDK_VERSION
לערך current
. (הגרסת מוגדרת באופן אוטומטי ל-current
כשרמת ה-API של המשלוח ליעד גבוהה מ-29). אכיפה מאפשרת:
- מודולים מקוריים במחיצה
product
לקישור:- באופן סטטי או דינמי למודול אחרים במחיצה
product
שכוללים ספריות סטטיות, ספריות משותפות או ספריות כותרות. - באופן דינמי לספריות VNDK במחיצה
system
.
- באופן סטטי או דינמי למודול אחרים במחיצה
- ספריות JNI ב-APKs לא מקוובצים במחיצה
product
כדי לקשר לספריות ב-/product/lib
או ב-/product/lib64
(בנוסף לספריות NDK).
האכיפה לא מאפשרת קישורים אחרים למחיצות מלבד המחיצה product
.
אכיפת זמן build (Android.bp)
ב-Android 11, מודולים של מערכת יכולים ליצור וריאנט של קובץ אימג' של מוצר, בנוסף לווריאנטים של קובץ אימג' ליבה וקובץ אימג' של ספק. כשמופעלת אכיפה של ממשק נייטיב (PRODUCT_PRODUCT_VNDK_VERSION
מוגדר ל-current
):
מודולים מקומיים במחיצה
product
נמצאים בגרסת המוצר במקום בגרסת הליבה.מודולים עם
product_available: true
בקובציAndroid.bp
זמינים לווריאציית המוצר.ספריות או קבצים בינאריים שצוין בהם
product_specific: true
יכולים לקשר לספריות אחרות שצוין בהןproduct_specific: true
אוproduct_available: true
בקבצים שלהן מסוגAndroid.bp
.בספריות VNDK צריך להופיע
product_available: true
בקובציAndroid.bp
שלהן, כדי שקבצים בינאריים שלproduct
יוכלו לקשר לספריות VNDK.
בטבלה הבאה מפורט סיכום של המאפיינים Android.bp
שמשמשים ליצירת וריאנטים של תמונות.
מאפיינים ב-Android.bp | הווריאציות נוצרו | |
---|---|---|
לפני האכיפה | אחרי האכיפה | |
ברירת מחדל (ללא) | הליבה
(כולל /system , /system_ext ו-/product ) |
הליבה (כולל /system ו-/system_ext , אבל לא את /product ) |
system_ext_specific: true |
ליבה | ליבה |
product_specific: true |
התוכן הקבוע | מוצר |
vendor: true |
ספק | ספק |
vendor_available: true |
ליבה, ספק | core, vendor |
product_available: true |
לא רלוונטי | ליבה, מוצר |
vendor_available: true וגם product_available:
true |
לא רלוונטי | core, product, vendor |
system_ext_specific: true וגם vendor_available:
true |
core, vendor | core, vendor |
product_specific: true וגם vendor_available:
true |
core, vendor | מוצר, ספק |
אכיפה בזמן ה-build (Android.mk)
כשמופעלת אכיפה של הממשק המקורי, המודולים המקוריים שהותקנו במחיצה product
כוללים קישור מסוג native:product
שאפשר לקשר רק למודולים אחרים של native:product
או native:vndk
. ניסיון לקשר למודולים אחרים מלבד אלה יגרום למערכת ה-build ליצור שגיאה בבדיקת סוג הקישור.
אכיפה של זמן ריצה
כשהאכיפה של ממשק מקורי מופעלת, הגדרת הקישור של ה-linker של Bionic לא מאפשרת לתהליכי המערכת להשתמש בספריות product
, ויוצרת קטע product
לתהליכי product
שלא יכולים לקשר לספריות מחוץ למחיצה product
(עם זאת, תהליכים כאלה יכולים לקשר לספריות VNDK). ניסיונות להפר את הגדרת הקישור בסביבת זמן הריצה גורמים לכך שהתהליך נכשל ומונפקת הודעת השגיאה CANNOT LINK EXECUTABLE
.
אכיפת ממשקי Java
כדי להפעיל את האכיפה של ממשק Java, מגדירים את PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE
ל-true
. (הערך מוגדר באופן אוטומטי ל-true
כשרמת ה-API של שירותי המשלוח ליעד גבוהה מ-29). כשהאכיפה מופעלת, היא מאפשרת או אוסרת את הגישה הבאה:
API | /system | /system_ext | /product | /vendor | /data |
---|---|---|---|---|---|
ממשק API ציבורי | |||||
@SystemApi | |||||
@hide API |
כמו במחיצה vendor
, אפליקציה או ספריית Java במחיצה product
יכולות להשתמש רק בממשקי API ציבוריים וממשקי API של מערכת. אסור לקשר לספרייה שמשתמשת בממשקי API מוסתרים. ההגבלה הזו כוללת קישור בזמן ה-build והשתקפות בזמן הריצה.
אכיפת זמן Build
בזמן ה-build, ה-Make וה-Soong בודקים את השדות platform_apis
ו-sdk_version
כדי לוודא שממשקי ה-API המוסתרים לא נמצאים בשימוש במודולים של Java במחיצה product
. השדה sdk_version
של האפליקציות במחיצה product
צריך להיות מלא ב-current
, ב-system_current
או בגרסה מספרית של ה-API, והשדה platform_apis
צריך להיות ריק.
אכיפה של זמן ריצה
סביבת זמן הריצה של Android מאמתת שהאפליקציות במחיצה product
לא משתמשות בממשקי API מוסתרים, כולל השתקפות. פרטים נוספים זמינים במאמר הגבלות על ממשקים שאינם SDK.
הפעלת אכיפה של ממשק המוצר
כדי להפעיל את האכיפה של ממשק המוצר, פועלים לפי השלבים שמפורטים בקטע הזה.
שלב | משימה | חובה |
---|---|---|
1 | מגדירים קובץ makefile משלכם למערכת שמציין את החבילות למחיצה system , ואז מגדירים את בדיקת דרישות הנתיב של הארטיפקטים ב-device.mk (כדי למנוע התקנה של מודולים שאינם מערכת במחיצה system ). |
לא |
2 | ניקוי רשימת ההיתרים. | לא |
3 | לאכוף ממשקים נייטיב ולזהות כשלים בקישור בזמן ריצה (יכול לרוץ במקביל לאכיפה של Java). | Y |
4 | אכיפת ממשקי Java ואימות התנהגות בסביבת זמן ריצה (אפשר להפעיל במקביל אכיפה מותאמת). | Y |
5 | בדיקת התנהגויות של סביבת זמן ריצה. | Y |
6 | מעדכנים את device.mk באמצעות אכיפה של ממשק המוצר. |
Y |
שלב 1: יוצרים קובץ makefile ומפעילים בדיקה של נתיב הארטיפקט
בשלב הזה מגדירים את קובץ ה-makefile system
.
יוצרים קובץ makefile שמגדיר את החבילות למחיצה
system
. לדוגמה, יוצרים קובץoem_system.mk
עם הפרטים הבאים:$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) # Applications PRODUCT_PACKAGES += \ CommonSystemApp1 \ CommonSystemApp2 \ CommonSystemApp3 \ # Binaries PRODUCT_PACKAGES += \ CommonSystemBin1 \ CommonSystemBin2 \ CommonSystemBin3 \ # Libraries PRODUCT_PACKAGES += \ CommonSystemLib1 \ CommonSystemLib2 \ CommonSystemLib3 \ PRODUCT_SYSTEM_NAME := oem_system PRODUCT_SYSTEM_BRAND := Android PRODUCT_SYSTEM_MANUFACTURER := Android PRODUCT_SYSTEM_MODEL := oem_system PRODUCT_SYSTEM_DEVICE := generic # For system-as-root devices, system.img should be mounted at /, so we # include ROOT here. _my_paths := \ $(TARGET_COPY_OUT_ROOT)/ \ $(TARGET_COPY_OUT_SYSTEM)/ \ $(call require-artifacts-in-path, $(_my_paths),)
בקובץ
device.mk
, יורשים את קובץ ה-getfile המשותף של המחיצהsystem
ומפעילים את בדיקת הדרישות של הנתיב של פריט המידע שנוצר בתהליך הפיתוח (Artifact). לדוגמה:$(call inherit-product, $(SRC_TARGET_DIR)/product/oem_system.mk) # Enable artifact path requirements checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := strict
מידע על הדרישות לגבי נתיב הארטיפקט
כשההגדרה של PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
מוגדרת לערך true
או לערך strict
, מערכת ה-build מונעת התקנה של חבילות שהוגדרו בקובצי makefile אחרים בנתיבים שהוגדרו ב-require-artifacts-in-path
וגם מונעת מחבילות שמוגדרות בקובץ ה-set הנוכחי להתקין ארטיפקטים מחוץ לנתיבים שהוגדרו ב-require-artifacts-in-path
.
בדוגמה שלמעלה, כשהערך של PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
מוגדר כ-strict
, קובצי makefile מחוץ ל-oem_system.mk
לא יכולים לכלול מודולים שהותקנו במחיצה root
או system
. כדי לכלול את המודולים האלה, צריך להגדיר אותם בקובץ oem_system.mk
עצמו או בקובץ makefile שכלול.
ניסיונות להתקין מודולים בנתיבים אסורים גורמים להפסקות ב-build. כדי לתקן את ההפסקות, מבצעים אחת מהפעולות הבאות:
אפשרות 1: כוללים את מודול המערכת בקובצי ה-makefile שכלולים ב-
oem_system.mk
. כתוצאה מכך, המערכת עומדת בדרישה של הנתיב של פריט המידע שנוצר בתהליך הפיתוח (Artifact) (כי המודולים קיימים עכשיו בקובץ getfile כלול) ולכן אפשר להתקין את קבוצת הנתיבים ב-'Require-artifacts-in-path'.אפשרות 2: מתקינים מודולים במחיצה
system_ext
אוproduct
(ולא מתקינים מודולים במחיצהsystem
).אפשרות 3: מוסיפים מודולים ל-
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
. ברשימה הזו מפורטים המודולים שמותר להתקין.
שלב 2: מרוקנים את רשימת ההיתרים
בשלב הזה, משאירים את PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
ריק כדי שכל המכשירים שמשתפים את oem_system.mk
יוכלו לשתף גם תמונה אחת של system
. כדי לרוקן את רשימת ההיתרים, מעבירים את המודולים ברשימה למחיצה system_ext
או product
, או מוסיפים אותם לקובצי make של system
. השלב הזה הוא אופציונלי כי לא צריך להגדיר קובץ system
משותף כדי להפעיל אכיפה של ממשק המוצר. עם זאת, ריקון רשימת ההיתרים עוזר להגדיר את הגבול של system
באמצעות system_ext
.
שלב 3: אכיפת ממשקים מותאמים
בשלב הזה מגדירים את PRODUCT_PRODUCT_VNDK_VERSION := current
, מחפשים שגיאות ב-build ובזמן הריצה ומתקנים אותן. כדי לבדוק את ההפעלה ואת היומנים של המכשיר, ולמצוא ולתקן כשלים בקישורים בסביבת זמן הריצה:
מגדירים את
PRODUCT_PRODUCT_VNDK_VERSION := current
.יוצרים את המכשיר ומחפשים שגיאות ב-build. סביר להניח שתראו כמה מעברי build חסרים בו וריאציות מוצר או וריאציות ליבה. הפסקות נפוצות כוללות:
- כל מודול של
hidl_interface
שכולל אתproduct_specific: true
לא יהיה זמין למודולים של המערכת. כדי לתקן את הבעיה, מחליפים אתproduct_specific: true
ב-system_ext_specific: true
. - יכול להיות שבמודולים חסרה הווריאציה של המוצר שנדרשת למודול המוצר. כדי לפתור את הבעיה, צריך להגדיר את המודול הזה כזמין למחיצה
product
באמצעות ההגדרהproduct_available: true
, או להעביר את המודול למחיצהproduct
באמצעות ההגדרהproduct_specific: true
.
- כל מודול של
פותרים שגיאות ב-build ומוודאים שה-build של המכשיר הצליח.
צריך להעלות את התמונה להבהוב ולחפש שגיאות בזמן הריצה באתחול המכשיר וביומנים.
- אם בתג
linker
ביומן של מקרי הבדיקה מוצגת הודעהCANNOT LINK EXECUTABLE
, חסרה תלות בקובץ ה-create (והוא לא תועד בזמן ה-build). - כדי לבדוק את זה ממערכת ה-build, מוסיפים את הספרייה הנדרשת לשדה
shared_libs:
אוrequired:
.
- אם בתג
פותרים את יחסי התלות החסרים לפי ההנחיות שלמעלה.
שלב 4: אכיפה של ממשקי Java
בשלב הזה מגדירים את PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true
, ואז מאתרים את שגיאות ה-build שנוצרו ותוקנים אותן. מחפשים שני סוגים ספציפיים של שגיאות:
שגיאות מסוג קישור השגיאה הזו מציינת שאפליקציה מקשרת למודולים של Java בעלי
sdk_version
רחב יותר. כדי לפתור את הבעיה, אפשר להרחיב אתsdk_version
של האפליקציה או להגביל אתsdk_version
של הספרייה. שגיאה לדוגמה:error: frameworks/base/packages/SystemUI/Android.bp:138:1: module "SystemUI" variant "android_common": compiles against system API, but dependency "telephony-common" is compiling against private API.Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.
שגיאות בסמל השגיאה הזו מציינת שאי אפשר למצוא סמל כי הוא נמצא ב-API מוסתר. כדי לפתור את הבעיה, צריך להשתמש בממשק API גלוי (לא מוסתר) או למצוא חלופה. שגיאה לדוגמה:
frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java:1051: error: cannot find symbol ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( ^ symbol: class ProxyAuthenticate location: class SipSessionGroup.SipSessionImpl
שלב 5: בדיקת התנהגויות של סביבת זמן ריצה
בשלב הזה מוודאים שההתנהגויות בסביבת זמן הריצה תקינות. באפליקציות שאפשר לנפות בהן באגים, אפשר לעקוב אחרי השימוש ב-API מוסתר באמצעות יומן באמצעות StrictMode.detectNonSdkApiUsage
(שמייצר יומן כשהאפליקציה משתמשת ב-API מוסתר). לחלופין, אפשר להשתמש בכלי הניתוח הסטטי veridex כדי לקבל את סוג השימוש (קישור או שיקוף), רמת ההגבלה וסטאק הקריאות.
תחביר Veridex:
./art/tools/veridex/appcompat.sh --dex-file={apk file}
דוגמה לתוצאה של Veridex:
#1: Linking greylist-max-o Landroid/animation/AnimationHandler;-><init>()V use(s): Lcom/android/systemui/pip/phone/PipMotionHelper;-><init>(Landroid/content/Context;Landroid/app/IActivityManager;Landroid/app/IActivityTaskManager;Lcom/android/systemui/pip/phone/PipMenuActivityController;Lcom/android/internal/policy/PipSnapAlgorithm;Lcom/android/systemui/statusbar/FlingAnimationUtils;)V #1332: Reflection greylist Landroid/app/Activity;->mMainThread use(s): Landroidx/core/app/ActivityRecreator;->getMainThreadField()Ljava/lang/reflect/Field;
לפרטים על השימוש ב-Veridex, אפשר לעיין במאמר בדיקה באמצעות הכלי של Veridex.
שלב 6: מעדכנים את device.mk
אחרי שמתקנים את כל הכישלונות בזמן ה-build ובזמן הריצה, ומוודאים שההתנהגויות בזמן הריצה תואמות לציפיות, מגדירים את הפרמטרים הבאים ב-device.mk
:
PRODUCT_PRODUCT_VNDK_VERSION := current
PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true