Buộc thực thi giao diện phân vùng sản phẩm

Android 11 tách phân vùng product, giúp phân vùng này độc lập với các phân vùng systemvendor. Trong số những thay đổi này, giờ đây, bạn có thể kiểm soát quyền truy cập của phân vùng product vào các giao diện gốc và Java (tương tự như cách hoạt động của việc thực thi giao diện đối với các phân vùng vendor).

Thực thi các giao diện gốc

Để bật tính năng thực thi giao diện gốc, hãy đặt PRODUCT_PRODUCT_VNDK_VERSION thành current. (Phiên bản này được tự động đặt thành current khi cấp API vận chuyển cho mục tiêu lớn hơn 29.) Việc thực thi cho phép:

  • Các mô-đun gốc trong phân vùng product cần liên kết:
    • Tĩnh hoặc động đối với các mô-đun khác trong phân vùng product bao gồm các thư viện tĩnh, thư viện dùng chung hoặc thư viện tiêu đề.
    • Động đến các thư viện VNDK trong phân vùng system.
  • Các thư viện JNI trong các APK không đi kèm trong phân vùng product để liên kết với các thư viện trong /product/lib hoặc /product/lib64 (ngoài các thư viện NDK).

Việc thực thi không cho phép các đường liên kết khác đến các phân vùng khác ngoài phân vùng product.

Thực thi thời gian xây dựng (Android.bp)

Trong Android 11, các mô-đun hệ thống có thể tạo một biến thể hình ảnh sản phẩm ngoài các biến thể hình ảnh cốt lõi và nhà cung cấp. Khi bạn bật tính năng thực thi giao diện gốc (PRODUCT_PRODUCT_VNDK_VERSION được đặt thành current):

  • Các mô-đun gốc trong phân vùng product nằm trong biến thể sản phẩm thay vì biến thể cốt lõi.

  • Các mô-đun có product_available: true trong tệp Android.bp sẽ có sẵn cho biến thể sản phẩm.

  • Các thư viện hoặc tệp nhị phân chỉ định product_specific: true có thể liên kết với các thư viện khác chỉ định product_specific: true hoặc product_available: true trong tệp Android.bp.

  • Các thư viện VNDK phải có product_available: true trong tệp Android.bp để các tệp nhị phân product có thể liên kết đến các thư viện VNDK.

Bảng sau đây tóm tắt các thuộc tính Android.bp dùng để tạo các biến thể hình ảnh.

Các thuộc tính trong Android.bp Số biến thể đã tạo
Trước khi áp dụng Sau khi áp dụng
mặc định (không có) core
(bao gồm /system, /system_ext/product)
core
(bao gồm /system/system_ext nhưng không bao gồm /product)
system_ext_specific: true core core
product_specific: true core sản phẩm
vendor: true nhà cung cấp nhà cung cấp
vendor_available: true core, vendor core, vendor
product_available: true Không áp dụng core, product
vendor_available: trueproduct_available: true Không áp dụng core, product, vendor
system_ext_specific: truevendor_available: true core, vendor core, vendor
product_specific: truevendor_available: true core, vendor sản phẩm, nhà cung cấp

Thực thi thời gian xây dựng (Android.mk)

Khi tính năng thực thi giao diện gốc được bật, các mô-đun gốc được cài đặt vào phân vùng product sẽ có loại đường liên kết native:product chỉ có thể liên kết với các mô-đun native:product hoặc native:vndk khác. Nếu cố gắng liên kết với bất kỳ mô-đun nào khác ngoài các mô-đun này, hệ thống sẽ tạo ra một lỗi kiểm tra loại liên kết.

Thực thi trong thời gian chạy

Khi tính năng thực thi giao diện gốc được bật, cấu hình trình liên kết cho trình liên kết bionic sẽ không cho phép các quy trình hệ thống sử dụng thư viện product, tạo một phần product cho các quy trình product không thể liên kết đến các thư viện bên ngoài phân vùng product (tuy nhiên, các quy trình như vậy có thể liên kết đến các thư viện VNDK). Các nỗ lực vi phạm cấu hình liên kết thời gian chạy sẽ khiến quy trình không thành công và tạo ra thông báo lỗi CANNOT LINK EXECUTABLE.

Thực thi các giao diện Java

Để bật chế độ thực thi giao diện Java, hãy đặt PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE thành true. (Giá trị này được tự động đặt thành true khi cấp API vận chuyển cho mục tiêu lớn hơn 29.) Khi được bật, chế độ thực thi sẽ cho phép hoặc không cho phép truy cập vào những nội dung sau:

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

