Formato de arquivo APEX

O formato de contêiner Android Pony EXpress (APEX) foi introduzido no Android 10 e é usado no fluxo de instalação para módulos de sistema de nível inferior. Este formato facilita as atualizações de componentes do sistema que não se enquadram no modelo padrão do aplicativo Android. Alguns exemplos de componentes são serviços e bibliotecas nativos, camadas de abstração de hardware ( HALs ), tempo de execução ( ART ) e bibliotecas de classes.

O termo "APEX" também pode se referir a um arquivo APEX.

Fundo

Embora o Android suporte atualizações de módulos que se enquadram no modelo de aplicativo padrão (por exemplo, serviços, atividades) por meio de aplicativos instaladores de pacotes (como o aplicativo Google Play Store), o uso de um modelo semelhante para componentes de sistema operacional de nível inferior tem as seguintes desvantagens:

  • Módulos baseados em APK não podem ser usados ​​no início da sequência de inicialização. O gerenciador de pacotes é o repositório central de informações sobre aplicativos e só pode ser iniciado a partir do gerenciador de atividades, que fica pronto em um estágio posterior do procedimento de inicialização.
  • O formato APK (particularmente o manifesto) foi projetado para aplicativos Android e os módulos do sistema nem sempre são adequados.

Projeto

Esta seção descreve o design de alto nível do formato de arquivo APEX e do gerenciador APEX, que é um serviço que gerencia arquivos APEX.

Para obter mais informações sobre por que esse design para APEX foi selecionado, consulte Alternativas consideradas ao desenvolver o APEX .

Formato APEX

Este é o formato de um arquivo APEX.

Formato de arquivo APEX

Figura 1. Formato de arquivo APEX

No nível superior, um arquivo APEX é um arquivo zip no qual os arquivos são armazenados descompactados e localizados em limites de 4 KB.

Os quatro arquivos em um arquivo APEX são:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

O arquivo apex_manifest.json contém o nome e a versão do pacote, que identificam um arquivo APEX. Este é um buffer de protocolo ApexManifest no formato JSON.

O arquivo AndroidManifest.xml permite que o arquivo APEX use ferramentas e infraestrutura relacionadas ao APK, como ADB, PackageManager e aplicativos instaladores de pacotes (como Play Store). Por exemplo, o arquivo APEX pode usar uma ferramenta existente como aapt para inspecionar metadados básicos do arquivo. O arquivo contém o nome do pacote e informações de versão. Geralmente, essas informações também estão disponíveis em apex_manifest.json .

apex_manifest.json é recomendado em vez de AndroidManifest.xml para novos códigos e sistemas que lidam com APEX. AndroidManifest.xml pode conter informações de segmentação adicionais que podem ser usadas pelas ferramentas de publicação de aplicativos existentes.

apex_payload.img é uma imagem do sistema de arquivos ext4 apoiada por dm-verity. A imagem é montada em tempo de execução por meio de um dispositivo de loopback. Especificamente, a árvore hash e o bloco de metadados são criados usando a biblioteca libavb . A carga útil do sistema de arquivos não é analisada (porque a imagem deve ser montável no local). Arquivos regulares estão incluídos no arquivo apex_payload.img .

apex_pubkey é a chave pública usada para assinar a imagem do sistema de arquivos. Em tempo de execução, esta chave garante que o APEX baixado seja assinado com a mesma entidade que assina o mesmo APEX nas partições integradas.

Diretrizes de nomenclatura APEX

Para ajudar a evitar conflitos de nomenclatura entre novos APEXes à medida que a plataforma avança, use as seguintes diretrizes de nomenclatura:

  • com.android.*
    • Reservado para APEXes AOSP. Não é exclusivo de nenhuma empresa ou dispositivo.
  • com.<companyname>.*
    • Reservado para uma empresa. Potencialmente usado por vários dispositivos dessa empresa.
  • com.<companyname>.<devicename>.*
    • Reservado para APEXes exclusivos de um dispositivo específico (ou subconjunto de dispositivos).

