Wymuszanie interfejsów podziału produktów

W Androidzie 11 partycja product jest oddzielona od partycji system i vendor. W ramach tych zmian możesz teraz kontrolować dostęp partycji product do interfejsów natywnych i Java (podobnie jak w przypadku wymuszania interfejsów w partycjach vendor).

Wymuszanie interfejsów natywnych

Aby włączyć wymuszanie interfejsów natywnych, ustaw PRODUCT_PRODUCT_VNDK_VERSION na current. (Gdy poziom interfejsu API wysyłki dla miejsca docelowego jest większy niż 29, wersja jest automatycznie ustawiana na current.) Wymuszanie umożliwia:

  • łączenie modułów natywnych w partycji product:
    • statyczne lub dynamiczne z innymi modułami w partycji product, które zawierają statyczne, współdzielone lub nagłówkowe biblioteki;
    • dynamiczne z bibliotekami VNDK w partycji system.
  • łączenie bibliotek JNI w oddzielnych plikach APK w partycji product z bibliotekami w /product/lib lub /product/lib64 (oprócz bibliotek NDK).

Wymuszanie nie zezwala na inne połączenia z partycjami innymi niż product.

Wymuszanie podczas tworzenia kompilacji (Android.bp)

W Androidzie 11 moduły systemowe mogą tworzyć wariant obrazu produktu oprócz wariantów obrazu podstawowego i dostawcy. Gdy włączone jest wymuszanie interfejsów natywnych (PRODUCT_PRODUCT_VNDK_VERSION jest ustawione na current):

  • moduły natywne w partycji product znajdują się w wariancie produktu, a nie w wariancie podstawowym;

  • moduły z product_available: true w plikach Android.bp są dostępne dla wariantu produktu;

  • biblioteki lub pliki binarne, które określają product_specific: true, mogą łączyć się z innymi bibliotekami, które określają product_specific: true lub product_available: true w plikach Android.bp;

  • biblioteki VNDK muszą mieć product_available: true w plikach Android.bp aby pliki binarne product mogły łączyć się 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 Android.bp Utworzone warianty
