Android содержит автомобильный уровень абстракции оборудования (HIDL Hardware Abstraction Layer, HAL), который обеспечивает захват и отображение изображений на самых ранних этапах загрузки Android и продолжает функционировать на протяжении всего срока службы системы. HAL включает в себя стек системы внешнего вида (EVS) и обычно используется для поддержки камеры заднего вида и дисплеев кругового обзора в автомобилях с информационно-развлекательными системами In-Vehicle Infotainment (IVI) на базе Android. EVS также позволяет реализовывать расширенные функции в пользовательских приложениях.
Android также включает в себя интерфейс драйвера захвата и отображения, специфичный для EVS (в /hardware/interfaces/automotive/evs/1.0
). Хотя приложение камеры заднего вида можно создать поверх существующих служб камеры и отображения Android, такое приложение, скорее всего, будет запущено слишком поздно в процессе загрузки Android. Использование выделенного HAL обеспечивает оптимизированный интерфейс и чётко показывает, какие действия необходимо реализовать OEM-производителю для поддержки стека EVS.
Компоненты системы
EVS включает в себя следующие системные компоненты:

Приложение EVS
Пример приложения EVS на C++ ( /packages/services/Car/evs/app
) служит эталонной реализацией. Это приложение отвечает за запрос видеокадров из диспетчера EVS и отправку готовых кадров для отображения обратно диспетчеру EVS. Ожидается, что оно будет запущено процессом init, как только EVS и Car Service станут доступны, в течение двух (2) секунд после включения питания. Производители оригинального оборудования могут изменять или заменять приложение EVS по своему усмотрению.
Менеджер EVS
Менеджер EVS ( /packages/services/Car/evs/manager
) предоставляет необходимые компоненты для реализации любых функций приложения EVS: от простого отображения изображения с камеры заднего вида до многокамерного рендеринга с шестью степенями свободы. Интерфейс приложения реализован на языке HIDL и рассчитан на работу с несколькими одновременными клиентами. Другие приложения и сервисы (в частности, Car Service) могут запрашивать состояние менеджера EVS, чтобы узнать, активна ли система EVS.
Интерфейс EVS HIDL
Система EVS, включая камеру и элементы дисплея, определена в пакете android.hardware.automotive.evs
. Пример реализации, проверяющий работу интерфейса (генерирующий синтетические тестовые изображения и проверяющий их прохождение), находится в /hardware/interfaces/automotive/evs/1.0/default
.
OEM-производитель отвечает за реализацию API, выраженного в файлах .hal в каталоге /hardware/interfaces/automotive/evs
. Такие реализации отвечают за настройку и сбор данных с физических камер и их передачу через буферы общей памяти, распознаваемые Gralloc. Дисплейная часть реализации отвечает за предоставление буфера общей памяти, который может быть заполнен приложением (обычно с помощью рендеринга EGL), и отображение готовых кадров в приоритетном порядке по сравнению с любыми другими данными, которые могут быть выведены на физический дисплей. Реализации интерфейса EVS от поставщиков могут храниться в каталогах /vendor/… /device/…
или hardware/…
(например, /hardware/[vendor]/[platform]/evs
).
Драйверы ядра
Для устройства с поддержкой стека EVS требуются драйверы ядра. Вместо создания новых драйверов OEM-производители могут реализовать поддержку функций, необходимых для EVS, через существующие драйверы камеры или дисплея. Повторное использование драйверов может быть полезным, особенно для драйверов дисплея, где отображение изображения может требовать координации с другими активными потоками. В Android 8.0 включен пример драйвера на основе v4l2 (в packages/services/Car/evs/sampleDriver
), который зависит от ядра для поддержки v4l2 и от SurfaceFlinger для отображения выходного изображения.
Описание аппаратного интерфейса EVS
В этом разделе описывается HAL. Ожидается, что поставщики предоставят реализации этого API, адаптированные для своего оборудования.
IEvsEnumerator
Этот объект отвечает за перечисление доступного оборудования EVS в системе (одна или несколько камер и одно устройство отображения).
getCameraList() generates (vec<CameraDesc> cameras);
Возвращает вектор, содержащий описания всех камер в системе. Предполагается, что набор камер фиксирован и доступен на момент загрузки. Подробнее об описаниях камер см. в CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Получает объект интерфейса, используемый для взаимодействия с конкретной камерой, идентифицируемой уникальной строкой camera_id . Возвращает NULL в случае возникновения ошибки. Попытки повторно открыть уже открытую камеру не могут завершиться неудачей. Чтобы избежать состояний гонки, связанных с запуском и завершением работы приложения, повторное открытие камеры должно завершать работу предыдущего экземпляра, чтобы новый запрос мог быть выполнен. Экземпляр камеры, вытесненный таким образом, должен быть переведен в неактивное состояние, ожидая окончательного уничтожения и отвечая на любой запрос, изменяющий состояние камеры, кодом возврата OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Освобождает интерфейс IEvsCamera (и является противоположностью вызову openCamera()
). Видеопоток с камеры необходимо остановить вызовом stopVideoStream()
перед вызовом closeCamera
.
openDisplay() generates (IEvsDisplay display);
Получает объект интерфейса, используемый для эксклюзивного взаимодействия с дисплеем EVS системы. Только один клиент может одновременно содержать функциональный экземпляр IEvsDisplay. Аналогично агрессивному поведению открытия, описанному в openCamera
, новый объект IEvsDisplay может быть создан в любой момент и отключает все предыдущие экземпляры. Невалидированные экземпляры продолжают существовать и отвечать на вызовы функций своих владельцев, но не должны выполнять никаких операций мутации после прекращения работы. В конечном итоге клиентское приложение должно обнаружить коды возврата ошибки OWNERSHIP_LOST
, закрыть и освободить неактивный интерфейс.
closeDisplay(IEvsDisplay display);
Освобождает интерфейс IEvsDisplay (и является противоположностью вызова openDisplay()
). Неиспользованные буферы, полученные с помощью вызовов getTargetBuffer()
должны быть возвращены на дисплей перед его закрытием.
getDisplayState() generates (DisplayState state);
Получает текущее состояние дисплея. Реализация HAL должна сообщать фактическое текущее состояние, которое может отличаться от последнего запрошенного. Логика, отвечающая за изменение состояний дисплея, должна располагаться выше уровня устройства, что исключает возможность спонтанного изменения состояний дисплея реализацией HAL. Если дисплей в данный момент не удерживается ни одним клиентом (вызовом openDisplay), эта функция возвращает NOT_OPEN
. В противном случае она сообщает текущее состояние дисплея EVS (см. API IEvsDisplay ).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
-
camera_id
. Строка, однозначно идентифицирующая данную камеру. Может быть именем устройства ядра или именем устройства, например, rearview . Значение этой строки выбирается реализацией HAL и используется непрозрачно стеком выше. -
vendor_flags
. Метод для непрозрачной передачи специализированной информации с камеры от водителя в пользовательское приложение EVS. Эта информация передается без интерпретации от водителя в приложение EVS, которое может ее игнорировать.
IEvsCamera
Этот объект представляет собой отдельную камеру и является основным интерфейсом для захвата изображений.
getCameraInfo() generates (CameraDesc info);
Возвращает CameraDesc
данной камеры.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Задаёт глубину цепочки буферов, которую камера должна поддерживать. Клиент IEvsCamera может одновременно хранить до указанного количества кадров. Если приемнику доставлено указанное количество кадров, но метод doneWithFrame
не вернул их, поток пропускает кадры до тех пор, пока не будет возвращён буфер для повторного использования. Этот вызов может быть выполнен в любое время, даже если потоки уже запущены. В этом случае буферы следует добавлять или удалять из цепочки по мере необходимости. Если вызов к этой точке входа не производится, IEvsCamera по умолчанию поддерживает как минимум один кадр; допустимо и большее количество.
Если запрошенное значение bufferCount не может быть обработано, функция возвращает BUFFER_NOT_AVAILABLE
или другой соответствующий код ошибки. В этом случае система продолжает работать с ранее установленным значением.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Запрашивает доставку кадров камеры EVS с этой камеры. IEvsCameraStream начинает получать периодические вызовы с новыми кадрами изображения до вызова stopVideoStream()
. Доставка кадров должна начаться в течение 500 мс после вызова startVideoStream
и после начала должна генерироваться с частотой не менее 10 кадров в секунду. Время, необходимое для запуска видеопотока, фактически учитывается при расчете времени запуска камеры заднего вида. Если поток не запущен, должен быть возвращен код ошибки; в противном случае возвращается OK.
oneway doneWithFrame(BufferDesc buffer);
Возвращает кадр, доставленный интерфейсом IEvsCameraStream. После завершения использования кадра, доставленного в интерфейс IEvsCameraStream, этот кадр должен быть возвращён в IEvsCamera для повторного использования. Доступно небольшое, конечное количество буферов (возможно, всего один), и если запас исчерпан, дальнейшие кадры не доставляются до тех пор, пока буфер не будет возвращён, что может привести к пропуску кадров (буфер с нулевым дескриптором обозначает конец потока и не требует возврата этой функцией). Возвращает OK в случае успешного выполнения или соответствующий код ошибки, возможно, включая INVALID_ARG
или BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Останавливает доставку кадров с камеры EVS. Поскольку доставка асинхронная, кадры могут продолжать поступать ещё некоторое время после завершения этого вызова. Каждый кадр должен быть возвращён до тех пор, пока IEvsCameraStream не получит сигнал о закрытии потока. Вызов stopVideoStream
допустим для потока, который уже был остановлен или никогда не запускался; в этом случае он игнорируется.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Запрашивает информацию, специфичную для драйвера, из реализации HAL. Допустимые значения opaqueIdentifier
зависят от драйвера, но ни одно из переданных значений не может привести к сбою драйвера. Драйвер должен возвращать 0 для любого нераспознанного opaqueIdentifier
.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Отправляет значение, специфичное для водителя, в реализацию HAL. Это расширение предоставляется только для поддержки расширений, специфичных для транспортного средства, и ни одна реализация HAL не должна требовать, чтобы этот вызов работал в состоянии по умолчанию. Если водитель распознает и принимает значения, следует вернуть OK; в противном случае следует вернуть INVALID_ARG
или другой репрезентативный код ошибки.
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 }
Описывает изображение, переданное через API. HAL-дисковод отвечает за заполнение этой структуры для описания буфера изображения, а клиент HAL должен рассматривать эту структуру как доступную только для чтения. Поля содержат достаточно информации, чтобы клиент мог реконструировать объект ANativeWindowBuffer
, что может потребоваться для использования изображения с EGL с расширением eglCreateImageKHR()
.
-
width
. Ширина представленного изображения в пикселях. -
height
. Высота представленного изображения в пикселях. -
stride
. Количество пикселей, которое каждая строка фактически занимает в памяти, с учётом отступов для выравнивания строк. Выражается в пикселях в соответствии с соглашением, принятым gralloc для описания буферов. -
pixelSize
. Количество байтов, занимаемое каждым отдельным пикселем, что позволяет вычислить размер в байтах, необходимый для перехода между строками изображения (stride
в байтах =stride
в пикселях *pixelSize
). -
format
. Формат пикселей, используемый изображением. Предоставленный формат должен быть совместим с реализацией OpenGL на платформе. Для прохождения тестирования совместимости для использования с камерой следует выбрать форматHAL_PIXEL_FORMAT_YCRCB_420_SP
, а для отображения —RGBA
илиBGRA
. -
usage
. Флаги использования, устанавливаемые реализацией HAL. Клиенты HAL должны передавать их без изменений (подробности см. в описании флаговGralloc.h
). -
bufferId
. Уникальное значение, заданное реализацией HAL, позволяющее распознавать буфер после прохождения через API HAL. Значение, хранящееся в этом поле, может быть выбрано реализацией HAL произвольно. -
memHandle
. Дескриптор базового буфера памяти, содержащего данные изображения. Реализация HAL может хранить здесь дескриптор буфера Gralloc.
IEvsCameraStream
Клиент реализует этот интерфейс для получения асинхронных поставок видеокадров.
deliverFrame(BufferDesc buffer);
Получает вызовы от HAL каждый раз, когда видеокадр готов к проверке. Дескрипторы буфера, полученные этим методом, должны быть возвращены посредством вызовов IEvsCamera::doneWithFrame()
. При остановке видеопотока вызовом IEvsCamera::stopVideoStream()
, этот обратный вызов может продолжаться по мере опустошения конвейера. Каждый кадр по-прежнему должен быть возвращен; после доставки последнего кадра в потоке возвращается нулевой bufferHandle
, означающий конец потока и прекращение дальнейшей доставки кадров. Сам нулевой буферный bufferHandle
не требуется возвращать с помощью doneWithFrame()
, но все остальные дескрипторы должны быть возвращены.
Хотя использование собственных форматов буферов технически возможно, для тестирования совместимости требуется буфер в одном из четырёх поддерживаемых форматов: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 бит R:G:B:x), BGRA (32 бит B:G:R:x). Выбранный формат должен быть допустимым источником GL-текстуры в реализации GLES платформы.
Приложение не должно полагаться на какое-либо соответствие между полем bufferId
и memHandle
в структуре BufferDesc
. Значения bufferId
по сути являются частными для реализации драйвера HAL, и приложение может использовать их (в том числе повторно) по своему усмотрению.
IEvsDisplay
Этот объект представляет дисплей Evs, управляет состоянием дисплея и управляет фактическим представлением изображений.
getDisplayInfo() generates (DisplayDesc info);
Возвращает основную информацию об отображении EVS, предоставленную системой (см. DisplayDesc ).
setDisplayState(DisplayState state) generates (EvsResult result);
Устанавливает состояние отображения. Клиенты могут устанавливать состояние отображения для отображения желаемого состояния, а реализация HAL должна корректно принимать запрос на любое состояние, находясь в любом другом состоянии, хотя ответом может быть игнорирование запроса.
При инициализации дисплей запускается в состоянии NOT_VISIBLE
, после чего клиент должен запросить состояние VISIBLE_ON_NEXT_FRAME
и начать передачу видео. Когда дисплей больше не нужен, клиент должен запросить состояние NOT_VISIBLE
после передачи последнего видеокадра.
Запрос любого состояния возможен в любое время. Если отображение уже отображается, оно должно оставаться видимым, если задано значение VISIBLE_ON_NEXT_FRAME
. Всегда возвращает OK, если запрошенное состояние не является нераспознанным значением перечисления; в этом случае возвращается INVALID_ARG
.
getDisplayState() generates (DisplayState state);
Получает состояние отображения. Реализация HAL должна сообщать фактическое текущее состояние, которое может отличаться от последнего запрошенного. Логика, отвечающая за изменение состояний отображения, должна располагаться выше уровня устройства, что исключает возможность спонтанного изменения состояний отображения реализацией HAL.
getTargetBuffer() generates (handle bufferHandle);
Возвращает дескриптор буфера кадра, связанного с дисплеем. Этот буфер может быть заблокирован и может быть записан программным обеспечением и/или GL. Этот буфер должен быть возвращён с помощью вызова returnTargetBufferForDisplay()
даже если дисплей больше не отображается.
Хотя использование собственных форматов буферов технически возможно, для тестирования совместимости требуется буфер в одном из четырёх поддерживаемых форматов: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 бит R:G:B:x), BGRA (32 бит B:G:R:x). Выбранный формат должен быть допустимым целевым объектом рендеринга GL в реализации GLES платформы.
В случае ошибки возвращается буфер с нулевым дескриптором, но такой буфер не нужно передавать обратно в returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Сообщает дисплею о готовности буфера к отображению. Для использования с этим вызовом допустимы только буферы, полученные с помощью вызова getTargetBuffer()
, а содержимое BufferDesc
не может быть изменено клиентским приложением. После этого вызова буфер больше не может быть использован клиентом. Возвращает OK в случае успешного выполнения или соответствующий код ошибки, возможно, INVALID_ARG
или BUFFER_NOT_AVAILABLE
.
struct DisplayDesc { string display_id; int32 vendor_flags; // Opaque value }
Описывает основные свойства дисплея EVS, требуемые для реализации EVS. HAL отвечает за заполнение этой структуры для описания дисплея EVS. Может быть физическим дисплеем или виртуальным дисплеем, наложенным или смешанным с другим устройством отображения.
-
display_id
. Строка, уникально идентифицирующая дисплей. Это может быть имя устройства ядра или имя самого устройства, например, rearview . Значение этой строки выбирается реализацией HAL и используется неявно стеком выше. -
vendor_flags
. Метод для непрозрачной передачи специализированной информации с камеры от водителя в пользовательское приложение EVS. Эта информация передается без интерпретации от водителя в приложение EVS, которое может ее игнорировать.
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 }
Описывает состояние дисплея EVS, который может быть отключён (не виден водителю) или включён (показывает изображение водителю). Включает переходное состояние, в котором дисплей ещё не виден, но готов стать видимым при получении следующего кадра изображения с помощью вызова returnTargetBufferForDisplay()
.
Менеджер EVS
EVS Manager предоставляет общедоступный интерфейс к системе EVS для сбора и отображения изображений с внешних камер. В тех случаях, когда драйверы оборудования допускают только один активный интерфейс для каждого ресурса (камеры или дисплея), EVS Manager обеспечивает общий доступ к камерам. Одно основное приложение EVS является первым клиентом EVS Manager и единственным клиентом, которому разрешено записывать данные дисплея (дополнительным клиентам может быть предоставлен доступ только для чтения к изображениям с камер).
EVS Manager реализует тот же API, что и базовые драйверы HAL, и обеспечивает расширенный сервис за счет поддержки нескольких одновременных клиентов (более одного клиента могут открыть камеру через EVS Manager и получать видеопоток).