Gerente APEX

O gerenciador APEX (ou apexd ) é um processo nativo independente responsável por verificar, instalar e desinstalar arquivos APEX. Este processo é iniciado e fica pronto no início da sequência de inicialização. Os arquivos APEX normalmente são pré-instalados no dispositivo em /system/apex . O gerenciador APEX usa esses pacotes por padrão se nenhuma atualização estiver disponível.

A sequência de atualização de um APEX usa a classe PackageManager e é a seguinte.

  1. Um arquivo APEX é baixado por meio de um aplicativo instalador de pacote, ADB ou outra fonte.
  2. O gerenciador de pacotes inicia o procedimento de instalação. Ao reconhecer que o arquivo é um APEX, o gerenciador de pacotes transfere o controle para o gerenciador APEX.
  3. O gerenciador APEX verifica o arquivo APEX.
  4. Se o arquivo APEX for verificado, o banco de dados interno do gerenciador APEX será atualizado para refletir que o arquivo APEX será ativado na próxima inicialização.
  5. O solicitante da instalação recebe uma transmissão após a verificação bem-sucedida do pacote.
  6. Para continuar a instalação, o sistema deve ser reinicializado.
  7. Na próxima inicialização, o gerenciador APEX é iniciado, lê o banco de dados interno e faz o seguinte para cada arquivo APEX listado:

    1. Verifica o arquivo APEX.
    2. Cria um dispositivo de loopback a partir do arquivo APEX.
    3. Cria um dispositivo de bloco mapeador de dispositivos na parte superior do dispositivo de loopback.
    4. Monta o dispositivo de bloco mapeador de dispositivos em um caminho exclusivo (por exemplo, /apex/ name @ ver ).

Quando todos os arquivos APEX listados no banco de dados interno são montados, o gerenciador APEX fornece um serviço de binder para outros componentes do sistema consultarem informações sobre os arquivos APEX instalados. Por exemplo, os demais componentes do sistema podem consultar a lista de arquivos APEX instalados no dispositivo ou consultar o caminho exato onde um APEX específico está montado, para que os arquivos possam ser acessados.

Arquivos APEX são arquivos APK

Os arquivos APEX são arquivos APK válidos porque são arquivos zip assinados (usando o esquema de assinatura APK) contendo um arquivo AndroidManifest.xml . Isso permite que os arquivos APEX usem a infraestrutura para arquivos APK, como um aplicativo instalador de pacotes, o utilitário de assinatura e o gerenciador de pacotes.

O arquivo AndroidManifest.xml dentro de um arquivo APEX é mínimo, consistindo no name do pacote, versionCode e targetSdkVersion , minSdkVersion e maxSdkVersion opcionais para uma segmentação mais detalhada. Essas informações permitem que os arquivos APEX sejam entregues por meio de canais existentes, como aplicativos instaladores de pacotes e ADB.

Tipos de arquivos suportados

O formato APEX suporta estes tipos de arquivo:

  • Bibliotecas compartilhadas nativas
  • Executáveis ​​nativos
  • Arquivos JAR
  • Arquivos de dados
  • Arquivos de configuração

Isso não significa que o APEX possa atualizar todos esses tipos de arquivo. Se um tipo de arquivo pode ser atualizado depende da plataforma e da estabilidade das definições das interfaces para os tipos de arquivo.

Opções de assinatura

Os arquivos APEX são assinados de duas maneiras. Primeiro, o arquivo apex_payload.img (especificamente, o descritor vbmeta anexado a apex_payload.img ) é assinado com uma chave. Em seguida, todo o APEX é assinado usando o esquema de assinatura APK v3 . Duas chaves diferentes são usadas neste processo.

