Автомобильная камера 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;