這個簡單的架構服務可讓供應商程序在 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 用戶端會失去螢幕,即使兩者不相同也是如此。