No lado do dispositivo, é instalada uma chave pública correspondente à chave privada usada para assinar o descritor vbmeta. O gerenciador APEX usa a chave pública para verificar os APEXs cuja instalação é solicitada. Cada APEX deve ser assinado com chaves diferentes e é aplicado tanto em tempo de construção quanto em tempo de execução.

APEX em partições integradas

Os arquivos APEX podem estar localizados em partições internas, como /system . A partição já passou do dm-verity, então os arquivos APEX são montados diretamente no dispositivo de loopback.

Se um APEX estiver presente em uma partição integrada, o APEX poderá ser atualizado fornecendo um pacote APEX com o mesmo nome de pacote e um código de versão maior ou igual. O novo APEX é armazenado em /data e, semelhante aos APKs, a versão recém-instalada oculta a versão já presente na partição integrada. Mas, diferentemente dos APKs, a versão recém-instalada do APEX só é ativada após a reinicialização.

Requisitos do kernel

Para oferecer suporte aos módulos principais do APEX em um dispositivo Android, são necessários os seguintes recursos do kernel Linux: o driver de loopback e dm-verity. O driver de loopback monta a imagem do sistema de arquivos em um módulo APEX e o dm-verity verifica o módulo APEX.

O desempenho do driver de loopback e do dm-verity é importante para alcançar um bom desempenho do sistema ao usar módulos APEX.

Versões de kernel suportadas

Os módulos principais do APEX são suportados em dispositivos que usam versões de kernel 4.4 ou superior. Novos dispositivos lançados com Android 10 ou superior devem usar a versão 4.9 ou superior do kernel para oferecer suporte aos módulos APEX.

Patches de kernel necessários

Os patches de kernel necessários para suportar módulos APEX estão incluídos na árvore comum do Android. Para obter os patches de suporte ao APEX, use a versão mais recente da árvore comum do Android.

Versão do kernel 4.4

Esta versão é compatível apenas com dispositivos atualizados do Android 9 para o Android 10 e que desejam oferecer suporte a módulos APEX. Para obter os patches necessários, é altamente recomendável fazer uma mesclagem descendente da ramificação android-4.4 . A seguir está uma lista dos patches individuais necessários para a versão 4.4 do kernel.

  • UPSTREAM: loop: adicione ioctl para alterar o tamanho do bloco lógico ( 4.4 )
  • BACKPORT: bloco/loop: definir hw_sectors ( 4.4 )
  • UPSTREAM: loop: Adicione LOOP_SET_BLOCK_SIZE em compatibilidade ioctl ( 4.4 )
  • ANDROID: mnt: Corrigir next_descendent ( 4.4 )
  • ANDROID: mnt: a remontagem deve ser propagada para escravos de escravos ( 4.4 )
  • ANDROID: mnt: Propague a remontagem corretamente ( 4.4 )
  • Reverter "ANDROID: dm verity: adicionar tamanho mínimo de pré-busca" ( 4.4 )
  • UPSTREAM: loop: descarta caches se offset ou block_size forem alterados ( 4.4 )

Versões do kernel 4.9/4.14/4.19

Para obter os patches necessários para as versões do kernel 4.9/4.14/4.19, faça downmerge do branch android-common .

Opções de configuração de kernel necessárias

A lista a seguir mostra os requisitos básicos de configuração para suporte aos módulos APEX introduzidos no Android 10. Os itens com um asterisco (*) são requisitos existentes do Android 9 e versões anteriores.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Requisitos de parâmetro de linha de comando do kernel

Para oferecer suporte ao APEX, certifique-se de que os parâmetros de linha de comando do kernel atendam aos seguintes requisitos:

  • loop.max_loop NÃO deve ser definido
  • loop.max_part deve ser <= 8

Construa um APEX

Esta seção descreve como construir um APEX usando o sistema de compilação Android. A seguir está um exemplo de Android.bp para um APEX denominado apex.test .

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

Exemplo apex_manifest.json :

{
  "name": "com.android.example.apex",
  "version": 1
}

