Fornecedor APEX

Você pode usar o formato de arquivo APEX para empacotar e instalar módulos do sistema operacional Android de nível inferior. Ele permite a construção e instalação independente de componentes como serviços e bibliotecas nativos, implementações HAL, firmware, arquivos de configuração, etc.

Os APEXes do fornecedor são instalados automaticamente pelo sistema de compilação na partição /vendor e ativados em tempo de execução pelo apexd , assim como os APEXes em outras partições.

Casos de uso

Modularização de imagens de fornecedores

Os APEXes facilitam o agrupamento e a modularização naturais de implementações de recursos em imagens de fornecedores.

Quando as imagens do fornecedor são criadas como uma combinação de APEXes de fornecedores independentes, os fabricantes de dispositivos podem escolher facilmente as implementações específicas do fornecedor desejadas em seus dispositivos. Os fabricantes podem até criar um novo APEX de fornecedor se nenhum dos APEX fornecidos atender às suas necessidades ou se eles tiverem um novo hardware personalizado.

Por exemplo, um OEM pode optar por compor seu dispositivo com a implementação de wifi AOSP APEX, a implementação de Bluetooth SoC APEX e uma implementação de telefonia OEM personalizada APEX.

Sem APEXes de fornecedores, uma implementação com tantas dependências entre componentes de fornecedores requer coordenação e rastreamento cuidadosos. Ao agrupar todos os componentes (incluindo arquivos de configuração e bibliotecas extras) em APEXes com interfaces claramente definidas em qualquer ponto de comunicação entre recursos, os diferentes componentes tornam-se intercambiáveis.

Iteração do desenvolvedor

Os APEXes do fornecedor ajudam os desenvolvedores a iterar mais rapidamente durante o desenvolvimento de módulos do fornecedor, agrupando uma implementação completa de recursos, como o HAL wifi, dentro de um APEX do fornecedor. Os desenvolvedores podem então criar e enviar individualmente o APEX do fornecedor para testar as alterações, em vez de reconstruir toda a imagem do fornecedor.

Isso simplifica e acelera o ciclo de iteração do desenvolvedor para desenvolvedores que trabalham principalmente em uma área de recurso e desejam iterar apenas nessa área de recurso.

O agrupamento natural de uma área de recurso em um APEX também simplifica o processo de construção, envio e teste de alterações para essa área de recurso. Por exemplo, reinstalar um APEX atualiza automaticamente qualquer biblioteca incluída ou arquivos de configuração que o APEX inclui.

Agrupar uma área de recurso em um APEX também simplifica a depuração ou a reversão quando um mau comportamento do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em uma nova compilação, os desenvolvedores poderão tentar instalar uma implementação de telefonia APEX mais antiga em um dispositivo (sem a necessidade de atualizar uma compilação completa) e verificar se o bom comportamento é restaurado.

Fluxo de trabalho de exemplo:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Exemplos

Fundamentos

Consulte a página principal do formato de arquivo APEX para obter informações genéricas do APEX, incluindo requisitos do dispositivo, detalhes do formato do arquivo e etapas de instalação.

Em Android.bp , definir a vendor: true torna um módulo APEX um APEX de fornecedor.

apex {
  ..
  vendor: true,
  ..
}

Binários e bibliotecas compartilhadas

Um APEX inclui dependências transitivas dentro da carga útil do APEX, a menos que tenham interfaces estáveis.

Interfaces nativas estáveis ​​para dependências APEX de fornecedores incluem cc_library com stubs , ndk_library ou llndk_library . Essas dependências são excluídas do pacote e são registradas no manifesto APEX. O manifesto é processado pelo linkerconfig para que as dependências nativas externas estejam disponíveis em tempo de execução.

Ao contrário dos APEXes na partição /system , os APEXes do fornecedor normalmente estão vinculados a uma versão específica do VNDK. As bibliotecas VNDK garantem a estabilidade da ABI dentro do lançamento, para que possamos tratar as bibliotecas VNDK como estáveis ​​e reduzir o tamanho dos APEXes do fornecedor, excluindo-os dos APEXes usando a propriedade use_vndk_as_stable .

