אכיפה של ממשקי מחיצות של מוצרים

ב-Android 11, המחיצה product מופרדת, כך שהיא לא תלויה במחיצות system ו-vendor. כחלק מהשינויים האלה, עכשיו אפשר לשלוט בגישה של מחיצת product לממשקים מקוריים ולממשקי Java (בדומה לאופן שבו נאכפת גישה לממשקים במחיצות vendor).

אכיפת ממשקים מקוריים

כדי להפעיל את האכיפה של הממשק המקומי, מגדירים את PRODUCT_PRODUCT_VNDK_VERSION לערך current. (הגרסה מוגדרת אוטומטית ל-current אם רמת ה-API של המשלוח ליעד גבוהה מ-29). האכיפה מאפשרת:

  • מודולים מותאמים במחיצה product לקישור:
    • באופן סטטי או דינמי למודולים אחרים במחיצה product שכוללים ספריות סטטיות, משותפות או של כותרות.
    • באופן דינמי לספריות VNDK במחיצה system.
  • ספריות JNI ב-APK לא ארוז במחיצה 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 הווריאציות נוצרו
לפני האכיפה אחרי האכיפה
ברירת מחדל (ללא) core
(כולל /system, ‏ /system_ext ו-/product)
core
(כולל /system ו-/system_ext אבל לא /product)
system_ext_specific: true core core
product_specific: true core מכפלה
vendor: true ספק ספק
vendor_available: true core, vendor core, vendor
product_available: true לא רלוונטי core, product
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 linker לא מאפשרת לתהליכי מערכת להשתמש בספריות 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
Public API
@SystemApi
‫@hide API

כמו במחיצה vendor, אפליקציה או ספריית Java במחיצה product יכולות להשתמש רק בממשקי API ציבוריים וממשקי API של המערכת. אסור לקשר לספרייה שמשתמשת בממשקי API מוסתרים. ההגבלה הזו כוללת קישור בזמן הבנייה ושיקוף בזמן הריצה.

אכיפה בזמן הבנייה

בזמן הבנייה, מערכות Make ו-Soong בודקות את השדות platform_apis ו-sdk_version כדי לוודא שמודולי Java במחיצה product לא משתמשים בממשקי API מוסתרים. הערך sdk_version של אפליקציות במחיצה product חייב להיות current,‏ system_current או גרסה מספרית של ה-API, והשדה platform_apis חייב להיות ריק.

אכיפה בזמן ריצה

סביבת זמן הריצה של Android מאמתת שאפליקציות במחיצה product לא משתמשות בממשקי API מוסתרים, כולל רפלקציה. פרטים נוספים זמינים במאמר בנושא הגבלות על ממשקי API שאינם SDK.

הפעלת אכיפה של ממשק המוצר

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

שלב משימה חובה
1 מגדירים קובץ makefile משלכם למערכת שמציין את החבילות עבור המחיצה system, ואז מגדירים את בדיקת הדרישה של נתיב הארטיפקטים ב-device.mk (כדי למנוע התקנה של מודולים שאינם של המערכת במחיצה system). N
2 מפנים את רשימת ההיתרים. N
3 אכיפה של ממשקי מקומיים וזיהוי של כשלים בקישורי זמן ריצה (אפשר להריץ במקביל לאכיפה של Java). Y
4 אכיפה של ממשקי Java ואימות של התנהגות בזמן ריצה (אפשר להפעיל במקביל לאכיפה מקורית). Y
5 בודקים את ההתנהגויות בזמן הריצה. Y
6 עדכון device.mk עם אכיפה של ממשק מוצר. Y

שלב 1: יצירת קובץ makefile והפעלת בדיקה של נתיב הארטיפקט