Приложения не видят никакой разницы при работе через реализацию EVS Hardware HAL или API EVS Manager, за исключением того, что API EVS Manager обеспечивает одновременный доступ к потоку с камеры. EVS Manager сам по себе является единственным разрешённым клиентом уровня EVS Hardware HAL и действует как прокси для EVS Hardware HAL.
В следующих разделах описываются только те вызовы, которые имеют иное (расширенное) поведение в реализации EVS Manager; остальные вызовы идентичны описаниям EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Получает объект интерфейса, используемый для взаимодействия с конкретной камерой, идентифицируемой уникальной строкой camera_id . Возвращает NULL в случае возникновения ошибки. На уровне EVS Manager, при наличии достаточных системных ресурсов, уже открытая камера может быть снова открыта другим процессом, что позволяет направлять видеопоток нескольким приложениям-пользователям. Строки camera_id
на уровне EVS Manager совпадают со строками, передаваемыми на уровень оборудования EVS.
IEvsCamera
Реализация IEvsCamera, реализованная в EVS Manager, внутренне виртуализирована, поэтому операции с камерой одного клиента не влияют на других клиентов, которые сохраняют независимый доступ к своим камерам.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Запускает видеопотоки. Клиенты могут независимо запускать и останавливать видеопотоки с одной и той же базовой камеры. Базовая камера запускается при запуске первого клиента.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Возвращает кадр. Каждый клиент обязан вернуть свои кадры по завершении, но может хранить их столько, сколько пожелает. Когда количество кадров, удерживаемых клиентом, достигает заданного предела, он перестаёт получать новые кадры, пока не вернёт один. Этот пропуск кадров не влияет на других клиентов, которые продолжают получать все кадры, как и ожидалось.
stopVideoStream();
Останавливает видеопоток. Каждый клиент может остановить свой видеопоток в любой момент, не влияя на работу других клиентов. Базовый поток камеры на аппаратном уровне останавливается, когда последний клиент данной камеры останавливает свой поток.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Отправляет значение, специфичное для драйвера, что потенциально позволяет одному клиенту влиять на другой клиент. Поскольку EVS Manager не может распознать последствия управляющих слов, определяемых поставщиком, они не виртуализируются, и любые побочные эффекты применяются ко всем клиентам данной камеры. Например, если поставщик использовал этот вызов для изменения частоты кадров, все клиенты затронутой аппаратной камеры будут получать кадры с новой частотой.
IEvsDisplay
Разрешён только один владелец дисплея, даже на уровне менеджера EVS. Менеджер не добавляет никакой функциональности и просто передаёт интерфейс IEvsDisplay напрямую в базовую реализацию HAL.
Приложение EVS
Android включает в себя собственную реализацию приложения EVS на C++, которое взаимодействует с EVS Manager и Vehicle HAL для обеспечения базовых функций камеры заднего вида. Ожидается, что приложение будет запускаться на ранних этапах загрузки системы, отображая видео в зависимости от доступных камер и состояния автомобиля (включенной передачи и указателей поворота). OEM-производители могут модифицировать или заменять приложение EVS, используя собственную логику и представление, специфичные для конкретного автомобиля.


