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

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

Менеджер EVS

Менеджер EVS ( /packages/services/Car/evs/manager ) предоставляет необходимые компоненты для реализации любых функций приложения EVS: от простого отображения изображения с камеры заднего вида до многокамерного рендеринга с шестью степенями свободы. Интерфейс приложения реализован на языке HIDL и рассчитан на работу с несколькими одновременными клиентами. Другие приложения и сервисы (в частности, Car Service) могут запрашивать состояние менеджера EVS, чтобы узнать, активна ли система EVS.

Интерфейс EVS HIDL

Система EVS, включая камеру и элементы дисплея, определена в пакете android.hardware.automotive.evs . Пример реализации, проверяющий работу интерфейса (генерирующий синтетические тестовые изображения и проверяющий их прохождение), находится в /hardware/interfaces/automotive/evs/1.0/default .

OEM-производитель отвечает за реализацию API, выраженного в файлах .hal в каталоге /hardware/interfaces/automotive/evs . Такие реализации отвечают за настройку и сбор данных с физических камер и их передачу через буферы общей памяти, распознаваемые Gralloc. Дисплейная часть реализации отвечает за предоставление буфера общей памяти, который может быть заполнен приложением (обычно с помощью рендеринга EGL), и отображение готовых кадров в приоритетном порядке по сравнению с любыми другими данными, которые могут быть выведены на физический дисплей. Реализации интерфейса EVS от поставщиков могут храниться в каталогах /vendor/… /device/… или hardware/… (например, /hardware/[vendor]/[platform]/evs ).

Драйверы ядра

Для устройства с поддержкой стека EVS требуются драйверы ядра. Вместо создания новых драйверов OEM-производители могут реализовать поддержку функций, необходимых для EVS, через существующие драйверы камеры или дисплея. Повторное использование драйверов может быть полезным, особенно для драйверов дисплея, где отображение изображения может требовать координации с другими активными потоками. В Android 8.0 включен пример драйвера на основе v4l2 (в packages/services/Car/evs/sampleDriver ), который зависит от ядра для поддержки v4l2 и от SurfaceFlinger для отображения выходного изображения.

Описание аппаратного интерфейса EVS

В этом разделе описывается HAL. Ожидается, что поставщики предоставят реализации этого API, адаптированные для своего оборудования.

IEvsEnumerator

Этот объект отвечает за перечисление доступного оборудования EVS в системе (одна или несколько камер и одно устройство отображения).

getCameraList() generates (vec<CameraDesc> cameras);

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

openCamera(string camera_id) generates (IEvsCamera camera);

Получает объект интерфейса, используемый для взаимодействия с конкретной камерой, идентифицируемой уникальной строкой camera_id . Возвращает NULL в случае возникновения ошибки. Попытки повторно открыть уже открытую камеру не могут завершиться неудачей. Чтобы избежать состояний гонки, связанных с запуском и завершением работы приложения, повторное открытие камеры должно завершать работу предыдущего экземпляра, чтобы новый запрос мог быть выполнен. Экземпляр камеры, вытесненный таким образом, должен быть переведен в неактивное состояние, ожидая окончательного уничтожения и отвечая на любой запрос, изменяющий состояние камеры, кодом возврата OWNERSHIP_LOST .

closeCamera(IEvsCamera camera);

Освобождает интерфейс IEvsCamera (и является противоположностью вызову openCamera() ). Видеопоток с камеры необходимо остановить вызовом stopVideoStream() перед вызовом closeCamera .

openDisplay() generates (IEvsDisplay display);

Получает объект интерфейса, используемый для эксклюзивного взаимодействия с дисплеем EVS системы. Только один клиент может одновременно содержать функциональный экземпляр IEvsDisplay. Аналогично агрессивному поведению открытия, описанному в openCamera , новый объект IEvsDisplay может быть создан в любой момент и отключает все предыдущие экземпляры. Невалидированные экземпляры продолжают существовать и отвечать на вызовы функций своих владельцев, но не должны выполнять никаких операций мутации после прекращения работы. В конечном итоге клиентское приложение должно обнаружить коды возврата ошибки OWNERSHIP_LOST , закрыть и освободить неактивный интерфейс.

closeDisplay(IEvsDisplay display);

Освобождает интерфейс IEvsDisplay (и является противоположностью вызова openDisplay() ). Неиспользованные буферы, полученные с помощью вызовов getTargetBuffer() должны быть возвращены на дисплей перед его закрытием.

getDisplayState() generates (DisplayState state);

Получает текущее состояние дисплея. Реализация HAL должна сообщать фактическое текущее состояние, которое может отличаться от последнего запрошенного. Логика, отвечающая за изменение состояний дисплея, должна располагаться выше уровня устройства, что исключает возможность спонтанного изменения состояний дисплея реализацией HAL. Если дисплей в данный момент не удерживается ни одним клиентом (вызовом openDisplay), эта функция возвращает NOT_OPEN . В противном случае она сообщает текущее состояние дисплея EVS (см. API IEvsDisplay ).

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

