HAL da câmera veicular

O Android contém uma camada de abstração de hardware (HAL) automotiva de HIDL que oferece a captura e a exibição de imagens no início do processo de inicialização e continua funcionando durante a vida útil do sistema. O HAL inclui a pilha do sistema de visualização externa (EVS, na sigla em inglês) e normalmente é usado para oferecer suporte à câmera traseira e às telas de visão periférica em veículos com sistemas de infoentretenimento no veículo (IVI, na sigla em inglês) baseados no Android. O EVS também permite que recursos avançados sejam implementados em apps do usuário.

O Android também inclui uma interface de captura e exibição específica do EVS (em /hardware/interfaces/automotive/evs/1.0). Embora seja possível criar um app de câmera traseira em cima dos serviços de câmera e exibição do Android, esse app provavelmente será executado muito tarde no processo de inicialização do Android. O uso de uma HAL dedicada permite uma interface simplificada e deixa claro o que um OEM precisa implementar para oferecer suporte à pilha EVS.

Componentes do sistema

O EVS inclui os seguintes componentes do sistema:

Diagrama de componentes do sistema de EVS
Figura 1. Visão geral dos componentes do sistema EVS.

App de EVS

Um exemplo de app EVS em C++ (/packages/services/Car/evs/app) serve como uma implementação de referência. Esse app é responsável por solicitar frames de vídeo do gerenciador de EVS e enviar frames concluídos para exibição de volta ao gerenciador de EVS. Ele será iniciado por init assim que o EVS e o serviço do carro estiverem disponíveis, direcionados em até dois (2) segundos após a inicialização. Os OEMs podem modificar ou substituir o app EVS conforme necessário.

Gerenciador de EVS

O gerenciador de EVS (/packages/services/Car/evs/manager) fornece os blocos de construção necessários para que um app de EVS implemente qualquer coisa, desde uma simples exibição de câmera retrovisor até uma renderização de várias câmeras de 6DOF. A interface é apresentada por meio do HIDL e é criada para aceitar vários clientes simultâneos. Outros apps e serviços (especificamente o Car Service) podem consultar o estado do gerenciador de EVS para descobrir quando o sistema de EVS está ativo.

Interface HIDL do EVS

O sistema EVS, tanto a câmera quanto os elementos de exibição, é definido no pacote android.hardware.automotive.evs. Um exemplo de implementação que testa a interface (gera imagens de teste sintéticas e valida se as imagens fazem a ida e volta) está disponível em /hardware/interfaces/automotive/evs/1.0/default.

O OEM é responsável por implementar a API expressa pelos arquivos .hal em /hardware/interfaces/automotive/evs. Essas implementações são responsáveis por configurar e coletar dados de câmeras físicas e os enviar por buffers de memória compartilhada, reconhecíveis pelo Gralloc. O lado da exibição da implementação é responsável por fornecer um buffer de memória compartilhada que pode ser preenchido pelo app (geralmente com renderização EGL) e apresentar os frames finalizados em vez de qualquer outra coisa que possa aparecer na tela física. As implementações do fornecedor da interface EVS podem ser armazenadas em /vendor/… /device/… ou hardware/… (por exemplo, /hardware/[vendor]/[platform]/evs).

Drivers do kernel

Um dispositivo compatível com a pilha EVS exige drivers de kernel. Em vez de criar novos drivers, os OEMs têm a opção de oferecer suporte aos recursos necessários do EVS usando os drivers de hardware de câmera ou de exibição atuais. Reutilizar drivers pode ser vantajoso, especialmente para drivers de exibição em que a apresentação de imagens pode exigir coordenação com outras linhas de execução ativas. O Android 8.0 inclui um driver de amostra baseado na v4l2 (em packages/services/Car/evs/sampleDriver) que depende do kernel para oferecer suporte à v4l2 e do SurfaceFlinger para apresentar a imagem de saída.

Descrição da interface de hardware da EVS

