자동차 디스플레이 프록시 서비스

간단한 이 새 프레임워크 서비스는 공급업체 프로세스에서 libgui를 연결하지 않고도 HAL 구현에서 SurfaceFlinger/EGL을 사용할 수 있도록 하기 위해 제공됩니다. 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);
}

이 서비스를 사용하려면 다음 단계를 따르세요.

  1. IAutomotiveDisplayProxyService를 다운로드합니다.
    android::sp<IAutomotiveDisplayProxyService> windowProxyService =
        IAutomotiveDisplayProxyService::getService("default");
    if (windowProxyService == nullptr) {
        LOG(ERROR) << "Cannot use AutomotiveDisplayProxyService. Exiting.";
        return 1;
    }
    
  2. 서비스에서 활성 디스플레이 정보를 검색하여 해상도를 확인합니다.
    // We will 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;
    });
    
  3. IAutomotiveDisplayProxyService에서 하드웨어 IGraphicBufferProducer(또는 HIDL GraphicBufferProducer(HGBP))를 검색합니다.
    mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
    if (mGfxBufferProducer == nullptr) {
        LOG(ERROR) << "Failed to get IGraphicBufferProducer from "
                   << "IAutomotiveDisplayProxyService.";
        return false;
    }
    
  4. API libbufferqueueconverter을 사용하여, 검색된 HGBP에서 SurfaceHolder를 가져옵니다.
    mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
    if (mSurfaceHolder == nullptr) {
        LOG(ERROR) << "Failed to get a Surface from HGBP.";
        return false;
    }
    
  5. API libbufferqueueconverter를 사용하여 SurfaceHolder을 네이티브 창으로 변환합니다.
    mWindow = getNativeWindow(mSurfaceHolder.get());
    if (mWindow == nullptr) {
        LOG(ERROR) << "Failed to get a native window from Surface.";
        return false;
    }
    
  6. 네이티브 창이 있는 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;
    }
    ...
    
  7. IAutomotiveDisplayProxyService::showWindow()를 호출하여 화면에 렌더링된 뷰를 표시합니다. 이 서비스가 우선순위가 가장 높으므로 현재 소유자의 화면을 항상 제어합니다.
    mAutomotiveDisplayProxyService->showWindow();
    

구현 세부정보는 $ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/service.cppGlWrapper.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 프레임워크는 사용 가능한 디스플레이를 열거하는 메서드를 제공합니다. 정적 디스플레이 식별자는 long 유형의 식별자, 낮은 바이트에서는 포트 정보를, 높은 비트에서는 Extended Display IDentification Data를 인코딩합니다. IAutomotiveDisplayProxyService::getDisplayIdList()는 EVS 서비스에 사용할 수 있는 물리적 로컬 디스플레이의 표시 ID 목록을 반환하고, IEvsEnumerator::getDisplayIdList()는 감지된 디스플레이가 연결된 디스플레이 포트 목록을 반환합니다. 목록의 첫 번째 ID는 항상 기본 디스플레이의 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 클라이언트는 디스플레이를 잃게 됩니다. 요청한 디스플레이가 같지 않은 경우에도 마찬가지입니다.