Câmera do veículo HAL

O Android contém uma camada de abstração de hardware HIDL (HAL) automotiva que fornece captura e exibição de imagens muito cedo no 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 externa (EVS) e normalmente é usado para oferecer suporte a câmeras de visão traseira e telas de visão surround em veículos com sistemas de Infotainment In-Vehicle (IVI) baseados em Android. O EVS também permite que recursos avançados sejam implementados em aplicativos de usuário.

O Android também inclui uma interface de driver de captura e exibição específica para EVS (em /hardware/interfaces/automotive/evs/1.0 ). Embora seja possível construir um aplicativo de câmera de visão traseira em cima da câmera Android existente e serviços de exibição, 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

O EVS inclui os seguintes componentes do sistema:

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

Aplicação EVS

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

Gerente de EVS

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

Interface EVS HIDL

O sistema EVS, tanto a câmera quanto os elementos de exibição, é definido no pacote android.hardware.automotive.evs . Uma implementação de exemplo que exercita a interface (gera imagens de teste sintéticas e valida as imagens fazendo 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 . Tais implementações são responsáveis ​​por configurar e coletar dados de câmeras físicas e entregá-los por meio 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 via renderização EGL) e apresentar os quadros finalizados em preferência a qualquer outra coisa que possa aparecer na exibição física. As implementações de 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 monitor existentes. A reutilização de drivers pode ser vantajosa, especialmente para drivers de exibição em que a apresentação da imagem 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 para seu hardware.

IEvsEnumerator

Este objeto é responsável por enumerar o hardware 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 desligamento 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 maneira 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 de 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 no momento. Semelhante ao comportamento aberto agressivo descrito em openCamera , um novo objeto IEvsDisplay pode ser criado a qualquer momento e desabilitará qualquer instância anterior. Instâncias inválidas continuam a existir e respondem a chamadas de função de seus proprietários, mas não devem executar operações de mutação quando inativas. Eventualmente, espera-se que o aplicativo cliente observe 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() ). Os buffers pendentes recebidos por meio de chamadas getTargetBuffer() devem ser retornados ao display antes de fechar o display.

getDisplayState() generates (DisplayState state);

Obtém o estado de exibição atual. A implementação HAL deve relatar o estado atual real, que pode diferir do estado solicitado mais recentemente. A lógica responsável por alterar os estados de exibição deve existir acima da camada do dispositivo, tornando indesejável para a implementação HAL alterar espontaneamente os estados de exibição. Se a exibição não for mantida atualmente por nenhum cliente (por uma chamada para openDisplay), essa função retornará NOT_OPEN . Caso contrário, ele relata o estado atual do EVS Display (consulte IEvsDisplay API ).

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 retrovisor . O valor para esta 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. Ele é passado sem interpretação do driver para o aplicativo EVS, que é livre para ignorá-lo.

IEvsCamera

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 deve suportar. Até isso, muitos quadros podem ser mantidos simultaneamente pelo cliente do IEvsCamera. Se esses muitos quadros tiverem sido entregues ao receptor sem serem retornados por doneWithFrame , o fluxo pulará quadros até que um buffer seja retornado para reutilização. É legal que essa chamada venha a qualquer momento, mesmo enquanto os fluxos já estiverem em execução, caso em que os buffers devem ser adicionados ou removidos da cadeia conforme apropriado. Se nenhuma chamada for feita para este 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 ajustado.

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 quadros devem começar a ser entregues dentro de 500ms da chamada startVideoStream e, após o início, devem ser gerados em um mínimo de 10 FPS. O tempo necessário para iniciar o fluxo de vídeo conta efetivamente com 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 é retornado.

oneway doneWithFrame(BufferDesc buffer);

