Wymuszanie interfejsów podziału produktów

Android 11 odłącza partycję product, dzięki czemu staje się ona niezależna od partycji systemvendor. W ramach tych zmian możesz teraz kontrolować dostęp partycji product do interfejsów natywnych i Java (co działa podobnie do wymuszania interfejsu w przypadku partycji vendor).

Wymuś interfejsy natywne

Aby włączyć natywny interfejs, ustaw wartość PRODUCT_PRODUCT_VNDK_VERSION na current. (wersja jest automatycznie ustawiana na current, gdy poziom interfejsu Shipping API dla celu jest większy niż 29). Egzekwowanie umożliwia:

  • Natywne moduły w partycji product, które chcesz połączyć:
    • Statycznie lub dynamicznie do innych modułów w partycji product, które obejmują biblioteki statyczne, współdzielone lub nagłówkowe.
    • Dynamicznie do bibliotek VNDK na partycji system.
  • biblioteki JNI w niespakowanych plikach APK na partycji product, aby połączyć je z bibliotekami w folderze /product/lib lub /product/lib64 (oprócz bibliotek NDK);

Zespół ds. egzekwowania zasad nie zezwala na inne linki do partycji niż partycja product.

Egzekwowanie w czasie kompilacji (Android.bp)

W Androidzie 11 moduły systemowe mogą tworzyć warianty obrazów produktów oprócz podstawowych i wariantów obrazów dostawcy. Gdy jest włączone egzekwowanie interfejsu natywnego (opcja PRODUCT_PRODUCT_VNDK_VERSION ma wartość current):

  • Natywne moduły w podziale product znajdują się w wersji produktu, a nie w wersji podstawowej.

  • Moduły z wartością product_available: true w pliku Android.bp są dostępne dla wariantu produktu.

  • Biblioteki lub pliki binarne, które podają product_specific: true, mogą zawierać linki do innych bibliotek, które podają product_specific: true lub product_available: true w plikach Android.bp.

  • Biblioteki VNDK muszą zawierać product_available: true w plikach Android.bp, aby pliki binarne product mogły się łączyć z bibliotekami VNDK.

W tabeli poniżej znajdziesz podsumowanie właściwości Android.bp używanych do tworzenia wariantów obrazu.

Właściwości w pliku Android.bp Utworzone warianty
Przed egzekwowaniem Po egzekwowaniu
domyślnie (brak) podstawowe:
(w tym /system, /system_ext i /product);
podstawowe
(obejmuje /system i /system_ext, ale nie /product)
system_ext_specific: true core core
product_specific: true core iloczyn
vendor: true firma firma
vendor_available: true core, vendor core, vendor
product_available: true Nie dotyczy core, product
vendor_available: true ORAZ product_available: true Nie dotyczy core, product, vendor
system_ext_specific: true ORAZ vendor_available: true core, vendor core, vendor
product_specific: true ORAZ vendor_available: true core, vendor produkt, dostawca

Egzekwowanie w czasie kompilacji (Android.mk)

Gdy włączone jest egzekwowanie natywnych interfejsów, natywny moduł zainstalowany w partycji product ma typ linku native:product, który może wskazywać tylko inne moduły native:product lub native:vndk. Próba połączenia z innymi modułami spowoduje wygenerowanie przez system kompilacji błędu sprawdzania typu linku.

Egzekwowanie w czasie działania

Gdy włączone jest wymuszanie natywnego interfejsu, konfiguracja linkera dla binarnego linkera nie zezwala procesom systemowym na korzystanie z bibliotek product. Tworzy ona sekcję product dla procesów product, które nie mogą łączyć się z bibliotekami spoza partycji product (takie procesy mogą jednak łączyć się z bibliotekami VNDK). Próby naruszenia konfiguracji linku w czasie wykonywania powodują niepowodzenie procesu i generowanie komunikatu o błędzie CANNOT LINK EXECUTABLE.

Wymuszanie interfejsów Java

Aby włączyć wymuszanie interfejsu Java, ustaw parametr PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE na true. (wartość jest automatycznie ustawiana na true, gdy poziom interfejsu API dostawy dla celu jest większy niż 29). Po włączeniu egzekwowanie umożliwia zezwolenie lub odmowę zezwolenia na te rodzaje dostępu:

