Câmera do veículo HAL

O Android contém uma camada de abstração de hardware (HAL) HIDL automotiva que fornece captura e exibição de imagens logo no início do processo de inicialização do Android e continua funcionando durante a vida útil do sistema. O HAL inclui a pilha do sistema de visão exterior (EVS) e normalmente é usado para oferecer suporte a câmeras retrovisoras e exibições de visão surround em veículos com sistemas de infoentretenimento no veículo (IVI) baseados em Android. O EVS também permite que recursos avançados sejam implementados em aplicativos de usuários.

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 aplicativo de câmera retrovisor sobre os serviços de câmera e exibição do Android existentes, esse aplicativo provavelmente seria executado muito tarde no processo de inicialização do Android. O uso de um HAL dedicado permite uma interface simplificada e deixa claro o que um OEM precisa implementar para dar suporte à pilha EVS.

Componentes do sistema

EVS inclui os seguintes componentes do sistema:

Diagrama de componentes do sistema EVS

Figura 1. Visão geral dos componentes do sistema SVE.

Aplicativo SVE

Um exemplo de aplicativo C++ EVS ( /packages/services/Car/evs/app ) serve como implementação de referência. Este aplicativo é responsável por solicitar frames de vídeo ao Gerenciador EVS e enviar frames finalizados para exibição de volta ao Gerenciador EVS. Ele espera ser iniciado pelo init assim que o EVS e o Car Service estiverem disponíveis, dentro de dois (2) segundos após ser ligado. Os OEMs podem modificar ou substituir o aplicativo EVS conforme desejado.

Gestor de SVE

O Gerenciador EVS ( /packages/services/Car/evs/manager ) fornece os blocos de construção necessários para um aplicativo EVS implementar qualquer coisa, desde uma simples exibição de câmera retrovisora ​​até uma renderização multicâmera 6DOF. Sua interface é apresentada através de HIDL e foi construída para aceitar múltiplos clientes simultâneos. Outras aplicações e serviços (especificamente o Car Service) podem consultar o estado do Gestor do SVE para saber quando o sistema SVE está ativo.

Interface EVS-HIDL

O sistema EVS, tanto a câmera quanto os elementos de exibição, são definidos no pacote android.hardware.automotive.evs . Um exemplo de implementação que exercita a interface (gera imagens de teste sintéticas e valida as imagens na viagem de ida e volta) é fornecido 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 . Tais implementações são responsáveis ​​por configurar e coletar dados de câmeras físicas e entregá-los através de buffers de memória compartilhada reconhecíveis pelo Gralloc. O lado de exibição da implementação é responsável por fornecer um buffer de memória compartilhada que pode ser preenchido pelo aplicativo (geralmente por meio de renderização EGL) e apresentar os quadros finalizados em preferência a qualquer outra coisa que queira aparecer na exibição 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 de kernel

Um dispositivo que suporte a pilha EVS requer drivers de kernel. Em vez de criar novos drivers, os OEMs têm a opção de oferecer suporte aos recursos exigidos pelo EVS por meio de drivers de hardware de câmera e/ou vídeo existentes. A reutilização de drivers pode ser vantajosa, especialmente para drivers de vídeo onde a apresentação de imagens pode exigir coordenação com outros threads ativos. O Android 8.0 inclui um driver de amostra baseado em v4l2 (em packages/services/Car/evs/sampleDriver ) que depende do kernel para suporte a v4l2 e do SurfaceFlinger para apresentar a imagem de saída.

Descrição da interface de hardware EVS

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

IEvsEnumerador

Este objeto é responsável por enumerar o hardware do 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 contendo descrições de todas as câmeras do sistema. Supõe-se que o conjunto de câmeras seja fixo e reconhecível no momento da inicialização. Para obter detalhes sobre as descrições das câmeras, 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 camera_id exclusiva. 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 ao encerramento do aplicativo, a reabertura de uma câmera deve encerrar a instância anterior para que a nova solicitação possa ser atendida. Uma instância de câmera que foi preemptada dessa forma deve 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() ). O fluxo de vídeo da câmera deve ser interrompido chamando stopVideoStream() antes de chamar closeCamera .

openDisplay() generates (IEvsDisplay display);

