Aplicar interfaces de partição de produtos

O Android 11 desagrupa a partição product, tornando-a independente das partições system e vendor. Como parte dessas mudanças, agora é possível controlar o acesso da partição product a interfaces nativas e Java, o que é semelhante ao funcionamento da aplicação de interfaces para partições vendor.

Aplicar interfaces nativas

Para ativar a aplicação da interface nativa, defina PRODUCT_PRODUCT_VNDK_VERSION como current. A versão é definida automaticamente como current quando o nível da API de envio para o destino é maior que 29. A restrição permite:

  • Módulos nativos na partição product que serão vinculados:
    • Estática ou dinamicamente a outros módulos na partição product que incluem bibliotecas estáticas, compartilhadas ou de cabeçalho.
    • Dinâmica para bibliotecas do VNDK na partição system.
  • Bibliotecas JNI em APKs não agrupados na partição product para vincular a bibliotecas em /product/lib ou /product/lib64 (além das bibliotecas do NDK).

A aplicação não permite outros links para partições diferentes da partição product.

Aplicação do tempo de build (Android.bp)

No Android 11, os módulos do sistema podem criar uma variante de imagem de produto, além das variantes de imagem do núcleo e do fornecedor. Quando a aplicação da interface nativa está ativada (PRODUCT_PRODUCT_VNDK_VERSION é definido como current):

  • Os módulos nativos na partição product estão na variante do produto, e não na variante principal.

  • Módulos com product_available: true nos arquivos Android.bp estão disponíveis para a variante do produto.

  • Bibliotecas ou binários que especificam product_specific: true podem ser vinculados a outras bibliotecas que especificam product_specific: true ou product_available: true nos arquivos Android.bp.

  • As bibliotecas do VNDK precisam ter product_available: true nos arquivos Android.bp para que os binários product possam ser vinculados às bibliotecas do VNDK.

A tabela a seguir resume as propriedades Android.bp usadas para criar variantes de imagem.

Propriedades no Android.bp Variantes criadas
Antes da aplicação Após a aplicação
padrão (nenhuma)
do núcleo(inclui /system, /system_ext e /product)
núcleo
(inclui /system e /system_ext, mas não /product)
system_ext_specific: true core core
product_specific: true core produto
vendor: true fornecedor fornecedor
vendor_available: true núcleo, fornecedor núcleo, fornecedor
product_available: true N/A núcleo, produto
vendor_available: true E product_available: true N/A core, produto, fornecedor
system_ext_specific: true E vendor_available: true núcleo, fornecedor núcleo, fornecedor
product_specific: true E vendor_available: true núcleo, fornecedor produto, fornecedor

Aplicação de regras no tempo de build (Android.mk)

Quando a aplicação de interface nativa está ativada, os módulos nativos instalados na partição product têm um tipo de link native:product que pode se vincular apenas a outros módulos native:product ou native:vndk. A tentativa de vincular a um módulo diferente desses faz com que o sistema de build gere um erro de verificação de tipo de link.

Aplicação no ambiente de execução

Quando a aplicação da interface nativa está ativada, a configuração do vinculador para o vinculador bionic não permite que os processos do sistema usem bibliotecas product, criando uma seção product para os processos product que não podem ser vinculados a bibliotecas fora da partição product. No entanto, esses processos podem ser vinculados a bibliotecas do VNDK. As tentativas de violar a configuração de vinculação do ambiente de execução fazem com que o processo falhe e gere uma mensagem de erro CANNOT LINK EXECUTABLE.

Aplicar interfaces Java

Para ativar a aplicação da interface Java, defina PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE como true. O valor é definido automaticamente como true quando o nível da API de envio para o destino é maior que 29. Quando ativada, a aplicação permite ou proíbe o seguinte acesso:

API /system /system_ext /product /vendor /data
API pública
@SystemApi
API @hide

Como na partição vendor, um app ou uma biblioteca Java na partição product pode usar apenas APIs públicas e do sistema. Não é permitido vincular a uma biblioteca que usa APIs ocultas. Essa restrição inclui vinculação no tempo de build e reflexão no ambiente de execução.

Aplicação de regras no tempo de build

No tempo de build, o Make e o Soong verificam se os módulos Java na partição product não usam APIs ocultas verificando os campos platform_apis e sdk_version. O sdk_version dos apps na partição product precisa ser preenchido com current, system_current ou a versão numérica da API, e o campo platform_apis precisa estar vazio.

Aplicação no ambiente de execução

O ambiente de execução do Android verifica se os apps na partição product não usam APIs ocultas, incluindo a reflexão. Para mais detalhes, consulte Restrições para interfaces não SDK.

Ativar a aplicação de interfaces de produtos

Siga as etapas desta seção para ativar a aplicação da interface do produto.

Etapa Tarefa Obrigatório
1 Defina seu próprio makefile do sistema que especifica os pacotes para a partição system e defina a verificação de requisito de caminho de artefatos no device.mk para impedir que módulos não do sistema sejam instalados na partição system. N
2 Limpe a lista de permissões. N
3 Aplicar interfaces nativas e identificar falhas de links no ambiente de execução (pode ser executada em paralelo com a aplicação do Java). Y
4 Aplicar interfaces Java e verificar o comportamento do ambiente de execução (pode ser executado em paralelo com a aplicação nativa). Y
5 Confira os comportamentos de execução. Y
6 Atualização de device.mk com a aplicação de interface do produto. Y