exemplo de file_contexts :

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Tipos e locais de arquivos no APEX

Tipo de arquivo Localização no APEX
Bibliotecas compartilhadas /lib e /lib64 ( /lib/arm para braço traduzido em x86)
Executáveis /bin
Bibliotecas Java /javalib
Pré-construídos /etc

Dependências transitivas

Os arquivos APEX incluem automaticamente dependências transitivas de bibliotecas ou executáveis ​​compartilhados nativos. Por exemplo, se libFoo depender de libBar , as duas bibliotecas serão incluídas quando apenas libFoo estiver listado na propriedade native_shared_libs .

Lidar com várias ABIs

Instale a propriedade native_shared_libs para interfaces binárias de aplicativo (ABIs) primárias e secundárias do dispositivo. Se um APEX tiver como alvo dispositivos com uma única ABI (ou seja, somente 32 bits ou somente 64 bits), somente bibliotecas com a ABI correspondente serão instaladas.

Instale a propriedade binaries apenas para a ABI primária do dispositivo conforme descrito abaixo:

  • Se o dispositivo for apenas de 32 bits, apenas a variante de 32 bits do binário será instalada.
  • Se o dispositivo for apenas de 64 bits, apenas a variante de 64 bits do binário será instalada.

Para adicionar controle refinado sobre as ABIs das bibliotecas e binários nativos, use as propriedades multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] .

  • first : corresponde à ABI primária do dispositivo. Este é o padrão para binários.
  • lib32 : Corresponde à ABI de 32 bits do dispositivo, se houver suporte.
  • lib64 : Corresponde à ABI de 64 bits do dispositivo compatível.
  • prefer32 : Corresponde à ABI de 32 bits do dispositivo, se houver suporte. Se a ABI de 32 bits não for compatível, corresponde à ABI de 64 bits.
  • both : corresponde a ambas as ABIs. Este é o padrão para native_shared_libraries .

As propriedades java , libraries e prebuilts são independentes de ABI.

Este exemplo é para um dispositivo que suporta 32/64 e não prefere 32:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

assinatura vbmeta

Assine cada APEX com chaves diferentes. Quando uma nova chave for necessária, crie um par de chaves pública-privada e crie um módulo apex_key . Use a propriedade key para assinar o APEX usando a chave. A chave pública é incluída automaticamente no APEX com o nome avb_pubkey .

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

No exemplo acima, o nome da chave pública ( foo ) torna-se o ID da chave. O ID da chave usada para assinar um APEX está escrito no APEX. Em tempo de execução, apexd verifica o APEX usando uma chave pública com o mesmo ID no dispositivo.

Assinatura APEX

Assine APEXes da mesma forma que assina APKs. Assine APEXes duas vezes; uma vez para o mini sistema de arquivos (arquivo apex_payload.img ) e uma vez para o arquivo inteiro.

Para assinar um APEX no nível do arquivo, defina a propriedade certificate de uma destas três maneiras:

  • Não definido: se nenhum valor for definido, o APEX será assinado com o certificado localizado em PRODUCT_DEFAULT_DEV_CERTIFICATE . Se nenhum sinalizador for definido, o caminho padrão será build/target/product/security/testkey .
  • <name> : O APEX é assinado com o certificado <name> no mesmo diretório que PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : O APEX é assinado com o certificado definido pelo módulo Soong denominado <name> . O módulo de certificado pode ser definido da seguinte forma.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

Instale um APEX

Para instalar um APEX, use ADB.

adb install apex_file_name
adb reboot

Se supportsRebootlessUpdate estiver definido como true em apex_manifest.json e o APEX atualmente instalado não for utilizado (por exemplo, todos os serviços que ele contém foram interrompidos), então um novo APEX pode ser instalado sem reinicialização com o sinalizador --force-non-staged .

adb install --force-non-staged apex_file_name

Utilize um APEX