No trecho abaixo, o APEX conterá o binário ( my_service ) e suas dependências não estáveis ​​( arquivos *.so ). Ele não conterá bibliotecas VNDK, mesmo quando my_service for criado com bibliotecas VNDK como libbase . Em vez disso, em tempo de execução my_service usará libbase das bibliotecas VNDK fornecidas pelo sistema.

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  binaries: ["my_service"],
  ..
}

No trecho abaixo, o APEX conterá a biblioteca compartilhada my_standalone_lib e qualquer uma de suas dependências não estáveis ​​(conforme descrito acima).

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Implementações HAL

Para definir uma implementação HAL, forneça os binários e bibliotecas correspondentes dentro de um APEX de fornecedor semelhante aos exemplos a seguir:

Para encapsular totalmente a implementação HAL, o APEX também deve especificar quaisquer fragmentos VINTF e scripts de inicialização relevantes.

Fragmentos VINTF

Os fragmentos VINTF podem ser servidos a partir de um APEX de fornecedor quando os fragmentos estão localizados em etc/vintf do APEX.

Use a propriedade prebuilts para incorporar os fragmentos VINTF no APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

Scripts de inicialização

APEXes podem incluir scripts de inicialização de duas maneiras: (A) um arquivo de texto pré-construído dentro da carga útil do APEX ou (B) um script de inicialização regular em /vendor/etc . Você pode definir ambos para o mesmo APEX.

Script de inicialização no APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Os scripts de inicialização nos APEXes podem ter apenas definições service . Os scripts de inicialização nos APEXes do fornecedor também podem ter diretivas on <property> .

Tenha cuidado ao usar diretivas on . Como os scripts de inicialização em APEXes são analisados ​​e executados após a ativação dos APEXes, alguns eventos ou propriedades não podem ser usados. Use apex.all.ready=true para disparar ações o mais cedo possível.

Firmware

Exemplo:

