บริการเฟรมเวิร์กแบบง่ายนี้ช่วยให้กระบวนการของผู้ให้บริการใช้ SurfaceFlinger/EGL ในการใช้งาน HAL ได้โดยไม่ต้องลิงก์ 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; });
- เรียกข้อมูลฮาร์ดแวร์
IGraphicBufferProducer
(หรือ HIDL GraphicBufferProducer (HGBP) จากIAutomotiveDisplayProxyService
mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId); if (mGfxBufferProducer == nullptr) { LOG(ERROR) << "Failed to get IGraphicBufferProducer from " << "IAutomotiveDisplayProxyService."; return false; }
- รับ
SurfaceHolder
จาก HGBP ที่ดึงข้อมูลมาโดยใช้ APIlibbufferqueueconverter
mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer); if (mSurfaceHolder == nullptr) { LOG(ERROR) << "Failed to get a Surface from HGBP."; return false; }
- แปลง
SurfaceHolder
เป็นหน้าต่างเนทีฟโดยใช้ APIlibbufferqueueconverter
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();
ดูรายละเอียดการติดตั้งใช้งานเพิ่มเติมได้ใน service.cpp
และ GlWrapper.cpp
ใน $ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/
การติดตั้งใช้งาน 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 มีวิธีการแสดงรายการจอแสดงผลที่พร้อมใช้งานเช่นเดียวกับการแจกแจงอุปกรณ์กล้อง
ตัวระบุจอแสดงผลแบบคงที่จะเข้ารหัสตัวระบุแบบยาว ข้อมูลพอร์ตแสดงผลในไบต์ล่าง และ Extended Display IDentification
Data
ในบิตบน
IAutomotiveDisplayProxyService::getDisplayIdList()
จะแสดงรายการรหัสจอแสดงผลของจอแสดงผลจริงในเครื่อง ซึ่งพร้อมให้บริการสำหรับบริการ EVS และ IEvsEnumerator::getDisplayIdList()
จะแสดงรายการพอร์ตจอแสดงผลที่เชื่อมต่อกับจอแสดงผลที่ตรวจพบ รหัสแรกในรายการจะเป็นของจอแสดงผลหลักเสมอ
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 รายอื่นขอเปิดจอแสดงผล แม้ว่าไคลเอ็นต์ดังกล่าวจะไม่ใช้จอแสดงผลเดียวกันก็ตาม