Như trong phân vùng vendor, một ứng dụng hoặc thư viện Java trong phân vùng product chỉ được phép sử dụng các API công khai và API hệ thống; không được phép liên kết đến một thư viện sử dụng các API ẩn. Quy định hạn chế này bao gồm việc liên kết tại thời gian xây dựng và phản ánh trong thời gian chạy.

Thực thi thời gian tạo

Tại thời điểm tạo bản dựng, Make và Soong sẽ xác minh rằng các mô-đun Java trong phân vùng product không sử dụng các API ẩn bằng cách kiểm tra các trường platform_apissdk_version. sdk_version của các ứng dụng trong phân vùng product phải được điền bằng current, system_current hoặc phiên bản số của API, đồng thời trường platform_apis phải trống.

Thực thi trong thời gian chạy

Thời gian chạy Android xác minh rằng các ứng dụng trong phân vùng product không sử dụng các API ẩn, bao gồm cả phản chiếu. Để biết thông tin chi tiết, hãy tham khảo bài viết Hạn chế đối với giao diện không phải SDK.

Bật tính năng thực thi giao diện sản phẩm

Hãy làm theo các bước trong phần này để bật tính năng thực thi giao diện sản phẩm.

Bước Việc cần làm Bắt buộc
1 Xác định makefile hệ thống của riêng bạn để chỉ định các gói cho phân vùng system, sau đó đặt yêu cầu kiểm tra đường dẫn của cấu phần phần mềm trong device.mk (để ngăn các mô-đun không thuộc hệ thống cài đặt vào phân vùng system). Không
2 Dọn dẹp danh sách cho phép. Không
3 Thực thi các giao diện gốc và xác định các lỗi liên kết thời gian chạy (có thể chạy song song với việc thực thi Java).
4 Thực thi các giao diện Java và xác minh hành vi trong thời gian chạy (có thể chạy song song với hoạt động thực thi gốc).
5 Kiểm tra hành vi trong thời gian chạy.
6 Cập nhật device.mk bằng việc thực thi giao diện sản phẩm.

Bước 1: Tạo tệp makefile và bật tính năng kiểm tra đường dẫn cấu phần phần mềm

