HAL da câmera veicular

O Android contém uma camada de abstração de hardware (HAL) HIDL automotiva que permite a captura e exibição de imagens no início do processo de inicialização do Android e continua funcionando durante toda a vida útil do sistema. A HAL inclui a pilha do sistema de visualização externa (EVS) e geralmente é usada para oferecer suporte a câmeras retrovisoras e telas de visualização do entorno em veículos com sistemas de infoentretenimento no veículo (IVI) baseados no Android. O EVS também permite a implementação de recursos avançados em apps do usuário.

O Android também inclui uma interface de driver 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 com base nos serviços de câmera e exibição do Android, é provável que ele seja executado muito tarde no processo de inicialização do Android. Usar 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 EVS
Figura 1. Visão geral dos componentes do sistema EVS.

App 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 EVS Manager e enviar frames concluídos para exibição de volta ao EVS Manager. Ele espera ser iniciado pelo init assim que o EVS e o serviço de carro estiverem disponíveis, direcionados em até dois (2) segundos após a inicialização. Os OEMs podem modificar ou substituir o app EVS como quiserem.

Gerente do EVS

O EVS Manager (/packages/services/Car/evs/manager) fornece os blocos de construção necessários para um app de EVS implementar qualquer coisa, desde uma tela simples de câmera traseira até uma renderização de várias câmeras 6DOF. A interface é apresentada pelo HIDL e foi criada para aceitar vários clientes simultâneos. Outros apps e serviços (especificamente o Car Service) podem consultar o estado do EVS Manager para saber quando o sistema 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. Uma implementação de amostra que exercita a interface (gera imagens de teste sintéticas e valida se as imagens fazem a viagem de ida e volta) é fornecida 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 entregá-los 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 concluídos 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 podem oferecer suporte aos recursos necessários do EVS usando drivers de hardware de câmera ou tela atuais. A reutilização de drivers pode ser vantajosa, especialmente para drivers de tela 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 exemplo baseado em v4l2 (em packages/services/Car/evs/sampleDriver) que depende do kernel para compatibilidade com v4l2 e do SurfaceFlinger para apresentar a imagem de saída.

Descrição da interface de hardware do EVS

A seção descreve a HAL. Os fornecedores precisam oferecer implementações dessa API adaptadas para o hardware deles.

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 que contém descrições de todas as câmeras no sistema. É presumido que o conjunto de câmeras seja fixo e conhecido no momento da inicialização. Para mais detalhes sobre descrições de câmera, consulte CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Recebe 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. As tentativas de reabrir uma câmera que já está aberta não podem falhar. Para evitar condições de disputa associadas à inicialização e ao encerramento do app, a reabertura de uma câmera precisa encerrar a instância anterior para que a nova solicitação possa ser atendida. Uma instância de câmera que foi desalojada dessa forma 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()). O fluxo de vídeo da câmera precisa ser interrompido chamando stopVideoStream() antes de chamar closeCamera.

openDisplay() generates (IEvsDisplay display);

Recebe um objeto de interface usado para interagir exclusivamente com a tela do EVS do sistema. Apenas um cliente pode ter uma instância funcional de IEvsDisplay por vez. Semelhante ao comportamento de abertura agressiva descrito em openCamera, um novo objeto IEvsDisplay pode ser criado a qualquer momento e desativa todas as instâncias anteriores. As instâncias invalidadas continuam existindo e respondendo a chamadas de função dos proprietários, mas não podem realizar 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);

Recebe 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 estar acima da camada do dispositivo, o que torna indesejável que a implementação da HAL mude espontaneamente os estados de exibição. Se a exibição não estiver sendo mantida por nenhum cliente (por uma chamada para openDisplay), essa função vai retornar NOT_OPEN. Caso contrário, ele vai informar o estado atual da tela EVS (consulte API IEvsDisplay).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id: uma string que identifica de forma exclusiva 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 forma opaca do driver para um app EVS personalizado. Ele é transmitido sem interpretação do driver para o app EVS, que pode ignorá-lo.

IEvsCamera

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

getCameraInfo() generates (CameraDesc info);

Retorna o CameraDesc desta câmera.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

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

Se o bufferCount solicitado não puder ser acomodado, a função vai 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 a entrega de frames da câmera EVS. 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 entregues em até 500 ms da chamada startVideoStream e, depois de iniciados, precisam ser gerados a uma taxa mínima de 10 FPS. O tempo necessário para iniciar o fluxo de vídeo conta para qualquer requisito de tempo de inicialização da câmera traseira. Se o fluxo não for iniciado, um código de erro vai ser retornado. Caso contrário, OK será retornado.

oneway doneWithFrame(BufferDesc buffer);

