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

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

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

หากต้องการเปิดใช้การบังคับใช้อินเทอร์เฟซแบบเนทีฟ ให้ตั้งค่า PRODUCT_PRODUCT_VNDK_VERSIONเป็น current (ระบบจะตั้งค่าเวอร์ชันเป็น current โดยอัตโนมัติเมื่อระดับ Shipping 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 ผลิตภัณฑ์ย่อยที่สร้างแล้ว
ก่อนการบังคับใช้ หลังจากการบังคับใช้
ค่าเริ่มต้น (ไม่มี) core
(รวม /system, /system_ext และ /product)
core
(รวม /system และ /system_ext แต่ยกเว้น /product)
system_ext_specific: true แกนกลาง แกนกลาง
product_specific: true แกนกลาง ผลิตภัณฑ์
vendor: true ผู้จำหน่าย ผู้จำหน่าย
vendor_available: true หลัก, ผู้ให้บริการ หลัก, ผู้ให้บริการ
product_available: true ไม่มี core, product
vendor_available: true และ product_available: true ไม่มี core, product, vendor
system_ext_specific: true และ vendor_available: true หลัก, ผู้ให้บริการ หลัก, ผู้ให้บริการ
product_specific: true และ vendor_available: true หลัก, ผู้ให้บริการ ผลิตภัณฑ์ ผู้ให้บริการ

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

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

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

เมื่อเปิดใช้การบังคับใช้อินเทอร์เฟซแบบเนทีฟ การกําหนดค่า 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 จะยืนยันว่าโมดูล Java ในพาร์ติชัน product ไม่ได้ใช้ API ที่ซ่อนอยู่โดยตรวจสอบช่อง platform_apis และ sdk_version sdk_version ของแอปในพาร์ติชัน product ต้องกรอก API เวอร์ชัน current, system_current หรือตัวเลข และช่อง platform_apis ต้องว่างเปล่า

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

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

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

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

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

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

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

  1. สร้างไฟล์ make ที่กําหนดแพ็กเกจสําหรับพาร์ติชัน 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 ระบบบิลด์จะป้องกันไม่ให้แพ็กเกจที่กําหนดไว้ในไฟล์ make อื่นๆ ติดตั้งไปยังเส้นทางที่กําหนดไว้ใน require-artifacts-in-path และป้องกันไม่ให้แพ็กเกจที่กําหนดไว้ในไฟล์ make ปัจจุบันติดตั้งอาร์ติแฟกต์นอกเส้นทางที่กําหนดไว้ใน require-artifacts-in-path

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

  • ตัวเลือกที่ 1: รวมโมดูลระบบไว้ในไฟล์ make ที่รวมอยู่ใน oem_system.mk ซึ่งจะทำให้เป็นไปตามข้อกำหนดของเส้นทางอาร์ติแฟกต์ (เนื่องจากตอนนี้โมดูลอยู่ในไฟล์ make ที่รวมไว้) จึงอนุญาตให้ติดตั้งไปยังชุดเส้นทางใน "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 ขั้นตอนนี้ไม่บังคับเนื่องจากคุณไม่จำเป็นต้องกำหนดรูปภาพ 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 ไม่มีไฟล์ที่ต้องพึ่งพา (และไม่ได้บันทึกไว้เมื่อสร้าง)
    • หากต้องการตรวจสอบจากระบบบิลด์ ให้เพิ่มไลบรารีที่จำเป็นลงในช่อง 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