Obtém um objeto de interface utilizado para interagir exclusivamente com o display EVS do sistema. Apenas um cliente pode manter uma instância funcional de IEvsDisplay por vez. Semelhante ao comportamento agressivo de abertura 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 a responder às chamadas de função de seus proprietários, mas não devem realizar operações de mutação quando mortas. Eventualmente, espera-se que o aplicativo cliente perceba os códigos de retorno de erro OWNERSHIP_LOST e feche e libere a interface inativa.

closeDisplay(IEvsDisplay display);

Libera a interface IEvsDisplay (e é o oposto da chamada openDisplay() ). Buffers pendentes recebidos por meio de chamadas getTargetBuffer() devem ser retornados ao display antes de fechá-lo.

getDisplayState() generates (DisplayState state);

Obtém o estado de exibição atual. A implementação do HAL deve relatar o estado atual real, que pode diferir do estado solicitado mais recentemente. A lógica responsável pela alteração dos estados de exibição deve existir acima da camada do dispositivo, tornando indesejável que a implementação HAL altere espontaneamente os estados de exibição. Se a exibição não estiver atualmente mantida por nenhum cliente (por uma chamada para openDisplay), esta função retornará NOT_OPEN . Caso contrário, ele relata o estado atual do EVS Display (consulte 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 kernel do dispositivo ou um nome para o dispositivo, como rearview . O valor desta string é escolhido pela implementação HAL e usado de forma opaca pela pilha acima.
  • vendor_flags . Um método para passar informações especializadas da câmera de forma opaca do driver para um aplicativo EVS personalizado. É transmitido sem interpretação desde o condutor até à aplicação EVS, que pode ignorá-lo.

Câmera IEvs

Este objeto representa uma única câmera e é a interface principal para captura de 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 deverá suportar. Até esse número de quadros podem ser mantidos simultaneamente pelo cliente do IEvsCamera. Se esse número de quadros tiver sido entregue ao receptor sem ser retornado por doneWithFrame , o fluxo ignorará os quadros até que um buffer seja retornado para reutilização. É legal que esta chamada ocorra a qualquer momento, mesmo enquanto os fluxos já estão em execução, caso em que os buffers devem ser adicionados ou removidos da cadeia, conforme apropriado. Se nenhuma chamada for feita para esse ponto de entrada, o IEvsCamera suporta pelo menos um quadro 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. Neste caso o sistema continua operando com o valor previamente configurado.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Solicita a entrega de quadros de câmera EVS desta câmera. O IEvsCameraStream começa a receber chamadas periódicas com novos quadros de imagem até que stopVideoStream() seja chamado. Os frames devem começar a ser entregues dentro de 500 ms da chamada startVideoStream e após o início, devem ser gerados a no mínimo 10 FPS. O tempo necessário para iniciar o fluxo de vídeo conta efetivamente em relação a qualquer requisito de tempo de inicialização da câmera retrovisora. Se o fluxo não for iniciado, um código de erro deverá ser retornado; caso contrário, OK será retornado.

oneway doneWithFrame(BufferDesc buffer);

Retorna um quadro que foi entregue ao IEvsCameraStream. Ao terminar de consumir um quadro entregue à interface IEvsCameraStream, o quadro deve ser retornado ao IEvsCamera para reutilização. Um número pequeno e finito de buffers está disponível (possivelmente tão pequeno quanto um) e, se o suprimento se esgotar, nenhum outro quadro será entregue até que um buffer seja retornado, resultando potencialmente em quadros ignorados (um buffer com identificador nulo indica o final de um fluxo e não precisa ser retornado por meio desta função). Retorna OK em caso de sucesso ou código de erro apropriado, potencialmente incluindo INVALID_ARG ou BUFFER_NOT_AVAILABLE .

stopVideoStream();

Interrompe a entrega de quadros de câmeras EVS. Como a entrega é assíncrona, os quadros podem continuar chegando por algum tempo após o retorno da chamada. Cada quadro deve ser retornado até que o fechamento do stream seja sinalizado para o IEvsCameraStream. É legal chamar stopVideoStream em um stream que já foi interrompido ou nunca iniciado e, nesses casos, é ignorado.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Solicita informações específicas do driver da implementação HAL. Os valores permitidos para opaqueIdentifier são específicos do driver, mas nenhum valor passado pode travar o driver. O driver deve 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 HAL. Esta extensão é fornecida apenas para facilitar extensões específicas de veículos e nenhuma implementação HAL deve exigir que esta chamada funcione em um estado padrão. Caso o driver reconheça e aceite os valores, OK deverá ser retornado; caso contrário, INVALID_ARG ou outro código de erro representativo deverá 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 passada pela API. O drive HAL é responsável por preencher esta estrutura para descrever o buffer de imagem e o cliente HAL deve tratar esta estrutura como somente leitura. Os campos contêm informações suficientes para permitir que o cliente reconstrua um objeto ANativeWindowBuffer , conforme pode ser necessário para usar a imagem com EGL por meio da 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 realmente ocupa na memória, contabilizando qualquer preenchimento para alinhamento de linhas. Expressado em pixels para corresponder à convenção adotada pelo gralloc para suas descrições de buffer.
  • pixelSize . Número de bytes ocupados por cada pixel individual, permitindo o cálculo do tamanho em bytes necessários para alternar entre as linhas da imagem ( stride em bytes = stride em pixels * pixelSize ).
  • format . O formato de pixel usado pela imagem. O formato fornecido deve ser compatível com a implementação OpenGL da plataforma. Para passar no teste de compatibilidade, HAL_PIXEL_FORMAT_YCRCB_420_SP deve ser preferido para uso de câmera e RGBA ou BGRA deve ser preferido para exibição.
  • usage . Sinalizadores de uso definidos pela implementação HAL. Espera-se que os clientes HAL passem estes não modificados (para obter detalhes, consulte sinalizadores relacionados ao Gralloc.h ).
  • bufferId . Um valor exclusivo especificado pela implementação HAL para permitir que um buffer seja reconhecido após uma viagem de ida e volta pelas APIs HAL. O valor armazenado neste campo pode ser escolhido arbitrariamente pela implementação HAL.
  • memHandle . O identificador do buffer de memória subjacente que contém os dados da imagem. A implementação HAL pode optar por armazenar um identificador de buffer Gralloc aqui.

IEvsCameraStream

O cliente implementa esta interface para receber entregas assíncronas de quadros de vídeo.

deliverFrame(BufferDesc buffer);

Recebe chamadas do HAL sempre que um quadro de vídeo está pronto para inspeção. Os identificadores de buffer recebidos por este método devem ser retornados por meio de chamadas para IEvsCamera::doneWithFrame() . Quando o stream de vídeo é interrompido por meio de uma chamada para IEvsCamera::stopVideoStream() , esse retorno de chamada pode continuar enquanto o pipeline é drenado. Cada quadro ainda deve ser retornado; quando o último quadro do fluxo for entregue, um bufferHandle NULL será entregue, significando o fim do fluxo e nenhuma outra entrega de quadros ocorrerá. O bufferHandle NULL em si não precisa ser enviado de volta via doneWithFrame() , mas todos os outros identificadores devem 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 suportados: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 intercalado), RGBA (R:G:B:x de 32 bits), BGRA (B:G:R:x de 32 bits). O formato selecionado deve ser uma fonte de textura GL válida na implementação GLES da plataforma.