Retorna um frame entregue ao IEvsCameraStream. Quando terminar de consumir um frame entregue à interface IEvsCameraStream, ele precisará ser retornado à IEvsCamera para reutilização. Um número pequeno e finito de buffers está disponível (possivelmente apenas um), e, se o fornecimento se esgotar, nenhum outro frame será entregue até que um buffer seja retornado, o que pode resultar em frames ignorados. Um buffer com um identificador nulo indica o fim de um fluxo e não precisa ser retornado por essa função. Retorna "OK" em caso de sucesso ou código de erro apropriado, incluindo potencialmente 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 depois que essa chamada é retornada. Cada frame precisa ser retornado até que o fechamento do fluxo seja sinalizado para o IEvsCameraStream. É permitido chamar stopVideoStream em um fluxo que já foi interrompido ou nunca iniciado. Nesses casos, a chamada é ignorada.

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 falhar. 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 dessa chamada para funcionar em um estado padrão. Se o driver reconhecer e aceitar os valores, "OK" será retornado. 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 driver HAL é responsável por preencher essa estrutura para descrever o buffer de imagem, e o cliente HAL deve tratar essa estrutura como somente leitura. Os campos contêm informações suficientes para permitir que o cliente reconstrua um objeto ANativeWindowBuffer, conforme necessário para usar a imagem com EGL e 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 padding para alinhamento das linhas. Expressa 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 passar entre as linhas da 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 do OpenGL da plataforma. Para passar no teste de compatibilidade, HAL_PIXEL_FORMAT_YCRCB_420_SP precisa ser preferido para uso da câmera, e RGBA ou BGRA precisam ser preferidos para exibição.
  • usage. Flags de uso definidas pela implementação da HAL. Espera-se que os clientes HAL transmitam esses dados sem modificação. Para mais detalhes, consulte as flags relacionadas Gralloc.h.
  • bufferId: um valor exclusivo especificado pela implementação do HAL para permitir que um buffer seja reconhecido após uma viagem de 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 da HAL pode optar por armazenar um identificador de buffer do Gralloc aqui.

IEvsCameraStream

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

deliverFrame(BufferDesc buffer);

Recebe chamadas da 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 for entregue, um bufferHandle NULL será entregue, indicando o fim do stream e que não haverá mais entregas de frames. O NULL bufferHandle não precisa ser enviado de volta com doneWithFrame(), mas todos os outros identificadores precisam ser retornados.

Embora seja tecnicamente possível usar formatos de buffer proprietários, o teste de compatibilidade exige que o buffer esteja em um dos quatro formatos compatíveis: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved) e RGBA (32 bits R:G:B:x) e BGRA (32 bits B:G:R:x). O formato selecionado precisa ser uma origem 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 particulares à implementação do driver HAL, que pode usá-los (e reutilizá-los) conforme achar adequado.

IEvsDisplay

Esse objeto representa a tela do EVS, controla o estado dela e processa a apresentação real das 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 da HAL precisa aceitar normalmente uma solicitação de qualquer estado enquanto estiver em qualquer outro estado, embora a resposta possa ser ignorar a solicitação.

Na inicialização, a exibição é definida para começar no estado NOT_VISIBLE. Depois disso, espera-se que o cliente solicite o estado VISIBLE_ON_NEXT_FRAME e comece a fornecer vídeo. Quando a exibição não for mais necessária, o cliente deverá solicitar o estado NOT_VISIBLE depois de transmitir o último frame de vídeo.

É válido solicitar qualquer estado a qualquer momento. Se a tela já estiver visível, ela vai continuar assim se for 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);

Recebe o estado da tela. 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 estar acima da camada do dispositivo, o que torna indesejável que a implementação da HAL mude espontaneamente os estados de exibição.

getTargetBuffer() generates (handle bufferHandle);

Retorna um handle para um buffer de frame 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 formatos de buffer proprietários sejam tecnicamente possíveis, o teste de compatibilidade exige que o buffer esteja em um dos quatro formatos compatíveis: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), 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 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 exibição. Somente os 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. Depois dessa chamada, o buffer não é mais válido para uso pelo cliente. Retorna OK em caso de sucesso ou o código de erro apropriado, 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 EVS e é exigido por uma implementação de EVS. A HAL é responsável por preencher essa estrutura para descrever a tela 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 o display 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 maneira opaca pela pilha acima.
  • vendor_flags. Um método para transmitir informações especializadas da câmera de forma opaca do driver para um app EVS personalizado. Ele é transmitido sem interpretação do driver para o app EVS, que pode ignorá-lo.
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 desativada (não visível para o motorista) ou ativada (mostrando uma imagem para o motorista). Inclui um estado transitório em que a exibição ainda não está visível, mas está preparada para ficar visível com a entrega do próximo frame de imagens com a chamada returnTargetBufferForDisplay().

