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

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

Рисунок 1. Обзор компонентов системы EVS.

Приложение EVS

Пример приложения C++ EVS ( /packages/services/Car/evs/app ) служит в качестве эталонной реализации. Это приложение отвечает за запрос видеокадров из EVS Manager и отправку готовых кадров для отображения обратно в EVS Manager. Оно должно быть запущено init, как только EVS и Car Service станут доступны, в течение двух (2) секунд после включения питания. OEM-производители могут изменять или заменять приложение EVS по своему усмотрению.

Менеджер EVS

EVS Manager ( /packages/services/Car/evs/manager ) предоставляет строительные блоки, необходимые приложению EVS для реализации чего угодно, от простого отображения камеры заднего вида до многокамерного рендеринга 6DOF. Его интерфейс представлен через 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 кадров в секунду. Время, необходимое для запуска видеопотока, фактически учитывается в любых требованиях по времени запуска камеры заднего вида. Если поток не запущен, должен быть возвращен код ошибки; в противном случае возвращается 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() , этот обратный вызов может продолжаться по мере опустошения конвейера. Каждый кадр все равно должен быть возвращен; когда последний кадр в потоке будет доставлен, будет доставлен NULL bufferHandle, что означает конец потока и дальнейшие доставки кадров не происходят. Сам NULL 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 и получать видеопоток).

Диаграмма API EVS Manager и EVS Hardware.

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

Приложения не видят никаких различий при работе через реализацию 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 Manager. Менеджер не добавляет никакой функциональности и просто передает интерфейс IEvsDisplay напрямую в базовую реализацию HAL.

Приложение EVS

Android включает собственную реализацию C++-эталона приложения EVS, которое взаимодействует с EVS Manager и Vehicle HAL для предоставления базовых функций камеры заднего вида. Ожидается, что приложение будет запускаться очень рано в процессе загрузки системы, с соответствующим видео, показываемым в зависимости от доступных камер и состояния автомобиля (состояние передачи и сигнала поворота). OEM-производители могут изменять или заменять приложение EVS собственной логикой и представлением, специфичными для автомобиля.

Рисунок 3. Пример логики приложения EVS, получение списка камер.



Рисунок 4. Пример логики приложения EVS, обратный вызов приема кадра.

Поскольку данные изображения представлены приложению в стандартном графическом буфере, приложение отвечает за перемещение изображения из исходного буфера в выходной буфер. Хотя это и вносит стоимость копирования данных, это также дает возможность приложению отображать изображение в буфере отображения любым желаемым им способом.

Например, приложение может выбрать перемещение самих пиксельных данных, потенциально с помощью встроенной операции масштабирования или вращения. Приложение также может выбрать использование исходного изображения в качестве текстуры OpenGL и визуализировать сложную сцену в выходном буфере, включая виртуальные элементы, такие как значки, направляющие и анимацию. Более сложное приложение может также выбрать несколько параллельных входных камер и объединить их в один выходной кадр (например, для использования в виртуальном виде сверху вниз на окрестности транспортного средства).

Используйте EGL/SurfaceFlinger в EVS Display HAL

В этом разделе объясняется, как использовать EGL для рендеринга реализации HAL EVS Display в Android 10.

Реализация эталонного HAL EVS использует 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

Однако добавление этих разрешений приводит к сбою сборки, поскольку оно нарушает следующие правила 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;
,

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

Рисунок 1. Обзор компонентов системы EVS.

Приложение EVS

Пример приложения C++ EVS ( /packages/services/Car/evs/app ) служит в качестве эталонной реализации. Это приложение отвечает за запрос видеокадров из EVS Manager и отправку готовых кадров для отображения обратно в EVS Manager. Оно должно быть запущено init, как только EVS и Car Service станут доступны, в течение двух (2) секунд после включения питания. OEM-производители могут изменять или заменять приложение EVS по своему усмотрению.

Менеджер EVS