O aplicativo não deve 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 ele pode usá-los (e reutilizá-los) conforme achar adequado.

IEvsDisplay

Este objeto representa a exibição Evs, controla o estado da exibição e trata da apresentação real das imagens.

getDisplayInfo() generates (DisplayDesc info);

Retorna informações básicas sobre o display EVS fornecido pelo sistema (ver 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 HAL deve aceitar normalmente uma solicitação para qualquer estado enquanto estiver em qualquer outro estado, embora a resposta possa ser ignorar a solicitação.

Após a inicialização, o display é definido para iniciar no estado NOT_VISIBLE , após o qual o cliente deverá solicitar o estado VISIBLE_ON_NEXT_FRAME e começar a fornecer vídeo. Quando a exibição não for mais necessária, espera-se que o cliente solicite o estado NOT_VISIBLE após passar o último quadro de vídeo.

É válido para qualquer estado para ser solicitado a qualquer momento. Se a exibição já estiver visível, ela deverá permanecer visível se definida como VISIBLE_ON_NEXT_FRAME . Sempre retorna OK, a menos que o estado solicitado seja um valor enum não reconhecido; nesse caso, INVALID_ARG é retornado.

getDisplayState() generates (DisplayState state);

Obtém o estado de exibição. A implementação do HAL deve relatar o estado atual real, que pode diferir do estado solicitado mais recentemente. A lógica responsável pela alteração dos estados de exibição deve existir acima da camada do dispositivo, tornando indesejável que a implementação HAL altere espontaneamente os estados de exibição.

getTargetBuffer() generates (handle bufferHandle);

Retorna um identificador para um buffer de quadros associado à exibição. Este buffer pode ser bloqueado e gravado por software e/ou GL. Este buffer deve ser retornado por meio de uma chamada para returnTargetBufferForDisplay() mesmo que a exibição 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 suportados: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 intercalado), RGBA (R:G:B:x de 32 bits), BGRA (B:G:R:x de 32 bits). O formato selecionado deve 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 retornado para returnTargetBufferForDisplay .

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Informa ao display 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 esta chamada, e o conteúdo do BufferDesc não pode ser modificado pelo aplicativo cliente. Após esta chamada, o buffer não é mais válido para uso pelo cliente. Retorna OK em caso de sucesso ou código de erro apropriado, potencialmente incluindo INVALID_ARG ou BUFFER_NOT_AVAILABLE .

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

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

  • display_id . Uma string que identifica exclusivamente a exibição. Este pode ser o nome do dispositivo kernel do dispositivo ou um nome para o dispositivo, como rearview . O valor desta string é escolhido pela implementação HAL e usado de forma opaca pela pilha acima.
  • vendor_flags . Um método para passar informações especializadas da câmera de forma opaca do driver para um aplicativo EVS personalizado. É transmitido sem interpretação desde o condutor até à aplicação 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 do display do EVS, que pode ser desabilitado (não visível ao motorista) ou habilitado (mostrando uma imagem ao motorista). Inclui um estado transitório em que a exibição ainda não está visível, mas está preparada para se tornar visível com a entrega do próximo quadro de imagens por meio da chamada returnTargetBufferForDisplay() .

