自 2025 年 3 月 27 日起,我們建議您使用 android-latest-release
而非 aosp-main
建構及貢獻 AOSP。詳情請參閱「Android 開放原始碼計畫變更」。
汽車顯示 Proxy 服務
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
這個簡單的架構服務可讓供應商程序在 HAL 實作中使用 SurfaceFlinger/EGL,而無須連結 libgui。AOSP 提供這項服務的預設實作方式,可提供完整功能。不過,供應商也必須實作 API,才能在平台上提供這項服務。
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);
}
如要使用這項服務,請按照下列步驟操作:
- 取得
IAutomotiveDisplayProxyService
。android::sp<IAutomotiveDisplayProxyService> windowProxyService =
IAutomotiveDisplayProxyService::getService("default");
if (windowProxyService == nullptr) {
LOG(ERROR) << "Cannot use AutomotiveDisplayProxyService. Exiting.";
return 1;
}
- 從服務中擷取有效的顯示資訊,以便判斷解析度。
// 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;
});
- 從
IAutomotiveDisplayProxyService
擷取硬體 IGraphicBufferProducer
(或 HIDL GraphicBufferProducer (HGBP):mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
if (mGfxBufferProducer == nullptr) {
LOG(ERROR) << "Failed to get IGraphicBufferProducer from "
<< "IAutomotiveDisplayProxyService.";
return false;
}
- 使用 API
libbufferqueueconverter
從擷取的 HGBP 取得 SurfaceHolder
:mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
if (mSurfaceHolder == nullptr) {
LOG(ERROR) << "Failed to get a Surface from HGBP.";
return false;
}
- 使用 API
libbufferqueueconverter
,將 SurfaceHolder
轉換為原生視窗:mWindow = getNativeWindow(mSurfaceHolder.get());
if (mWindow == nullptr) {
LOG(ERROR) << "Failed to get a native window from Surface.";
return false;
}
- 使用原生視窗建立 EGL 視窗介面,然後算繪:
// 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;
}
...
- 呼叫
IAutomotiveDisplayProxyService::showWindow()
即可在螢幕上顯示算繪的檢視畫面。此服務的優先順序最高,因此一律會從目前的擁有者手中取得螢幕控制權:mAutomotiveDisplayProxyService->showWindow();
如需進一步瞭解實作方式,請參閱 $ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/
中的 service.cpp
和 GlWrapper.cpp
。
實作 EVS HAL 時,需要使用下方以粗體顯示的額外程式庫。
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",
],
多螢幕支援
與相機裝置列舉相同,EVS 架構也提供方法來列舉可用的螢幕。
靜態顯示 ID會編碼類型長 ID、顯示埠資訊 (位於較低位元組) 和 Extended Display IDentification
Data
(位於較高位元)。IAutomotiveDisplayProxyService::getDisplayIdList()
會傳回實體本機螢幕的顯示 ID 清單,供 EVS 服務使用,而 IEvsEnumerator::getDisplayIdList()
會傳回偵測到的螢幕連線顯示器埠清單。清單中的第一個 ID 一律是主要顯示裝置。
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);
};
開啟目標顯示裝置
EVS 應用程式會使用目標顯示器埠號碼呼叫 IEvsEnumerator::openDisplay_1_1():
android::sp<IEvsDisplay> pDisplay = pEvs->openDisplay_1_1(displayId);
if (pDisplay.get() == nullptr) {
LOG(ERROR) << "EVS Display unavailable. Exiting.";
return 1;
}
注意:一次只能使用單一螢幕,也就是說,當另一個 EVS 用戶端要求開啟螢幕時,目前的 EVS 用戶端會失去螢幕,即使兩者不相同也是如此。
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-08-08 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-08-08 (世界標準時間)。"],[],[],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."]]