EVS Manager ( /packages/services/Car/evs/manager ) предоставляет строительные блоки, необходимые приложению EVS для реализации чего угодно, от простого отображения камеры заднего вида до многокамерного рендеринга 6DOF. Его интерфейс представлен через 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 (см. Ievsdisplay API ).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id . Строка, которая однозначно идентифицирует данную камеру. Может быть название устройства ядра устройства или имя для устройства, например, задний вид . Значение для этой строки выбирается реализацией HAL и используется непоколечески в стеке выше.
  • vendor_flags . Метод для передачи специализированной информации о камере произвел от драйвера в пользовательское приложение EVS. Он передается неинтересованным от драйвера до приложения EVS, которое может игнорировать его.

Ievscamera

Этот объект представляет одну камеру и является основным интерфейсом для захвата изображений.

getCameraInfo() generates (CameraDesc info);

Возвращает CameraDesc этой камеры.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Указывает глубину буферной цепи, которую камера просят поддержать. До этого множества кадров могут быть проведены одновременно клиентом Ievsamera. Если это много кадров было доставлено в приемник без возврата с помощью doneWithFrame , потоковой пропуск рамки до тех пор, пока буфер не будет возвращен для повторного использования. Это законно, чтобы этот призыв пришел в любое время, даже когда потоки уже работают, и в этом случае буферы должны быть добавлены или удалены из цепи по мере необходимости. Если в этой точке записи не будет вызова, IEVSCAMERA поддерживает хотя бы один кадр по умолчанию; с более приемлемым.

Если запрошенная буферист не может быть приспособлен, функция возвращает BUFFER_NOT_AVAILABLE или другой соответствующий код ошибки. В этом случае система продолжает работать с ранее установленным значением.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Запрашивает доставку кадров камеры EVS с этой камеры. IEVSCAMERAREAM начинает получать периодические вызовы с новыми кадрами изображений до тех пор, пока не будет вызван stopVideoStream() . Кадры должны начать доставлять в течение 500 мс после вызова startVideoStream , и после начала должен генерироваться как минимум 10 кадров в секунду. Время, необходимое для эффективного запуска видеопотока против любого требования запуска камеры заднего вида. Если поток не запускается, код ошибки должен быть возвращен; В противном случае ОК возвращается.

oneway doneWithFrame(BufferDesc buffer);

Возвращает кадр, которая была доставлена ​​в IEVSCAMERAREAM. Когда закончите потребление кадра, доставленного в интерфейс ievscamerastream, кадр должен быть возвращен в Ievscamera для повторного использования. Небольшое, конечное количество буферов доступно (возможно, так же мало, как и одно), и если предложение исчерпано, дальнейшие кадры не доставляются до возврата буфера, что потенциально приводит к пропущенным кадрам (буфер с нулевой ручкой обозначает конец потока и не нуждается в возвращении через эту функцию). Возвращает ОК на успехе или соответствующий код ошибки, потенциально включающий 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 не должна требовать, чтобы этот вызов функционировал в состоянии по умолчанию. Если драйвер распознает и принимает значения, должна быть возвращена ОК; В противном случае следует возвращать 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 . Пиксельный формат, используемый изображением. Предоставляемый формат должен быть совместим с реализацией Platform OpenGL. Чтобы пройти тестирование совместимости, HAL_PIXEL_FORMAT_YCRCB_420_SP должен быть предпочтительным для использования камеры, а RGBA или BGRA должны быть предпочтительны для отображения.
  • usage . Флаги использования, установленные реализацией HAL. Ожидается, что клиенты HAL пройдут эти немодифицированные (для получения подробной информации см. Вмещаемые флаги Gralloc.h ).
  • bufferId . Уникальное значение, указанное в реализации HAL, позволяющее распознавать буфер после поездки туда и обработки через APIS HAL. Значение, хранящее в этом поле, может быть произвольно выбрано реализацией HAL.
  • memHandle . Ручка для базового буфера памяти, который содержит данные изображения. Реализация HAL может выбрать хранить здесь буферную ручку Gralloc.

Ievscamerastream

Клиент реализует этот интерфейс, чтобы получить асинхронные поставки видео кадров.

deliverFrame(BufferDesc buffer);

