Wymuszanie interfejsów partycji produktu

Android 11 rozdziela partycję product , czyniąc ją niezależną od partycji system i vendor . W ramach tych zmian można teraz kontrolować dostęp partycji product do interfejsów natywnych i interfejsów Java (co jest podobne do sposobu, w jaki działa wymuszanie interfejsu w przypadku partycji vendor ).

Wymuszanie natywnych interfejsów

Aby włączyć wymuszanie interfejsu natywnego, ustaw PRODUCT_PRODUCT_VNDK_VERSION na current . (Wersja jest automatycznie ustawiana na current , gdy poziom API wysyłki dla miejsca docelowego jest większy niż 29.) Egzekwowanie umożliwia:

  • Natywne moduły w partycji product do połączenia:
    • Statycznie lub dynamicznie do innych modułów w partycji product , które zawierają biblioteki statyczne, współdzielone lub nagłówkowe.
    • Dynamicznie do bibliotek VNDK na partycji system .
  • Biblioteki JNI w uwolnionych plikach APK na partycji product w celu połączenia z bibliotekami w /product/lib lub /product/lib64 (jest to dodatek do bibliotek NDK).

Wymuszanie nie pozwala na inne łącza do partycji innych niż partycja product .

Egzekwowanie czasu kompilacji (Android.bp)

W systemie Android 11 moduły systemu mogą tworzyć warianty obrazu produktu oprócz wariantów obrazu rdzenia i dostawcy. Gdy włączone jest wymuszanie interfejsu natywnego ( PRODUCT_PRODUCT_VNDK_VERSION jest ustawione na current ):

  • Moduły natywne w partycji product znajdują się w wariancie produktu, a nie w wariancie rdzenia.

  • Moduły z product_available: true w swoich 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 w swoich plikach Android.bp określają product_specific: true lub product_available: true .

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

W poniższej tabeli podsumowano właściwości Android.bp używane do tworzenia wariantów obrazu.

Właściwości w Android.bp Utworzono warianty
Przed wykonaniem Po wykonaniu
domyślny (brak) rdzeń

(zawiera /system , /system_ext i /product )

rdzeń

(zawiera /system i /system_ext , ale nie /product )

system_ext_specific: true rdzeń rdzeń
product_specific: true rdzeń produkt
vendor: true sprzedawca sprzedawca
vendor_available: true rdzeń, sprzedawca rdzeń, sprzedawca
product_available: true Nie dotyczy rdzeń, produkt
vendor_available: true ORAZ product_available: true Nie dotyczy rdzeń, produkt, dostawca
system_ext_specific: true ORAZ vendor_available: true rdzeń, sprzedawca rdzeń, sprzedawca
product_specific: true ORAZ vendor_available: true rdzeń, sprzedawca produkt, sprzedawca

Egzekwowanie czasu kompilacji (Android.mk)

Gdy włączone jest wymuszanie interfejsu natywnego, moduły natywne zainstalowane na partycji product mają typ łącza native:product , który może łączyć się tylko z innymi modułami native:product lub native:vndk . Próba połączenia z modułami innymi niż te powoduje, że system kompilacji generuje błąd sprawdzania typu łącza.

Egzekwowanie czasu wykonania

Gdy włączone jest wymuszanie interfejsu natywnego, konfiguracja linkera dla linkera bionicznego nie pozwala procesom systemowym na korzystanie z bibliotek product , tworząc sekcję product dla procesów product , które nie mogą łączyć się z bibliotekami znajdującymi się poza partycją product (jednak takie procesy mogą link do bibliotek VNDK). Próby naruszenia konfiguracji łącza w czasie wykonywania powodują awarię procesu i wygenerowanie komunikatu o błędzie CANNOT LINK EXECUTABLE .

Egzekwowanie interfejsów Java

Aby włączyć wymuszanie interfejsu Java, ustaw PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE na true . (Wartość jest automatycznie ustawiana na true , gdy poziom interfejsu API wysyłki dla miejsca docelowego jest większy niż 29.) Po włączeniu wymuszanie zezwala/zabrania następującego dostępu.

API /system /system_ext /produkt /sprzedawca /dane
Publiczne API
@SystemApi
@ukryj API

Podobnie jak w przypadku partycji vendor , aplikacja lub biblioteka Java w partycji product może używać wyłącznie publicznych i systemowych interfejsów API; łączenie z biblioteką korzystającą z ukrytych interfejsów API jest niedozwolone. To ograniczenie obejmuje łączenie w czasie kompilacji i odbicie w czasie wykonywania.

Buduj egzekwowanie czasu

W czasie kompilacji Make i Soong sprawdzają, czy moduły Java w partycji product nie korzystają z ukrytych interfejsów API, sprawdzając pola platform_apis i sdk_version . Pole sdk_version aplikacji w partycji product musi być wypełnione current , system_current lub numeryczną wersją interfejsu API, a pole platform_apis musi być puste.

Egzekwowanie czasu wykonania

Środowisko wykonawcze systemu Android sprawdza, czy aplikacje w partycji product nie korzystają z ukrytych interfejsów API, w tym z refleksji. Aby uzyskać szczegółowe informacje, zobacz Ograniczenia dotyczące interfejsów innych niż SDK .

Włączanie wymuszania interfejsu produktu

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

