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 encaixam no modelo de aplicativo Android padrão. Alguns exemplos de componentes são serviços nativos e bibliotecas, camadas de abstração de hardware (HALs), ambiente de execução (ART) e bibliotecas de classes.
O termo "APEX" também pode se referir a um arquivo APEX.
Contexto
Embora o Android ofereça suporte a atualizações de módulos que se encaixam no modelo de app padrão (por exemplo, serviços, atividades) por apps de instalação de pacotes (como o app da Google Play Store), o uso de 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 apps e só pode ser iniciado pelo gerenciador de atividades, que fica pronto em uma fase posterior do procedimento de inicialização.
- O formato APK (principalmente o manifesto) foi criado para apps Android, e os módulos do sistema nem sempre são uma boa opção.
Design
Esta seção descreve o design de alto nível do formato de arquivo APEX e o gerenciador APEX, que é um serviço que gerencia arquivos APEX.
Para mais informações sobre por que esse design foi selecionado para o APEX, consulte Alternativas consideradas ao desenvolver o APEX.
Formato APEX
Esse é o formato de um arquivo APEX.
Figura 1. Formato de arquivo APEX
No nível superior, um arquivo APEX é um arquivo ZIP em que os arquivos são armazenados sem compactação 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
infraestruturas relacionadas ao APK, como ADB, PackageManager e apps de instalação de pacotes (como
a 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. Essas informações geralmente também estão disponíveis em
apex_manifest.json
.
Recomendamos o uso de apex_manifest.json
em vez de AndroidManifest.xml
para novos códigos e
sistemas que lidam com o APEX. O AndroidManifest.xml
pode conter outras
informações de segmentação que podem ser usadas pelas ferramentas de publicação de apps atuais.
apex_payload.img
é uma imagem do sistema de arquivos ext4 com suporte do dm-verity. A imagem
é montada no momento da execução por um dispositivo de loopback. Especificamente, a árvore de hash e
o bloco de metadados são criados usando a biblioteca libavb
. O payload do sistema de arquivos
não é analisado porque a imagem precisa ser montada no local. Os 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. No momento da execução,
essa chave garante que o APEX baixado seja assinado com a mesma entidade
que assina o mesmo APEX nas partições integradas.
Diretrizes de nomenclatura do APEX
Para evitar conflitos de nomenclatura entre novos APEXs à medida que a plataforma avança, use as seguintes diretrizes de nomenclatura:
com.android.*
- Reservado para APEXs do AOSP. Não é exclusivo de nenhuma empresa ou dispositivo.
com.<companyname>.*
- Reservado para uma empresa. Pode ser usado por vários dispositivos dessa empresa.
com.<companyname>.<devicename>.*
- Reservado para APEXes exclusivos de um dispositivo específico (ou subconjunto de dispositivos).
Gerenciador APEX
O gerenciador APEX (ou apexd
) é um processo nativo independente 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 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.
- Um arquivo APEX é transferido por download por um app instalador de pacotes, pelo ADB ou por outra fonte.
- 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.
- O gerenciador APEX verifica o arquivo APEX.
- 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.
- O solicitante de instalação recebe uma transmissão após a verificação do pacote.
- Para continuar a instalação, o sistema precisa ser reinicializado.
Na próxima inicialização, o gerenciador APEX é iniciado, lê o banco de dados interno e faz o seguinte para cada arquivo APEX listado:
- Verifica o arquivo APEX.
- Cria um dispositivo de loopback com base no arquivo APEX.
- Cria um dispositivo de bloco de mapeamento de dispositivo em cima do dispositivo de loopback.
- Monta o dispositivo de bloco do 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 vinculação para outros componentes do sistema consultarem 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 em que um APEX específico é 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 de APK) que contêm um arquivo AndroidManifest.xml
. Isso permite que os arquivos APEX
usem a infraestrutura para arquivos APK, como um app instalador de pacotes,
o utilitário de assinatura e o gerenciador de pacotes.
O arquivo AndroidManifest.xml
em um arquivo APEX é mínimo, consistindo no
pacote name
, versionCode
e targetSdkVersion
, minSdkVersion
e maxSdkVersion
opcionais para segmentação detalhada. Com essas informações, os arquivos APEX
podem ser enviados por canais existentes, como apps de instalação de pacotes e
ADB.
Tipos de arquivos aceitos
O formato APEX oferece suporte a 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. A possibilidade de atualizar um tipo de arquivo depende da plataforma e da estabilidade das 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 de APK v3. Duas chaves diferentes são usadas
neste processo.
No dispositivo, uma chave pública correspondente à chave privada usada para assinar o descritor vbmeta é instalada. O gerenciador de APEX usa a chave pública para verificar os APEXs que são solicitados para instalação. Cada APEX precisa ser assinado com chaves diferentes e é aplicado no momento de build e de execução.
APEX em partições integradas
Os arquivos APEX podem ser localizados em partições integradas, como /system
. A
partição já está sobre o dm-verity, portanto, os arquivos APEX são montados diretamente
sobre o dispositivo de loopback.
Se um APEX estiver presente em uma partição integrada, ele poderá ser atualizado
fornecendo um pacote APEX com o mesmo nome e um código de versão maior ou igual. O novo APEX é armazenado em /data
e, assim como os APKs, a
versão recém-instalada oculta a versão já presente na partição
integrada. No entanto, ao contrário dos APKs, a versão recém-instalada do APEX só é
ativada após a reinicialização.
Requisitos do kernel
Para oferecer suporte a módulos principais do APEX em um dispositivo Android, os seguintes recursos do kernel do Linux são necessários: 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 do dm-verity é importante para alcançar um bom desempenho do sistema ao usar módulos APEX.
Versões do kernel com suporte
Os módulos principais do APEX têm suporte em dispositivos que usam versões do kernel 4.4 ou mais recentes. Os novos dispositivos lançados com o Android 10 ou versões mais recentes precisam usar a versão 4.9 ou mais recente do kernel para oferecer suporte a módulos APEX.
Patches do kernel necessários
Os patches de kernel necessários para oferecer suporte a módulos APEX estão incluídos na árvore comum do Android. Para que os patches ofereçam suporte ao APEX, use a versão mais recente da árvore comum do Android.
Versão do kernel 4.4
Essa versão só oferece suporte a dispositivos que fizeram upgrade do Android 9 para
o Android 10 e querem oferecer suporte a módulos APEX. Para receber os
patches necessários, é altamente recomendado fazer uma mesclagem para baixo da ramificação
android-4.4
. Confira a seguir uma lista dos patches individuais necessários
para a versão 4.4 do kernel.
- UPSTREAM: loop: adição de ioctl para alterar o tamanho do bloco lógico (4.4).
- BACKPORT: bloco/loop: define hw_sectors (4.4)
- UPSTREAM: loop: adição de LOOP_SET_BLOCK_SIZE no ioctl de compatibilidade (4.4).
- ANDROID: mnt: correção de next_descendent (4.4).
- ANDROID: mnt: a remontagem precisa ser propagada para escravos de escravos (4.4).
- ANDROID: mnt: propagação de remontagem correta (4.4).
- Reversão de "ANDROID: dm verity: add minimum prefetch size" (4.4).
- UPSTREAM: loop: descarta caches se o deslocamento ou o block_size forem alterados (4.4).
Versões 4.9/4.14/4.19 do kernel
Para receber os patches necessários para as versões 4.9/4.14/4.19 do kernel, faça uma mesclagem para baixo do
ramo android-common
.
Opções de configuração do kernel necessárias
A lista a seguir mostra os requisitos de configuração básica para oferecer suporte a módulos APEX introduzidos no Android 10. Os itens com um asterisco (*) são requisitos 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âmetros 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 pode ser definidoloop.max_part
precisa ser <= 8
Criar um APEX
Esta seção descreve como criar um APEX usando o sistema de build do Android.
Confira 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 de 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 | Local no APEX |
---|---|
Bibliotecas compartilhadas | /lib e /lib64 (/lib/arm para
ARM traduzido em x86) |
Executáveis | /bin |
Bibliotecas Java | /javalib |
Pré-versões | /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 bibliotecas serão
incluídas quando apenas libFoo
for listado na propriedade native_shared_libs
.
Processar várias ABIs
Instale a propriedade native_shared_libs
para as interfaces binárias do aplicativo (ABIs)
principal e secundária do dispositivo. Se um APEX tiver como destino dispositivos
com uma única ABI (somente 32 bits ou 64 bits), apenas as bibliotecas com a
ABI correspondente serão instaladas.
Instale a propriedade binaries
apenas para a ABI principal 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 um controle refinado sobre as ABIs das bibliotecas nativas e dos binários,
use as
propriedades
multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
first
: corresponde à ABI principal do dispositivo. Esse é 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, com suporte.prefer32
: corresponde à ABI de 32 bits do dispositivo, se houver suporte. Se a ABI de 32 bits não tiver suporte, ela será igual à ABI de 64 bits.both
: corresponde a ambas as ABIs. Esse é o padrão paranative_shared_libraries
.
As propriedades java
, libraries
e prebuilts
não dependem de ABI.
Este exemplo é para um dispositivo compatível com 32/64 e que não tem preferência por 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 do vbmeta
Assine cada APEX com chaves diferentes. Quando uma nova chave for necessária, crie um
par de chaves pública e 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 pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_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 é gravado no APEX. No momento da 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 você assina APKs. Assine o APEX duas vezes: uma para o
minisistema de arquivos (arquivo apex_payload.img
) e outra para o arquivo inteiro.
Para assinar um APEX no nível do arquivo, defina a propriedade certificate
de uma destas
maneiras:
- Não definido: se nenhum valor for definido, o APEX será assinado com o certificado localizado
em
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Se nenhuma flag for definida, o caminho padrão serábuild/target/product/security/testkey
. <name>
: o APEX é assinado com o certificado<name>
no mesmo diretório quePRODUCT_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 maneira.
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 o ADB.
adb install apex_file_name
adb reboot
Se supportsRebootlessUpdate
estiver definido como true
em apex_manifest.json
e o
APEX instalado atualmente não estiver em uso (por exemplo, todos os serviços que ele contém foram
interrompidos), um novo APEX poderá ser instalado sem uma reinicialização com a
flag --force-non-staged
.
adb install --force-non-staged apex_file_name
Usar 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 de montagem de vinculação para ler ou executar arquivos do APEX.
Os APEXes geralmente são usados da seguinte maneira:
- Um OEM ou ODM carrega um APEX com
/system/apex
quando o dispositivo é enviado. - Os arquivos no APEX são acessados pelo caminho
/apex/<apex_name>/
. - 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:
Marque o serviço na partição do sistema como atualizável. Adicione a opção
updatable
à definição do serviço./system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
Crie um novo arquivo
.rc
para o serviço atualizado. Use a opçãooverride
para redefinir o serviço atual./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 acionadores de ação não são compatíveis com APEXes.
Se um serviço marcado como atualizável for iniciado antes que os APEXs sejam ativados, o início será adiado até a conclusão da ativação dos APEXs.
Configurar o sistema para oferecer suporte a atualizações APEX
Defina a propriedade do sistema a seguir 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 criado
sem CONFIG_BLK_DEV_LOOP=Y
, que é crucial para montar a imagem do sistema de arquivos
dentro de um APEX.
O APEX achatado é um APEX criado especialmente que pode ser ativado em dispositivos com
um kernel legado. Os arquivos em um APEX achatado são instalados diretamente em um diretório
na partição integrada. Por exemplo, lib/libFoo.so
em um my.apex
APEX
achatado é instalado em /system/apex/my.apex/lib/libFoo.so
.
A ativação de um APEX achatado não envolve o dispositivo de loop. O diretório
/system/apex/my.apex
inteiro é vinculado diretamente a /apex/name@ver
.
Não é possível atualizar APEXes achatados fazendo o download de versões atualizadas deles da rede, porque os APEXes transferidos por download não podem ser achatados. As APEXs achatadas só podem ser atualizadas por uma OTA normal.
APEX achatado é a configuração padrão. Isso significa que todos os APEXes são achatados por padrão, a menos que você configure explicitamente o dispositivo para criar APEXes não achatados com suporte a atualizações APEX (conforme explicado acima).
Não é possível misturar APEXes achatados e não achatados em um dispositivo. Os APEXes em um dispositivo precisam estar todos não achatados ou todos achatados.
Isso é especialmente importante ao enviar pré-builds APEX pré-assinados para
projetos como o Mainline. Os APEXes que não são pré-assinados (ou seja, criados a partir
da origem) também não podem ser achatados e precisam ser assinados com chaves adequadas. O
dispositivo precisa herdar de updatable_apex.mk
, conforme explicado em
Como atualizar um serviço com um APEX.
APEXs compactados
O Android 12 e versões mais recentes oferecem compactação APEX para reduzir o impacto no armazenamento de pacotes APEX atualizáveis. Depois que uma atualização para um APEX é instalada, embora a versão pré-instalada não seja mais usada, ela ainda ocupa a mesma quantidade de espaço. O 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 mais recentes usam um algoritmo de compactação DEFLATE.
A compactação não oferece otimização para:
APEXes de inicialização que precisam ser montados bem no início da sequência de inicialização.
APEXes não atualizáveis. A compactação só é benéfica se uma versão atualizada de um APEX for instalada na partição
/data
. Uma lista completa de APEXes atualizáveis está disponível na página Componentes do sistema modular.APEXes de bibliotecas compartilhadas dinâmicas. Como
apexd
sempre ativa as duas versões de APEX (pré-instaladas e atualizadas), a compactação delas não agrega valor.
Formato de arquivo APEX compactado
Esse é o formato de um arquivo APEX compactado.
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 forma desinflada com um nível de compactação de 9 e com outros arquivos armazenados sem compactação.
Quatro arquivos compõem um arquivo APEX:
original_apex
: desinflado com nível de compactação 9 Este é o arquivo APEX original e descompactado.apex_manifest.pb
: armazenado apenasAndroidManifest.xml
: armazenado apenasapex_pubkey
: armazenado apenas
Os arquivos apex_manifest.pb
, AndroidManifest.xml
e apex_pubkey
são
cópias dos 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 build.
Em Android.bp
, a compressão de um arquivo APEX é controlada pela
propriedade compressible
:
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
Uma flag de produto PRODUCT_COMPRESSED_APEX
controla se uma imagem do sistema criada
a partir da origem precisa conter arquivos APEX compactados.
Para experimentos locais, você pode forçar um build a compactar APEXs definindo
OVERRIDE_PRODUCT_COMPRESSED_APEX=
como true
.
Os arquivos APEX compactados gerados pelo sistema de build 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 com suporte
O Android 12 só oferece suporte à 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
.
Confira 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 o versionCode 37.
- O arquivo
original_apex
dentro de/system/apex/com.android.foo.capex
é descompactado em/data/apex/decompressed/com.android.foo@37.apex
. - O
restorecon /data/apex/decompressed/com.android.foo@37.apex
é executado para verificar se ele tem um rótulo SELinux correto. - As verificações de verificação são realizadas em
/data/apex/decompressed/com.android.foo@37.apex
para garantir a validade:apexd
verifica a chave pública agrupada em/data/apex/decompressed/com.android.foo@37.apex
para verificar se ela é igual à chave agrupada em/system/apex/com.android.foo.capex
. - O arquivo
/data/apex/decompressed/com.android.foo@37.apex
tem um link físico para o diretório/data/apex/active/com.android.foo@37.apex
. - A lógica de ativação regular para arquivos APEX não compactados é executada em
/data/apex/active/com.android.foo@37.apex
.
Interação com o OTA
Os arquivos APEX compactados têm implicações na entrega e na aplicação OTA. Como uma atualização OTA pode conter um arquivo APEX compactado com um nível de versão mais alto do que o ativo em um dispositivo, uma certa quantidade de espaço livre precisa ser reservada antes que um dispositivo seja reinicializado para aplicar uma atualização OTA.
Para oferecer suporte ao sistema OTA, o apexd
expõe estas duas APIs de vinculaçã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 do download de uma OTA.reserveSpaceForCompressedApex
: reserva espaço no disco para uso futuro porapexd
para descompactar arquivos APEX compactados no pacote OTA.
No caso de uma atualização OTA A/B, o apexd
tenta fazer a descompressão em segundo plano
como parte da rotina OTA pós-instalação. Se a descompactação falhar,
apexd
vai realizar a descompactação durante a inicialização que aplica a atualização
OTA.
Alternativas consideradas ao desenvolver o APEX
Confira 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 do 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 sem serem notados. Essa é
uma regressão para o Android, em que todos os componentes do sistema foram armazenados em sistemas de arquivos
somente leitura, com a integridade protegida pelo dm-verity para todas as E/S. Qualquer
adulteração em componentes do sistema precisa ser proibida ou detectável para
que o dispositivo possa se recusar a inicializar se for comprometido.
dm-crypt para integridade
Os arquivos em um contêiner APEX são de partições integradas (por exemplo, a
/system
) protegidas por dm-verity, em que 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 que é pareada com uma árvore de hashes e um descritor vbmeta. Sem
o dm-verity, um APEX na partição /data
fica vulnerável a modificações
involuntárias feitas após a verificação e instalação.
Na verdade, a partição /data
também é protegida por camadas de criptografia, como
dm-crypt. Embora isso ofereça algum nível de proteção contra adulteração, o
objetivo principal é a privacidade, não a integridade. Quando um invasor consegue acesso à
partição /data
, não há mais proteção, e isso é uma
regressão em comparação com todos os componentes do sistema na partição /system
.
A árvore de hash em um arquivo APEX com o dm-verity oferece 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 novos caminhos, como
/apex/<name>/lib/libfoo.so
. Quando os arquivos faziam parte da partição /system
, eles eram acessíveis por caminhos como /system/lib/libfoo.so
. Um
cliente de um arquivo APEX (outros arquivos APEX ou a plataforma) precisa usar os novos
caminhos. Talvez seja necessário atualizar o código atual como resultado da mudança de 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 à medida que o
número de arquivos sobrepostos (possivelmente empilhados um após o outro)
aumentasse.
Outra opção era sequestrar funções de acesso a arquivos, como open
, stat
e
readlink
, para que caminhos que começavam com /system
fossem redirecionados para os
caminhos correspondentes em /apex
. A equipe do Android descartou essa opção
porque é inviável mudar todas as funções que aceitam caminhos.
Por exemplo, alguns apps vinculam o Bionic de forma estática, o que implementa as funções.
Nesses casos, esses apps não são redirecionados.