Автомобильная камера HAL

Android содержит автомобильный уровень аппаратной абстракции HIDL (HAL), который обеспечивает захват и отображение изображений на самых ранних этапах процесса загрузки Android и продолжает функционировать в течение всего срока службы системы. HAL включает в себя стек системы внешнего вида (EVS) и обычно используется для поддержки камеры заднего вида и дисплеев объемного обзора в автомобилях с бортовыми информационно-развлекательными системами (IVI) на базе Android. EVS также позволяет реализовать расширенные функции в пользовательских приложениях.

Android также включает в себя интерфейс драйвера захвата и отображения, предназначенный для EVS (в /hardware/interfaces/automotive/evs/1.0 ). Хотя можно создать приложение камеры заднего вида поверх существующих служб камер и дисплеев Android, такое приложение, скорее всего, будет запускаться слишком поздно в процессе загрузки Android. Использование выделенного HAL обеспечивает оптимизированный интерфейс и дает понять, что OEM-производителю необходимо реализовать для поддержки стека EVS.

Системные компоненты

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 Manager и API оборудования EVS.

Рисунок 2. EVS Manager отражает базовый аппаратный API EVS.

Приложения не видят различий при работе через реализацию аппаратного обеспечения 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 включает в себя эталонную реализацию приложения EVS на языке C++, которое взаимодействует с 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;