IEvsCamera

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

getCameraInfo() generates (CameraDesc info);

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

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

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

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

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

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

oneway doneWithFrame(BufferDesc buffer);

Возвращает кадр, доставленный интерфейсом IEvsCameraStream. После завершения использования кадра, доставленного в интерфейс IEvsCameraStream, этот кадр должен быть возвращён в IEvsCamera для повторного использования. Доступно небольшое, конечное количество буферов (возможно, всего один), и если запас исчерпан, дальнейшие кадры не доставляются до тех пор, пока буфер не будет возвращён, что может привести к пропуску кадров (буфер с нулевым дескриптором обозначает конец потока и не требует возврата этой функцией). Возвращает OK в случае успешного выполнения или соответствующий код ошибки, возможно, включая INVALID_ARG или BUFFER_NOT_AVAILABLE .

stopVideoStream();

Останавливает доставку кадров с камеры EVS. Поскольку доставка асинхронная, кадры могут продолжать поступать ещё некоторое время после завершения этого вызова. Каждый кадр должен быть возвращён до тех пор, пока IEvsCameraStream не получит сигнал о закрытии потока. Вызов stopVideoStream допустим для потока, который уже был остановлен или никогда не запускался; в этом случае он игнорируется.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Запрашивает информацию, специфичную для драйвера, из реализации HAL. Допустимые значения opaqueIdentifier зависят от драйвера, но ни одно из переданных значений не может привести к сбою драйвера. Драйвер должен возвращать 0 для любого нераспознанного opaqueIdentifier .

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Отправляет значение, специфичное для водителя, в реализацию HAL. Это расширение предоставляется только для поддержки расширений, специфичных для транспортного средства, и ни одна реализация HAL не должна требовать, чтобы этот вызов работал в состоянии по умолчанию. Если водитель распознает и принимает значения, следует вернуть OK; в противном случае следует вернуть INVALID_ARG или другой репрезентативный код ошибки.

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

Описывает изображение, переданное через API. HAL-дисковод отвечает за заполнение этой структуры для описания буфера изображения, а клиент HAL должен рассматривать эту структуру как доступную только для чтения. Поля содержат достаточно информации, чтобы клиент мог реконструировать объект ANativeWindowBuffer , что может потребоваться для использования изображения с EGL с расширением eglCreateImageKHR() .

  • width . Ширина представленного изображения в пикселях.
  • height . Высота представленного изображения в пикселях.
  • stride . Количество пикселей, которое каждая строка фактически занимает в памяти, с учётом отступов для выравнивания строк. Выражается в пикселях в соответствии с соглашением, принятым gralloc для описания буферов.
  • pixelSize . Количество байтов, занимаемое каждым отдельным пикселем, что позволяет вычислить размер в байтах, необходимый для перехода между строками изображения ( stride в байтах = stride в пикселях * pixelSize ).
  • format . Формат пикселей, используемый изображением. Предоставленный формат должен быть совместим с реализацией OpenGL на платформе. Для прохождения тестирования совместимости для использования с камерой следует выбрать формат HAL_PIXEL_FORMAT_YCRCB_420_SP , а для отображения — RGBA или BGRA .
  • usage . Флаги использования, устанавливаемые реализацией HAL. Клиенты HAL должны передавать их без изменений (подробности см. в описании флагов Gralloc.h ).
  • bufferId . Уникальное значение, заданное реализацией HAL, позволяющее распознавать буфер после прохождения через API HAL. Значение, хранящееся в этом поле, может быть выбрано реализацией HAL произвольно.
  • memHandle . Дескриптор базового буфера памяти, содержащего данные изображения. Реализация HAL может хранить здесь дескриптор буфера Gralloc.

IEvsCameraStream

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

deliverFrame(BufferDesc buffer);

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

Хотя использование собственных форматов буферов технически возможно, для тестирования совместимости требуется буфер в одном из четырёх поддерживаемых форматов: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 бит R:G:B:x), BGRA (32 бит B:G:R:x). Выбранный формат должен быть допустимым источником GL-текстуры в реализации GLES платформы.

Приложение не должно полагаться на какое-либо соответствие между полем bufferId и memHandle в структуре BufferDesc . Значения bufferId по сути являются частными для реализации драйвера HAL, и приложение может использовать их (в том числе повторно) по своему усмотрению.

IEvsDisplay

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

getDisplayInfo() generates (DisplayDesc info);

Возвращает основную информацию об отображении EVS, предоставленную системой (см. DisplayDesc ).

setDisplayState(DisplayState state) generates (EvsResult result);

Устанавливает состояние отображения. Клиенты могут устанавливать состояние отображения для отображения желаемого состояния, а реализация HAL должна корректно принимать запрос на любое состояние, находясь в любом другом состоянии, хотя ответом может быть игнорирование запроса.

