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. Esse formato facilita as atualizações de componentes do sistema que não se enquadram no modelo padrão de aplicativos Android. Alguns componentes de exemplo 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 encaixam no modelo de aplicativo padrão (por exemplo, serviços, atividades) por meio de aplicativos de instalação de pacotes (como o aplicativo Google Play Store), usar um modelo semelhante para componentes de SO de nível inferior tem as seguintes desvantagens:

  • Os 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 (especialmente o manifesto) foi desenvolvido 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 o 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.

O arquivo AndroidManifest.xml permite que o arquivo APEX use ferramentas e infraestrutura relacionadas a APK, como ADB, PackageManager e aplicativos de instalação 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 as informações da versão. Essas informações geralmente também estão disponíveis em apex_manifest.json .

apex_manifest.json é recomendado sobre 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 de 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 de hash e o bloco de metadados são criados usando a biblioteca libavb . A carga do sistema de arquivos não é analisada (porque a imagem deve ser montável no local). Arquivos regulares sã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, essa chave garante que o APEX baixado seja assinado com a mesma entidade que assina o mesmo APEX nas partições internas.

gerente APEX

O gerenciador APEX (ou apexd ) é um processo nativo autônomo responsável por verificar, instalar e desinstalar arquivos APEX. Esse 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 do APEX usa como padrão esses pacotes 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 do 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 é ativado na próxima inicialização.
  5. O solicitante de 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 do mapeador de dispositivo na parte superior do dispositivo de loopback.
    4. Monta o dispositivo de bloco do mapeador de dispositivo 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 que outros componentes do sistema consultem informações sobre os arquivos APEX instalados. Por exemplo, os outros 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.

Os 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 para direcionamento refinado. Essas informações permitem que os arquivos APEX sejam entregues por meio de canais existentes, como aplicativos de instalação de pacotes e ADB.

Tipos de arquivo 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 pode 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 arquivos.

Assinatura

Os arquivos APEX são assinados de duas maneiras. Primeiro, o 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 do APEX usa a chave pública para verificar os APEXes cuja instalação é solicitada. Cada APEX deve ser assinado com chaves diferentes e é aplicado tanto em tempo de compilaçã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á está em dm-verity, portanto, 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 interna. 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 da linha principal 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 obter um bom desempenho do sistema ao usar módulos APEX.

Versões de kernel compatíveis

Os módulos da linha principal 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 o kernel versão 4.9 ou superior para oferecer suporte aos módulos APEX.

Patches de kernel necessários

Os patches de kernel necessários para oferecer suporte aos módulos APEX estão incluídos na árvore comum do Android. Para obter os patches para oferecer 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 que foram atualizados do Android 9 para o Android 10 e desejam oferecer suporte aos módulos APEX. Para obter os patches necessários, é altamente recomendável fazer um down-merge do branch android-4.4 . A seguir está uma lista dos patches individuais necessários para o kernel versão 4.4.

  • 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 compat ioctl ( 4.4 )
  • ANDROID: mnt: Corrige next_descendent ( 4.4 )
  • ANDROID: mnt: remount deve propagar para slaves de slaves ( 4.4 )
  • ANDROID: mnt: Propagar remontagem corretamente ( 4.4 )
  • Reverter "ANDROID: dm verity: adicionar tamanho mínimo de pré-busca" ( 4.4 )
  • UPSTREAM: loop: drop 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 o down-merge do branch android-common .

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

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

(*) 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, verifique se os parâmetros de linha de comando do kernel atendem aos seguintes requisitos:

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

Construindo um APEX

Esta seção descreve como criar um APEX usando o sistema de compilação do Android. Veja a seguir um exemplo de Android.bp para um APEX chamado 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 file_contexts :

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

Tipos de arquivo e locais 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 compartilhadas nativas ou executáveis. Por exemplo, se libFoo depender de libBar , as duas libs serão incluídas quando apenas libFoo estiver listado na propriedade native_shared_libs .

