Как принудительно использовать интерфейсы сегментов товаров

В Android 11 раздел product отделен от разделов system и vendor. В рамках этих изменений вы можете управлять доступом раздела product к нативным интерфейсам и интерфейсам Java (по аналогии с тем, как работает принудительное применение интерфейсов для разделов vendor).

Как принудительно использовать нативные интерфейсы

Чтобы включить принудительное применение нативного интерфейса, задайте для параметра PRODUCT_PRODUCT_VNDK_VERSION значение current. (Если уровень API доставки для целевого устройства выше 29, автоматически устанавливается версия current). Меры принуждения позволяют:

  • Нативные модули в разделе product, которые нужно связать:
    • Статически или динамически к другим модулям в разделе product, которые включают статические, общие или заголовочные библиотеки.
    • Динамически к библиотекам VNDK в разделе system.
  • Библиотеки JNI в несвязанных APK-файлах в разделе product для связи с библиотеками в разделах /product/lib или /product/lib64 (в дополнение к библиотекам NDK).

Принудительное применение не позволяет создавать ссылки на другие разделы, кроме product.

Принудительное применение времени сборки (Android.bp)

В Android 11 системные модули могут создавать вариант изображения продукта в дополнение к основным и вариантам изображений поставщика. Если принудительное применение нативного интерфейса включено (для параметра PRODUCT_PRODUCT_VNDK_VERSION задано значение current):

  • Нативные модули в разделе product относятся к варианту товара, а не к основному варианту.

  • Модули с product_available: true в файлах Android.bp доступны для варианта товара.

  • Библиотеки или двоичные файлы, в которых указано product_specific: true, могут ссылаться на другие библиотеки, в файлах Android.bp которых указано product_specific: true или product_available: true.

  • В файлах Android.bp библиотек VNDK должен быть тег product_available: true, чтобы двоичные файлы product могли ссылаться на библиотеки VNDK.

В таблице ниже приведены свойства Android.bp, которые используются для создания вариантов изображений.

Свойства в Android.bp Созданные варианты
До вступления в силу После применения мер
по умолчанию (нет) core
(включает /system, /system_ext и /product)
core
(включает /system и /system_ext, но не /product)
system_ext_specific: true ядро ядро
product_specific: true ядро товар
vendor: true поставщик поставщик
vendor_available: true основной, поставщик; основной, поставщик;
product_available: true Н/Д основной продукт
vendor_available: true И product_available: true Н/Д основной, товар, поставщик
system_ext_specific: true И vendor_available: true core, vendor основной, поставщик;
product_specific: true И vendor_available: true основной, поставщик; product, vendor

Принудительное применение времени сборки (Android.mk)

Если принудительное использование встроенного интерфейса включено, у встроенных модулей, установленных в раздел product, будет тип ссылки native:product, который может ссылаться только на другие модули native:product или native:vndk. При попытке связать другие модули система сборки выдаст ошибку проверки типа связи.

Применение во время выполнения

Когда принудительное применение нативных интерфейсов включено, конфигурация компоновщика для компоновщика Bionic не позволяет системным процессам использовать библиотеки product, создавая раздел product для процессов product, которые не могут связываться с библиотеками за пределами раздела product (однако такие процессы могут связываться с библиотеками VNDK). При попытке нарушить конфигурацию связывания во время выполнения процесс завершается с ошибкой и генерируется сообщение об ошибке CANNOT LINK EXECUTABLE.

Принудительное применение интерфейсов Java

Чтобы включить принудительное применение интерфейса Java, установите для PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE значение true. (Значение true устанавливается автоматически, если целевой уровень API доставки выше 29.) Если этот параметр включен, принудительное применение разрешает или запрещает следующие виды доступа:

API /system /system_ext /product /vendor /data
Общедоступный API
@SystemApi
@hide API

Как и в разделе vendor, приложение или библиотека Java в разделе product может использовать только общедоступные и системные API. Связываться с библиотекой, которая использует скрытые API, запрещено. Это ограничение распространяется на связывание во время сборки и рефлексию во время выполнения.

Принудительное применение во время сборки

Во время сборки Make и Soong проверяют, что модули Java в разделе product не используют скрытые API, сверяясь с полями platform_apis и sdk_version. Поле sdk_version в приложениях из раздела product должно содержать значение current, system_current или числовую версию API, а поле platform_apis должно быть пустым.

Применение во время выполнения

Среда выполнения Android проверяет, чтобы приложения в разделе product не использовали скрытые API, в том числе отражение. Подробнее об ограничениях на использование интерфейсов, не входящих в SDK…

Как включить принудительное использование интерфейса продукта

Чтобы включить принудительное использование интерфейса продукта, выполните следующие действия:

Шаг Задача Обязательно
1 Создайте собственный системный файл makefile, в котором будут указаны пакеты для раздела system, а затем задайте проверку пути к артефактам в разделе device.mk (чтобы запретить установку в раздел system модулей, не относящихся к системе). НЕТ
2 Очистите белый список. НЕТ
3 Принудительное использование нативных интерфейсов и выявление ошибок связывания во время выполнения (можно выполнять параллельно с принудительным использованием Java). Г
4 Принудительное использование интерфейсов Java и проверка поведения во время выполнения (можно выполнять параллельно с принудительным использованием нативных интерфейсов). Г
5 Проверьте поведение во время выполнения. Г
6 Обновление device.mk с принудительным использованием интерфейса продукта Г