Retorna um quadro que foi entregue pelo IEvsCameraStream. Ao terminar de consumir um quadro entregue à interface IEvsCameraStream, o quadro deve ser devolvido 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 estiver esgotado, nenhum outro quadro será entregue até que um buffer seja retornado, potencialmente resultando em quadros ignorados (um buffer com um identificador nulo indica o fim de um stream e não precisa ser retornado através desta 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 quadros de câmera EVS. Como a entrega é assíncrona, os quadros podem continuar a chegar por algum tempo após o retorno dessa chamada. Cada quadro deve ser retornado até que o fechamento do fluxo seja sinalizado para o IEvsCameraStream. É legal chamar stopVideoStream em um stream que já foi interrompido ou nunca iniciado, nesses casos ele é 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. Essa extensão é fornecida apenas para facilitar extensões específicas do veículo e nenhuma implementação de HAL deve exigir que essa chamada funcione em um estado padrão. Caso o driver reconheça e aceite os valores, deve ser devolvido OK; caso contrário, INVALID_ARG ou outro código de erro representativo deve 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. A unidade 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 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. Expresso em pixels para corresponder à convenção adotada por 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ário para percorrer as linhas na 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 da câmera e RGBA ou BGRA devem ser preferidos para exibição.
  • usage . Sinalizadores de uso definidos pela implementação HAL. Espera-se que os clientes HAL os passem sem modificações (para obter detalhes, consulte os 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 para o 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 essa interface para receber entregas de quadros de vídeo assíncronas.

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 fluxo de vídeo é interrompido por meio de uma chamada para IEvsCamera::stopVideoStream() , esse retorno de chamada pode continuar à medida que o pipeline é drenado. Cada quadro ainda deve ser devolvido; quando o último quadro do fluxo for entregue, um bufferHandle NULL será entregue, significando o fim do fluxo e não ocorrerão mais entregas de quadros. O próprio bufferHandle NULL não precisa ser enviado de volta via doneWithFrame() , mas todos os outros handles devem ser retornados

Embora os formatos de buffer proprietários sejam tecnicamente possíveis, o teste de compatibilidade exige que o buffer esteja em um dos quatro formatos suportados: NV21 (YCrCb 4:2:0 Semiplanar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 intercalado), RGBA (32 bits R:G:B:x), BGRA (32 bits B:G:R:x). 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 de bufferId são essencialmente privados para a implementação do driver HAL e ele pode usá-los (e reutilizá-los) como achar melhor.

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 a exibição 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 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, a exibição é definida para iniciar no estado NOT_VISIBLE , após o qual o cliente deve solicitar o estado VISIBLE_ON_NEXT_FRAME e começar a fornecer vídeo. Quando a exibição não é 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 ser solicitado a qualquer momento. Se a exibição já estiver visível, ela deve permanecer visível se definida como VISIBLE_ON_NEXT_FRAME . Sempre retorna OK, a menos que o estado solicitado seja um valor de enumeração não reconhecido, caso em que INVALID_ARG é retornado.

getDisplayState() generates (DisplayState state);

Obtém o estado de exibição. A implementação HAL deve relatar o estado atual real, que pode diferir do estado solicitado mais recentemente. A lógica responsável por alterar os estados de exibição deve existir acima da camada do dispositivo, tornando indesejável para a implementação HAL alterar 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. Esse buffer deve ser retornado por meio de uma chamada para returnTargetBufferForDisplay() mesmo se a exibição não estiver mais visível.

Embora os formatos de buffer proprietários sejam tecnicamente possíveis, o teste de compatibilidade exige que o buffer esteja em um dos quatro formatos suportados: NV21 (YCrCb 4:2:0 Semiplanar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 intercalado), RGBA (32 bits R:G:B:x), BGRA (32 bits B:G:R:x). 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 passado de volta para returnTargetBufferForDisplay .

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Diz ao monitor que o buffer está pronto para exibição. Apenas 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 essa chamada, o buffer não é mais válido para uso do cliente. Retorna OK em caso de sucesso ou o código de erro apropriado possivelmente incluindo INVALID_ARG ou BUFFER_NOT_AVAILABLE .

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

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

  • display_id . Uma string que identifica exclusivamente a exibição. Este pode ser o nome do dispositivo do kernel do dispositivo ou um nome para o dispositivo, como retrovisor . O valor para esta 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. Ele é passado sem interpretação do driver até o aplicativo EVS, que é livre para 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 EVS, que pode ser desabilitado (não visível para o driver) ou habilitado (mostrando uma imagem para o driver). 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() .

Gerente de EVS

O EVS Manager fornece a interface pública para o sistema EVS para coletar e apresentar visualizações de câmeras externas. Onde 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 aplicativo EVS primário é o primeiro cliente do EVS Manager 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 por meio do EVS Manager e receber um fluxo de vídeo).

EVS Manager e diagrama de 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 de HAL de hardware do EVS ou da API do EVS Manager, exceto que a API do EVS Manager permite acesso simultâneo ao fluxo da câmera. O EVS Manager é, ele próprio, o único cliente permitido da camada EVS Hardware HAL, e atua como um proxy para o EVS Hardware HAL.

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

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

IEvsCamera

A implementação de 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 retêm acesso independente às suas câmeras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Inicia fluxos de vídeo. Os clientes podem iniciar e interromper independentemente fluxos de vídeo 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 seus quadros quando terminar, mas é permitido manter seus quadros pelo tempo que desejar. Quando a contagem de quadros mantida por um cliente atinge seu limite configurado, ele não receberá mais quadros até retornar um. Esse salto de quadro não afeta outros clientes, que continuam a receber todos os quadros conforme o 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, potencialmente permitindo que um cliente afete outro cliente. Como o EVS Manager não consegue entender as implicações das palavras de controle definidas pelo fornecedor, elas não são virtualizadas e quaisquer efeitos colaterais se aplicam a todos os clientes de uma determinada câmera. 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 um proprietário do monitor é permitido, mesmo no nível do EVS Manager. O Manager não adiciona nenhuma funcionalidade e simplesmente passa a interface IEvsDisplay diretamente para a implementação HAL subjacente.

Aplicação EVS

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 de ré. Espera-se que o aplicativo seja iniciado muito cedo no processo de inicialização do sistema, com vídeo adequado exibido dependendo das câmeras disponíveis e do estado do carro (estado da marcha e do sinal de direção). Os OEMs podem modificar ou substituir o aplicativo EVS com sua própria lógica e apresentação específica do veículo.

Figura 3. Lógica de amostra do aplicativo EVS, obter lista de câmeras.


Figura 4. Lógica de amostra do aplicativo EVS, receber retorno de chamada do quadro.

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 em linha 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 para o 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 o EGL para renderizar uma implementação de 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 de HAL devem residir na partição do fornecedor, os fornecedores são impedidos de usar o Surface em implementações de HAL.

Construindo libgui para processos de fornecedores

O uso de libgui serve como a única opção para usar EGL/SurfaceFlinger em implementações de EVS Display HAL. A maneira mais direta de implementar libgui é através de frameworks/native/libs/gui diretamente usando um alvo de compilação adicional no script de compilação. Este destino é exatamente o mesmo que o destino 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 lote. Como o SurfaceFlinger espera este campo que foi removido, o SurfaceFlinger falha ao analisar o lote. 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);

As instruções de compilação de amostra são fornecidas 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

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

No Android 8 (e superior), o nó de 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 devem converter qualquer interface AIDL em HIDL. Para aqueles que desejam continuar usando interfaces AIDL entre processos de fornecedores, 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 estrutura/fornecedor com interfaces HIDL
IPC entre processos de fornecedores 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 da estrutura. Uma quantidade não trivial de trabalho é necessária para converter as interfaces AIDL existentes em HIDL. Felizmente, o Android fornece um método para selecionar o driver do binder para libbinder , ao qual os processos da biblioteca de 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 devem chamar isso 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 full treble, o SELinux impede 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 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 capturar 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)

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

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