Gerente do EVS

O EVS Manager fornece a interface pública ao sistema EVS para coletar e apresentar visualizações de câmeras externas. 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 EVS principal é o primeiro cliente do EVS Manager e o único cliente permitido a gravar dados de exibição. Outros clientes podem receber acesso somente leitura às imagens da câmera.

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

Diagrama do EVS Manager e da API de hardware do EVS.
Figura 2. O EVS Manager espelha a API de hardware EVS subjacente.

Os apps não veem diferenças ao operar pela implementação da HAL de hardware do EVS ou pela API EVS Manager, exceto que a API EVS Manager permite acesso simultâneo ao fluxo de câmera. O EVS Manager é 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 demais chamadas são idênticas às descrições da HAL do EVS.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Recebe 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, desde que haja recursos suficientes do sistema, uma câmera já aberta pode ser aberta novamente por outro processo, permitindo a ramificação do fluxo de vídeo para vários apps consumidores. As strings camera_id na camada do EVS Manager são as mesmas informadas à camada de hardware do EVS.

IEvsCamera

A implementação IEvsCamera fornecida pelo EVS Manager é virtualizada internamente. Assim, as operações em uma câmera por um cliente não afetam outros clientes, que mantêm acesso independente às próprias câmeras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Inicia streams de vídeo. Os clientes podem iniciar e interromper transmissões 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 pelo tempo que quiser. Quando a contagem de frames mantida por um cliente atinge o limite configurado, ele não recebe mais frames até retornar um. Essa ação 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 stream de vídeo a qualquer momento sem afetar os outros. O stream de câmera na camada de hardware é interrompido quando o último cliente de uma determinada câmera para o stream.

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

Envia um valor específico do motorista, permitindo que um cliente afete outro. Como o EVS Manager 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 um proprietário do display, mesmo no nível do EVS Manager. O Manager não adiciona funcionalidade e simplesmente passa a interface IEvsDisplay diretamente para a implementação da HAL subjacente.

App EVS

O Android inclui uma implementação de referência nativa em C++ de um app EVS que se comunica com o EVS Manager e a HAL do veículo para fornecer funções básicas de câmera traseira. O app deve ser iniciado bem 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 estado da seta). Os OEMs podem modificar ou substituir o app EVS pela própria lógica e apresentação específicas do veículo.

Figura 3. Lógica de exemplo do app EVS, receba a lista de câmeras.


Figura 4. Lógica de exemplo do app EVS, receber callback de frame.

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

Por exemplo, o app pode mover os dados do pixel, talvez com uma operação de escala ou rotação inline. O app também pode usar a imagem de origem como uma textura OpenGL e renderizar uma cena complexa no 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 em um único frame de saída (como para uso em uma visualização virtual de cima para baixo do entorno do veículo).

Usar o EGL/SurfaceFlinger no EVS Display HAL

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

Uma implementação de referência do HAL do EVS 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 é classificado como VNDK-private, que se refere a um grupo de bibliotecas disponíveis para bibliotecas VNDK que os processos do fornecedor não podem usar. Como as implementações de HAL precisam estar na partição do fornecedor, os fornecedores não podem usar Surface nas implementações de HAL.

Como criar 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 uma meta de build adicional no script de build. Essa segmentação é exatamente igual à segmentação 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: as segmentações por fornecedor são criadas com a macro NO_INPUT, que remove uma palavra de 32 bits dos dados do pacote. Como o SurfaceFlinger espera esse campo 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 binder na implementação da HAL do EVS

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

Domínio 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

Embora o SurfaceFlinger defina interfaces AIDL, os processos do fornecedor só podem usar interfaces HIDL para se comunicar com processos do framework. É necessário um trabalho considerável para converter interfaces AIDL em HIDL. Felizmente, o Android oferece um método para selecionar o driver do binder para libbinder, a que os processos da 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 antes de chamar Process ou IPCThreadState ou antes de fazer qualquer chamada de binder.

Políticas do SELinux

Se a implementação do dispositivo for treble completo, o SELinux vai impedir que os processos do fornecedor usem /dev/binder. Por exemplo, uma implementação de amostra da HAL do EVS é atribuída ao domínio hal_evs_driver e exige 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, adicionar essas permissões causa uma falha de build porque viola as seguintes regras neverallow definidas em system/sepolicy/domain.te para um dispositivo treble completo.

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 uma implementação de referência da HAL do EVS como um processo de fornecedor

Como referência, aplique as seguintes mudanças a packages/services/Car/evs/Android.mk. Confirme se todas as mudanças descritas funcionam na 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;