Lidando com várias ABIs

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

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

  • Se o dispositivo for apenas de 32 bits, somente a variante de 32 bits do binário será instalada.
  • Se o dispositivo for apenas de 64 bits, somente 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 principal do dispositivo. Este é o padrão para binários.
  • lib32 : Corresponde à ABI de 32 bits do dispositivo, se compatível.
  • lib64 : Corresponde à ABI de 64 bits do dispositivo, compatível.
  • prefer32 : corresponde à ABI de 32 bits do dispositivo, se compatível. 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 -compiladas 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 faça 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 ) se torna o ID da chave. O ID da chave usada para assinar um APEX é escrito no APEX. Em tempo de execução, o apexd verifica o APEX usando uma chave pública com o mesmo ID no dispositivo.

Assinatura ZIP

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 do certificate de uma destas três maneiras:

  • Não definido: Se nenhum valor for definido, o APEX é 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 chamado <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)
}

Instalando um APEX

Para instalar um APEX, use o ADB.

adb install apex_file_name
adb reboot

Usando 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 ligação para ler ou executar arquivos do APEX.

Os 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.

Atualizando 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 de 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 que os APEXes sejam ativados, o início será atrasado até que a ativação dos APEXes seja concluída.

Configurando o sistema para dar suporte às atualizações do APEX

Defina a propriedade do sistema a seguir como true para oferecer suporte a atualizações de arquivo 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.

O APEX achatado é 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 achatado não envolve o dispositivo de loop. Todo o diretório /system/apex/my.apex é montado diretamente em /apex/name@ver .

Os APEXes nivelados não podem ser atualizados baixando as versões atualizadas dos APEXes da rede porque os APEXes baixados não podem ser nivelados. Os APEXes achatados podem ser atualizados apenas por meio de um OTA regular.

O APEX achatado é 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 APEXs não nivelados para oferecer suporte a atualizações do APEX (conforme explicado acima).

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

APEXs compactados

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

A compactação do 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 muito cedo na sequência de inicialização.

  • APEXes não atualizáveis. A compactação só é 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 .

  • APEXes de bibliotecas compartilhadas dinâmicas. 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 : deflated com nível de compactação de 9 Este é o arquivo APEX original e não compactado .
  • apex_manifest.pb : armazenado apenas
  • AndroidManifest.xml : armazenado apenas
  • apex_pubkey : armazenado apenas

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

Criando APEX compactado

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

Vários parâmetros relacionados à compactação do 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 configurando OVERRIDE_PRODUCT_COMPRESSED_APEX= como true .

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

Algoritmos de compactação compatíveis

O Android 12 é compatível apenas com a compactação deflate-zip.

Ativando 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 empacotada 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 é 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

Os arquivos APEX compactados têm implicações na entrega e no aplicativo 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, o apexd expõe essas duas APIs de binder:

  • 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, o apexd realizará 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 elas foram incluídas ou excluídas.

Sistemas regulares de gerenciamento de pacotes

As distribuições Linux têm sistemas de gerenciamento de pacotes como dpkg e rpm , que são poderosos, maduros e robustos. No entanto, eles não foram adotados para o APEX porque não podem proteger os pacotes após a instalação. A verificação é realizada apenas quando os pacotes estão sendo instalados. Os invasores podem quebrar a integridade dos pacotes instalados, despercebidos. Esta é uma regressão para Android em que todos os componentes do sistema foram armazenados em sistemas de arquivos somente leitura cuja integridade é protegida por dm-verity para cada E/S. Qualquer adulteração de componentes do sistema deve ser proibida ou detectável para que o dispositivo possa se recusar a inicializar se 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 por 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 de hash e um descritor vbmeta. Sem o dm-verity, um APEX na partição /data fica vulnerável a modificações não intencionais que são feitas após sua verificação e instalação.

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 mais proteção, e isso novamente é uma regressão em comparação com todos os componentes do sistema que estão na partição /system . A árvore de hash dentro de um arquivo APEX junto com o dm-verity fornece o mesmo nível de proteção de conteúdo.

Redirecionando caminhos de /system para /apex

Os arquivos de componentes do sistema empacotados em um APEX podem ser acessados ​​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. Pode ser necessário atualizar o código existente como resultado da alteração do caminho.

Embora uma maneira de evitar a alteração do 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 que começassem com /system fossem redirecionados para seus caminhos correspondentes em /apex . A equipe do Android descartou essa 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.