Шаг 1. Создайте файл makefile и включите проверку пути к артефакту

На этом шаге вы определяете файл system makefile.

  1. Создайте файл makefile, в котором определены пакеты для раздела system. Например, создайте файл oem_system.mk со следующим содержимым:

    $(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. В файле device.mk наследуйте общий файл makefile для раздела system и включите проверку требований к пути артефакта. Пример:

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

Требования к пути к артефакту

Если для переменной PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS задано значение true или strict, система сборки не позволит устанавливать пакеты, определенные в других файлах makefile, по путям, указанным в require-artifacts-in-path, и не позволит устанавливать артефакты пакетов, определенных в текущем файле makefile, по путям, не указанным в require-artifacts-in-path.

В приведенном выше примере, если для PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS задано значение strict, файлы makefile вне oem_system.mk не могут включать модули, установленные в раздел root или system. Чтобы включить эти модули, их нужно определить в самом файле oem_system.mk или в файле makefile, который включен в. Попытки установить модули по запрещенным путям приводят к сбоям сборки. Чтобы исправить разрывы, выполните одно из следующих действий:

  • Вариант 1. Добавьте системный модуль в make-файлы, включенные в oem_system.mk. Это позволяет выполнить требование к пути артефакта (поскольку модули теперь существуют в включенном файле makefile) и, таким образом, разрешить установку в набор путей в `require-artifacts-in-path`.

  • Вариант 2. Установите модули в раздел system_ext или product (и не устанавливайте их в раздел system).

  • Вариант 3. Добавьте модули в файл PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. В этом списке перечислены разрешенные модули, которые можно установить.

Шаг 2. Очистите белый список

На этом шаге вы делаете PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST пустым, чтобы все устройства, использующие oem_system.mk, могли также использовать одно изображение system. Чтобы очистить список разрешенных модулей, переместите все модули из списка в раздел system_ext или product или добавьте их в system. Этот шаг необязателен, поскольку для включения принудительного использования интерфейса товара не требуется определять общее изображение system. Однако очистка списка разрешенных каналов помогает определить границу system с помощью system_ext.

Шаг 3. Включите принудительное использование нативных интерфейсов

На этом этапе вы задаете значение PRODUCT_PRODUCT_VNDK_VERSION := current, а затем ищете и устраняете ошибки сборки и выполнения. Чтобы проверить загрузку устройства и журналы, а также найти и устранить ошибки связывания во время выполнения, сделайте следующее:

  1. Задайте значение PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Соберите устройство и проверьте, нет ли ошибок сборки. Скорее всего, вы увидите несколько ошибок сборки из-за отсутствия вариантов товара или основных вариантов. Распространенные причины:

    • Любой модуль hidl_interface, у которого есть product_specific: true, не будет доступен для системных модулей. Чтобы исправить это, замените product_specific: true на system_ext_specific: true.
    • В модулях может отсутствовать вариант товара, необходимый для модулей товаров. Чтобы устранить проблему, сделайте модуль доступным для раздела product, задав значение product_available: true, или переместите модуль в раздел product, задав значение product_specific: true.
  3. Устраните ошибки сборки и убедитесь, что сборка устройства выполнена успешно.

  4. Прошейте образ и проверьте, нет ли ошибок выполнения при загрузке устройства и в журналах.

    • Если тег linker из журнала отчета о тестировании показывает сообщение CANNOT LINK EXECUTABLE, в файле make отсутствует зависимость (и она не была зафиксирована во время сборки).
    • Чтобы проверить это в системе сборки, добавьте нужную библиотеку в поле shared_libs: или required:.
  5. Устраните отсутствующие зависимости, следуя приведенным выше инструкциям.

Шаг 4. Принудительное применение интерфейсов Java

На этом этапе вы задаете PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true, а затем находите и исправляете ошибки сборки. Обратите внимание на два типа ошибок:

  • Ошибки типа связи. Эта ошибка означает, что приложение ссылается на модули Java, у которых более широкая область действия sdk_version. Чтобы устранить проблему, расширьте sdk_version приложения или ограничьте sdk_version библиотеки. Пример ошибки:

    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.
    
  • Ошибки символов. Эта ошибка означает, что символ не найден, поскольку он находится в скрытом API. Чтобы устранить проблему, используйте видимый API или найдите альтернативный вариант. Пример ошибки:

    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
    

Шаг 5. Проверьте поведение во время выполнения

На этом этапе вы проверяете, соответствует ли поведение во время выполнения ожидаемому. Для приложений, которые доступны для отладки, вы можете отслеживать использование скрытых API с помощью журнала, используя StrictMode.detectNonSdkApiUsage (приложение будет создавать журнал при использовании скрытого API). Вы также можете использовать инструмент статического анализа veridex, чтобы узнать тип использования (связывание или рефлексия), уровень ограничения и стек вызовов.

  • Синтаксис Veridex:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
  • Пример результата проверки 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;
    

Подробную информацию об использовании veridex можно найти в разделе Тестирование с помощью инструмента veridex.

Шаг 6. Обновите файл device.mk

После того как вы устраните все ошибки сборки и выполнения и убедитесь, что поведение во время выполнения соответствует ожиданиям, задайте в файле device.mk следующие значения:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true