A seção descreve o HAL. Espera-se que os fornecedores forneçam implementações dessa API adaptadas para o hardware.

IEvsEnumerator

Esse objeto é responsável por enumerar o hardware de EVS disponível no sistema (uma ou mais câmeras e o único dispositivo de exibição).

getCameraList() generates (vec<CameraDesc> cameras);

Retorna um vetor com descrições de todas as câmeras do sistema. Supõe-se que o conjunto de câmeras é fixo e conhecido no momento da inicialização. Para mais detalhes sobre as descrições da câmera, consulte CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Obtém um objeto de interface usado para interagir com uma câmera específica identificada pela string exclusiva camera_id. Retorna um NULL em caso de falha. As tentativas de reabrir uma câmera que já está aberta não podem falhar. Para evitar condições de corrida associadas à inicialização e desativação do app, a reabertura de uma câmera desativa a instância anterior para que a nova solicitação possa ser atendida. Uma instância de câmera que foi interrompida dessa maneira precisa ser colocada em um estado inativo, aguardando a destruição final e respondendo a qualquer solicitação para afetar o estado da câmera com um código de retorno de OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Libera a interface IEvsCamera (e é o oposto da chamada openCamera()). A transmissão de vídeo da câmera precisa ser interrompida chamando stopVideoStream() antes de chamar closeCamera.

openDisplay() generates (IEvsDisplay display);

Obtém um objeto de interface usado para interagir exclusivamente com a tela EVS do sistema. Somente um cliente pode manter uma instância funcional do IEvsDisplay por vez. Assim como o comportamento de abertura agressivo descrito em openCamera, um novo objeto IEvsDisplay pode ser criado a qualquer momento e desativa as instâncias anteriores. As instâncias invalidadas continuam a existir e respondem a chamadas de função dos proprietários, mas não podem executar operações de mutação quando estão inativas. Eventualmente, o app cliente vai notar os códigos de retorno de erro OWNERSHIP_LOST e fechar e liberar a interface inativa.

closeDisplay(IEvsDisplay display);

Libera a interface IEvsDisplay (e é o oposto da chamada openDisplay()). Os buffers pendentes recebidos com chamadas getTargetBuffer() precisam ser retornados à tela antes de fechar a tela.

getDisplayState() generates (DisplayState state);

Consegue o estado de exibição atual. A implementação da HAL precisa informar o estado atual real, que pode ser diferente do estado solicitado mais recentemente. A lógica responsável por mudar os estados de exibição precisa existir acima da camada do dispositivo, fazendo com que não seja desejável que a implementação da HAL mude os estados de exibição de forma espontânea. Se a tela não estiver retida por nenhum cliente (por uma chamada para openDisplay), essa função retornará NOT_OPEN. Caso contrário, ele informa o estado atual da tela EVS (consulte a API IEvsDisplay).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id: uma string que identifica exclusivamente uma determinada câmera. Pode ser o nome do dispositivo do kernel ou um nome para o dispositivo, como rearview. O valor dessa string é escolhido pela implementação da HAL e usado de forma opaca pela pilha acima.
  • vendor_flags. Um método para transmitir informações especializadas da câmera de maneira opaca do driver para um app EVS personalizado. Elas são transmitidas não interpretadas do driver para o app EVS, que podem ser ignoradas por elas.

IEvsCamera

Esse objeto representa uma única câmera e é a interface principal para capturar imagens.

getCameraInfo() generates (CameraDesc info);

Retorna CameraDesc desta câmera.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Especifica a profundidade da cadeia de buffer que a câmera precisa oferecer suporte. Até esse número de frames pode ser mantido simultaneamente pelo cliente da IEvsCamera. Se esse número de frames tiver sido entregue ao receptor sem ser retornado por doneWithFrame, o stream vai pular frames até que um buffer seja retornado para reutilização. É legal que essa chamada ocorra a qualquer momento, mesmo que os streams já estejam em execução. Nesse caso, é necessário adicionar ou remover buffers da cadeia, conforme apropriado. Se nenhuma chamada for feita para esse ponto de entrada, a IEvsCamera vai oferecer suporte a pelo menos um frame por padrão, com mais aceitável.

