Android содержит автомобильный уровень аппаратной абстракции HIDL (HAL), который обеспечивает захват и отображение изображений на самых ранних этапах процесса загрузки Android и продолжает функционировать в течение всего срока службы системы. HAL включает в себя стек системы внешнего обзора (EVS) и обычно используется для поддержки камеры заднего вида и дисплеев кругового обзора в автомобилях с бортовыми информационно-развлекательными системами (IVI) на базе Android. EVS также позволяет реализовать расширенные функции в пользовательских приложениях.
Android также включает в себя интерфейс драйвера захвата и отображения, предназначенный для EVS (в /hardware/interfaces/automotive/evs/1.0
). Хотя можно создать приложение для камеры заднего вида поверх существующих служб камер и дисплеев Android, такое приложение, скорее всего, будет запускаться слишком поздно в процессе загрузки Android. Использование выделенного HAL обеспечивает оптимизированный интерфейс и дает понять, что OEM-производителю необходимо реализовать для поддержки стека EVS.
Компоненты системы
EVS включает в себя следующие компоненты системы:
Рисунок 1. Обзор компонентов системы EVS.
приложение EVS
Пример приложения EVS на C++ ( /packages/services/Car/evs/app
) служит эталонной реализацией. Это приложение отвечает за запрос видеокадров у EVS Manager и отправку готовых кадров для отображения обратно в EVS Manager. Ожидается, что он будет запущен с помощью init, как только EVS и Car Service станут доступны, в течение двух (2) секунд после включения питания. OEM-производители могут модифицировать или заменить приложение EVS по своему желанию.
Менеджер ЕВС
Менеджер EVS ( /packages/services/Car/evs/manager
) предоставляет стандартные блоки, необходимые приложению EVS для реализации чего угодно: от простого отображения камеры заднего вида до многокамерного рендеринга с 6 степенями свободы. Его интерфейс представлен через HIDL и создан для одновременной работы нескольких клиентов. Другие приложения и службы (в частности, Car Service) могут запрашивать состояние EVS Manager, чтобы узнать, когда система 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 кадров в секунду. Время, необходимое для запуска видеопотока, эффективно учитывается при любых требованиях ко времени запуска камеры заднего вида. Если поток не запущен, должен быть возвращен код ошибки; в противном случае возвращается ОК.
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()
, этот обратный вызов может продолжаться по мере истощения конвейера. Каждый кадр все равно должен быть возвращен; когда последний кадр в потоке будет доставлен, будет доставлен NULLufferHandle, обозначающий конец потока и дальнейшая доставка кадров не произойдет. Сам NULL bufferHandle не нужно отправлять обратно через doneWithFrame()
, но все остальные дескрипторы должны быть возвращены.
Хотя технически возможны собственные форматы буферов, для тестирования совместимости требуется, чтобы буфер был в одном из четырех поддерживаемых форматов: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 с чередованием), 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 с чередованием), 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 для сбора и представления изображений с внешних камер. Если драйверы оборудования допускают только один активный интерфейс для каждого ресурса (камеры или дисплея), EVS Manager обеспечивает общий доступ к камерам. Единственное основное приложение EVS является первым клиентом EVS Manager и единственным клиентом, которому разрешено записывать данные отображения (дополнительным клиентам может быть предоставлен доступ только для чтения к изображениям с камеры).
EVS Manager реализует тот же API, что и базовые драйверы HAL, и предоставляет расширенные услуги за счет поддержки нескольких одновременных клиентов (более одного клиента могут открыть камеру через EVS Manager и получить видеопоток).
Приложения не видят различий при работе через реализацию аппаратного обеспечения EVS HAL или API EVS Manager, за исключением того, что API EVS Manager обеспечивает одновременный доступ к потоку камеры. EVS Manager сам по себе является единственным разрешенным клиентом уровня HAL оборудования EVS и действует как прокси для HAL оборудования EVS.
В следующих разделах описаны только те вызовы, которые имеют другое (расширенное) поведение в реализации EVS Manager; остальные вызовы идентичны описаниям EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Получает объект интерфейса, используемый для взаимодействия с определенной камерой, идентифицируемой уникальной строкой camera_id . Возвращает NULL в случае ошибки. На уровне EVS Manager, пока доступны достаточные системные ресурсы, уже открытая камера может быть открыта снова другим процессом, что позволяет передавать видеопоток нескольким потребительским приложениям. Строки camera_id
на уровне EVS Manager такие же, как и строки, передаваемые на уровень оборудования EVS.
IEvsCamera
В EVS Manager реализована внутренняя виртуализация IEvsCamera, поэтому операции с камерой одного клиента не влияют на других клиентов, которые сохраняют независимый доступ к своим камерам.
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 включает в себя встроенную эталонную реализацию C++ приложения EVS, которое взаимодействует с EVS Manager и HAL транспортного средства для предоставления базовых функций камеры заднего вида. Ожидается, что приложение запустится на очень ранней стадии процесса загрузки системы, и будет показано подходящее видео в зависимости от доступных камер и состояния автомобиля (состояние передачи и указателя поворота). OEM-производители могут модифицировать или заменить приложение EVS своей собственной логикой и представлением, специфичными для конкретного автомобиля.
Рисунок 3. Пример логики приложения EVS, получение списка камер.
Рисунок 4. Пример логики приложения EVS, обратный вызов получения кадра.
Поскольку данные изображения передаются приложению в стандартном графическом буфере, приложение отвечает за перемещение изображения из исходного буфера в выходной буфер. Хотя это увеличивает стоимость копирования данных, оно также дает приложению возможность визуализировать изображение в буфер дисплея любым желаемым способом.
Например, приложение может выбрать перемещение самих данных пикселей, возможно, с помощью встроенного масштабирования или операции вращения. Приложение также может использовать исходное изображение в качестве текстуры OpenGL и отображать сложную сцену в выходном буфере, включая виртуальные элементы, такие как значки, направляющие и анимацию. Более сложное приложение может также выбрать несколько одновременных входных камер и объединить их в один выходной кадр (например, для использования в виртуальном виде сверху вниз на окрестности транспортного средства).
Используйте EGL/SurfaceFlinger в HAL дисплея EVS.
В этом разделе объясняется, как использовать EGL для рендеринга реализации EVS Display HAL в Android 10.
Эталонная реализация EVS HAL использует EGL для предварительного просмотра изображения камеры на экране и использует libgui
для создания целевой поверхности рендеринга EGL. В Android 8 (и более поздних версиях) libgui
классифицируется как VNDK-private , что относится к группе библиотек, доступных библиотекам VNDK, которые процессы поставщика не могут использовать. Поскольку реализации HAL должны находиться в разделе поставщика, поставщики не могут использовать Surface в реализациях HAL.
Создание libgui для процессов поставщиков
Использование libgui
является единственным вариантом использования EGL/SurfaceFlinger в реализациях EVS Display HAL. Самый простой способ реализовать 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
Однако добавление этих разрешений приводит к сбою сборки, поскольку нарушает следующие правила никогда не разрешать, определенные в 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;