Incorpore o firmware em um fornecedor APEX com o tipo de módulo prebuilt_firmware , conforme a seguir.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Os módulos prebuilt_firmware são instalados no diretório <apex name>/etc/firmware do APEX. ueventd verifica os diretórios /apex/*/etc/firmware para encontrar módulos de firmware.

Os file_contexts do APEX devem rotular adequadamente quaisquer entradas de carga útil do firmware para garantir que esses arquivos sejam acessíveis pelo ueventd em tempo de execução; normalmente, o rótulo vendor_file é suficiente. Por exemplo:

(/.*)? u:object_r:vendor_file:s0

Módulos do kernel

Incorpore módulos de kernel em um APEX de fornecedor como módulos pré-construídos, conforme a seguir.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

Os file_contexts do APEX devem rotular corretamente qualquer entrada de carga útil do módulo do kernel. Por exemplo:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Os módulos do kernel devem ser instalados explicitamente. O exemplo de script de inicialização a seguir na partição do fornecedor mostra a instalação via insmod :

my_init.rc :

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Sobreposições de recursos de tempo de execução

Exemplo:

Incorpore sobreposições de recursos de tempo de execução em um APEX de fornecedor usando a propriedade rros .

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Outros arquivos de configuração

Os APEXes do fornecedor suportam vários outros arquivos de configuração normalmente encontrados na partição do fornecedor como pré-construídos dentro dos APEXes do fornecedor, e mais estão sendo adicionados.

Exemplos:

Recursos extras de desenvolvimento

Seleção APEX na inicialização

Exemplo:

Os desenvolvedores também podem instalar várias versões de APEXes de fornecedores que compartilham o mesmo nome e chave APEX e, em seguida, escolher qual versão será ativada durante cada inicialização usando sysprops persistentes. Para determinados casos de uso de desenvolvedor, isso pode ser mais simples do que instalar uma nova cópia do APEX usando adb install .

Exemplos de casos de uso:

  • Instale 3 versões do fornecedor WiFi HAL APEX: As equipes de controle de qualidade podem executar testes manuais ou automatizados usando uma versão, reinicializar em outra versão e executar novamente os testes e, em seguida, comparar os resultados finais.
  • Instale 2 versões da câmera HAL do fornecedor APEX, atual e experimental : Dogfooders podem usar a versão experimental sem baixar e instalar um arquivo adicional, para que possam facilmente trocar de volta.

Durante a inicialização, apexd procura sysprops seguindo um formato específico para ativar a versão correta do APEX.

Os formatos esperados para a chave da propriedade são:

  • Configuração de inicialização
    • Usado para definir o valor padrão, em BoardConfig.mk .
    • androidboot.vendor.apex.<apex name>
  • Sysprop persistente
    • Usado para alterar o valor padrão, definido em um dispositivo já inicializado.
    • Substitui o valor bootconfig, se presente.
    • persist.vendor.apex.<apex name>

O valor da propriedade deverá ser o nome do arquivo do APEX que deverá ser ativado.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

A versão padrão também deve ser configurada usando bootconfig em BoardConfig.mk :

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Depois que o dispositivo for inicializado, altere a versão ativada configurando o sysprop persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Se o dispositivo suportar a atualização do bootconfig após a atualização (como por meio de comandos fastboot oem ), a alteração da propriedade bootconfig do APEX multiinstalado também alterará a versão ativada na inicialização.

Para dispositivos de referência virtual baseados em Cuttlefish , você pode usar o comando --extra_bootconfig_args para definir a propriedade bootconfig diretamente durante a inicialização. Por exemplo:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";
,

Você pode usar o formato de arquivo APEX para empacotar e instalar módulos do sistema operacional Android de nível inferior. Ele permite a construção e instalação independente de componentes como serviços e bibliotecas nativos, implementações HAL, firmware, arquivos de configuração, etc.

Os APEXes do fornecedor são instalados automaticamente pelo sistema de compilação na partição /vendor e ativados em tempo de execução pelo apexd , assim como os APEXes em outras partições.

Casos de uso

Modularização de imagens de fornecedores

Os APEXes facilitam o agrupamento e a modularização naturais de implementações de recursos em imagens de fornecedores.

Quando as imagens do fornecedor são criadas como uma combinação de APEXes de fornecedores independentes, os fabricantes de dispositivos podem escolher facilmente as implementações específicas do fornecedor desejadas em seus dispositivos. Os fabricantes podem até criar um novo APEX de fornecedor se nenhum dos APEX fornecidos atender às suas necessidades ou se eles tiverem um novo hardware personalizado.

Por exemplo, um OEM pode optar por compor seu dispositivo com a implementação de wifi AOSP APEX, a implementação de Bluetooth SoC APEX e uma implementação de telefonia OEM personalizada APEX.

Sem APEXes de fornecedores, uma implementação com tantas dependências entre componentes de fornecedores requer coordenação e rastreamento cuidadosos. Ao agrupar todos os componentes (incluindo arquivos de configuração e bibliotecas extras) em APEXes com interfaces claramente definidas em qualquer ponto de comunicação entre recursos, os diferentes componentes tornam-se intercambiáveis.

Iteração do desenvolvedor

Os APEXes do fornecedor ajudam os desenvolvedores a iterar mais rapidamente durante o desenvolvimento de módulos do fornecedor, agrupando uma implementação completa de recursos, como o HAL wifi, dentro de um APEX do fornecedor. Os desenvolvedores podem então criar e enviar individualmente o APEX do fornecedor para testar as alterações, em vez de reconstruir toda a imagem do fornecedor.

Isso simplifica e acelera o ciclo de iteração do desenvolvedor para desenvolvedores que trabalham principalmente em uma área de recurso e desejam iterar apenas nessa área de recurso.

O agrupamento natural de uma área de recurso em um APEX também simplifica o processo de construção, envio e teste de alterações para essa área de recurso. Por exemplo, reinstalar um APEX atualiza automaticamente qualquer biblioteca incluída ou arquivos de configuração que o APEX inclui.

Agrupar uma área de recurso em um APEX também simplifica a depuração ou a reversão quando um mau comportamento do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em uma nova compilação, os desenvolvedores poderão tentar instalar uma implementação de telefonia APEX mais antiga em um dispositivo (sem a necessidade de atualizar uma compilação completa) e verificar se o bom comportamento é restaurado.

Fluxo de trabalho de exemplo:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Exemplos

Fundamentos

Consulte a página principal do formato de arquivo APEX para obter informações genéricas do APEX, incluindo requisitos do dispositivo, detalhes do formato do arquivo e etapas de instalação.

Em Android.bp , definir a vendor: true torna um módulo APEX um APEX de fornecedor.

apex {
  ..
  vendor: true,
  ..
}

Binários e bibliotecas compartilhadas

Um APEX inclui dependências transitivas dentro da carga útil do APEX, a menos que tenham interfaces estáveis.

Interfaces nativas estáveis ​​para dependências APEX de fornecedores incluem cc_library com stubs , ndk_library ou llndk_library . Essas dependências são excluídas do pacote e são registradas no manifesto APEX. O manifesto é processado pelo linkerconfig para que as dependências nativas externas estejam disponíveis em tempo de execução.

Ao contrário dos APEXes na partição /system , os APEXes do fornecedor normalmente estão vinculados a uma versão específica do VNDK. As bibliotecas VNDK garantem a estabilidade da ABI dentro do lançamento, para que possamos tratar as bibliotecas VNDK como estáveis ​​e reduzir o tamanho dos APEXes do fornecedor, excluindo-os dos APEXes usando a propriedade use_vndk_as_stable .

No trecho abaixo, o APEX conterá o binário ( my_service ) e suas dependências não estáveis ​​( arquivos *.so ). Ele não conterá bibliotecas VNDK, mesmo quando my_service for criado com bibliotecas VNDK como libbase . Em vez disso, em tempo de execução my_service usará libbase das bibliotecas VNDK fornecidas pelo sistema.

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  binaries: ["my_service"],
  ..
}

No trecho abaixo, o APEX conterá a biblioteca compartilhada my_standalone_lib e qualquer uma de suas dependências não estáveis ​​(conforme descrito acima).

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Implementações HAL

Para definir uma implementação HAL, forneça os binários e bibliotecas correspondentes dentro de um APEX de fornecedor semelhante aos exemplos a seguir:

Para encapsular totalmente a implementação HAL, o APEX também deve especificar quaisquer fragmentos VINTF e scripts de inicialização relevantes.

Fragmentos VINTF

Os fragmentos VINTF podem ser servidos a partir de um APEX de fornecedor quando os fragmentos estão localizados em etc/vintf do APEX.

Use a propriedade prebuilts para incorporar os fragmentos VINTF no APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

Scripts de inicialização

APEXes podem incluir scripts de inicialização de duas maneiras: (A) um arquivo de texto pré-construído dentro da carga útil do APEX ou (B) um script de inicialização regular em /vendor/etc . Você pode definir ambos para o mesmo APEX.

Script de inicialização no APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Os scripts de inicialização nos APEXes podem ter apenas definições service . Os scripts de inicialização nos APEXes do fornecedor também podem ter diretivas on <property> .

Tenha cuidado ao usar diretivas on . Como os scripts de inicialização em APEXes são analisados ​​e executados após a ativação dos APEXes, alguns eventos ou propriedades não podem ser usados. Use apex.all.ready=true para disparar ações o mais cedo possível.

Firmware

Exemplo:

Incorpore o firmware em um fornecedor APEX com o tipo de módulo prebuilt_firmware , conforme a seguir.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Os módulos prebuilt_firmware são instalados no diretório <apex name>/etc/firmware do APEX. ueventd verifica os diretórios /apex/*/etc/firmware para encontrar módulos de firmware.