Получает вызовы от HAL каждый раз, когда видео кадр готов к проверке. Буферные ручки, полученные этим методом, должны возвращаться через вызовы IEvsCamera::doneWithFrame() . Когда видеопоток останавливается с помощью вызова IEvsCamera::stopVideoStream() , этот обратный вызов может продолжаться в качестве дренажного трубопровода. Каждый кадр все еще должен быть возвращен; Когда был доставлен последний кадр в потоке, будет доставлен нулевая буферная рукава, что означает конец потока и никаких дальнейших поставков кадров не происходит. Сама нулевая буфера не нужно отправлять обратно через doneWithFrame() , но все остальные ручки должны быть возвращены

В то время как фирменные форматы буфера технически возможны, тестирование на совместимость требует, чтобы буфер был в одном из четырех поддерживаемых форматов: NV21 (YCRCB 4: 2: 0 SemiPlanar), YV12 (YCRCB 4: 2: 0 Планар), Yuyv (YCRCB 4: 2: 2. 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 . Всегда возвращает ОК, если запрошенное состояние не является нераспознанным значением перечисления, и в этом случае не будет возвращено INVALID_ARG .

getDisplayState() generates (DisplayState state);

Получает состояние дисплея. Реализация HAL должна сообщать о фактическом текущем состоянии, которое может отличаться от последнего запрошенного состояния. Логика, ответственная за изменение состояний отображения, должна существовать над уровнем устройства, что делает ее нежелательной для реализации HAL, чтобы спонтанно изменить состояния дисплея.

getTargetBuffer() generates (handle bufferHandle);

Возвращает ручку в кадритный буфер, связанный с дисплеем. Этот буфер может быть заблокирован и записан программным обеспечением и/или GL. Этот буфер должен быть возвращен с помощью вызова для returnTargetBufferForDisplay() даже если дисплей больше не виден.

В то время как фирменные форматы буфера технически возможны, тестирование на совместимость требует, чтобы буфер был в одном из четырех поддерживаемых форматов: NV21 (YCRCB 4: 2: 0 SemiPlanar), YV12 (YCRCB 4: 2: 0 Планар), Yuyv (YCRCB 4: 2: 2. B: G: R: x). Выбранный формат должен быть действительной целью GL -рендеринга для реализации платформы GLES.

При ошибке возвращается буфер с нулевой ручкой, но такой буфер не нужно передавать обратно в returnTargetBufferForDisplay .

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Сообщает дисплее, буфер готов к дисплее. Только буферы, полученные с помощью вызова, чтобы getTargetBuffer() действительны для использования с этим вызовом, и содержимое BufferDesc не может быть изменено клиентским приложением. После этого вызова буфер больше не действителен для использования клиентом. Возвращает ОК на успехе или соответствующий код ошибки, потенциально включающий INVALID_ARG или BUFFER_NOT_AVAILABLE .

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

Описывает основные свойства отображения EVS и требуемые в реализации EVS. HAL отвечает за заполнение этой структуры, чтобы описать дисплей EVS. Может быть физический дисплей или виртуальный дисплей, который наложен или смешан с другим устройством презентации.

  • display_id . Строка, которая уникально идентифицирует дисплей. Это может быть название устройства ядра устройства или имя для устройства, такое как задний вид . Значение для этой строки выбирается реализацией 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 Manager

Manager EVS предоставляет общественный интерфейс системе EVS для сбора и представления видов внешней камеры. В тех случаях, когда аппаратные драйверы позволяют только один активный интерфейс для каждого ресурса (камера или дисплей), менеджер EVS облегчает общий доступ к камерам. Одно основное приложение EVS является первым клиентом EVS Manager, и является единственным клиентом, разрешенным для записи данных отображения (дополнительным клиентам может быть предоставлен доступ только для чтения к изображениям камеры).

Manager EVS реализует тот же API, что и базовые драйверы палаты, и предоставляет расширенный сервис, поддерживая несколько параллельных клиентов (более одного клиента может открыть камеру через диспетчер EVS и получить видеопоток).

EVS Manager и ЭВС Аппаратная диаграмма API.

Рисунок 2. Менеджер EVS зеркала, лежащие в основе API API EVS.

Приложения не видят различий при работе через аппаратную реализацию HAL EVS или API EVS Manager, за исключением того, что API EVS Manager позволяет одновременно доступ к потоку камеры. Диспетчер EVS сам по себе является одним из клиентов аппаратного уровня HALE EVS и выступает в качестве прокси для оборудования EVS HAL.

В следующих разделах описываются только те звонки, которые имеют другое (расширенное) поведение в реализации менеджера EVS; Остальные вызовы идентичны описаниям HAL EVS.

Ievsenumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Получает интерфейсный объект, используемый для взаимодействия с определенной камерой, идентифицированной уникальной строкой Camera_ID . Возвращает ноль при неудаче. На уровне Manager EVS, до тех пор, пока доступны достаточные системные ресурсы, уже открыта камера, которая уже открыта, может быть снова открыта другим процессом, что позволяет подключить видеопотока нескольким приложениям для потребителей. Строки camera_id на слое EVS Manager совпадают с тем, что сообщается на оборудование EVS.

Ievscamera

Менеджер EVS при условии, что реализация 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 не может понять последствия контрольных слов, определенных поставщиками, они не виртуализированы, и любые побочные эффекты применяются ко всем клиентам данной камеры. Например, если поставщик использовал этот вызов для изменения частоты кадров, все клиенты затронутой камеры аппаратного слоя получат рамки по новой ставке.

Ievsdisplay

Только один владелец дисплея разрешен, даже на уровне менеджера EVS. Менеджер не добавляет функциональности и просто передает интерфейс IEVSDisplay непосредственно к базовой реализации HAL.

EVS App

Android включает в себя нативную справочную реализацию C ++ приложения EVS, которое связывается с EVS Manager и автомобилем HAL, чтобы обеспечить основные функции камеры заднего вида. Ожидается, что приложение начнется очень рано в процессе загрузки системы, с подходящим видео, показанным в зависимости от доступных камер и состояния автомобиля (состояние передачи и сигнала поворота). OEM-производители могут изменить или заменить приложение EVS на свою собственную логику и презентацию, специфичную для транспортного средства.

Рисунок 3.



Рисунок 4. EVS Пример приложения логика, приобретение кадров.

Поскольку данные изображения представлены в приложении в стандартном графическом буфере, приложение отвечает за перемещение изображения из буфера источника в выходной буфер. Несмотря на то, что это представляет стоимость копии данных, оно также предоставляет возможность для приложения представить изображение в буфере дисплея в любом случае, который он желает.

Например, приложение может выбрать перемещение самого пиксельного данных, потенциально с помощью встроенной шкалы или операции вращения. Приложение также может использовать исходное изображение в качестве текстуры OpenGL и представить сложную сцену для выходного буфера, включая виртуальные элементы, такие как значки, руководящие принципы и анимации. Более сложное приложение также может выбрать несколько параллельных входных камер и объединить их в единый выходной кадр (например, для использования в нисходящем виртуальном виде обстановки транспортных средств).

Используйте EGL/SurfaceFlinger на дисплее EVS HAL

В этом разделе объясняется, как использовать EGL, чтобы отобразить реализацию EVS HAL в Android 10.

Реализация EVS HAL использует EGL для представления предварительного просмотра камеры на экране и использует libgui для создания целевой поверхности рендеринга EGL. В Android 8 (и выше) libgui классифицируется как VNDK-Private , что относится к группе библиотек, доступных для библиотек VNDK, которые процессы поставщиков не могут использовать. Поскольку реализации HAL должны находиться в разделе поставщиков, поставщики не позволяют использовать Surface в реализации HAL.

Создание Libgui для процессов поставщика

Использование libgui служит единственным вариантом для использования EGL/Surfaceflinger в EVS -дисплей HAL. Самый простой способ реализации libgui - это рамки/уроженец/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 .

Домен IPC Описание
/dev/binder МПК между процессами Framework/App с интерфейсами AIDL
/dev/hwbinder МПК между процессами Framework/поставщика с интерфейсами HIDL
МПК между процессами поставщиков с интерфейсами HIDL
/dev/vndbinder МПК между процессами поставщика/поставщика с интерфейсами 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 . Например, реализация образца HAL EVS назначается домену hal_evs_driver и требует разрешений R/W в домен 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;