Se o bufferCount solicitado não puder ser acomodado, a função retornará BUFFER_NOT_AVAILABLE ou outro código de erro relevante. Nesse caso, o sistema continua operando com o valor definido anteriormente.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Solicita o envio de frames da câmera EVS dessa câmera. O IEvsCameraStream começa a receber chamadas periódicas com novos frames de imagem até que stopVideoStream() seja chamado. Os frames precisam começar a ser enviados em até 500 ms da chamada startVideoStream e, após o início, precisam gerar um mínimo de 10 QPS. O tempo necessário para iniciar o fluxo de vídeo conta efetivamente contra qualquer requisito de tempo de inicialização da câmera retrovisor. Se o stream não for iniciado, um código de erro será retornado. Caso contrário, o retorno será "OK".

oneway doneWithFrame(BufferDesc buffer);

Retorna um frame que foi entregue para o IEvsCameraStream. Ao consumir um frame entregue à interface IEvsCameraStream, o frame precisa ser devolvido à IEvsCamera para reutilização. Um número pequeno e finito de buffers está disponível (talvez apenas um), e se o suprimento for esgotado, nenhum outro frame será entregue até que um buffer seja retornado, o que pode resultar em frames ignorados (um buffer com um identificador nulo denota o fim de um stream e não precisa ser retornado por essa função). Retorna OK em caso de sucesso ou um código de erro adequado, que pode incluir INVALID_ARG ou BUFFER_NOT_AVAILABLE.

stopVideoStream();

Interrompe a entrega de frames da câmera EVS. Como a entrega é assíncrona, os frames podem continuar chegando por algum tempo após o retorno da chamada. Cada frame precisa ser retornado até que o fechamento do stream seja sinalizado para o IEvsCameraStream. É possível chamar stopVideoStream em um stream que já foi interrompido ou nunca foi iniciado. Nesses casos, ele é ignorado.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Solicita informações específicas do driver da implementação da HAL. Os valores permitidos para opaqueIdentifier são específicos do driver, mas nenhum valor transmitido pode causar falha no driver. O driver precisa retornar 0 para qualquer opaqueIdentifier não reconhecido.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Envia um valor específico do driver para a implementação da HAL. Essa extensão é fornecida apenas para facilitar extensões específicas do veículo, e nenhuma implementação de HAL precisa exigir essa chamada para funcionar em um estado padrão. Se o driver reconhecer e aceitar os valores, o retorno será OK. Caso contrário, INVALID_ARG ou outro código de erro representativo será retornado.

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

Descreve uma imagem transmitida pela API. O drive HAL é responsável por preencher essa estrutura para descrever o buffer de imagem, e o cliente HAL precisa tratar essa estrutura como somente leitura. Os campos contêm informações suficientes para permitir que o cliente reconstrua um objeto ANativeWindowBuffer, como pode ser necessário usar a imagem com EGL com a extensão eglCreateImageKHR().

  • width: a largura em pixels da imagem apresentada.
  • height: a altura em pixels da imagem apresentada.
  • stride: número de pixels que cada linha ocupa na memória, considerando o preenchimento para alinhamento de linhas. Expresso em pixels para corresponder à convenção adotada pelo gralloc para as descrições de buffer.
  • pixelSize: número de bytes ocupados por cada pixel individual, permitindo o cálculo do tamanho em bytes necessário para avançar entre as linhas na imagem (stride em bytes = stride em pixels * pixelSize).
  • format: o formato de pixel usado pela imagem. O formato fornecido precisa ser compatível com a implementação OpenGL da plataforma. Para passar nos testes de compatibilidade, HAL_PIXEL_FORMAT_YCRCB_420_SP precisa ser a preferência para o uso da câmera, e RGBA ou BGRA precisam ser a preferência para a tela.
  • usage. Flags de uso definidas pela implementação do HAL. Os clientes da HAL precisam transmitir esses elementos não modificados. Para mais detalhes, consulte flags relacionadas a Gralloc.h.
  • bufferId: um valor exclusivo especificado pela implementação do HAL para permitir que um buffer seja reconhecido após uma ida e volta pelas APIs HAL. O valor armazenado nesse campo pode ser escolhido arbitrariamente pela implementação da HAL.
  • memHandle: o identificador do buffer de memória subjacente que contém os dados da imagem. A implementação do HAL pode escolher armazenar um identificador de buffer Gralloc aqui.

