HAL da câmera veicular

O Android contém uma camada de abstração de hardware (HAL, na sigla em inglês) do HIDL automotivo que permite a captura e a exibição de imagens muito cedo no processo de inicialização do Android e continua funcionando durante toda a vida 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 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 espera ser iniciado pelo init assim que o EVS e o Car Service estiverem disponíveis, atraído dentro de 2 (dois) segundos após ligar. 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 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 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 entregá-los por buffers de memória compartilhada reconhecidos pelo Gralloc. O lado da tela da implementação é responsável por fornecer um buffer de memória compartilhado que pode ser preenchido pelo app (geralmente por renderização de EGL) e apresentar os frames finalizados em preferência a 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 e/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 de 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. Presume-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 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 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 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. Apenas um cliente pode manter 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 desativará todas 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. Futuramente, o app cliente vai perceber 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 por 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 do 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, o que torna indesejável que a implementação da HAL mude espontaneamente os estados de exibição. Se a tela não estiver sendo mantida por nenhum cliente (por uma chamada para openDisplay), essa função vai 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 maneira opaca pela pilha acima.
  • vendor_flags: um método para transmitir informações de câmera especializadas de forma opaca do driver para um app EVS personalizado. Ele é transmitido sem interpretação do driver até 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 CameraDesc desta câmera.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Especifica a profundidade da cadeia do buffer com o qual a câmera precisa oferecer suporte. Até esse número de frames pode ser mantido simultaneamente pelo cliente da IEvsCamera. Se esses frames tiverem sido entregues ao receptor sem serem retornados por doneWithFrame, o stream vai pular os frames até que um buffer seja retornado para reutilização. Essa chamada pode ser feita a qualquer momento, mesmo quando os streams já estão em execução. Nesse caso, os buffers precisam ser adicionados ou removidos da cadeia conforme apropriado. Se nenhuma chamada for feita para esse ponto de entrada, o IEvsCamera aceita pelo menos um frame por padrão, com valores mais aceitáveis.

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 entregues em até 500 ms após a chamada startVideoStream e, após o início, precisam ser gerados com pelo menos 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 precisa ser retornado. Caso contrário, o valor "OK" será retornado.

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 o código de erro apropriado, possivelmente incluindo 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 falhas. 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 pela 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. Sinalizações de uso definidas pela implementação da HAL. Espera-se que os clientes do HAL transmitam essas flags sem modificações. Para saber mais, consulte as 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 da HAL pode optar por armazenar um identificador de buffer do 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 por 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, significando o fim do stream, e nenhum outro frame será entregue. O bufferHandle NULL não precisa ser enviado de volta por 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 Evs, controla o estado da tela 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 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 iniciar no estado NOT_VISIBLE. Depois disso, o cliente deve solicitar o estado VISIBLE_ON_NEXT_FRAME e começar a fornecer o 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.

Ele é válido para que qualquer estado seja 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 tipo enumerado não reconhecido. Nesse caso, INVALID_ARG é retornado.

getDisplayState() generates (DisplayState state);

Recebe o estado de exibição. 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, 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 buffer de frame associado à exibição. Esse buffer pode ser bloqueado e gravado por software e/ou GL. Esse buffer precisa ser retornado por uma chamada para returnTargetBufferForDisplay(), mesmo que a tela não esteja mais visível.

Embora os formatos de buffer reservados sejam tecnicamente possíveis, o teste de compatibilidade exige que o buffer esteja em um dos quatro formatos com suporte: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Intercalado, RGBA (32 bits R:GB:B:B:B). 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 exibição. Somente buffers recuperados por meio de uma chamada para getTargetBuffer() são válidos para uso com essa chamada, e o conteúdo de 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 de forma exclusiva a tela. Pode ser o nome do dispositivo com kernel ou um nome para o dispositivo, como rearview. O valor dessa string é escolhido pela implementação da HAL e usado de maneira opaca pela pilha acima.
  • vendor_flags: um método para transmitir informações de câmera especializadas de forma opaca do driver para um app EVS personalizado. Ele é transmitido sem interpretação do driver até 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 desativado (não visível para o motorista) ou ativado (mostrando 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 pela 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).

Diagrama da API EVS Manager e EVS Hardware.

Figura 2. O EVS Manager reflete a API Hardware do EVS.

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 (estendido) diferente na implementação do gerenciador de EVS. As demais chamadas são idênticas às descrições do HAL do 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 do gerenciador do EVS são as mesmas informadas para a camada do hardware do EVS.

IEvsCamera

A implementação do EVS Manager para IEvsCamera é virtualizada 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 de base é 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 retida por um cliente atingir o limite configurado, ele não receberá mais frames até retornar um. Esse salto de frame não afeta outros clientes, que continuam a receber todos os frames conforme 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 consegue entender 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 Manager não adiciona nenhuma funcionalidade e simplesmente transmite 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 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. Exemplo de lógica do app EVS, receber a lista de câmeras.



Figura 4. Exemplo de lógica do app EVS, receber o callback de 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 mesclar elas no único frame de saída (por exemplo, para uso em uma visualização virtual de cima para baixo do entorno 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 de 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: as segmentações do 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 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 em uma 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 do 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, 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 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 de build porque viola as seguintes regras de neverallow definidas em system/sepolicy/domain.te para um dispositivo de agudos completos.

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 de EVS HAL como um processo de fornecedor

Como referência, é possível aplicar as mudanças abaixo a packages/services/Car/evs/Android.mk. Confirme se todas as mudanças 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;