Etapa 1: criar o makefile e ativar a verificação de caminho do artefato

Nesta etapa, você define o makefile system.

  1. Crie um makefile que defina os pacotes para a partição system. Por exemplo, crie um arquivo oem_system.mk com o seguinte:

    $(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. No arquivo device.mk, herde o makefile comum para a partição system e ative a verificação de requisitos de caminho de artefato. Exemplo:

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

Sobre os requisitos do caminho do artefato

Quando PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS é definido como true ou strict, o sistema de build impede que pacotes definidos em outros makefiles sejam instalados nos caminhos definidos em require-artifacts-in-path e impede que pacotes definidos no makefile atual instalem artefatos fora dos caminhos definidos em require-artifacts-in-path.

No exemplo acima, com PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS definido como strict, os arquivos de make fora de oem_system.mk não podem incluir módulos instalados na partição root ou system. Para incluir esses módulos, você precisa defini-los no arquivo oem_system.mk ou em um makefile incluído. As tentativas de instalar módulos em caminhos não permitidos causam falhas de build. Para corrigir falhas, siga um destes procedimentos:

  • Opção 1:inclua o módulo do sistema nos makefiles incluídos em oem_system.mk. Isso faz com que o requisito do caminho do artefato seja atendido, já que os módulos agora existem em um makefile incluído, e permite a instalação no conjunto de caminhos em "require-artifacts-in-path".

  • Opção 2:instale módulos na partição system_ext ou product (e não instale módulos na partição system).

  • Opção 3:adicione módulos ao PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST. Isso lista os módulos que podem ser instalados.

Etapa 2: esvaziar a lista de permissões

Nesta etapa, você deixa o PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST vazio para que todos os dispositivos que compartilham oem_system.mk também possam compartilhar uma única imagem system. Para esvaziar a lista de permissões, mova todos os módulos da lista para a partição system_ext ou product ou adicione-os aos arquivos de criação system. Essa etapa é opcional, porque a definição de uma imagem system comum não é necessária para ativar a aplicação da interface do produto. No entanto, esvaziar a lista de permissões é útil para definir o limite system com system_ext.

Etapa 3: aplicar interfaces nativas

Nesta etapa, você define PRODUCT_PRODUCT_VNDK_VERSION := current e procura erros de build e de execução e os resolve. Para verificar a inicialização e os registros do dispositivo e encontrar e corrigir falhas de vinculação de execução:

  1. Defina PRODUCT_PRODUCT_VNDK_VERSION := current.

  2. Crie o dispositivo e procure erros de build. É provável que você encontre alguns erros de build para variantes de produto ou variantes principais ausentes. As pausas comuns incluem:

    • Qualquer módulo hidl_interface que tenha product_specific: true não estará disponível para módulos do sistema. Para corrigir, substitua product_specific: true por system_ext_specific: true.
    • Os módulos podem não ter a variante de produto necessária para os módulos de produtos. Para corrigir, disponibilize esse módulo para a partição product definindo product_available: true ou mova o módulo para a partição product definindo product_specific: true.
  3. Resolva os erros de build e verifique se o dispositivo foi criado.

  4. Faça o flash da imagem e procure erros de execução na inicialização e nos registros do dispositivo.

    • Se a tag linker de um registro de caso de teste mostrar uma mensagem CANNOT LINK EXECUTABLE, o arquivo de make não tem uma dependência (e não foi capturada no tempo de build).
    • Para verificar isso no sistema de build, adicione a biblioteca necessária ao campo shared_libs: ou required:.
  5. Resolva as dependências ausentes usando as orientações acima.

Etapa 4: aplicar interfaces Java

Nesta etapa, você define PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true e encontra e corrige os erros de build resultantes. Procure dois tipos específicos de erros:

  • Erros de tipo de link. Esse erro indica que um app está vinculado a módulos Java com uma sdk_version mais ampla. Para corrigir, amplie o sdk_version do app ou restrinja o sdk_version da biblioteca. Exemplo de erro:

    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.
    
  • Erros de símbolo. Esse erro indica que um símbolo não pode ser encontrado porque ele está em uma API oculta. Para corrigir, use uma API visível (não oculta) ou encontre uma alternativa. Exemplo de erro:

    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
    

Etapa 5: verificar os comportamentos do ambiente de execução

Nesta etapa, você verifica se os comportamentos do ambiente de execução estão como esperado. Para apps depuráveis, é possível monitorar o uso de APIs ocultas por registro usando StrictMode.detectNonSdkApiUsage, que gera um registro quando o app usa uma API oculta. Como alternativa, utilize a ferramenta de análise estática veridex para ver o tipo de uso (vinculação ou reflexão), o nível de restrição e a pilha de chamadas.

  • Sintaxe Veridex:

    ./art/tools/veridex/appcompat.sh --dex-file={apk file}
  • Exemplo de resultado da 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;
    

Para detalhes sobre o uso do veridex, consulte Testar usando a ferramenta veridex.

Etapa 6: atualizar o device.mk

Depois de corrigir todas as falhas de build e ambiente de execução e verificar se os comportamentos de execução estão conforme o esperado, defina o seguinte em device.mk:

  • PRODUCT_PRODUCT_VNDK_VERSION := current
  • PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true