Interfejs API /system /system_ext /product /vendor /data
Publiczny interfejs API
@SystemApi
@hide API

Podobnie jak w przypadku partycji vendor, aplikacja lub biblioteka Java w partycji product może używać tylko publicznych i systemowych interfejsów API. Łączenie z biblioteką, która korzysta z ukrytych interfejsów API, jest niedozwolone. Ta restrykcja obejmuje łączenie w czasie kompilacji i odwoływanie się do obiektów w czasie wykonywania.

Egzekwowanie czasu kompilacji

W momencie kompilacji Make i Soong sprawdzają, czy moduły Java w partycji product nie używają ukrytych interfejsów API. Sprawdzają to, czy pola platform_apissdk_version są zaznaczone. Wartość sdk_version aplikacji w partycji product musi być równa current, system_current lub numerycznej wersji interfejsu API, a pole platform_apis musi być puste.

Egzekwowanie w czasie działania

Środowisko uruchomieniowe Androida sprawdza, czy aplikacje na partycji product nie korzystają z ukrytych interfejsów API, w tym z odzwierania. Szczegółowe informacje znajdziesz w artykule Ograniczenia interfejsów innych niż SDK.

Włączanie egzekwowania interfejsu

Aby włączyć egzekwowanie interfejsu produktu, wykonaj czynności opisane w tej sekcji.

Krok Zadanie Wymagane
1 Zdefiniuj własny plik make systemu, który określa pakiety dla partycji system, a następnie ustaw sprawdzanie wymagań ścieżki artefaktów w device.mk (aby zapobiec instalowaniu modułów niesystemowych na partycji system). N
2 Oczyść listę dozwolonych. N
3 Wymuszanie interfejsów natywnych i identyfikowanie błędów linkowania w czasie wykonywania (można uruchamiać równolegle z wymuszaniem w Javi). Y
4 Wymuszanie interfejsów Java i weryfikowanie zachowania w czasie działania (można uruchamiać równolegle z natywnym wymuszaniem). Y
5 Sprawdź zachowanie podczas działania. Y
6 Zaktualizuj device.mk, aby wymusić interfejs produktu. Y

Krok 1. Utwórz makefile i włącz sprawdzanie ścieżki artefaktu