При инициализации дисплей запускается в состоянии NOT_VISIBLE , после чего клиент должен запросить состояние VISIBLE_ON_NEXT_FRAME и начать передачу видео. Когда дисплей больше не нужен, клиент должен запросить состояние NOT_VISIBLE после передачи последнего видеокадра.

Запрос любого состояния возможен в любое время. Если отображение уже отображается, оно должно оставаться видимым, если задано значение VISIBLE_ON_NEXT_FRAME . Всегда возвращает OK, если запрошенное состояние не является нераспознанным значением перечисления; в этом случае возвращается INVALID_ARG .

getDisplayState() generates (DisplayState state);

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

getTargetBuffer() generates (handle bufferHandle);

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

Хотя использование собственных форматов буферов технически возможно, для тестирования совместимости требуется буфер в одном из четырёх поддерживаемых форматов: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 бит R:G:B:x), BGRA (32 бит B:G:R:x). Выбранный формат должен быть допустимым целевым объектом рендеринга GL в реализации GLES платформы.

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

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

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

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

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

  • display_id . Строка, уникально идентифицирующая дисплей. Это может быть имя устройства ядра или имя самого устройства, например, rearview . Значение этой строки выбирается реализацией HAL и используется неявно стеком выше.
  • vendor_flags . Метод для непрозрачной передачи специализированной информации с камеры от водителя в пользовательское приложение EVS. Эта информация передается без интерпретации от водителя в приложение EVS, которое может ее игнорировать.
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been opened yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

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

Менеджер EVS

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

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

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

Приложение EVS

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

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


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

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

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

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

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

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

Сборка libgui для процессов поставщика

Использование libgui — единственный способ использовать EGL/SurfaceFlinger в реализациях HAL для EVS Display. Самый простой способ реализовать libgui — напрямую через frameworks/native/libs/gui , используя дополнительную цель сборки в скрипте сборки. Эта цель точно такая же, как и libgui , за исключением двух дополнительных полей:

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ],

Примечание: Цели поставщика формируются с помощью макроса NO_INPUT , который удаляет одно 32-битное слово из данных посылки. Поскольку SurfaceFlinger ожидает, что это поле будет удалено, SurfaceFlinger не может проанализировать посылку. Это отображается как ошибка fcntl :

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

Чтобы решить эту проблему:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
    output.writeFloat(color.b);
#ifndef NO_INPUT
    inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
#endif
    output.write(transparentRegion);
    output.writeUint32(transform);

Примеры инструкций по сборке приведены ниже. Ожидайте получить $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so .

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

Использовать связующее в реализации EVS HAL

В Android 8 (и более поздних версиях) узел устройства /dev/binder стал доступен только процессам фреймворка и, следовательно, недоступен для процессов-вендоров. Вместо этого процессы-вендоры должны использовать /dev/hwbinder и преобразовывать все интерфейсы AIDL в HIDL. Для тех, кто хочет продолжать использовать интерфейсы AIDL между процессами-вендорами, используйте домен-биндер /dev/vndbinder .

Домен МПК Описание
/dev/binder IPC между процессами фреймворка/приложения с интерфейсами AIDL
/dev/hwbinder IPC между процессами фреймворка/поставщика с интерфейсами HIDL
IPC между процессами поставщиков с интерфейсами HIDL
/dev/vndbinder IPC между процессами поставщика/поставщика с интерфейсами AIDL

Хотя SurfaceFlinger определяет интерфейсы AIDL, процессы-вендоры могут использовать только интерфейсы HIDL для взаимодействия с процессами фреймворка. Преобразование существующих интерфейсов AIDL в HIDL требует нетривиального объёма работы. К счастью, Android предоставляет метод выбора драйвера-биндера для libbinder , с которым связаны процессы библиотеки пользовательского пространства.

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


    // Start a thread to listen to video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

Примечание: Процессы поставщиков должны вызывать этот метод перед вызовом Process или IPCThreadState или перед выполнением любых вызовов связывателя.

Политики SELinux

Если реализация устройства — полная, SELinux запрещает процессам поставщика использовать /dev/binder . Например, реализация примера EVS HAL назначается домену hal_evs_driver и требует прав на чтение и запись в домене binder_device .

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

Однако добавление этих разрешений приводит к сбою сборки, поскольку оно нарушает следующие правила neverallow, определенные в system/sepolicy/domain.te для полнофункционального устройства.

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
} binder_device:chr_file rw_file_perms;
')

Атрибут binder_in_vendor_violators предназначен для обнаружения ошибок и руководства разработкой. Он также может быть использован для устранения описанного выше нарушения Android 10.

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)

Создание эталонной реализации EVS HAL в качестве процесса поставщика

Для справки, вы можете внести следующие изменения в packages/services/Car/evs/Android.mk . Убедитесь, что все описанные изменения подходят для вашей реализации.

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
    android.hardware.automotive.evs@1.0 \
    libui \
-    libgui \
+    libgui_vendor \
    libEGL \
    libGLESv2 \
    libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

#NOTE:  It can be helpful, while debugging, to disable optimizations
#LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

# Allow the driver to access kobject uevents
allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;