Wymuszanie interfejsów podziału produktów

Android 11 rozdziela partycję product, dzięki czemu jest ona niezależna od partycji systemvendor. W ramach tych zmian możesz teraz kontrolować dostęp partycji product do interfejsów natywnych i Java (podobnie jak w przypadku egzekwowania interfejsów w partycjach vendor).

Wymuszanie interfejsów natywnych

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

  • Moduły natywne w partycji product do połączenia:
    • statycznie lub dynamicznie do innych modułów w product, które zawierają biblioteki statyczne, współdzielone lub nagłówkowe;
    • dynamicznie do bibliotek VNDK w system.
  • biblioteki JNI w niezapakowanych plikach APK w partycji product, które mają być połączone z bibliotekami w partycjach /product/lib lub /product/lib64 (oprócz bibliotek NDK);

Egzekwowanie zasad nie zezwala na inne linki do partycji niż partycja product.

Wymuszanie czasu kompilacji (Android.bp)

W Androidzie 11 moduły systemowe mogą tworzyć wariant obrazu produktu oprócz wariantów obrazu podstawowego i wariantów obrazu dostawcy. Gdy wymuszanie interfejsu natywnego jest włączone (PRODUCT_PRODUCT_VNDK_VERSION ma wartość current):

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

  • Moduły z product_available: true w plikach Android.bp są dostępne dla wersji produktu.

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

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

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 wersje
Przed egzekwowaniem Po egzekwowaniu
domyślny (brak) podstawowe
(zawiera /system, /system_ext/product)
core
(zawiera /system/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 podstawowy, produkt, dostawca
system_ext_specific: true ORAZ vendor_available: true core, vendor core, vendor
product_specific: true ORAZ vendor_available: true core, vendor produkt, dostawca

Wymuszanie czasu kompilacji (Android.mk)

Gdy wymuszanie interfejsu natywnego jest włączone, moduły natywne zainstalowane w partycji product mają typ linku native:product, który może prowadzić tylko do innych modułów native:product lub native:vndk. Próba połączenia z innymi modułami powoduje wygenerowanie przez system kompilacji błędu sprawdzania typu połączenia.

Wymuszanie w środowisku wykonawczym

Gdy wymuszanie interfejsu natywnego jest włączone, konfiguracja linkera dla linkera bionic nie zezwala 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 linku w czasie działania powodują niepowodzenie procesu i wygenerowanie komunikatu o błędzie CANNOT LINK EXECUTABLE.

Wymuszanie interfejsów Java

Aby włączyć wymuszanie interfejsu Java, ustaw wartość PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE na true. (Wartość jest automatycznie ustawiana na true, gdy poziom interfejsu API dostawy dla miejsca docelowego jest większy niż 29). Gdy egzekwowanie jest włączone, zezwala na dostęp w tych przypadkach:

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. Ograniczenie to obejmuje łączenie w czasie kompilacji i odzwierciedlanie w czasie działania.

Wymuszanie weryfikacji dwuetapowej w czasie tworzenia kompilacji

Podczas kompilacji systemy Make i Soong sprawdzają, czy moduły Java w productnie używają ukrytych interfejsów API. W tym celu sprawdzają pola platform_apissdk_version. Pole sdk_version aplikacji w sekcji product musi zawierać wartość current, system_current lub numeryczną wersję interfejsu API, a pole platform_apis musi być puste.

Wymuszanie w środowisku wykonawczym

Środowisko wykonawcze Androida sprawdza, czy aplikacje w partycji product nie korzystają z ukrytych interfejsów API, w tym z odbicia. Szczegółowe informacje znajdziesz w sekcji Ograniczenia dotyczące interfejsów innych niż SDK.

Włączanie wymuszania interfejsu produktu

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

Krok Zadanie Wymagane
1 Zdefiniuj własny plik makefile systemu, który określa pakiety dla partycji system, a następnie ustaw w pliku device.mk sprawdzanie ścieżki artefaktów (aby zapobiec instalowaniu modułów niesystemowych na partycji system). N
2 Wyczyść listę dozwolonych. N
3 Wymuszanie interfejsów natywnych i wykrywanie błędów łączenia w czasie działania (może działać równolegle z wymuszaniem w przypadku Javy). Y
4 Wymuszanie interfejsów Java i weryfikowanie zachowania w czasie działania (może działać równolegle z wymuszaniem natywnym). Y
5 Sprawdzanie zachowań podczas działania. Y
6 Aktualizacja device.mk z wymuszaniem interfejsu produktu. Y

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

W tym kroku zdefiniujesz plik system makefile.

  1. Utwórz plik makefile, który definiuje pakiety dla partycji system. Na przykład utwórz plik oem_system.mk o tej zawartości:

    $(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 dziedzicz 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 wartość PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS jest ustawiona 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 i uniemożliwia instalowanie pakietów zdefiniowanych w bieżącym pliku makefile w artefaktach poza ścieżkami zdefiniowanymi w require-artifacts-in-path.

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

  • Opcja 1. Dołącz moduł systemowy do plików makefile znajdujących się w oem_system.mk. Dzięki temu wymaganie dotyczące ścieżki artefaktu jest spełnione (ponieważ moduły znajdują się teraz w dołączonym pliku makefile), co umożliwia instalację w zbiorze ścieżek w `require-artifacts-in-path`.

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

  • Opcja 3. Dodaj moduły do PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. Zawiera listę modułów, które można zainstalować.

Krok 2. Opróżnij listę dozwolonych

W tym kroku usuń PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST, aby wszystkie urządzenia udostępniające oem_system.mk mogły też udostępniać pojedynczy obraz system. Aby opróżnić listę dozwolonych, przenieś dowolne moduły z listy do partycji system_ext lub product albo dodaj je do system make files. Ten krok jest opcjonalny, ponieważ zdefiniowanie wspólnego obrazu system nie jest wymagane do włączenia egzekwowania zasad dotyczących interfejsu produktu. Opróżnienie listy dozwolonych jest jednak przydatne do określania granicy system za pomocą system_ext.

Krok 3. Wymuszanie interfejsów natywnych

W tym kroku ustawiasz PRODUCT_PRODUCT_VNDK_VERSION := current, a następnie szukasz błędów kompilacji i błędów środowiska wykonawczego oraz je rozwiązujesz. Aby sprawdzić uruchamianie urządzenia i dzienniki oraz znaleźć i naprawić błędy łączenia w czasie działania:

  1. Ustaw PRODUCT_PRODUCT_VNDK_VERSION := current.

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

    • Żaden moduł hidl_interface, który ma product_specific: true, nie będzie dostępny w przypadku 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 w modułach produktów. Aby rozwiązać ten problem, udostępnij moduł partycji product, ustawiając wartość product_available: true, lub przenieś moduł do partycji product, ustawiając wartość product_specific: true.
  3. Napraw błędy kompilacji i sprawdź, czy urządzenie zostało prawidłowo skompilowane.

  4. Wgraj obraz i sprawdź, czy podczas uruchamiania urządzenia 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ła ona zarejestrowana w czasie 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. Wymuszanie interfejsów Javy

W tym kroku ustawisz PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, a następnie znajdziesz i naprawisz wynikające z tego błędy kompilacji. Wyszukaj 2 rodzaje błędów:

  • Błędy typu linku Ten błąd oznacza, że aplikacja zawiera linki do modułów Java, które mają szerszy zakres 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ż znajduje się on w ukrytym interfejsie API. Aby rozwiązać ten problem, użyj widocznego (nieukrytego) interfejsu API lub znajdź alternatywne rozwiązanie. 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 działania

Na tym etapie sprawdzisz, czy działanie w czasie działania jest zgodne z oczekiwaniami. W przypadku aplikacji, które można debugować, możesz monitorować użycie ukrytych interfejsów API za pomocą logu, korzystając z funkcji StrictMode.detectNonSdkApiUsage (która generuje log, gdy aplikacja używa ukrytego interfejsu API). Możesz też użyć narzędzia do analizy statycznej veridex, aby uzyskać informacje o rodzaju 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 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 korzystaniu z narzędzia veridex znajdziesz w artykule Testowanie za pomocą narzędzia veridex.

Krok 6. Zaktualizuj plik device.mk

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

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true