Os file_contexts do APEX devem rotular adequadamente quaisquer entradas de carga útil do firmware para garantir que esses arquivos sejam acessíveis pelo ueventd em tempo de execução; normalmente, o rótulo vendor_file é suficiente. Por exemplo:

(/.*)? u:object_r:vendor_file:s0

Módulos do kernel

Incorpore módulos de kernel em um APEX de fornecedor como módulos pré-construídos, conforme a seguir.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

Os file_contexts do APEX devem rotular corretamente qualquer entrada de carga útil do módulo do kernel. Por exemplo:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Os módulos do kernel devem ser instalados explicitamente. O exemplo de script de inicialização a seguir na partição do fornecedor mostra a instalação via insmod :

my_init.rc :

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Sobreposições de recursos de tempo de execução

Exemplo:

Incorpore sobreposições de recursos de tempo de execução em um APEX de fornecedor usando a propriedade rros .

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Outros arquivos de configuração

Os APEXes do fornecedor suportam vários outros arquivos de configuração normalmente encontrados na partição do fornecedor como pré-construídos dentro dos APEXes do fornecedor, e mais estão sendo adicionados.

Exemplos:

Recursos extras de desenvolvimento

Seleção APEX na inicialização

Exemplo:

Os desenvolvedores também podem instalar várias versões de APEXes de fornecedores que compartilham o mesmo nome e chave APEX e, em seguida, escolher qual versão será ativada durante cada inicialização usando sysprops persistentes. Para determinados casos de uso de desenvolvedor, isso pode ser mais simples do que instalar uma nova cópia do APEX usando adb install .