Gestor de SVE

O Gestor EVS fornece a interface pública ao sistema EVS para recolher e apresentar imagens de câmaras externas. Onde os drivers de hardware permitem apenas uma interface ativa por recurso (câmera ou monitor), o EVS Manager facilita o acesso compartilhado às câmeras. Um único aplicativo EVS primário é o primeiro cliente do Gerenciador EVS e é o único cliente com permissão para gravar dados de exibição (clientes adicionais podem receber acesso somente leitura às imagens da câmera).

O EVS Manager implementa a mesma API que os drivers HAL subjacentes e fornece serviço expandido, suportando vários clientes simultâneos (mais de um cliente pode abrir uma câmera através do EVS Manager e receber um fluxo de vídeo).

Diagrama do Gerenciador EVS e da API de Hardware EVS.

Figura 2. O EVS Manager espelha a API de hardware EVS subjacente.

Os aplicativos não veem diferenças ao operar por meio da implementação HAL de hardware EVS ou da API do Gerenciador EVS, exceto que a API do Gerenciador EVS permite acesso simultâneo ao fluxo de câmera. O EVS Manager é, por si só, o cliente permitido da camada EVS Hardware HAL, e atua como proxy para o EVS Hardware HAL.

As secções seguintes descrevem apenas as chamadas que têm um comportamento diferente (estendido) na implementação do Gestor EVS; as chamadas restantes são idênticas às descrições do EVS HAL.

IEvsEnumerador

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 camera_id exclusiva. Retorna um NULL em caso de falha. Na camada do Gerenciador EVS, desde que recursos de sistema suficientes estejam disponíveis, uma câmera que já esteja aberta poderá ser aberta novamente por outro processo, permitindo a transferência do fluxo de vídeo para vários aplicativos de consumo. As strings camera_id na camada EVS Manager são as mesmas relatadas para a camada EVS Hardware.

Câmera IEvs

A implementação do IEvsCamera fornecida pelo EVS Manager é 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 suas câmeras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Inicia fluxos de vídeo. Os clientes podem iniciar e parar fluxos de vídeo de forma independente na mesma câmera subjacente. A câmera subjacente é iniciada quando o primeiro cliente é iniciado.

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

Retorna um quadro. Cada cliente deve devolver suas molduras quando terminar, mas pode mantê-las pelo tempo que desejar. Quando a contagem de quadros mantida por um cliente atingir o limite configurado, ele não receberá mais quadros até retornar um. Esse salto de quadro não afeta outros clientes, que continuam recebendo todos os quadros conforme esperado.

