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

Android 11 จะแยกproductพาร์ติชันออกจากกัน ทำให้productพาร์ติชันไม่ขึ้นอยู่กับsystemและvendorพาร์ติชัน ส่วนหนึ่งของการเปลี่ยนแปลงเหล่านี้คือ ตอนนี้คุณสามารถควบคุมการเข้าถึงอินเทอร์เฟซดั้งเดิมและ Java ของพาร์ติชัน product ได้แล้ว (ซึ่งคล้ายกับวิธีที่การบังคับใช้อินเทอร์เฟซทำงานสำหรับพาร์ติชัน 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 ได้

ตารางต่อไปนี้สรุปพร็อพเพอร์ตี้ 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 อื่นๆ การพยายามลิงก์ไปยังโมดูลอื่นๆ นอกเหนือจากโมดูลเหล่านี้จะทําให้ระบบบิลด์สร้างข้อผิดพลาดในการตรวจสอบประเภทลิงก์

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

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

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

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

API /system /system_ext /product /vendor /data
Public API
@SystemApi
@hide API

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

การบังคับใช้เวลาที่บิลด์ใช้

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

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

รันไทม์ Android จะยืนยันว่าแอปในพาร์ติชัน product ไม่ได้ใช้ 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 และเปิดใช้การตรวจสอบเส้นทางอาร์ติแฟกต์

ในขั้นตอนนี้ คุณจะกำหนด systemmakefile

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

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

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

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

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

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

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

    • หากแท็ก linker จากบันทึกของกรณีทดสอบแสดงCANNOT LINK EXECUTABLE ข้อความ แสดงว่าไฟล์ Make ไม่มีทรัพยากร Dependency (และไม่ได้บันทึกไว้ในเวลา สร้าง)
    • หากต้องการตรวจสอบจากระบบบิลด์ ให้เพิ่มไลบรารีที่จำเป็นลงในฟิลด์ shared_libs:หรือrequired:
  5. แก้ไขการขึ้นต่อกันที่ขาดหายไปโดยใช้คำแนะนำที่ระบุไว้ด้านบน

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

ในขั้นตอนนี้ คุณจะตั้งค่า PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true จากนั้นค้นหาและแก้ไขข้อผิดพลาดในการสร้างที่เกิดขึ้น มองหาข้อผิดพลาด 2 ประเภทต่อไปนี้

  • ข้อผิดพลาดเกี่ยวกับประเภทลิงก์ ข้อผิดพลาดนี้แสดงว่าแอปลิงก์กับโมดูล 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 ที่มองเห็นได้ (ไม่ใช่ 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