Przed wymuszeniem Po wymuszeniu
default (none) core
(includes /system, /system_ext and /product)
core
(includes /system and /system_ext but not /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 Nie dotyczy core, product
vendor_available: true AND product_available: true Nie dotyczy 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

Wymuszanie podczas tworzenia kompilacji (Android.mk)

Gdy włączone jest wymuszanie interfejsów natywnych, moduły natywne zainstalowane w partycji product mają typ połączenia native:product, który może łączyć się tylko z innymi native:product lub native:vndk modułami. Próba połączenia z innymi modułami spowoduje wygenerowanie przez system kompilacji błędu sprawdzania typu połączenia.

Wymuszanie podczas działania

Gdy włączone jest wymuszanie interfejsów natywnych, konfiguracja linkera dla linkera bionic nie pozwala procesom systemowym na używanie bibliotek product, tworząc sekcję product dla procesów product, które nie mogą łączyć się z bibliotekami poza partycją product (jednak takie procesy mogą łączyć się z bibliotekami VNDK). Próby naruszenia konfiguracji połączenia podczas działania powodują niepowodzenie procesu i wygenerowanie komunikatu o błędzie CANNOT LINK EXECUTABLE.

Wymuszanie interfejsów Java

Aby włączyć wymuszanie interfejsów Java, ustaw PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE na true. (Gdy poziom interfejsu API wysyłki dla miejsca docelowego jest większy niż 29, wartość jest automatycznie ustawiana na true.) Gdy ta opcja jest włączona, wymuszanie zezwala na dostęp lub go zabrania:

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. Nie można łączyć się z biblioteką, która używa ukrytych interfejsów API. To ograniczenie obejmuje łączenie podczas tworzenia kompilacji i odbicie podczas działania.

Wymuszanie podczas tworzenia kompilacji

Podczas tworzenia kompilacji Make i Soong sprawdzają, czy moduły Java w partycji product nie używają ukrytych interfejsów API, sprawdzając pola platform_apis i sdk_version. Pole sdk_version aplikacji w partycji product musi być wypełnione wartością current, system_current lub numeryczną wersją interfejsu API, a pole platform_apis musi być puste.

Wymuszanie podczas działania

Środowisko wykonawcze Androida sprawdza, czy aplikacje w partycji product nie używają ukrytych interfejsów API, w tym odbicia. Więcej informacji znajdziesz w artykule Ograniczenia dotyczące interfejsów innych niż SDK.

Włączanie wymuszania interfejsu produktu

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

Krok Zadanie Wymagane
1 Zdefiniuj własny plik makefile systemu, który określa pakiety dla system partycji, a następnie ustaw sprawdzanie wymagań dotyczących ścieżki artefaktów w device.mk (aby uniemożliwić instalowanie modułów innych niż systemowe w system partycji). N
2 Zwolnij miejsce na liście dozwolonych. N
3 Wymuś interfejsy natywne i zidentyfikuj błędy połączeń podczas działania (można uruchomić równolegle z wymuszaniem Java). y
4 Wymuś interfejsy Java i sprawdź zachowanie podczas działania (można uruchomić równolegle z wymuszaniem natywnym). y
5 Sprawdź zachowania podczas działania. y
6 Zaktualizuj device.mk o wymuszanie interfejsu produktu. y

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

W tym kroku zdefiniujesz plik makefile system.

  1. Utwórz plik makefile, który określa pakiety dla partycji system. Na przykład utwórz plik oem_system.mk z tymi treściami:

    $(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 makefile dla partycji system i włącz sprawdzanie wymagań dotyczących ścieżki artefaktu. Na 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 PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS jest ustawione na true lub strict, system kompilacji uniemożliwia instalowanie pakietów zdefiniowanych w innych plikach makefile w ścieżkach zdefiniowanych w require-artifacts-in-path oraz uniemożliwia instalowanie artefaktów pakietów zdefiniowanych w bieżącym pliku makefile poza ścieżkami zdefiniowanymi w require-artifacts-in-path.

W powyższym przykładzie, gdy PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS jest ustawione na strict, pliki makefile poza oem_system.mk nie mogą zawierać modułów zainstalowanych w partycji root lub system. Aby uwzględnić te moduły, musisz zdefiniować je w samym pliku oem_system.mk lub w dołączonym pliku makefile. Próby zainstalowania modułów w niedozwolonych ścieżkach powodują przerwy w kompilacji. Aby naprawić te przerwy, wykonaj jedną z tych czynności:

  • Opcja 1. Uwzględnij moduł systemowy w plikach makefile dołączonych do oem_system.mk. Dzięki temu wymaganie dotyczące ścieżki artefaktu zostanie spełnione (ponieważ moduły znajdują się teraz w dołączonym pliku makefile), co umożliwi instalację w zbiorze ścieżek w `require-artifacts-in-path`.

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

  • Opcja 3. Dodaj moduły do PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. Ta lista zawiera dozwolone moduły do zainstalowania.

Krok 2. Opróżnij listę dozwolonych

W tym kroku opróżnisz PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST, aby wszystkie urządzenia korzystające z oem_system.mk mogły też korzystać z jednego obrazu system. Aby opróżnić listę dozwolonych, przenieś wszystkie moduły z listy do partycji system_ext lub product albo dodaj je do plików makefile system. Ten krok jest opcjonalny, ponieważ zdefiniowanie wspólnego obrazu system nie jest wymagane do włączenia wymuszania interfejsu produktu. Opróżnienie listy dozwolonych jest jednak przydatne do zdefiniowania granicy system za pomocą system_ext.

Krok 3. Wymuś interfejsy natywne

W tym kroku ustawisz PRODUCT_PRODUCT_VNDK_VERSION := current, a następnie poszukasz błędów kompilacji i błędów podczas działania oraz je rozwiążesz. Aby sprawdzić uruchamianie urządzenia i logi oraz znaleźć i naprawić błędy połączeń podczas działania:

  1. Ustaw PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Utwórz kompilację urządzenia i poszukaj błędów kompilacji. Prawdopodobnie zobaczysz kilka przerw w kompilacji spowodowanych brakiem wariantów produktu lub wariantów podstawowych. Typowe przerwy to:

    • Każdy moduł hidl_interface, który ma product_specific: true, nie będzie dostępny dla modułów systemowych. Aby rozwiązać ten problem, zastąp product_specific: true wartością system_ext_specific: true.
    • W modułach może brakować wariantu produktu wymaganego przez moduły produktu. Aby rozwiązać ten problem, udostępnij ten moduł partycji product przez ustawienie product_available: true lub przenieś moduł do partycji product przez ustawienie product_specific: true.
  3. Rozwiąż błędy kompilacji i upewnij się, że kompilacja urządzenia zakończyła się powodzeniem.

  4. Flashuj obraz i poszukaj błędów środowiska wykonawczego w logach i podczas uruchamiania urządzenia.

    • Jeśli tag linker z logu przypadku testowego zawiera komunikat CANNOT LINK EXECUTABLE, w pliku makefile brakuje zależności (i nie została ona przechwycona podczas tworzenia kompilacji).
    • Aby sprawdzić to w systemie kompilacji, dodaj wymaganą bibliotekę do pola shared_libs: lub required:.
  5. Rozwiąż problem z brakującymi zależnościami, korzystając z podanych wyżej wskazówek.

Krok 4. Wymuś interfejsy Java

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

  • Błędy typu połączenia. Ten błąd oznacza, że aplikacja łączy się z modułami Java, które mają szerszy zakres sdk_version. Aby rozwiązać ten problem, 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 oznacza, ż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ź zachowania podczas działania

W tym kroku sprawdzisz, czy zachowania podczas działania są zgodne z oczekiwaniami. W przypadku aplikacji, które można debugować, możesz monitorować użycie ukrytych interfejsów API za pomocą dziennika, używając StrictMode.detectNonSdkApiUsage (który generuje dziennik, 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 (łączenie lub odbicie), poziomie ograniczeń i stosie wywołań.

  • Składnia veridex:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
  • Przykładowy wynik 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;
    

Więcej informacji o użyciu veridex znajdziesz w artykule Testowanie za pomocą narzędzia veridex.

Krok 6. Zaktualizuj device.mk

Po naprawieniu wszystkich błędów kompilacji i błędów podczas działania oraz sprawdzeniu, czy zachowania podczas działania są zgodne z oczekiwaniami, ustaw w device.mk te wartości:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true