Após a reinicialização, o APEX é montado no diretório /apex/<apex_name>@<version> . Várias versões do mesmo APEX podem ser montadas ao mesmo tempo. Entre os caminhos de montagem, aquele que corresponde à versão mais recente é montado por ligação em /apex/<apex_name> .

Os clientes podem usar o caminho montado em bind para ler ou executar arquivos do APEX.

APEXes são normalmente usados ​​da seguinte forma:

  1. Um OEM ou ODM pré-carrega um APEX em /system/apex quando o dispositivo é enviado.
  2. Os arquivos no APEX são acessados ​​por meio do caminho /apex/<apex_name>/ .
  3. Quando uma versão atualizada do APEX é instalada em /data/apex , o caminho aponta para o novo APEX após a reinicialização.

Atualizar um serviço com um APEX

Para atualizar um serviço usando um APEX:

  1. Marque o serviço na partição do sistema como atualizável. Adicione a opção updatable à definição de serviço.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Crie um novo arquivo .rc para o serviço atualizado. Use a opção override para redefinir o serviço existente.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

As definições de serviço só podem ser definidas no arquivo .rc de um APEX. Os gatilhos de ação não são compatíveis com APEXes.

Se um serviço marcado como atualizável for iniciado antes da ativação dos APEXes, o início será atrasado até que a ativação dos APEXes seja concluída.

Configure o sistema para suportar atualizações do APEX

Defina a seguinte propriedade do sistema como true para oferecer suporte a atualizações de arquivos APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

ou apenas

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

APEX achatado

Para dispositivos legados, às vezes é impossível ou inviável atualizar o kernel antigo para oferecer suporte total ao APEX. Por exemplo, o kernel pode ter sido construído sem CONFIG_BLK_DEV_LOOP=Y , o que é crucial para montar a imagem do sistema de arquivos dentro de um APEX.

Flattened APEX é um APEX especialmente construído que pode ser ativado em dispositivos com um kernel legado. Os arquivos em um APEX nivelado são instalados diretamente em um diretório na partição integrada. Por exemplo, lib/libFoo.so em um APEX nivelado my.apex é instalado em /system/apex/my.apex/lib/libFoo.so .

A ativação de um APEX nivelado não envolve o dispositivo de loop. Todo o diretório /system/apex/my.apex é montado diretamente em /apex/name@ver .

APEXes nivelados não podem ser atualizados baixando versões atualizadas dos APEXes da rede porque os APEXes baixados não podem ser nivelados. APEXes nivelados só podem ser atualizados por meio de um OTA normal.

APEX nivelado é a configuração padrão. Isso significa que todos os APEXes são nivelados por padrão, a menos que você configure explicitamente seu dispositivo para criar APEXes não nivelados para oferecer suporte a atualizações de APEX (conforme explicado acima).

A mistura de APEXes nivelados e não nivelados em um dispositivo NÃO é suportada. Os APEXes em um dispositivo devem ser todos não nivelados ou todos nivelados. Isso é especialmente importante ao enviar pré-construídos APEX pré-assinados para projetos como Mainline. APEXes que não são pré-assinados (ou seja, criados a partir da fonte) também devem ser não nivelados e assinados com chaves adequadas. O dispositivo deve herdar de updatable_apex.mk conforme explicado em Atualizando um serviço com um APEX .

APEX compactados

O Android 12 e versões posteriores apresentam compactação APEX para reduzir o impacto de armazenamento de pacotes APEX atualizáveis. Após a instalação de uma atualização para um APEX, embora sua versão pré-instalada não seja mais usada, ele ainda ocupa a mesma quantidade de espaço. Esse espaço ocupado permanece indisponível.

A compactação APEX minimiza esse impacto no armazenamento usando um conjunto altamente compactado de arquivos APEX em partições somente leitura (como a partição /system ). O Android 12 e versões posteriores usam um algoritmo de compactação zip DEFLATE.