W tym kroku zdefiniujesz plik make system.

  1. Utwórz plik makefile, który definiuje pakiety dla partycji system. Na przykład utwórz plik oem_system.mk z tymi elementami:

    $(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. W pliku device.mk odziedzicz wspólny plik make dla partycji system i włącz sprawdzanie wymagań dotyczących ścieżki artefaktu. Przykład:

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

Wymagania dotyczące ścieżki artefaktu

Gdy parametr PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS ma wartość true lub strict, system kompilacji uniemożliwia instalowanie pakietów zdefiniowanych w innych plikach makefile na ścieżkach zdefiniowanych w require-artifacts-in-path oraz uniemożliwia instalowanie artefaktów z pakietów zdefiniowanych w bieżącym pliku makefile poza ścieżkami zdefiniowanymi w require-artifacts-in-path.

W powyższym przykładzie, gdy parametr PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS ma wartość strict, makefiles spoza oem_system.mk nie mogą zawierać modułów zainstalowanych w partycji root ani system. Aby uwzględnić te moduły, musisz je zdefiniować w pliku oem_system.mk lub w załączonym pliku makefile. Próby instalacji modułów w niedozwolonych ścieżkach powodują przerwy w kompilacji. Aby naprawić przerwy, wykonaj jedną z tych czynności:

  • Opcja 1: dołącz moduł systemowy do plików make dołączonych w oem_system.mk. Dzięki temu spełnione są wymagania dotyczące ścieżki artefaktu (ponieważ moduły znajdują się teraz w załączonym pliku makefile), co umożliwia instalację na zestawie ścieżek w pliku require-artifacts-in-path.

  • Opcja 2. Zainstaluj moduły w partycji system_ext lub product (nie instaluj modułów w partycji system).

  • Opcja 3. Dodaj moduły do PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. Lista dozwolonych modułów do zainstalowania.

Krok 2. Opróżnij listę dozwolonych

W tym kroku ustawiasz PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST na pusty, aby wszystkie urządzenia udostępniające oem_system.mk mogły też udostępniać jeden obraz system. Aby opróżnić listę dozwolonych, przenieś wszystkie moduły z listy do partycji system_ext lub product albo dodaj je do plików make w folderze system. Ten krok jest opcjonalny, ponieważ zdefiniowanie wspólnego obrazu system nie jest wymagane do wdrożenia egzekwowania interfejsu produktu. Opróżnienie listy dozwolonych jest jednak przydatne do określenia granicy system za pomocą system_ext.

Krok 3. Wymuś stosowanie interfejsów natywnych

Na tym etapie ustawiasz PRODUCT_PRODUCT_VNDK_VERSION := current, a potem sprawdzasz i naprawiasz błędy kompilacji i czasu wykonywania. Aby sprawdzić rozruch urządzenia i logi oraz znaleźć i naprawić błędy łączenia w czasie wykonywania:

  1. Ustaw PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Utwórz urządzenie i sprawdź, czy nie ma błędów. Prawdopodobnie zobaczysz kilka przerw w kompilacji z powodu braku wersji produktu lub wersji głównych. Typowe przerwy:

    • Moduł hidl_interface, który ma wartość product_specific: true, nie będzie dostępny dla modułów systemowych. Aby to naprawić, zastąp product_specific: true kodem system_ext_specific: true.
    • Moduł może nie mieć wersji produktu wymaganej dla modułów produktów. Aby to naprawić, udostępnij ten moduł partycji product, ustawiając wartość product_available: true, lub przenieś moduł do partycji product, ustawiając wartość product_specific: true.
  3. Rozwiąż błędy kompilacji i upewnij się, że kompilacja urządzenia zakończyła się sukcesem.

  4. Wgraj obraz i sprawdź, czy podczas uruchamiania i w dziennikach nie występują błędy czasu działania.

    • Jeśli tag linker z logu testowego zawiera komunikat CANNOT LINK EXECUTABLE, w pliku make brakuje zależności (nie został on uwzględniony podczas kompilacji).
    • Aby sprawdzić to w systemie kompilacji, dodaj wymaganą bibliotekę do pola shared_libs: lub required:.
  5. Rozwiąż problem z brakami zależności, korzystając z podanych wyżej wskazówek.

Krok 4. Wymuś interfejsy Java

W tym kroku skonfigurujesz PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, a potem znajdziesz i naprawisz błędy kompilacji. Poszukaj 2 konkretnych typów błędów:

  • Błędy typu połączenia. Ten błąd wskazuje, że aplikacja zawiera linki do modułów Java, które mają szerszy sdk_version. Aby to naprawić, możesz rozszerzyć sdk_version aplikacji lub ograniczyć sdk_version biblioteki. Przykładowy błą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.
    
  • Błędy symboli. Ten błąd wskazuje, że nie można znaleźć symbolu, ponieważ znajduje się on w ukrytym interfejsie API. Aby rozwiązać ten problem, użyj widocznego (nieukrytego) interfejsu API lub znajdź alternatywę. Przykładowy błą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
    

Krok 5. Sprawdź zachowanie w czasie wykonywania

Na tym etapie sprawdzasz, czy działanie aplikacji jest zgodne z oczekiwaniami. W przypadku aplikacji, które można debugować, możesz monitorować użycie ukrytego interfejsu API, korzystając z pliku StrictMode.detectNonSdkApiUsage (generuje on plik dziennika, gdy aplikacja używa ukrytego interfejsu API). Możesz też użyć narzędzia do analizy statycznej veridex, aby uzyskać informacje o typie użycia (linkowaniu lub odbiciu), poziomie ograniczeń i zbiorze wywołań.

  • Składnia Veridex:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
  • Przykładowy wynik weryfikacji:

    #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;
    

Szczegółowe informacje o używaniu narzędzia veridex znajdziesz w artykule Testowanie za pomocą narzędzia veridex.

Krok 6. Zaktualizuj plik device.mk

Po naprawieniu wszystkich błędów kompilacji i błędów w czasie wykonywania oraz sprawdzeniu, czy zachowanie w czasie wykonywania jest zgodne z oczekiwaniami, ustaw w pliku device.mk te wartości:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true