IEvsCameraStream

O cliente implementa essa interface para receber entregas de frames de vídeo assíncronos.

deliverFrame(BufferDesc buffer);

Recebe chamadas do HAL sempre que um frame de vídeo está pronto para inspeção. Os identificadores de buffer recebidos por esse método precisam ser retornados por chamadas para IEvsCamera::doneWithFrame(). Quando o stream de vídeo é interrompido com uma chamada para IEvsCamera::stopVideoStream(), esse callback pode continuar enquanto o pipeline é drenado. Cada frame ainda precisa ser retornado. Quando o último frame no stream é entregue, um bufferHandle NULL é entregue, simbolizando o fim do stream, e nenhum outro frame é entregue. O bufferHandle NULL não precisa ser enviado de volta com doneWithFrame(), mas todos os outros identificadores precisam ser retornados.

Embora os formatos de buffer proprietários sejam tecnicamente possíveis, os testes de compatibilidade exigem que o buffer esteja em um dos quatro formatos aceitos: NV21 (YCrCb 4:2:0 semiplano), YV12 (YCrCb 4:2:0 plano), YUYV (YCrCb 4:2:2 intercalado), RGBA (32 bits R:G:B:x) e BGRA (32 bits B:G:R:x). O formato selecionado precisa ser uma fonte de textura GL válida na implementação GLES da plataforma.

O app não pode depender de nenhuma correspondência entre o campo bufferId e o memHandle na estrutura BufferDesc. Os valores bufferId são essencialmente privados para a implementação do driver HAL e podem ser usados (e reutilizados) conforme necessário.

IEvsDisplay

Esse objeto representa a tela do Evs, controla o estado da tela e processa a apresentação real de imagens.

getDisplayInfo() generates (DisplayDesc info);