A compactação não fornece otimização para o seguinte:

  • Bootstrap APEXes que precisam ser montados bem no início da sequência de inicialização.

  • APEXes não atualizáveis. A compactação só será benéfica se uma versão atualizada de um APEX estiver instalada na partição /data . Uma lista completa de APEXes atualizáveis ​​está disponível na página Modular System Components .

  • Bibliotecas compartilhadas dinâmicas APEXes. Como apexd sempre ativa ambas as versões desses APEXes (pré-instalados e atualizados), compactá-los não agrega valor.

Formato de arquivo APEX compactado

Este é o formato de um arquivo APEX compactado.

Diagram shows the format of a compressed APEX file

Figura 2. Formato de arquivo APEX compactado

No nível superior, um arquivo APEX compactado é um arquivo zip que contém o arquivo apex original em formato deflacionado com um nível de compactação de 9 e com outros arquivos armazenados descompactados.

Quatro arquivos compõem um arquivo APEX:

  • original_apex : deflacionado com nível de compactação 9 Este é o arquivo APEX original e descompactado.
  • apex_manifest.pb : armazenado apenas
  • AndroidManifest.xml : armazenado apenas
  • apex_pubkey : armazenado apenas

Os arquivos apex_manifest.pb , AndroidManifest.xml e apex_pubkey são cópias de seus arquivos correspondentes em original_apex .

Crie APEX compactado

O APEX compactado pode ser construído usando a ferramenta apex_compression_tool.py localizada em system/apex/tools .

Vários parâmetros relacionados à compactação APEX estão disponíveis no sistema de compilação.

No Android.bp , se um arquivo APEX é compressível é controlado pela propriedade compressible :

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Um sinalizador de produto PRODUCT_COMPRESSED_APEX controla se uma imagem do sistema criada a partir da origem deve conter arquivos APEX compactados.

Para experimentação local, você pode forçar uma compilação a compactar APEXes definindo OVERRIDE_PRODUCT_COMPRESSED_APEX= como true .

Os arquivos APEX compactados gerados pelo sistema de compilação possuem a extensão .capex . A extensão facilita a distinção entre versões compactadas e descompactadas de um arquivo APEX.

Algoritmos de compressão suportados

O Android 12 oferece suporte apenas à compactação deflate-zip.

Ative um arquivo APEX compactado durante a inicialização

Antes que um APEX compactado possa ser ativado, o arquivo original_apex dentro dele é descompactado no diretório /data/apex/decompressed . O arquivo APEX descompactado resultante é vinculado ao diretório /data/apex/active .

Considere o exemplo a seguir como uma ilustração do processo descrito acima.

Considere /system/apex/com.android.foo.capex como um APEX compactado sendo ativado, com versionCode 37.

  1. O arquivo original_apex dentro de /system/apex/com.android.foo.capex é descompactado em /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex é executado para verificar se ele possui um rótulo SELinux correto.
  3. As verificações são realizadas em /data/apex/decompressed/com.android.foo@37.apex para garantir sua validade: apexd verifica a chave pública incluída em /data/apex/decompressed/com.android.foo@37.apex para verifique se é igual ao incluído em /system/apex/com.android.foo.capex .
  4. O arquivo /data/apex/decompressed/com.android.foo@37.apex está vinculado ao diretório /data/apex/active/com.android.foo@37.apex .
  5. A lógica de ativação regular para arquivos APEX descompactados é executada em /data/apex/active/com.android.foo@37.apex .

Interação com OTA

Arquivos APEX compactados têm implicações na entrega e aplicação OTA. Como uma atualização OTA pode conter um arquivo APEX compactado com um nível de versão superior ao que está ativo em um dispositivo, uma certa quantidade de espaço livre deve ser reservada antes que um dispositivo seja reinicializado para aplicar uma atualização OTA.

