Egzekwuj interfejsy partycji produktów

Android 11 rozdziela 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ć egzekwowanie interfejsu natywnego, ustaw PRODUCT_PRODUCT_VNDK_VERSION na current. (gdy poziom interfejsu API dostawy dla docelowego typu dostawy jest większy niż 29, wersja jest automatycznie ustawiana na current). 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ć wariant obrazu produktu oprócz podstawowych i wariantów obrazu 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żytych 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);
podstawowy
(obejmuje /system i /system_ext, ale nie /product)
system_ext_specific: true core core
product_specific: true core Produkt
vendor: true firma firma
vendor_available: true core, vendor core, vendor
product_available: true Nie dotyczy podstawowy, produkt
vendor_available: true ORAZ product_available: true Nie dotyczy core, product, vendor
system_ext_specific: true ORAZ vendor_available: true core, vendor podstawowy, dostawca
product_specific: true ORAZ vendor_available: true podstawowy, dostawca produkt, dostawca

Egzekwowanie w czasie kompilacji (Android.mk)

Gdy wymuszanie interfejsu natywnego jest włączone, moduły natywne zainstalowane na partycji product mają typ połączenia native:product, który może się łączyć tylko z innymi modułami 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 wyświetlenie 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. To ograniczenie obejmuje możliwość łączenia w czasie kompilacji i refleksji w czasie działania aplikacji.

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. Pole sdk_version aplikacji na partycji product musi być wypełnione wartością current, system_current lub numeryczną wersją interfejsu API, a pole platform_apis musi być puste.

Egzekwowanie w czasie działania

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

Włączanie egzekwowania interfejsu

Aby włączyć egzekwowanie zasad w interfejsie usługi, wykonaj czynności opisane w tej sekcji.

Krok Zadanie Wymagane
1 Zdefiniuj własny plik marka systemu, który określa pakiety dla partycji system, a następnie ustaw kontrolę wymagań ścieżki artefaktów w device.mk (aby zapobiec instalowaniu modułów innych niż systemowe na partycji system). N
2 oczyścić 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 danymi:

    $(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. 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 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 zawartych 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ę w zbiorze ś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 jej moduły na partycję system_ext lub product albo dodaj je do system tworzenia plików. Ten krok jest opcjonalny, ponieważ zdefiniowanie wspólnego zdjęcia system nie jest wymagane do włączenia egzekwowania interfejsu usługi. Jednak opróżnianie listy dozwolonych jest pomocne przy definiowaniu 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 działania. Aby sprawdzić ustawienia rozruchu urządzenia i dzienniki oraz znaleźć i naprawić błędy połączenia w środowisku wykonawczym:

  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:

    • Żadne moduły hidl_interface zawierające product_specific: true nie będą dostępne dla modułów systemowych. Aby to naprawić, zastąp product_specific: true kodem system_ext_specific: true.
    • W modułach może brakować wersji produktu wymaganej przez moduły produktów. Aby rozwiązać ten problem, udostępnij ten moduł na partycji product, ustawiając product_available: true, lub przenieś moduł do partycji product, ustawiając 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 przypadku testowego zawiera komunikat CANNOT LINK EXECUTABLE, oznacza to, że w pliku Make brakuje zależności (i nie został on przechwycony 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 ustawisz PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, a potem znajdziesz i naprawisz wynikowe błędy kompilacji. Poszukaj 2 konkretnych typów błędów:

  • Błędy typu linku. 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 oznacza, że nie można znaleźć symbolu, ponieważ jest 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ź działanie środowiska wykonawczego

W tym kroku sprawdzisz, czy środowisko wykonawcze działa prawidłowo. W przypadku aplikacji, które można debugować, możesz monitorować użycie ukrytego interfejsu API, korzystając z pliku dziennika 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 czasu 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