Exemplos de casos de uso:

  • Instale 3 versões do fornecedor WiFi HAL APEX: As equipes de controle de qualidade podem executar testes manuais ou automatizados usando uma versão, reinicializar em outra versão e executar novamente os testes e, em seguida, comparar os resultados finais.
  • Instale 2 versões da câmera HAL do fornecedor APEX, atual e experimental : Dogfooders podem usar a versão experimental sem baixar e instalar um arquivo adicional, para que possam facilmente trocar de volta.

Durante a inicialização, apexd procura sysprops seguindo um formato específico para ativar a versão correta do APEX.

Os formatos esperados para a chave da propriedade são:

  • Configuração de inicialização
    • Usado para definir o valor padrão, em BoardConfig.mk .
    • androidboot.vendor.apex.<apex name>
  • Sysprop persistente
    • Usado para alterar o valor padrão, definido em um dispositivo já inicializado.
    • Substitui o valor bootconfig, se presente.
    • persist.vendor.apex.<apex name>

O valor da propriedade deverá ser o nome do arquivo do APEX que deverá ser ativado.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

A versão padrão também deve ser configurada usando bootconfig em BoardConfig.mk :

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Depois que o dispositivo for inicializado, altere a versão ativada configurando o sysprop persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Se o dispositivo suportar a atualização do bootconfig após a atualização (como por meio de comandos fastboot oem ), a alteração da propriedade bootconfig do APEX multiinstalado também alterará a versão ativada na inicialização.

Para dispositivos de referência virtual baseados em Cuttlefish , você pode usar o comando --extra_bootconfig_args para definir a propriedade bootconfig diretamente durante a inicialização. Por exemplo:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";
,

Você pode usar o formato de arquivo APEX para empacotar e instalar módulos do sistema operacional Android de nível inferior. Ele permite a construção e instalação independente de componentes como serviços e bibliotecas nativos, implementações HAL, firmware, arquivos de configuração, etc.

Os APEXes do fornecedor são instalados automaticamente pelo sistema de compilação na partição /vendor e ativados em tempo de execução pelo apexd , assim como os APEXes em outras partições.

Casos de uso

Modularização de imagens de fornecedores

Os APEXes facilitam o agrupamento e a modularização naturais de implementações de recursos em imagens de fornecedores.

Quando as imagens do fornecedor são criadas como uma combinação de APEXes de fornecedores independentes, os fabricantes de dispositivos podem escolher facilmente as implementações específicas do fornecedor desejadas em seus dispositivos. Os fabricantes podem até criar um novo APEX de fornecedor se nenhum dos APEX fornecidos atender às suas necessidades ou se eles tiverem um novo hardware personalizado.

Por exemplo, um OEM pode optar por compor seu dispositivo com a implementação de wifi AOSP APEX, a implementação de Bluetooth SoC APEX e uma implementação de telefonia OEM personalizada APEX.

Sem APEXes de fornecedores, uma implementação com tantas dependências entre componentes de fornecedores requer coordenação e rastreamento cuidadosos. Ao agrupar todos os componentes (incluindo arquivos de configuração e bibliotecas extras) em APEXes com interfaces claramente definidas em qualquer ponto de comunicação entre recursos, os diferentes componentes tornam-se intercambiáveis.

