Starting March 27, 2025, we recommend using android-latest-release
instead of aosp-main
to build and contribute to AOSP. For more information, see Changes to AOSP.
Automotive display proxy service
Stay organized with collections
Save and categorize content based on your preferences.
This simple framework service lets vendor
processes use SurfaceFlinger/EGL in HAL implementations, without linking
libgui. AOSP provides the default implementation of this service, which is fully
functional. However, the vendor must also implement APIs to provide this service
on their platform.
package android.frameworks.automotive.display@1.0;
import android.hardware.graphics.bufferqueue@2.0::IGraphicBufferProducer;
interface IAutomotiveDisplayProxyService {
/**
* Gets an IGraphicBufferProducer instance from the service.
*
* @param id Target's stable display identifier
*
* @return igbp Returns an IGraphicBufferProducer object, that can be
* converted to an ANativeWindow object.
*/
getIGraphicBufferProducer(uint64_t id) generates (IGraphicBufferProducer igbp);
/**
* Sets the ANativeWindow, which is associated with the
* IGraphicBufferProducer, to be visible and to take over the display.
*
* @param id Target display ID
*
* @return success Returns true on success.
*/
showWindow(uint64_t id) generates (bool success);
/**
* Sets the ANativeWindow, which is associated with the
* IGraphicBufferProducer, to be invisible and to release the control
* over display.
*
* @param id Target display ID
*
* @return success Returns true on success.
*/
hideWindow(uint64_t id) generates (bool success);
/**
* Returns the stable identifiers of all available displays.
*
* @return ids A list of stable display identifiers.
*/
getDisplayIdList() generates (vec<uint64_t> ids);
/**
* Returns the descriptor of the target display.
*
* @param id Stable ID of a target display.
* @return cfg DisplayConfig of the active display.
* @return state Current state of the active display.
*/
getDisplayInfo(uint64_t id) generates (HwDisplayConfig cfg, HwDisplayState state);
}
To use this service:
- Get
IAutomotiveDisplayProxyService
.
android::sp<IAutomotiveDisplayProxyService> windowProxyService =
IAutomotiveDisplayProxyService::getService("default");
if (windowProxyService == nullptr) {
LOG(ERROR) << "Cannot use AutomotiveDisplayProxyService. Exiting.";
return 1;
}
- Retrieve an active display information from the service to determine the resolution.
// We use the first display in the list as the primary.
pWindowProxy->getDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {
DisplayConfig *pConfig = (DisplayConfig*)dpyConfig.data();
mWidth = pConfig->resolution.getWidth();
mHeight = pConfig->resolution.getHeight();
ui::DisplayState* pState = (ui::DisplayState*)dpyState.data();
if (pState->orientation != ui::ROTATION_0 &&
pState->orientation != ui::ROTATION_180) {
// rotate
std::swap(mWidth, mHeight);
}
LOG(DEBUG) << "Display resolution is " << mWidth << " x " << mHeight;
});
- Retrieve a hardware
IGraphicBufferProducer
(or,
HIDL GraphicBufferProducer (HGBP) from IAutomotiveDisplayProxyService
:
mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
if (mGfxBufferProducer == nullptr) {
LOG(ERROR) << "Failed to get IGraphicBufferProducer from "
<< "IAutomotiveDisplayProxyService.";
return false;
}
- Get a
SurfaceHolder
from a retrieved HGBP, by using the API
libbufferqueueconverter
:
mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
if (mSurfaceHolder == nullptr) {
LOG(ERROR) << "Failed to get a Surface from HGBP.";
return false;
}
- Convert a
SurfaceHolder
into a native window by using the
API libbufferqueueconverter
:
mWindow = getNativeWindow(mSurfaceHolder.get());
if (mWindow == nullptr) {
LOG(ERROR) << "Failed to get a native window from Surface.";
return false;
}
- Create an EGL window surface with a native window and then render:
// Set up our OpenGL ES context associated with the default display
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (mDisplay == EGL_NO_DISPLAY) {
LOG(ERROR) << "Failed to get egl display";
return false;
}
...
// Create the EGL render target surface
mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);
if (mSurface == EGL_NO_SURFACE) {
LOG(ERROR) << "eglCreateWindowSurface failed.";
return false;
}
...
- Call
IAutomotiveDisplayProxyService::showWindow()
to
display the rendered view on the screen. This service has the highest priority and,
therefore, always takes control of the screen from the current owner:
mAutomotiveDisplayProxyService->showWindow();
See service.cpp
and GlWrapper.cpp
in $ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/
for
more implementation details.
An EVS HAL implementation requires the additional libraries displayed in
bold below.
cc_binary {
name: "android.hardware.automotive.evs@1.1-sample",
vendor: true,
srcs: [
...
],
shared_libs: [
...
"libbufferqueueconverter",
"android.hidl.token@1.0-utils",
"android.frameworks.automotive.display@1.0",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
],
Multi-display support
Like the camera device enumeration, the EVS framework provides a method to
enumerate the available displays. The
static display identifier encodes a type-long identifier, the display
port information in the lower byte and Extended Display IDentification
Data
in upper bits.
IAutomotiveDisplayProxyService::getDisplayIdList()
returns a list
of display IDs of physical local displays, which are available to EVS service,
and IEvsEnumerator::getDisplayIdList()
returns a list of display
ports detected displays are connected to. The first ID in the list is always of
the primary display.
interface IEvsEnumerator extends @1.0::IEvsEnumerator {
...
/**
* Returns a list of all EVS displays available to the system
*
* @return displayIds Identifiers of available displays.
*/
getDisplayIdList() generates (vec<uint8_t> displayIds);
};
Open target display device
EVS app calls IEvsEnumerator::openDisplay_1_1() with a target display
port number:
android::sp<IEvsDisplay> pDisplay = pEvs->openDisplay_1_1(displayId);
if (pDisplay.get() == nullptr) {
LOG(ERROR) << "EVS Display unavailable. Exiting.";
return 1;
}
Note: Only a single display can be in use at one time,
which means the current EVS client loses its display when another EVS client
requests to open the display, even when they are not the same.
Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
Last updated 2024-09-03 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2024-09-03 UTC."],[],[],null,["# Automotive display proxy service\n\nThis simple framework service lets vendor\nprocesses use SurfaceFlinger/EGL in HAL implementations, without linking\nlibgui. AOSP provides the default implementation of this service, which is fully\nfunctional. However, the vendor must also implement APIs to provide this service\non their platform. \n\n```python\npackage android.frameworks.automotive.display@1.0;\n\nimport android.hardware.graphics.bufferqueue@2.0::IGraphicBufferProducer;\n\ninterface IAutomotiveDisplayProxyService {\n /**\n * Gets an IGraphicBufferProducer instance from the service.\n *\n * @param id Target's stable display identifier\n *\n * @return igbp Returns an IGraphicBufferProducer object, that can be\n * converted to an ANativeWindow object.\n */\n getIGraphicBufferProducer(uint64_t id) generates (IGraphicBufferProducer igbp);\n\n /**\n * Sets the ANativeWindow, which is associated with the\n * IGraphicBufferProducer, to be visible and to take over the display.\n *\n * @param id Target display ID\n *\n * @return success Returns true on success.\n */\n showWindow(uint64_t id) generates (bool success);\n\n /**\n * Sets the ANativeWindow, which is associated with the\n * IGraphicBufferProducer, to be invisible and to release the control\n * over display.\n *\n * @param id Target display ID\n *\n * @return success Returns true on success.\n */\n hideWindow(uint64_t id) generates (bool success);\n\n /**\n * Returns the stable identifiers of all available displays.\n *\n * @return ids A list of stable display identifiers.\n */\n getDisplayIdList() generates (vec\u003cuint64_t\u003e ids);\n\n /**\n * Returns the descriptor of the target display.\n *\n * @param id Stable ID of a target display.\n * @return cfg DisplayConfig of the active display.\n * @return state Current state of the active display.\n */\n getDisplayInfo(uint64_t id) generates (HwDisplayConfig cfg, HwDisplayState state);\n}\n```\n\nTo use this service:\n\n1. Get `IAutomotiveDisplayProxyService`. \n\n ```css+lasso\n android::sp\u003cIAutomotiveDisplayProxyService\u003e windowProxyService =\n IAutomotiveDisplayProxyService::getService(\"default\");\n if (windowProxyService == nullptr) {\n LOG(ERROR) \u003c\u003c \"Cannot use AutomotiveDisplayProxyService. Exiting.\";\n return 1;\n }\n ```\n2. Retrieve an active display information from the service to determine the resolution. \n\n ```transact-sql\n // We use the first display in the list as the primary.\n pWindowProxy-\u003egetDisplayInfo(displayId, [this](auto dpyConfig, auto dpyState) {\n DisplayConfig *pConfig = (DisplayConfig*)dpyConfig.data();\n mWidth = pConfig-\u003eresolution.getWidth();\n mHeight = pConfig-\u003eresolution.getHeight();\n\n ui::DisplayState* pState = (ui::DisplayState*)dpyState.data();\n if (pState-\u003eorientation != ui::ROTATION_0 &&\n pState-\u003eorientation != ui::ROTATION_180) {\n // rotate\n std::swap(mWidth, mHeight);\n }\n\n LOG(DEBUG) \u003c\u003c \"Display resolution is \" \u003c\u003c mWidth \u003c\u003c \" x \" \u003c\u003c mHeight;\n });\n ```\n3. Retrieve a hardware `IGraphicBufferProducer` (or, HIDL GraphicBufferProducer (HGBP) from `IAutomotiveDisplayProxyService`: \n\n ```text\n mGfxBufferProducer = pWindowProxy-\u003egetIGraphicBufferProducer(displayId);\n if (mGfxBufferProducer == nullptr) {\n LOG(ERROR) \u003c\u003c \"Failed to get IGraphicBufferProducer from \"\n \u003c\u003c \"IAutomotiveDisplayProxyService.\";\n return false;\n }\n ```\n4. Get a `SurfaceHolder` from a retrieved HGBP, by using the API `libbufferqueueconverter`: \n\n ```text\n mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);\n if (mSurfaceHolder == nullptr) {\n LOG(ERROR) \u003c\u003c \"Failed to get a Surface from HGBP.\";\n return false;\n }\n ```\n5. Convert a `SurfaceHolder` into a native window by using the API `libbufferqueueconverter`: \n\n ```text\n mWindow = getNativeWindow(mSurfaceHolder.get());\n if (mWindow == nullptr) {\n LOG(ERROR) \u003c\u003c \"Failed to get a native window from Surface.\";\n return false;\n }\n ```\n6. Create an EGL window surface with a native window and then render: \n\n ```scilab\n // Set up our OpenGL ES context associated with the default display\n mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);\n if (mDisplay == EGL_NO_DISPLAY) {\n LOG(ERROR) \u003c\u003c \"Failed to get egl display\";\n return false;\n }\n ...\n\n // Create the EGL render target surface\n mSurface = eglCreateWindowSurface(mDisplay, egl_config, mWindow, nullptr);\n if (mSurface == EGL_NO_SURFACE) {\n LOG(ERROR) \u003c\u003c \"eglCreateWindowSurface failed.\";\n return false;\n }\n ...\n ```\n7. Call `IAutomotiveDisplayProxyService::showWindow()` to display the rendered view on the screen. This service has the highest priority and, therefore, always takes control of the screen from the current owner: \n\n ```text\n mAutomotiveDisplayProxyService-\u003eshowWindow();\n ```\n\nSee `service.cpp` and `GlWrapper.cpp`\nin `$ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/` for\nmore implementation details.\n\nAn EVS HAL implementation requires the additional libraries displayed in\n**bold** below. \n\n```objective-c\ncc_binary {\n name: \"android.hardware.automotive.evs@1.1-sample\",\n\n vendor: true,\n\n srcs: [\n ...\n ],\n\n shared_libs: [\n ...\n \"libbufferqueueconverter\",\n \"android.hidl.token@1.0-utils\",\n \"android.frameworks.automotive.display@1.0\",\n \"android.hardware.graphics.bufferqueue@1.0\",\n \"android.hardware.graphics.bufferqueue@2.0\",\n ],\n```\n\nMulti-display support\n---------------------\n\n### Display device enumeration and retrieve display information\n\nLike the camera device enumeration, the EVS framework provides a method to\nenumerate the available displays. The [static display identifier](/devices/tech/display/multi_display/displays#static) encodes a type-long identifier, the display\nport information in the lower byte and `Extended Display IDentification\nData` in upper bits.\n`IAutomotiveDisplayProxyService::getDisplayIdList()` returns a list\nof display IDs of physical local displays, which are available to EVS service,\nand `IEvsEnumerator::getDisplayIdList()` returns a list of display\nports detected displays are connected to. The first ID in the list is always of\nthe primary display. \n\n```objective-c\ninterface IEvsEnumerator extends @1.0::IEvsEnumerator {\n ...\n /**\n * Returns a list of all EVS displays available to the system\n *\n * @return displayIds Identifiers of available displays.\n */\n getDisplayIdList() generates (vec\u003cuint8_t\u003e displayIds);\n};\n```\n\n### Open target display device\n\nEVS app calls IEvsEnumerator::openDisplay_1_1() with a target display\nport number: \n\n```css+lasso\nandroid::sp\u003cIEvsDisplay\u003e pDisplay = pEvs-\u003eopenDisplay_1_1(displayId);\nif (pDisplay.get() == nullptr) {\n LOG(ERROR) \u003c\u003c \"EVS Display unavailable. Exiting.\";\n return 1;\n}\n```\n\n**Note:** Only a single display can be in use at one time,\nwhich means the current EVS client loses its display when another EVS client\nrequests to open the display, even when they are not the same."]]