Krok Zadanie Wymagany
1 Zdefiniuj własny systemowy plik Makefile, który będzie określał pakiety dla partycji system , a następnie ustaw sprawdzanie wymagań dotyczących ścieżki artefaktów w device.mk (aby zapobiec instalowaniu na partycji system modułów niesystemowych). N
2 Wyczyść listę dozwolonych. N
3 Egzekwuj natywne interfejsy i identyfikuj awarie łączy w czasie wykonywania (można działać równolegle z wymuszaniem Java). Y
4 Egzekwuj interfejsy Java i sprawdzaj zachowanie w czasie wykonywania (można działać równolegle z wymuszaniem natywnym). Y
5 Sprawdź zachowania w czasie wykonywania. Y
6 Zaktualizuj device.mk za pomocą wymuszania interfejsu produktu. Y

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

W tym kroku definiujesz system makefile.

  1. Utwórz plik makefile, który definiuje pakiety dla partycji system . Na przykład utwórz plik oem_system.mk zawierający następujące elementy:

    $(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 artefaktów. Na przykład:

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

Informacje o wymaganiach dotyczących ścieżki artefaktów

Gdy PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS jest ustawione na true lub strict , system kompilacji uniemożliwia instalowanie pakietów zdefiniowanych w innych plikach makefile na ścieżkach zdefiniowanych w require-artifacts-in-path i zapobiega instalowaniu artefaktó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 na partycji root lub system . Aby dołączyć te moduły, musisz je zdefiniować 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ć przerwy, wykonaj jedną z następujących czynności:

  • Opcja 1: Dołącz moduł systemowy do plików makefile zawartych w oem_system.mk . To sprawia, że ​​wymagania dotyczące ścieżki artefaktów są spełnione (ponieważ moduły istnieją teraz w dołączonym pliku makefile), co umożliwia instalację do zestawu ścieżek w `require-artifacts-in-path.

  • Opcja 2: Zainstaluj moduły na partycji system_ext lub partycji product (i nie instaluj modułów na 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 powodujesz, że PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST jest pusta, aby wszystkie urządzenia współdzielące oem_system.mk mogły także współużytkować pojedynczy obraz system . Aby opróżnić listę dozwolonych, przenieś dowolne moduły z listy do partycji system_ext lub partycji product albo dodaj je do system plików make. Ten krok jest opcjonalny, ponieważ zdefiniowanie wspólnego obrazu system nie jest wymagane do umożliwienia egzekwowania interfejsu produktu. Jednak opróżnienie listy dozwolonych jest pomocne przy definiowaniu granicy system za pomocą system_ext .

Krok 3: Wymuś natywne interfejsy

W tym kroku ustawisz PRODUCT_PRODUCT_VNDK_VERSION := current , a następnie wyszukasz błędy kompilacji i wykonania i rozwiążesz je. Aby sprawdzić rozruch urządzenia i dzienniki oraz znaleźć i naprawić awarie łączy w czasie wykonywania:

  1. Ustaw PRODUCT_PRODUCT_VNDK_VERSION := current .

  2. Zbuduj urządzenie i poszukaj błędów kompilacji. Prawdopodobnie zobaczysz kilka przerw w kompilacji z powodu brakujących wariantów produktu lub wariantów podstawowych. Typowe przerwy obejmują:

    • Żaden moduł hidl_interface , który ma product_specific: true nie będzie dostępny dla modułów systemowych. Aby to naprawić, zamień product_specific: true na system_ext_specfic: true .
    • W modułach może brakować wariantu produktu wymaganego dla modułów produktu. Aby to naprawić, udostępnij ten moduł partycji product , ustawiając product_available: true lub przenieś moduł na partycję product , ustawiając product_specific: true .
  3. Rozwiąż błędy kompilacji i upewnij się, że urządzenie pomyślnie się zbudowało.

  4. Flashuj obraz i poszukaj błędów wykonawczych w rozruchu urządzenia i dziennikach.

    • Jeśli tag linker z dziennika przypadków testowych wyświetla komunikat CANNOT LINK EXECUTABLE , w pliku make brakuje zależności (i nie został on przechwycony w czasie kompilacji).
    • Aby to sprawdzić z systemu kompilacji, dodaj wymaganą bibliotekę do pola shared_libs: lub required:
  5. Rozwiąż brakujące zależności, korzystając ze wskazówek podanych powyżej.

Krok 4: Wymuś interfejsy Java

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

  • Błędy typu łącza. Ten błąd wskazuje, że aplikacja łączy się z modułami Java, które mają szerszą 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 to naprawić, 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 w czasie wykonywania

Na tym etapie sprawdzasz, czy zachowania środowiska wykonawczego są zgodne z oczekiwaniami. W przypadku aplikacji, które można debugować, możesz monitorować użycie ukrytego interfejsu API za pomocą dziennika przy użyciu StrictMode.detectNonSdkApiUsage (który generuje dziennik, gdy aplikacja korzysta z ukrytego interfejsu API). Alternatywnie możesz użyć narzędzia analizy statycznej Veridex , aby uzyskać typ użycia (łączenie lub odbicie), poziom ograniczeń i stos wywołań.

  • Składnia Veridexu:

    ./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;
    

Aby uzyskać szczegółowe informacje na temat użycia Veridex, zobacz Testowanie za pomocą narzędzia Veridex .

Krok 6: Zaktualizuj Device.mk

Po naprawieniu wszystkich błędów kompilacji i środowiska wykonawczego oraz sprawdzeniu, czy zachowania środowiska wykonawczego są zgodne z oczekiwaniami, ustaw następujące ustawienia w device.mk :

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true