Retorna informações básicas sobre a tela do EVS fornecida pelo sistema (consulte DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

Define o estado de exibição. Os clientes podem definir o estado de exibição para expressar o estado desejado, e a implementação do HAL precisa aceitar uma solicitação para qualquer estado enquanto estiver em qualquer outro estado, embora a resposta possa ser para ignorar a solicitação.

Após a inicialização, a tela é definida para começar no estado NOT_VISIBLE. Depois disso, o cliente vai solicitar o estado VISIBLE_ON_NEXT_FRAME e começar a fornecer vídeo. Quando a tela não for mais necessária, o cliente deverá solicitar o estado NOT_VISIBLE após transmitir o último quadro de vídeo.

Qualquer estado pode ser solicitado a qualquer momento. Se a tela já estiver visível, ela vai continuar assim se definida como VISIBLE_ON_NEXT_FRAME. Sempre retorna OK, a menos que o estado solicitado seja um valor de enumeração não reconhecido. Nesse caso, INVALID_ARG é retornado.

getDisplayState() generates (DisplayState state);

Recupera o estado de exibição. A implementação do HAL precisa informar o estado atual, que pode ser diferente do estado solicitado mais recentemente. A lógica responsável por mudar os estados de exibição precisa existir acima da camada do dispositivo, o que torna indesejável que a implementação do HAL mude espontaneamente os estados de exibição.

getTargetBuffer() generates (handle bufferHandle);

Retorna um handle para um frame buffer associado à tela. Esse buffer pode ser bloqueado e gravado por software e/ou GL. Esse buffer precisa ser retornado com uma chamada para returnTargetBufferForDisplay(), mesmo que a tela não esteja mais visível.

Embora os formatos de buffer proprietários sejam tecnicamente possíveis, os testes de compatibilidade exigem que o buffer esteja em um dos quatro formatos aceitos: NV21 (YCrCb 4:2:0 semiplano), YV12 (YCrCb 4:2:0 plano), YUYV (YCrCb 4:2:2 intercalado), RGBA (32 bits R:G:B:x) e BGRA (32 bits B:G:R:x). O formato selecionado precisa ser um destino de renderização GL válido na implementação do GLES da plataforma.

Em caso de erro, um buffer com um identificador nulo é retornado, mas esse buffer não precisa ser transmitido de volta para returnTargetBufferForDisplay.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Informa à tela que o buffer está pronto para ser exibido. Somente buffers recuperados por uma chamada para getTargetBuffer() são válidos para uso com essa chamada, e o conteúdo do BufferDesc não pode ser modificado pelo app cliente. Após essa chamada, o buffer não é mais válido para uso pelo cliente. Retorna "OK" em caso de sucesso ou um código de erro adequado, potencialmente incluindo INVALID_ARG ou BUFFER_NOT_AVAILABLE.

struct DisplayDesc {
    string  display_id;
    int32   vendor_flags;  // Opaque value
}

Descreve as propriedades básicas de uma tela de EVS e exigidas por uma implementação de EVS. O HAL é responsável por preencher essa estrutura para descrever a exibição do EVS. Pode ser uma tela física ou virtual que é sobreposta ou misturada com outro dispositivo de apresentação.

  • display_id. Uma string que identifica a tela de forma exclusiva. Pode ser o nome do dispositivo do kernel ou um nome para o dispositivo, como rearview. O valor dessa string é escolhido pela implementação do HAL e usado de forma opaca pela pilha acima.
  • vendor_flags. Um método para transmitir informações especializadas da câmera de maneira opaca do driver para um app EVS personalizado. Elas são transmitidas não interpretadas do driver para o app EVS, que podem ser ignoradas pelo app.
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been “opened” yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

Descreve o estado da tela do EVS, que pode ser desativado (não visível para o motorista) ou ativado (exibindo uma imagem para o motorista). Inclui um estado transitório em que a tela ainda não está visível, mas está preparada para ficar visível com o envio do próximo frame de imagens com a chamada returnTargetBufferForDisplay().

Gerenciador de EVS

O gerenciador de EVS fornece a interface pública para o sistema de EVS para coletar e apresentar as visualizações da câmera externa. Quando os drivers de hardware permitem apenas uma interface ativa por recurso (câmera ou tela), o EVS Manager facilita o acesso compartilhado às câmeras. Um único app principal de EVS é o primeiro cliente do gerenciador de EVS e é o único cliente autorizado a gravar dados de exibição. Outros clientes podem receber acesso de leitura às imagens da câmera.

O gerenciador de EVS implementa a mesma API que os drivers HAL e oferece um serviço expandido com suporte a vários clientes simultâneos (mais de um cliente pode abrir uma câmera pelo gerenciador de EVS e receber um stream de vídeo).

EVS Manager e diagrama da
API EVS Hardware.
Figura 2. O EVS Manager espelha a API EVS Hardware.

Os apps não percebem diferenças ao operar pela implementação do HAL de hardware do EVS ou pela API EVS Manager, exceto que a API EVS Manager permite acesso simultâneo ao stream da câmera. O gerenciador de EVS é o único cliente permitido da camada HAL de hardware do EVS e atua como um proxy para o HAL de hardware do EVS.

As seções a seguir descrevem apenas as chamadas que têm um comportamento diferente (estendido) na implementação do EVS Manager. As chamadas restantes são idênticas às descrições de HAL de EVS.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Obtém um objeto de interface usado para interagir com uma câmera específica identificada pela string exclusiva camera_id. Retorna NULL em caso de falha. Na camada do EVS Manager, enquanto houver recursos suficientes do sistema disponíveis, uma câmera que já estiver aberta poderá ser aberta novamente por outro processo, permitindo o tethering do stream de vídeo para vários apps de consumo. As strings camera_id na camada EVS Manager são as mesmas informadas para a camada de hardware EVS.

IEvsCamera

O EVS Manager fornecido pela implementação de IEvsCamera é virtualizado internamente para que as operações em uma câmera por um cliente não afetem outros clientes, que mantêm acesso independente às câmeras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Inicia transmissões de vídeo. Os clientes podem iniciar e interromper os streams de vídeo de forma independente na mesma câmera. A câmera subjacente é iniciada quando o primeiro cliente é iniciado.

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

Retorna um frame. Cada cliente precisa retornar os frames quando terminar, mas pode manter os frames por quanto tempo quiser. Quando a contagem de frames de um cliente atinge o limite configurado, ele não recebe mais frames até retornar um. Esse pulo de frames não afeta outros clientes, que continuam recebendo todos os frames conforme o esperado.

stopVideoStream();

Interrompe um stream de vídeo. Cada cliente pode interromper o fluxo de vídeo a qualquer momento sem afetar outros clientes. O stream da câmera na camada de hardware é interrompido quando o último cliente de uma determinada câmera interrompe o stream.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Envia um valor específico do driver, potencialmente permitindo que um cliente afete outro. Como o gerenciador de EVS não entende as implicações das palavras de controle definidas pelo fornecedor, elas não são virtualizadas e todos os efeitos colaterais se aplicam a todos os clientes de uma determinada câmera. Por exemplo, se um fornecedor usasse essa chamada para mudar as taxas de frames, todos os clientes da câmera da camada de hardware afetada receberiam frames na nova taxa.

IEvsDisplay

Só é permitido ter um proprietário da tela, mesmo no nível do gerenciador de EVS. O gerenciador não adiciona nenhuma funcionalidade e simplesmente transmite a interface IEvsDisplay diretamente para a implementação de HAL.

App de EVS

O Android inclui uma implementação de referência nativa em C++ de um app EVS que se comunica com o gerenciador EVS e a HAL do veículo para fornecer funções básicas da câmera retrovisor. Espera-se que o app seja iniciado no início do processo de inicialização do sistema, com um vídeo adequado mostrado dependendo das câmeras disponíveis e do estado do carro (marcha e seta de direção). Os OEMs podem modificar ou substituir o app EVS com a própria lógica e apresentação específica do veículo.

Figura 3. Lógica de amostra do app EVS, "Acessar lista da câmera".


Figura 4. Lógica de amostra do app EVS, recebimento de callback do frame.

Como os dados de imagem são apresentados ao app em um buffer gráfico padrão, o app é responsável por mover a imagem do buffer de origem para o buffer de saída. Embora isso apresente o custo de uma cópia de dados, também oferece a oportunidade de o app renderizar a imagem no buffer de exibição da maneira que quiser.

Por exemplo, o app pode mover os dados de pixel, possivelmente com uma operação de rotação ou escala inline. O app também pode escolher usar a imagem de origem como uma textura OpenGL e renderizar uma cena complexa para o buffer de saída, incluindo elementos virtuais, como ícones, diretrizes e animações. Um app mais sofisticado também pode selecionar várias câmeras de entrada simultâneas e mesclá-las no único frame de saída (por exemplo, para uso em uma visualização virtual de cima para baixo dos arredores do veículo).

Usar o EGL/SurfaceFlinger no HAL de exibição do EVS

Esta seção explica como usar o EGL para renderizar uma implementação de HAL de exibição EVS no Android 10.

Uma implementação de referência EVS HAL usa EGL para renderizar a visualização da câmera na tela e usa libgui para criar a superfície de renderização EGL de destino. No Android 8 (e versões mais recentes), libgui é classificada como VNDK-private, que se refere a um grupo de bibliotecas disponíveis para bibliotecas do VNDK que os processos do fornecedor não podem usar. Como as implementações de HAL precisam residir na partição do fornecedor, os fornecedores são impedidos de usar implementações de Surface na HAL.

Como criar uma libgui para processos de fornecedores

O uso de libgui serve como a única opção para usar EGL/SurfaceFlinger em implementações de HAL de exibição do EVS. A maneira mais simples de implementar libgui é usando frameworks/native/libs/gui diretamente com um destino de build adicional no script de build. Essa segmentação é exatamente igual à libgui, exceto pela adição de dois campos:

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …

Observação: os destinos de fornecedores são criados com a macro NO_INPUT, que remove uma palavra de 32 bits dos dados de lotes. Como o SurfaceFlinger espera esse campo que foi removido, ele não consegue analisar o pacote. Isso é observado como uma falha de fcntl:

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

Para resolver essa condição:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
    output.writeFloat(color.b);
#ifndef NO_INPUT
    inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
#endif
    output.write(transparentRegion);
    output.writeUint32(transform);

Confira abaixo um exemplo de instruções de build. Você vai receber um $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so.

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

Usar o binder na implementação do HAL do EVS

No Android 8 (e versões mais recentes), o nó de dispositivo /dev/binder se tornou exclusivo para processos de framework e, portanto, inacessível para processos de fornecedores. Em vez disso, os processos do fornecedor precisam usar /dev/hwbinder e converter todas as interfaces AIDL em HIDL. Para quem quer continuar usando interfaces AIDL entre processos do fornecedor, use o domínio de vinculação, /dev/vndbinder.

Domínio de IPC Descrição
/dev/binder IPC entre processos de framework/app com interfaces AIDL
/dev/hwbinder IPC entre processos de framework/fornecedor com interfaces HIDL
IPC entre processos de fornecedor com interfaces HIDL
/dev/vndbinder IPC entre processos de fornecedor/fornecedor com interfaces AIDL

Enquanto o SurfaceFlinger define interfaces AIDL, os processos do fornecedor só podem usar interfaces HIDL para se comunicar com os processos do framework. É necessário um trabalho considerável para converter as interfaces AIDL atuais em HIDL. Felizmente, o Android oferece um método para selecionar o driver de vinculação para libbinder, ao qual os processos de biblioteca do espaço do usuário estão vinculados.

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


    // Start a thread to listen to video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

Observação: os processos do fornecedor precisam chamar esse método antes de chamar Process ou IPCThreadState ou de fazer chamadas de vinculação.

Políticas do SELinux

Se a implementação do dispositivo for aguda total, o SELinux vai impedir que os processos do fornecedor usem /dev/binder. Por exemplo, uma implementação de exemplo de HAL do EVS é atribuída ao domínio hal_evs_driver e requer permissões de leitura/gravação no domínio binder_device.

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

No entanto, a adição dessas permissões causa uma falha no build porque viola as seguintes regras de nunca permitir definidas em system/sepolicy/domain.te para um dispositivo com alto nível de agudos.

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
} binder_device:chr_file rw_file_perms;
')

binder_in_vendor_violators é um atributo fornecido para detectar um bug e orientar o desenvolvimento. Ele também pode ser usado para resolver a violação do Android 10 descrita acima.

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)

Criar a implementação de referência do HAL de EVS como um processo do fornecedor

Como referência, é possível aplicar as mudanças abaixo a packages/services/Car/evs/Android.mk. Confirme se todas as alterações descritas funcionam para sua implementação.

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
    android.hardware.automotive.evs@1.0 \
    libui \
-    libgui \
+    libgui_vendor \
    libEGL \
    libGLESv2 \
    libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

#NOTE:  It can be helpful, while debugging, to disable optimizations
#LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

# Allow the driver to access kobject uevents
allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;