Para oferecer suporte ao sistema OTA, apexd expõe estas duas APIs de ligação:

  • calculateSizeForCompressedApex - calcula o tamanho necessário para descompactar arquivos APEX em um pacote OTA. Isso pode ser usado para verificar se um dispositivo tem espaço suficiente antes que um OTA seja baixado.
  • reserveSpaceForCompressedApex - reserva espaço no disco para uso futuro pelo apexd para descompactar arquivos APEX compactados dentro do pacote OTA.

No caso de uma atualização A/B OTA, apexd tenta descompactar em segundo plano como parte da rotina OTA pós-instalação. Se a descompactação falhar, apexd executa a descompactação durante a inicialização que aplica a atualização OTA.

Alternativas consideradas ao desenvolver o APEX

Aqui estão algumas opções que o AOSP considerou ao projetar o formato de arquivo APEX e por que foram incluídas ou excluídas.

Sistemas regulares de gerenciamento de pacotes

As distribuições Linux possuem sistemas de gerenciamento de pacotes como dpkg e rpm , que são poderosos, maduros e robustos. Porém, eles não foram adotados para APEX porque não conseguem proteger os pacotes após a instalação. A verificação é realizada somente quando os pacotes estão sendo instalados. Os invasores podem quebrar a integridade dos pacotes instalados sem serem notados. Esta é uma regressão para Android onde todos os componentes do sistema foram armazenados em sistemas de arquivos somente leitura cuja integridade é protegida pelo dm-verity para cada E/S. Qualquer violação dos componentes do sistema deve ser proibida ou detectável para que o dispositivo possa se recusar a inicializar se estiver comprometido.

dm-crypt para integridade

Os arquivos em um contêiner APEX são de partições internas (por exemplo, a partição /system ) que são protegidas pelo dm-verity, onde qualquer modificação nos arquivos é proibida mesmo após a montagem das partições. Para fornecer o mesmo nível de segurança aos arquivos, todos os arquivos em um APEX são armazenados em uma imagem do sistema de arquivos emparelhada com uma árvore hash e um descritor vbmeta. Sem dm-verity, um APEX na partição /data fica vulnerável a modificações não intencionais feitas após ter sido verificado e instalado.

Na verdade, a partição /data também é protegida por camadas de criptografia como dm-crypt. Embora isso forneça algum nível de proteção contra adulteração, seu objetivo principal é a privacidade, não a integridade. Quando um invasor obtém acesso à partição /data , não pode haver proteção adicional, e isso novamente é uma regressão em comparação com todos os componentes do sistema que estão na partição /system . A árvore hash dentro de um arquivo APEX junto com dm-verity fornece o mesmo nível de proteção de conteúdo.

Redirecionar caminhos de /system para /apex

Os arquivos de componentes do sistema empacotados em um APEX são acessíveis por meio de novos caminhos como /apex/<name>/lib/libfoo.so . Quando os arquivos faziam parte da partição /system , eles eram acessíveis através de caminhos como /system/lib/libfoo.so . Um cliente de um arquivo APEX (outros arquivos APEX ou a plataforma) deve usar os novos caminhos. Talvez seja necessário atualizar o código existente como resultado da alteração do caminho.

Embora uma maneira de evitar a mudança de caminho seja sobrepor o conteúdo do arquivo em um arquivo APEX na partição /system , a equipe do Android decidiu não sobrepor arquivos na partição /system porque isso poderia afetar o desempenho, pois o número de arquivos sendo sobrepostos ( possivelmente até empilhados um após o outro) aumentaram.

Outra opção era sequestrar funções de acesso a arquivos, como open , stat e readlink , para que os caminhos começando com /system fossem redirecionados para seus caminhos correspondentes em /apex . A equipe do Android descartou esta opção porque é inviável alterar todas as funções que aceitam caminhos. Por exemplo, alguns aplicativos vinculam estaticamente o Bionic, que implementa as funções. Nesses casos, esses aplicativos não são redirecionados.