Поскольку данные изображения представлены приложению в стандартном графическом буфере, приложение отвечает за перемещение изображения из исходного буфера в выходной. Хотя это и приводит к затратам на копирование данных, это также даёт приложению возможность отображать изображение в буфере отображения любым желаемым способом.
Например, приложение может перемещать сами пиксельные данные, возможно, с помощью встроенной операции масштабирования или поворота. Приложение также может использовать исходное изображение в качестве текстуры OpenGL и визуализировать сложную сцену в выходном буфере, включая виртуальные элементы, такие как значки, направляющие и анимацию. Более сложное приложение может также выбрать несколько одновременно работающих входных камер и объединить их в один выходной кадр (например, для использования в виртуальном виде сверху на окружающую транспортную среду).
Используйте EGL/SurfaceFlinger в EVS Display HAL
В этом разделе объясняется, как использовать EGL для визуализации реализации HAL EVS Display в Android 10.
Референсная реализация EVS HAL использует EGL для рендеринга предварительного просмотра камеры на экране и libgui
для создания целевой поверхности рендеринга EGL. В Android 8 (и более поздних версиях) libgui
классифицируется как VNDK-private , что относится к группе библиотек, доступных библиотекам VNDK, которые процессы вендора не могут использовать. Поскольку реализации HAL должны располагаться в разделе вендора, вендоры не могут использовать Surface в реализациях HAL.
Сборка libgui для процессов поставщика
Использование libgui
— единственный способ использовать EGL/SurfaceFlinger в реализациях HAL для EVS Display. Самый простой способ реализовать libgui
— напрямую через frameworks/native/libs/gui , используя дополнительную цель сборки в скрипте сборки. Эта цель точно такая же, как и libgui
, за исключением двух дополнительных полей:
-
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", ], …
Примечание: Цели поставщика формируются с помощью макроса NO_INPUT
, который удаляет одно 32-битное слово из данных посылки. Поскольку SurfaceFlinger ожидает, что это поле будет удалено, SurfaceFlinger не может проанализировать посылку. Это отображается как ошибка 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
Чтобы решить эту проблему:
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);
Примеры инструкций по сборке приведены ниже. Ожидайте получить $(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
Использовать связующее в реализации EVS HAL
В Android 8 (и более поздних версиях) узел устройства /dev/binder
стал доступен только процессам фреймворка и, следовательно, недоступен для процессов-вендоров. Вместо этого процессы-вендоры должны использовать /dev/hwbinder
и преобразовывать все интерфейсы AIDL в HIDL. Для тех, кто хочет продолжать использовать интерфейсы AIDL между процессами-вендорами, используйте домен-биндер /dev/vndbinder
.
Домен МПК | Описание |
---|---|
/dev/binder | IPC между процессами фреймворка/приложения с интерфейсами AIDL |
/dev/hwbinder | IPC между процессами фреймворка/поставщика с интерфейсами HIDL IPC между процессами поставщиков с интерфейсами HIDL |
/dev/vndbinder | IPC между процессами поставщика/поставщика с интерфейсами AIDL |
Хотя SurfaceFlinger определяет интерфейсы AIDL, процессы-вендоры могут использовать только интерфейсы HIDL для взаимодействия с процессами фреймворка. Преобразование существующих интерфейсов AIDL в HIDL требует нетривиального объёма работы. К счастью, Android предоставляет метод выбора драйвера-биндера для libbinder
, с которым связаны процессы библиотеки пользовательского пространства.
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));
Примечание: Процессы поставщиков должны вызывать этот метод перед вызовом Process
или IPCThreadState
или перед выполнением любых вызовов связывателя.
Политики SELinux
Если реализация устройства — полная, SELinux запрещает процессам поставщика использовать /dev/binder
. Например, реализация примера EVS HAL назначается домену hal_evs_driver
и требует прав на чтение и запись в домене 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
Однако добавление этих разрешений приводит к сбою сборки, поскольку оно нарушает следующие правила neverallow, определенные в system/sepolicy/domain.te
для полнофункционального устройства.
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
предназначен для обнаружения ошибок и руководства разработкой. Он также может быть использован для устранения описанного выше нарушения Android 10.
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)
Создание эталонной реализации EVS HAL в качестве процесса поставщика
Для справки, вы можете внести следующие изменения в packages/services/Car/evs/Android.mk
. Убедитесь, что все описанные изменения подходят для вашей реализации.
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;