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:
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, eRGBA
ouBGRA
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 aGralloc.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).
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;