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

Android 11 แยกพาร์ติชัน 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 รูปแบบที่สร้างขึ้น
ก่อนการบังคับใช้ หลังการบังคับใช้
default (none) core
(รวม /system, /system_ext และ /product)
core
(รวม /system และ /system_ext แต่ไม่รวม /product)
system_ext_specific: true core core
product_specific: true core product
vendor: true vendor vendor
vendor_available: true core, vendor core, vendor
product_available: true ไม่มี core, product
vendor_available: true AND product_available: true ไม่มี core, product, vendor
system_ext_specific: true AND vendor_available: true core, vendor core, vendor
product_specific: true AND vendor_available: true core, vendor product, vendor

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

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

ในขั้นตอนนี้ คุณจะได้กำหนด Make ไฟล์ของ system

  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 ให้รับช่วง Make ไฟล์ทั่วไปสำหรับพาร์ติชัน 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 ไฟล์ไม่มีทรัพยากร 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 tool

ขั้นตอนที่ 6: อัปเดต device.mk

หลังจากแก้ไขความล้มเหลวในการบิวด์และรันไทม์ทั้งหมดแล้ว และตรวจสอบว่าลักษณะการทำงานของรันไทม์เป็นไปตามที่คาดไว้ ให้ตั้งค่าต่อไปนี้ใน device.mk

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true