stopVideoStream();

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

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

Envia um valor específico do driver, permitindo potencialmente que um cliente afete outro cliente. Como o Gestor EVS não consegue compreender as implicações das palavras de controlo definidas pelo fornecedor, estas não são virtualizadas e quaisquer efeitos secundários aplicam-se a todos os clientes de uma determinada câmara. Por exemplo, se um fornecedor usasse essa chamada para alterar as taxas de quadros, todos os clientes da câmera da camada de hardware afetada receberiam quadros na nova taxa.

IEvsDisplay

Apenas é permitido um proprietário do monitor, mesmo ao nível do Gestor EVS. O Manager não adiciona nenhuma funcionalidade e simplesmente passa a interface IEvsDisplay diretamente para a implementação HAL subjacente.

Aplicativo SVE

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

Figura 3. Exemplo de lógica do aplicativo EVS, obter lista de câmeras.



Figura 4. Exemplo de lógica do aplicativo EVS, retorno de chamada de quadro de recebimento.

Como os dados da imagem são apresentados ao aplicativo em um buffer gráfico padrão, o aplicativo é 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 aplicativo renderizar a imagem no buffer de exibição da maneira que desejar.

Por exemplo, o aplicativo pode optar por mover os próprios dados de pixel, potencialmente com uma escala inline ou operação de rotação. O aplicativo também pode optar por 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 aplicativo mais sofisticado também pode selecionar várias câmeras de entrada simultâneas e mesclá-las em um único quadro de saída (como para uso em uma visão virtual de cima para baixo dos arredores do veículo).

Use o EGL/SurfaceFlinger no EVS Display HAL

Esta seção explica como usar EGL para renderizar uma implementação EVS Display HAL 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 superior), 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 HAL devem residir na partição do fornecedor, os fornecedores são impedidos de usar o Surface em implementações HAL.

Construindo libgui para processos de fornecedores

O uso de libgui serve como a única opção para usar EGL/SurfaceFlinger em implementações EVS Display HAL. A maneira mais direta de implementar libgui é através de frameworks/native/libs/gui diretamente usando um alvo de construção adicional no script de construção. Este alvo é exatamente igual ao alvo 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", ], …

Nota: Os destinos do fornecedor são criados com a macro NO_INPUT , que remove uma palavra de 32 bits dos dados do pacote. Como o SurfaceFlinger espera que esse campo tenha sido removido, o SurfaceFlinger falha ao analisar o pacote. Isso é observado como uma falha 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 esta 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);

Exemplos de instruções de construção são fornecidos abaixo. Espere 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

Use o fichário em uma implementação EVS HAL

No Android 8 (e superior), o nó do dispositivo /dev/binder tornou-se exclusivo para processos de estrutura e, portanto, inacessível para processos de fornecedores. Em vez disso, os processos do fornecedor devem usar /dev/hwbinder e converter quaisquer interfaces AIDL em HIDL. Para aqueles que desejam continuar usando interfaces AIDL entre processos de fornecedores, use o domínio do fichário /dev/vndbinder .

Domínio IPC Descrição
/dev/binder IPC entre processos de estrutura/aplicativo com interfaces AIDL
/dev/hwbinder IPC entre processos de estrutura/fornecedor com interfaces HIDL
IPC entre processos de fornecedores 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 os processos da estrutura. Uma quantidade nada trivial de trabalho é necessária para converter interfaces AIDL existentes em HIDL. Felizmente, o Android fornece um método para selecionar o driver binder para libbinder , ao qual 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));

Nota: Os processos do fornecedor devem chamar isso antes de chamar Process ou IPCThreadState ou antes de fazer qualquer chamada de fichário.

Políticas SELinux

Se a implementação do dispositivo for completa, o SELinux impedirá que os processos do fornecedor usem /dev/binder . Por exemplo, uma implementação de amostra EVS HAL é atribuída ao domínio hal_evs_driver e requer permissões r/w para o 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

Adicionar essas permissões, no entanto, causa uma falha de compilação porque viola as seguintes regras de neverallow definidas em system/sepolicy/domain.te para um dispositivo full-treble.

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. 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)

Construa uma implementação de referência EVS HAL como um processo de fornecedor

Como referência, você pode aplicar as seguintes alterações em packages/services/Car/evs/Android.mk . Certifique-se de confirmar 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;