Iteração do desenvolvedor

Os APEXes do fornecedor ajudam os desenvolvedores a iterar mais rapidamente durante o desenvolvimento de módulos do fornecedor, agrupando uma implementação completa de recursos, como o HAL wifi, dentro de um APEX do fornecedor. Os desenvolvedores podem então criar e enviar individualmente o APEX do fornecedor para testar as alterações, em vez de reconstruir toda a imagem do fornecedor.

Isso simplifica e acelera o ciclo de iteração do desenvolvedor para desenvolvedores que trabalham principalmente em uma área de recurso e desejam iterar apenas nessa área de recurso.

O agrupamento natural de uma área de recurso em um APEX também simplifica o processo de construção, envio e teste de alterações para essa área de recurso. Por exemplo, reinstalar um APEX atualiza automaticamente qualquer biblioteca incluída ou arquivos de configuração que o APEX inclui.

Agrupar uma área de recurso em um APEX também simplifica a depuração ou a reversão quando um mau comportamento do dispositivo é observado. Por exemplo, se a telefonia estiver funcionando mal em uma nova compilação, os desenvolvedores poderão tentar instalar uma implementação de telefonia APEX mais antiga em um dispositivo (sem a necessidade de atualizar uma compilação completa) e verificar se o bom comportamento é restaurado.

Fluxo de trabalho de exemplo:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Exemplos

Fundamentos

Consulte a página principal do formato de arquivo APEX para obter informações genéricas do APEX, incluindo requisitos do dispositivo, detalhes do formato do arquivo e etapas de instalação.

Em Android.bp , definir a vendor: true torna um módulo APEX um APEX de fornecedor.

apex {
  ..
  vendor: true,
  ..
}

Binários e bibliotecas compartilhadas

Um APEX inclui dependências transitivas dentro da carga útil do APEX, a menos que tenham interfaces estáveis.

Interfaces nativas estáveis ​​para dependências APEX de fornecedores incluem cc_library com stubs , ndk_library ou llndk_library . Essas dependências são excluídas do pacote e são registradas no manifesto APEX. O manifesto é processado pelo linkerconfig para que as dependências nativas externas estejam disponíveis em tempo de execução.

Ao contrário dos APEXes na partição /system , os APEXes do fornecedor normalmente estão vinculados a uma versão específica do VNDK. As bibliotecas VNDK garantem a estabilidade da ABI dentro do lançamento, para que possamos tratar as bibliotecas VNDK como estáveis ​​e reduzir o tamanho dos APEXes do fornecedor, excluindo-os dos APEXes usando a propriedade use_vndk_as_stable .

No trecho abaixo, o APEX conterá o binário ( my_service ) e suas dependências não estáveis ​​( arquivos *.so ). Ele não conterá bibliotecas VNDK, mesmo quando my_service for criado com bibliotecas VNDK como libbase . Em vez disso, em tempo de execução my_service usará libbase das bibliotecas VNDK fornecidas pelo sistema.

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  binaries: ["my_service"],
  ..
}

No trecho abaixo, o APEX conterá a biblioteca compartilhada my_standalone_lib e qualquer uma de suas dependências não estáveis ​​(conforme descrito acima).

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Implementações HAL

Para definir uma implementação HAL, forneça os binários e bibliotecas correspondentes dentro de um APEX de fornecedor semelhante aos exemplos a seguir:

Para encapsular totalmente a implementação HAL, o APEX também deve especificar quaisquer fragmentos VINTF e scripts de inicialização relevantes.

Fragmentos VINTF

Os fragmentos VINTF podem ser servidos a partir de um APEX de fornecedor quando os fragmentos estão localizados em etc/vintf do APEX.

Use a propriedade prebuilts para incorporar os fragmentos VINTF no APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

Scripts de inicialização

APEXes podem incluir scripts de inicialização de duas maneiras: (A) um arquivo de texto pré-construído dentro da carga útil do APEX ou (B) um script de inicialização regular em /vendor/etc . Você pode definir ambos para o mesmo APEX.

