การบังคับใช้อินเทอร์เฟซพาร์ติชันผลิตภัณฑ์

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

การบังคับใช้เวลาสร้าง (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 libs ได้

ตารางต่อไปนี้สรุปคุณสมบัติ Android.bp ที่ใช้ในการสร้างรูปแบบรูปภาพ

คุณสมบัติใน Android.bp สร้างสายพันธุ์แล้ว
ก่อนบังคับใช้ ภายหลังการบังคับใช้
ค่าเริ่มต้น (ไม่มี) แกนกลาง

(รวมถึง /system , /system_ext และ /product )

แกนกลาง

(รวมถึง /system และ /system_ext แต่ไม่ใช่ /product )

system_ext_specific: true แกนกลาง แกนกลาง
product_specific: true แกนกลาง ผลิตภัณฑ์
vendor: true ผู้ขาย ผู้ขาย
vendor_available: true หลักผู้ขาย หลักผู้ขาย
product_available: true ไม่มี ผลิตภัณฑ์หลัก
vendor_available: true และ product_available: true ไม่มี แกนหลักผลิตภัณฑ์ผู้ขาย
system_ext_specific: true และ vendor_available: true หลักผู้ขาย หลักผู้ขาย
product_specific: true และ vendor_available: true หลักผู้ขาย สินค้า, ผู้ขาย

การบังคับใช้เวลาสร้าง (Android.mk)

เมื่อเปิดใช้งานการบังคับใช้อินเทอร์เฟซดั้งเดิม โมดูลดั้งเดิมที่ติดตั้งในพาร์ติชัน product จะมีประเภทลิงก์ native:product ที่สามารถลิงก์กับ native:product หรือ native:vndk อื่น ๆ เท่านั้น การพยายามลิงก์ไปยังโมดูลใดๆ นอกเหนือจากนี้จะทำให้ระบบบิลด์สร้างข้อผิดพลาดในการตรวจสอบประเภทลิงก์

การบังคับใช้รันไทม์

เมื่อเปิดใช้งานการบังคับใช้อินเทอร์เฟซดั้งเดิม การกำหนดค่าตัวเชื่อมโยงสำหรับตัวเชื่อมโยงไบโอนิคจะไม่อนุญาตให้กระบวนการของระบบใช้ไลบรารี product การสร้างส่วน product สำหรับกระบวนการ product ที่ไม่สามารถลิงก์ไปยังไลบรารีภายนอกพาร์ติชัน product (อย่างไรก็ตาม กระบวนการดังกล่าวสามารถทำได้ เชื่อมโยงไปยังห้องสมุด VNDK) ความพยายามที่จะละเมิดการกำหนดค่าลิงก์รันไทม์ทำให้กระบวนการล้มเหลวและสร้างข้อความแสดงข้อผิดพลาด CANNOT LINK EXECUTABLE

การบังคับใช้อินเทอร์เฟซ Java

หากต้องการเปิดใช้งานการบังคับใช้อินเทอร์เฟซ Java ให้ตั้งค่า PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE เป็น true (ค่าจะถูกตั้งค่าเป็น true โดยอัตโนมัติเมื่อระดับ API การจัดส่งสำหรับเป้าหมายมากกว่า 29) เมื่อเปิดใช้งาน การบังคับใช้จะอนุญาต/ไม่อนุญาตการเข้าถึงต่อไปนี้

เอพีไอ /ระบบ /system_ext /ผลิตภัณฑ์ /ผู้ขาย /ข้อมูล
API สาธารณะ
@SystemApi
@ซ่อน API

เช่นเดียวกับในพาร์ติชัน vendor แอปหรือไลบรารี Java ในพาร์ติชัน product ได้รับอนุญาตให้ใช้เฉพาะ API สาธารณะและของระบบเท่านั้น ไม่อนุญาตให้ลิงก์ไปยังไลบรารีที่ใช้ API ที่ซ่อนอยู่ ข้อจำกัดนี้รวมถึงการลิงก์ ณ เวลาบิลด์และการสะท้อนกลับในรันไทม์

สร้างการบังคับใช้เวลา

ณ เวลาสร้าง Make และ Soong จะตรวจสอบว่าโมดูล Java ในพาร์ติชัน product ไม่ได้ใช้ API ที่ซ่อนอยู่โดยการตรวจสอบฟิลด์ platform_apis และ sdk_version sdk_version ของแอปในพาร์ติชัน product จะต้องเต็มไปด้วย API เวอร์ชัน current , system_current หรือตัวเลข และฟิลด์ platform_apis จะต้องว่างเปล่า

การบังคับใช้รันไทม์

รันไทม์ของ Android จะตรวจสอบว่าแอปในพาร์ติชัน product ไม่ได้ใช้ API ที่ซ่อนอยู่รวมถึงการสะท้อนกลับด้วย สำหรับรายละเอียด โปรดดู ข้อจำกัดเกี่ยวกับอินเทอร์เฟซที่ไม่ใช่ SDK

การเปิดใช้งานการบังคับใช้อินเทอร์เฟซผลิตภัณฑ์

ใช้ขั้นตอนในส่วนนี้เพื่อเปิดใช้งานการบังคับใช้อินเทอร์เฟซผลิตภัณฑ์

ขั้นตอน งาน ที่จำเป็น
1 กำหนด makefile ระบบของคุณเองที่ระบุแพ็คเกจสำหรับพาร์ติชัน system จากนั้นตั้งค่าการตรวจสอบข้อกำหนดพาธของอาร์ติแฟกต์ใน device.mk (เพื่อป้องกันไม่ให้โมดูลที่ไม่ใช่ระบบติดตั้งลงในพาร์ติชัน system ) เอ็น
2 ทำความสะอาดรายการที่อนุญาต เอ็น
3 บังคับใช้อินเทอร์เฟซดั้งเดิมและระบุความล้มเหลวของลิงก์รันไทม์ (สามารถทำงานคู่ขนานกับการบังคับใช้ Java)
4 บังคับใช้อินเทอร์เฟซ Java และตรวจสอบพฤติกรรมรันไทม์ (สามารถทำงานคู่ขนานกับการบังคับใช้ดั้งเดิม)
5 ตรวจสอบพฤติกรรมรันไทม์
6 อัปเดต device.mk ด้วยการบังคับใช้อินเทอร์เฟซผลิตภัณฑ์

ขั้นตอนที่ 1: สร้าง makefile และเปิดใช้งานการตรวจสอบเส้นทางสิ่งประดิษฐ์

ในขั้นตอนนี้ คุณกำหนด makefile system

  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 ระบบบิลด์จะป้องกันไม่ให้แพ็คเกจที่กำหนดใน makefiles อื่นติดตั้งไปยังพาธที่กำหนดใน require-artifacts-in-path และ ป้องกันแพ็คเกจที่กำหนดใน makefile ปัจจุบันจากการติดตั้งส่วนนอกพาธที่กำหนดใน require-artifacts-in-path

ในตัวอย่างข้างต้น เมื่อตั้งค่า PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS เป็น strict แล้ว makefiles ภายนอก oem_system.mk จะไม่สามารถรวมโมดูลที่ติดตั้งไว้ที่ root หรือพาร์ติชัน system ได้ หากต้องการรวมโมดูลเหล่านี้ คุณต้องกำหนดโมดูลเหล่านั้นในไฟล์ oem_system.mk เองหรือใน makefile ที่รวมไว้ ความพยายามที่จะติดตั้งโมดูลไปยังเส้นทางที่ไม่ได้รับอนุญาตทำให้เกิดการหยุดทำงานของบิลด์ หากต้องการแก้ไขการหยุดพัก ให้ทำอย่างใดอย่างหนึ่งต่อไปนี้:

  • อ็อพชันที่ 1: รวมโมดูลระบบใน makefiles ที่รวมอยู่ใน 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 หรือเพิ่มลงในไฟล์ make system ขั้นตอนนี้เป็นทางเลือกเนื่องจากไม่จำเป็นต้องกำหนดอิมเมจ system ทั่วไปเพื่อเปิดใช้งานการบังคับใช้อินเทอร์เฟซผลิตภัณฑ์ อย่างไรก็ตาม การล้างรายการที่อนุญาตจะเป็นประโยชน์ในการกำหนดขอบเขต system ด้วย system_ext

ขั้นตอนที่ 3: บังคับใช้อินเทอร์เฟซดั้งเดิม

ในขั้นตอนนี้ คุณจะต้องตั้งค่า PRODUCT_PRODUCT_VNDK_VERSION := current จากนั้นค้นหาข้อผิดพลาดของบิลด์และรันไทม์และแก้ไข หากต้องการตรวจสอบการบูตและบันทึกของอุปกรณ์ ตลอดจนค้นหาและแก้ไขความล้มเหลวของลิงก์รันไทม์ ให้ทำดังนี้

  1. ตั้งค่า PRODUCT_PRODUCT_VNDK_VERSION := current

  2. สร้างอุปกรณ์และค้นหาข้อผิดพลาดในการสร้าง คุณมีแนวโน้มที่จะเห็นการแตกของบิลด์เล็กน้อยเนื่องจากตัวเลือกสินค้าหรือตัวเลือกสินค้าหลักหายไป การหยุดพักโดยทั่วไปได้แก่:

    • โมดูล hidl_interface ใดๆ ที่มี product_specific: true จะไม่สามารถใช้ได้สำหรับโมดูลระบบ หากต้องการแก้ไข ให้แทนที่ product_specific: true ด้วย system_ext_specfic: true
    • โมดูลอาจขาดตัวเลือกสินค้าที่จำเป็นสำหรับโมดูลผลิตภัณฑ์ ในการแก้ไข ให้ทำให้โมดูลนั้นพร้อมใช้งานสำหรับพาร์ติชัน product โดยการตั้งค่า product_available: true หรือย้ายโมดูลไปยังพาร์ติชัน product โดยการตั้ง product_specific: true
  3. แก้ไขข้อผิดพลาดในการสร้างและตรวจสอบให้แน่ใจว่าอุปกรณ์สร้างได้สำเร็จ

  4. แฟลชรูปภาพและค้นหาข้อผิดพลาดรันไทม์ในการบูตอุปกรณ์และบันทึก

    • หากแท็ก linker จากบันทึกกรณีทดสอบแสดงข้อความ CANNOT LINK EXECUTABLE แสดงว่าไฟล์ make ขาดการอ้างอิง (และไม่ได้บันทึกไว้ในขณะสร้าง)
    • หากต้องการตรวจสอบจากระบบบิลด์ ให้เพิ่มไลบรารีที่จำเป็นลงในฟิลด์ 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}
    
  • ตัวอย่างผลการตรวจสอบ:

    #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