בשלב הזה מגדירים את system makefile.

  1. יוצרים קובץ 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),)
    
  2. בקובץ device.mk, מעבירים בירושה את קובץ ה-Makefile המשותף למחיצה system ומפעילים את בדיקת הדרישות של נתיב הארטיפקט. לדוגמה:

    $(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 ו מונעת התקנה של חבילות שהוגדרו בקובץ ה-makefile הנוכחי בארטיפקטים מחוץ לנתיבים שהוגדרו ב-require-artifacts-in-path.

בדוגמה שלמעלה, אם PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS מוגדר כ-strict, קובצי makefile מחוץ ל-oem_system.mk לא יכולים לכלול מודולים שהותקנו במחיצה root או system. כדי לכלול את המודולים האלה, צריך להגדיר אותם בקובץ oem_system.mk עצמו או בקובץ makefile שכלול בו. ניסיונות להתקין מודולים בנתיבים אסורים גורמים להפסקת הבנייה. כדי לתקן את המעברים, מבצעים אחת מהפעולות הבאות:

  • אפשרות 1: כוללים את מודול המערכת בקובצי ה-Makefile שכלולים ב-oem_system.mk. כך מתקיימת הדרישה לגבי נתיב הארטיפקט (כי המודולים קיימים עכשיו בקובץ makefile שכלול), ולכן אפשר להתקין את קבוצת הנתיבים ב-`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, או מוסיפים אותם ל-system make files. השלב הזה הוא אופציונלי כי לא צריך להגדיר תמונה משותפת של system כדי להפעיל את האכיפה של ממשק המוצר. עם זאת, ריקון הרשימה המותרת עוזר להגדיר את הגבול של system באמצעות system_ext.

שלב 3: אכיפה של ממשקי משתמש מקוריים

בשלב הזה מגדירים את PRODUCT_PRODUCT_VNDK_VERSION := current, מחפשים שגיאות build ושגיאות runtime ופותרים אותן. כדי לבדוק את אתחול המכשיר ואת היומנים, ולמצוא ולתקן כשלים בקישור בזמן ריצה:

  1. מגדירים את PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. מבצעים את הבנייה של המכשיר ומחפשים שגיאות בנייה. סביר להניח שתיתקלו בכמה בעיות בבנייה בגלל וריאציות מוצרים או וריאציות ליבה חסרות. הפסקות נפוצות:

    • כל מודול hidl_interface עם product_specific: true לא יהיה זמין למודולים של המערכת. כדי לתקן את הבעיה, מחליפים את product_specific: true ב-system_ext_specific: true.
    • יכול להיות שבמודולים חסרה וריאציית המוצר שנדרשת למודולים של מוצרים. כדי לפתור את הבעיה, צריך להגדיר את product_available: true כדי שהמודול יהיה זמין למחיצה product, או להעביר את המודול למחיצה product על ידי הגדרת product_specific: true.
  3. צריך לפתור את שגיאות הבנייה ולוודא שהמכשיר נבנה בהצלחה.

  4. מבצעים Flash לתמונה ומחפשים שגיאות בזמן ריצה באתחול המכשיר וביומנים.

    • אם התג linker מיומן של מקרה בדיקה מציג הודעה CANNOT LINK EXECUTABLE, חסרה תלות בקובץ ה-make (והיא לא נלכדה בזמן ה-build).
    • כדי לבדוק את זה ממערכת ה-build, מוסיפים את הספרייה הנדרשת לשדה shared_libs: או required:.
  5. פותרים את הבעיות שקשורות לתלות חסרה באמצעות ההנחיות שלמעלה.

שלב 4: אוכפים ממשקי Java

בשלב הזה, מגדירים את PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, ואז מוצאים ומתקנים את שגיאות הבנייה שנובעות מכך. מחפשים שני סוגים ספציפיים של שגיאות:

  • שגיאות בסוג הקישור. השגיאה הזו מציינת שאפליקציה מקשרת למודולי 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

אחרי שמתקנים את כל הכשלים בבנייה ובזמן הריצה, ומוודאים שההתנהגויות בזמן הריצה הן כמצופה, מגדירים את הערכים הבאים ב-device.mk:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true