Trong bước này, bạn sẽ xác định tệp system makefile.

  1. Tạo một makefile xác định các gói cho phân vùng system. Ví dụ: tạo tệp oem_system.mk có nội dung sau:

    $(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. Trong tệp device.mk, hãy kế thừa makefile chung cho phân vùng system và bật chế độ kiểm tra các yêu cầu về đường dẫn của cấu phần phần mềm. Ví dụ:

    $(call inherit-product, $(SRC_TARGET_DIR)/product/oem_system.mk)
    
    # Enable artifact path requirements checking
    PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := strict
    

Giới thiệu về các yêu cầu đối với đường dẫn cấu phần phần mềm

Khi PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS được đặt thành true hoặc strict, hệ thống xây dựng sẽ ngăn các gói được xác định trong các tệp đóng góp khác cài đặt vào các đường dẫn được xác định trong require-artifacts-in-path ngăn các gói được xác định trong tệp đóng góp hiện tại cài đặt cấu phần phần mềm bên ngoài các đường dẫn được xác định trong require-artifacts-in-path.

Trong ví dụ trên, khi PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS được đặt thành strict, các tệp makefile bên ngoài oem_system.mk không thể bao gồm các mô-đun được cài đặt vào phân vùng root hoặc system. Để đưa các mô-đun này vào, bạn phải xác định chúng trong chính tệp oem_system.mk hoặc trong một tệp makefile được đưa vào. Việc cố gắng cài đặt các mô-đun vào các đường dẫn không được phép sẽ khiến bản dựng bị gián đoạn. Để khắc phục các điểm ngắt, hãy làm theo một trong những cách sau:

  • Lựa chọn 1: Đưa mô-đun hệ thống vào các tệp makefile có trong oem_system.mk. Điều này giúp đáp ứng yêu cầu về đường dẫn cấu phần phần mềm (vì các mô-đun hiện có trong tệp makefile được đưa vào) và do đó cho phép cài đặt vào tập hợp đường dẫn trong `require-artifacts-in-path.

  • Cách 2: Cài đặt các mô-đun vào phân vùng system_ext hoặc product (và không cài đặt các mô-đun vào phân vùng system).

  • Cách 3: Thêm học phần vào PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. Danh sách này liệt kê các mô-đun được phép cài đặt.

Bước 2: Xoá danh sách cho phép

Trong bước này, bạn sẽ tạo PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST trống để tất cả các thiết bị chia sẻ oem_system.mk cũng có thể chia sẻ một hình ảnh system duy nhất. Để xoá danh sách cho phép, hãy di chuyển mọi mô-đun trong danh sách đến phân vùng system_ext hoặc product hoặc thêm các mô-đun đó vào tệp system make. Bước này là không bắt buộc vì bạn không cần xác định hình ảnh system chung để bật tính năng thực thi giao diện sản phẩm. Tuy nhiên, việc làm trống danh sách cho phép sẽ hữu ích khi xác định ranh giới system bằng system_ext.

Bước 3: Thực thi các giao diện gốc

Trong bước này, bạn sẽ đặt PRODUCT_PRODUCT_VNDK_VERSION := current, sau đó tìm lỗi trong quá trình tạo và thời gian chạy rồi giải quyết các lỗi đó. Cách kiểm tra quá trình khởi động và nhật ký của thiết bị, cũng như tìm và khắc phục lỗi liên kết thời gian chạy:

  1. Đặt PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Tạo thiết bị và tìm lỗi khi tạo. Bạn có thể thấy một số bản dựng bị gián đoạn do thiếu biến thể sản phẩm hoặc biến thể cốt lõi. Các điểm ngắt phổ biến bao gồm:

    • Mọi mô-đun hidl_interfaceproduct_specific: true sẽ không có sẵn cho các mô-đun hệ thống. Để khắc phục, hãy thay thế product_specific: true bằng system_ext_specific: true.
    • Các mô-đun có thể bị thiếu biến thể sản phẩm bắt buộc đối với các mô-đun sản phẩm. Để khắc phục, hãy cung cấp mô-đun đó cho phân vùng product bằng cách đặt product_available: true hoặc di chuyển mô-đun đến phân vùng product bằng cách đặt product_specific: true.
  3. Giải quyết các lỗi bản dựng và đảm bảo rằng thiết bị tạo thành công.

  4. Lưu hình ảnh và tìm lỗi thời gian chạy trong quá trình khởi động và nhật ký của thiết bị.

    • Nếu thẻ linker trong nhật ký trường hợp kiểm thử cho thấy thông báo CANNOT LINK EXECUTABLE, thì tệp make đang thiếu một phần phụ thuộc (và không được ghi lại tại thời gian tạo).
    • Để kiểm tra từ hệ thống xây dựng, hãy thêm thư viện cần thiết vào trường shared_libs: hoặc required:.
  5. Giải quyết các phần phụ thuộc bị thiếu bằng hướng dẫn ở trên.

Bước 4: Thực thi các giao diện Java

Trong bước này, bạn sẽ đặt PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, sau đó tìm và khắc phục các lỗi bản dựng phát sinh. Hãy tìm hai loại lỗi cụ thể:

  • Lỗi về loại đường liên kết. Lỗi này cho biết một ứng dụng liên kết đến các mô-đun Java có sdk_version rộng hơn. Để khắc phục, bạn có thể mở rộng sdk_version của ứng dụng hoặc hạn chế sdk_version của thư viện. Lỗi ví dụ:

    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.
    
  • Lỗi biểu tượng. Lỗi này cho biết không tìm thấy một biểu tượng vì biểu tượng đó nằm trong một API ẩn. Để khắc phục, hãy sử dụng một API hiển thị (không ẩn) hoặc tìm một API thay thế. Lỗi ví dụ:

    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
    

Bước 5: Kiểm tra hành vi trong thời gian chạy

Trong bước này, bạn xác minh rằng các hành vi trong thời gian chạy đúng như dự kiến. Đối với các ứng dụng có thể gỡ lỗi, bạn có thể theo dõi mức sử dụng API ẩn bằng cách ghi nhật ký bằng StrictMode.detectNonSdkApiUsage (thao tác này sẽ tạo nhật ký khi ứng dụng sử dụng một API ẩn). Ngoài ra, bạn có thể sử dụng công cụ phân tích tĩnh veridex để biết loại hình sử dụng (liên kết hoặc phản chiếu), cấp hạn chế và ngăn xếp lệnh gọi.

  • Cú pháp Veridex:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
  • Ví dụ về kết quả 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;
    

Để biết thông tin chi tiết về cách sử dụng veridex, hãy tham khảo bài viết Kiểm thử bằng công cụ veridex.

Bước 6: Cập nhật device.mk

Sau khi khắc phục tất cả các lỗi trong quá trình tạo và thời gian chạy, đồng thời xác minh rằng các hành vi trong thời gian chạy đúng như dự kiến, hãy đặt những nội dung sau trong device.mk:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true