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

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

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

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

    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