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 de aplicativo Android padrão. Alguns componentes de exemplo são serviços nativos e bibliotecas, camadas de abstração de hardware ( HALs ), tempo de execução ( ART ) e bibliotecas de classe.

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 instaladores de pacotes (como o aplicativo Google Play Store), o uso de um modelo semelhante para componentes de sistema operacional de nível inferior apresenta 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 (particularmente o manifesto) é 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 de instalação de pacotes (como Play Store). Por exemplo, o arquivo APEX pode usar uma ferramenta existente, como aapt , para inspecionar os metadados básicos do arquivo. O arquivo contém o nome do pacote e as informações da versão. Geralmente, essas informações 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 suportada 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 ú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, essa chave garante que o APEX baixado seja assinado com a mesma entidade que assina o mesmo APEX nas partições internas.

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 AOSP APEXes. 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 APEXs exclusivos para um dispositivo específico (ou subconjunto de dispositivos).

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 APEX assume como padrão o uso desses 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 APEX.
  3. O gerenciador APEX verifica o arquivo APEX.
  4. Se o arquivo APEX for verificado, o banco de dados interno do gerenciador APEX é atualizado para refletir que o arquivo APEX é 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 inicia, 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 dispositivo no topo 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 fichário para outros componentes do sistema para consultar 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 determinado APEX 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 do 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 opcional 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 arquivos suportados

O formato APEX suporta estes tipos de arquivo:

  • Bibliotecas nativas compartilhadas
  • 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 quão estáveis ​​são as definições das interfaces para os tipos de arquivos.

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 do APK v3 . Duas chaves diferentes são usadas neste processo.

Do 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 que são solicitados para serem instalados. Cada APEX deve ser assinado com chaves diferentes e é aplicado tanto no tempo de construção quanto no tempo de execução.

APEX em partições embutidas

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

Se um APEX estiver presente em uma partição interna, o APEX pode 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, ao contrário dos APKs, a versão recém-instalada do APEX só é ativada após a reinicialização.

Requisitos do kernel

Para dar suporte aos módulos principais do APEX em um dispositivo Android, são necessários os seguintes recursos do kernel do Linux: o driver de loopback e o 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 dm-verity é importante para obter 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 o kernel versão 4.9 ou superior para dar suporte aos módulos APEX.

Patches de kernel necessários

Os patches de kernel necessários para dar 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.

Kernel versão 4.4

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

  • UPSTREAM: loop: adicionar ioctl para alterar o tamanho do bloco lógico ( 4.4 )
  • BACKPORT: bloco/loop: definir hw_sectors ( 4.4 )
  • UPSTREAM: loop: Adicionar LOOP_SET_BLOCK_SIZE em compatibilidade ioctl ( 4.4 )
  • ANDROID: mnt: Fix next_descendent ( 4.4 )
  • ANDROID: mnt: remount deve se propagar para escravos de escravos ( 4.4 )
  • ANDROID: mnt: Propagar 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 down-merge 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 dar suporte aos módulos APEX que foram 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 dar 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

Construir um APEX

Esta seção descreve como construir um APEX usando o sistema de construçã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 arquivos e localizações 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 .

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 destino dispositivos com uma única ABI (ou seja, apenas 32 bits ou apenas 64 bits), apenas as 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 os ABIs das bibliotecas nativas e binários, 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 ambos os 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 é 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 é 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)
}

Instalar um APEX

Para instalar um APEX, use ADB.

adb install apex_file_name
adb reboot

Se supportsRebootlessUpdate for definido como true em apex_manifest.json e o APEX atualmente instalado não for usado (por exemplo, todos os serviços nele contidos foram interrompidos), um novo APEX poderá ser instalado sem reinicialização com o sinalizador --force-non-staged .

adb install --force-non-staged apex_file_name

Use 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 em /apex/<apex_name> .

Os clientes podem usar o caminho montado por ligação para ler ou executar arquivos do APEX.

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

Configurar o sistema para suportar atualizações do APEX

Defina a seguinte propriedade do sistema 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)

ÁPICE 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 herdado. 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 é diretamente montado 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 achatados podem ser atualizados apenas por meio de um OTA regular.

APEX achatado é a configuração padrão. Isso significa que todos os APEXs 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 de APEX (conforme explicado acima).

A mistura de APEXs achatados e não achatados em um dispositivo NÃO é suportada. APEXes em um dispositivo devem ser todos não achatados ou todos achatados. Isso é especialmente importante ao enviar APEX pré-assinados e pré-construídos para projetos como o Mainline. Os APEXs que não são pré-assinados (ou seja, construídos 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 do 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 APEX minimiza esse impacto de 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 DEFLATE zip.

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

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

  • APEXs 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 dinâmicos de bibliotecas compartilhadas. 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 reduzido 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 de 9 Este é o arquivo APEX original e descompactado.
  • apex_manifest.pb : apenas armazenado
  • AndroidManifest.xml : apenas armazenado
  • apex_pubkey : apenas armazenado

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

Criar 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 APEX estão disponíveis no sistema de compilação.

Em 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 fonte 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 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 compressão suportados

O Android 12 suporta apenas compactação deflate-zip.

Ativar 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 de verificação são realizadas em /data/apex/decompressed/com.android.foo@37.apex para garantir sua validade: apexd verifica a chave pública agrupada 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

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 dar suporte ao sistema OTA, apexd expõe essas duas APIs de fichário:

  • 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 pós-instalação OTA. 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 elas 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. 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 é executada apenas 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 em que todos os componentes do sistema foram armazenados em sistemas de arquivos somente leitura cuja integridade é protegida por dm-verity para cada I/O. 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 depois que as partições são montadas. 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 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 proteção adicional e, novamente, isso é 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 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 compactados 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 por meio 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 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 como o número de arquivos sendo sobrepostos ( possivelmente até empilhados um após o outro) aumentou.

Outra opção era sequestrar as 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 Bionic estaticamente, que implementa as funções. Nesses casos, esses aplicativos não são redirecionados.