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 e bibliotecas nativas, 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:
- 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 do APK, principalmente o manifesto, foi projetado para apps Android, e os módulos do sistema nem sempre são adequados.
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 do pacote e a versão, 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. Arquivos regulares estão
incluídos dentro do arquivo apex_payload.img
.
apex_pubkey
é a chave pública usada para assinar a imagem do sistema de arquivos. No ambiente de execução,
essa chave garante que o APEX transferido seja assinado com a mesma entidade
que assina o mesmo APEX nas partições integradas.
Diretrizes de nomenclatura 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. Potencialmente usado por vários dispositivos da 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 pacote, 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, reinicie o sistema.
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 mapeador de dispositivo sobre o dispositivo de loopback.
- Monta o dispositivo de bloco 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 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 o caminho exato em que um APEX específico é 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 de APK) que contêm um arquivo AndroidManifest.xml
. Isso permite que os arquivos
APEX usem a infraestrutura de 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, como apps de instalação de pacotes e
ADB.
Tipos de arquivo compatíveis
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 atualização de um tipo de arquivo 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 de APK v3. Duas chaves diferentes são usadas
neste processo.
No lado do 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 tempo de build e na execução.
APEX em partições integradas
Os arquivos APEX podem estar localizados em partições integradas, como /system
. A
partição já está acima de 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, ele poderá ser atualizado
fornecendo um pacote APEX com o mesmo nome de pacote e um código de versão maior ou
igual a. 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. 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 de linha principal 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 garantir 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 4.4 ou mais recentes do kernel. 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 conseguir os
patches necessários, é altamente recomendável fazer uma downmerge 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: add ioctl para alterar o tamanho do bloco lógico (4.4)
- BACKPORT: block/loop: set hw_sectors (4.4)
- UPSTREAM: loop: foi adicionado LOOP_SET_BLOCK_SIZE ao Compatível com ioctl (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 a remontagem corretamente (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 do kernel 4.9/4.14/4.19
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 de kernel obrigató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â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 pode ser definido- O valor de
loop.max_part
precisa ser menor ou igual a 8
Criar um APEX
Esta seção descreve como criar um APEX usando o sistema de build 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 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 de arquivo e locais no APEX
Tipo de arquivo | Local no APEX |
---|---|
Bibliotecas compartilhadas | /lib e /lib64 (/lib/arm para
o grupo 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
estiver listado na propriedade native_shared_libs
.
Lidar com várias ABIs
Instale a propriedade native_shared_libs
para as interfaces binárias de aplicativo (ABIs, na sigla em inglês) primárias e secundárias
do dispositivo. Se um APEX for destinado a dispositivos
com uma única ABI (ou seja, somente 32 bits ou 64 bits), somente 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 de apenas 32 bits, apenas a variante de 32 bits do binário será instalada.
- Se o dispositivo tiver apenas 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 compatível.lib64
: corresponde à ABI de 64 bits do dispositivo, com suporte.prefer32
: corresponde à ABI de 32 bits do dispositivo, se compatível. Se a ABI de 32 bits não tiver suporte, será feita a correspondência com a 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 da ABI.
Este exemplo é para um dispositivo que oferece suporte a 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 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 está escrito 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 APEXs 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 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 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 ativação, aquele que corresponde à versão mais recente é
vinculado em /apex/<apex_name>
.
Os clientes podem usar o caminho ativado de vinculação para ler ou executar arquivos do APEX.
Os APEXes geralmente são usados da seguinte maneira:
- Um OEM ou ODM pré-carrega um APEX em
/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. Gatilhos
de ação não têm suporte em APEXs.
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 nivelado
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
, o que é crucial para ativar a imagem do sistema de arquivos
dentro de um APEX.
O APEX nivelado é um APEX especialmente criado 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
está 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
é vinculado diretamente a /apex/name@ver
.
As APEXs achatadas não podem ser atualizadas fazendo o download de versões atualizadas das APEXs da rede, porque as APEXs baixadas não podem ser achatadas. As APEXs planas só podem ser atualizadas por uma OTA normal.
O APEX nivelado é 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 e oferecer suporte a atualizações APEX (como 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 APEXs que não são pré-assinados (ou seja, criados a partir da
origem) também precisam ser não nivelados e assinados com as 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 incluem a 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 ZIP DEFLATE.
A compactação não otimiza o seguinte:
Inicializar APEXs que precisam ser montados muito 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.APEXs 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 compreendem um arquivo APEX:
original_apex
: esvaziado com o nível de compactação de 9. Este é o arquivo APEX original e descompactado.apex_manifest.pb
: somente armazenadosAndroidManifest.xml
: somente armazenadosapex_pubkey
: somente armazenados
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 experimentação local, 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
. restorecon /data/apex/decompressed/com.android.foo@37.apex
é executado para verificar se tem um rótulo SELinux correto.- Verificações são realizadas em
/data/apex/decompressed/com.android.foo@37.apex
para garantir a validade:apexd
confere a chave pública agrupada em/data/apex/decompressed/com.android.foo@37.apex
para conferir se ela é igual àquela agrupada em/system/apex/com.android.foo.capex
. - O arquivo
/data/apex/decompressed/com.android.foo@37.apex
está vinculado ao diretório/data/apex/active/com.android.foo@37.apex
. - A lógica de ativação normal 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 mais alto do que o ativo em um dispositivo, é necessário reservar uma certa quantidade de espaço livre 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 peloapexd
para descompactar arquivos APEX compactados dentro do 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 no desenvolvimento de 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 Android em que todos os componentes do sistema foram armazenados em sistemas de arquivos
somente leitura, e a integridade deles é protegida por dm-verity para cada 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 pareada 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
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 dentro de um arquivo APEX, junto com o dm-verity, fornece o mesmo
nível de proteção de conteúdo.
Redirecionar caminhos de /system para /apex
Arquivos de componentes do sistema empacotados em um APEX podem ser acessados 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 existente como resultado da mudança do caminho.
Embora uma maneira de evitar a mudança de caminho seja sobrepor o conteúdo de 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, já que
o número de arquivos sobrepostos (possivelmente até mesmo empilhados um após o outro)
aumentava.
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 estaticamente o Bionic, que implementa as funções.
Nesses casos, esses apps não são redirecionados.