שירות proxy לתצוגה ברכב

שירות המסגרת הפשוט הזה מאפשר לתהליכים של ספקים להשתמש ב-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);
}

כדי להשתמש בשירות הזה:

  1. מקבלים את IAutomotiveDisplayProxyService.
    android::sp<IAutomotiveDisplayProxyService> windowProxyService =
        IAutomotiveDisplayProxyService::getService("default");
    if (windowProxyService == nullptr) {
        LOG(ERROR) << "Cannot use AutomotiveDisplayProxyService. Exiting.";
        return 1;
    }
  2. אחזור מידע על תצוגה פעילה מהשירות כדי לקבוע את הרזולוציה.
    // 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;
    });
  3. אחזור של חומרה IGraphicBufferProducer (או,‏ HIDL GraphicBufferProducer‏ (HGBP) מ-IAutomotiveDisplayProxyService:
    mGfxBufferProducer = pWindowProxy->getIGraphicBufferProducer(displayId);
    if (mGfxBufferProducer == nullptr) {
        LOG(ERROR) << "Failed to get IGraphicBufferProducer from "
                   << "IAutomotiveDisplayProxyService.";
        return false;
    }
  4. אחזור SurfaceHolder מ-HGBP שאוחזר באמצעות ה-API‏libbufferqueueconverter:
    mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
    if (mSurfaceHolder == nullptr) {
        LOG(ERROR) << "Failed to get a Surface from HGBP.";
        return false;
    }
  5. כדי להמיר SurfaceHolder לחלון מקורי, משתמשים ב-API libbufferqueueconverter:
    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();

פרטים נוספים על ההטמעה מופיעים בקטע service.cpp ובקטע GlWrapper.cpp ב-$ANDROID_BUILD_TOP/packages/services/Car/evs/sampleDriver/.

כדי להטמיע HAL של EVS, נדרשות הספריות הנוספות שמופיעות מודגשות בהמשך.

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 אחר מבקש לפתוח את המסך, גם אם הם לא זהים.