Script de inicialização no APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Os scripts de inicialização nos APEXes podem ter apenas definições service . Os scripts de inicialização nos APEXes do fornecedor também podem ter diretivas on <property> .

Tenha cuidado ao usar diretivas on . Como os scripts de inicialização em APEXes são analisados ​​e executados após a ativação dos APEXes, alguns eventos ou propriedades não podem ser usados. Use apex.all.ready=true para disparar ações o mais cedo possível.

Firmware

Exemplo:

Incorpore o firmware em um fornecedor APEX com o tipo de módulo prebuilt_firmware , conforme a seguir.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Os módulos prebuilt_firmware são instalados no diretório <apex name>/etc/firmware do APEX. ueventd verifica os diretórios /apex/*/etc/firmware para encontrar módulos de firmware.

Os file_contexts do APEX devem rotular adequadamente quaisquer entradas de carga útil do firmware para garantir que esses arquivos sejam acessíveis pelo ueventd em tempo de execução; normalmente, o rótulo vendor_file é suficiente. Por exemplo:

(/.*)? u:object_r:vendor_file:s0

Módulos do kernel

Incorpore módulos de kernel em um APEX de fornecedor como módulos pré-construídos, conforme a seguir.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

Os file_contexts do APEX devem rotular corretamente qualquer entrada de carga útil do módulo do kernel. Por exemplo:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Os módulos do kernel devem ser instalados explicitamente. O exemplo de script de inicialização a seguir na partição do fornecedor mostra a instalação via insmod :

my_init.rc :

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Sobreposições de recursos de tempo de execução

Exemplo:

Incorpore sobreposições de recursos de tempo de execução em um APEX de fornecedor usando a propriedade rros .

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Outros arquivos de configuração

Os APEXes do fornecedor suportam vários outros arquivos de configuração normalmente encontrados na partição do fornecedor como pré-construídos dentro dos APEXes do fornecedor, e mais estão sendo adicionados.

Exemplos:

Recursos extras de desenvolvimento

Seleção APEX na inicialização

Exemplo:

Os desenvolvedores também podem instalar várias versões de APEXes de fornecedores que compartilham o mesmo nome e chave APEX e, em seguida, escolher qual versão será ativada durante cada inicialização usando sysprops persistentes. Para determinados casos de uso de desenvolvedor, isso pode ser mais simples do que instalar uma nova cópia do APEX usando adb install .

Exemplos de casos de uso:

  • Instale 3 versões do fornecedor WiFi HAL APEX: As equipes de controle de qualidade podem executar testes manuais ou automatizados usando uma versão, reinicializar em outra versão e executar novamente os testes e, em seguida, comparar os resultados finais.
  • Instale 2 versões da câmera HAL do fornecedor APEX, atual e experimental : Dogfooders podem usar a versão experimental sem baixar e instalar um arquivo adicional, para que possam facilmente trocar de volta.

Durante a inicialização, apexd procura sysprops seguindo um formato específico para ativar a versão correta do APEX.

Os formatos esperados para a chave da propriedade são:

  • Configuração de inicialização
    • Usado para definir o valor padrão, em BoardConfig.mk .
    • androidboot.vendor.apex.<apex name>
  • Sysprop persistente
    • Usado para alterar o valor padrão, definido em um dispositivo já inicializado.
    • Substitui o valor bootconfig, se presente.
    • persist.vendor.apex.<apex name>

O valor da propriedade deverá ser o nome do arquivo do APEX que deverá ser ativado.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

A versão padrão também deve ser configurada usando bootconfig em BoardConfig.mk :

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Depois que o dispositivo for inicializado, altere a versão ativada configurando o sysprop persistente:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Se o dispositivo suportar a atualização do bootconfig após a atualização (como por meio de comandos fastboot oem ), a alteração da propriedade bootconfig do APEX multiinstalado também alterará a versão ativada na inicialização.

Para dispositivos de referência virtual baseados em Cuttlefish , você pode usar o comando --extra_bootconfig_args para definir a propriedade bootconfig diretamente durante a inicialização. Por exemplo:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";