Android Q graphics system uses pure native API to draw a window

This article draws a window according to the previous analysis and implements it with the pure native API. The native API here is not ndk, but a native layer function, which needs to be implemented in the AOSP compilation environment.

First, create the drawWindowTest test directory in the / frameworks/native/libs/gui / directory, and create it in the drawWindowTest directory Android.bp and DrawWindowTest.cpp :

Android.bp:

cc_binary {
    name: "drawWindow",
    srcs: ["DrawWindowTest.cpp"],
    shared_libs: [
        "liblog",
        "libbinder",
        "libgui",
        "libui",
        "libutils",
    ],
}

Let's see DrawWindowTest.cpp How to write? In the analysis of the previous articles, we have roughly understood the process of drawing a window:

  1. First, you need to establish a connection with the SurfaceFlinger process
  2. Create the carrier Surface of graphic data corresponding to SurfaceFlinger process Layer
  3. With Surface, you also need to connect the Surface to the BufferQueue
  4. Set various data of Surface, such as width, height, format, Z-order, position, scale, etc
  5. Call dequeueBuffer to get buffer
  6. Fill graph data into buffer
  7. Call queueBuffer to send buffer to SurfaceFlinger synthesis

Our code is written according to the above steps, DrawWindowTest.cpp The source code is as follows:

#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <system/window.h>
#include <ui/GraphicBuffer.h>
#include <ui/Fence.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <gui/SurfaceControl.h>
#include <gui/SurfaceComposerClient.h>
#include <binder/IBinder.h>
#include <ui/DisplayInfo.h>
#include <gui/Surface.h>
using namespace android;
//Status value
status_t err;

sp<SurfaceComposerClient> mSurfaceComposerClient;

sp<SurfaceControl> mSurfaceControl;
//Screen width and height
int mWidth,mHeight;
//The parent class of GraphicBuffer, antiativewindowbuffer
ANativeWindowBuffer *mNativeBuffer = nullptr;
//Connect SurfaceFlinger
void connectSurfaceFlinger();
//Get Surface
sp<ANativeWindow> getSurface();
//Connect to BufferQueue
void connectBufferQueue(ANativeWindow *surface);
//Set Transaction
void setBufferTransaction();
//Set up additional information
void setBuffer(ANativeWindow *surface);

int main() {
    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    //Connect SurfaceFlinger
    connectSurfaceFlinger();
    //Get surface
    ANativeWindow *surface = getSurface().get();
    //surface connection bufferqueue
    connectBufferQueue(surface);
    //Set Transaction
    setBufferTransaction();
    //Set up additional information
    setBuffer(surface);

    int fenceFD= -1;
    //Apply for buffer
    err = surface->dequeueBuffer(surface, &mNativeBuffer, &fenceFD);
    if (err != NO_ERROR) {
            ALOGE("dequeueBuffer err....");
        }
        //Confirm whether the requested buffer is completely used by the previous user through Fence
        sp<Fence> fence(new Fence(fenceFD));
        //Waiting to receive releaseFence
        int waitResult = fence->waitForever("dequeueBuffer_EmptyNative");
        if (waitResult != OK) {
           ALOGE("Fence wait err....");
        }
        //ANativeWindowBuffer to GraphicBuffer
        sp<GraphicBuffer> buff(GraphicBuffer::from(mNativeBuffer));
        //buffer data
        uint8_t *data = NULL;
        //Lock the buffer first through lock
        err = buff->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&data));
        if (err != NO_ERROR) {
            ALOGE("lock buffer err....");
        }

        //Fill in the data. If the value is 0, a black window will be drawn
        *data = 0;
        err = buff->unlock();
        if (err != NO_ERROR) {
            ALOGE("unlock buffer err....");
        }

        //Send the filled buffer to SurfaceFlinger for composite display
        err = surface->queueBuffer(surface, buff->getNativeBuffer(), -1);
        if (err != NO_ERROR) {
            ALOGE("queueBuffer buffer err....");
            return err;
        }
    mNativeBuffer = NULL;
    IPCThreadState::self()->joinThreadPool();
    return EXIT_SUCCESS;
}

//1. Create SurfaceComposerClient, which is the Client side of SurfaceFlinger
void connectSurfaceFlinger(){
    
    mSurfaceComposerClient = new SurfaceComposerClient;
    err = mSurfaceComposerClient->initCheck();
    if (err != NO_ERROR) {
        ALOGE("SurfaceComposerClient initCheck err....");
        return;
    }
}
//2. Create Surface. Antiativewindow is the parent class of Surface
sp<ANativeWindow> getSurface(){
    sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
    android::DisplayInfo mainDisplayInfo;
    //Get screen information of mobile phone
    err = SurfaceComposerClient::getDisplayInfo(display, &mainDisplayInfo);
    if (err != NO_ERROR) {
        ALOGE("getDisplayInfo err....");
    }
    //Screen width
    mWidth = mainDisplayInfo.w;
    //Screen height
    mHeight = mainDisplayInfo.h;
    //Create surface corresponding surface linker process layer
    mSurfaceControl = mSurfaceComposerClient->createSurface(
            String8("drawWindow"), mWidth/2, mHeight/2,
            PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
    if (mSurfaceControl == NULL || !mSurfaceControl->isValid()) {
        ALOGE("mSurfaceControl err....");
    }
    //Get surface
    sp<ANativeWindow> anw = mSurfaceControl->getSurface();
    return anw;
}
//3.Surface connection BufferQueue
void connectBufferQueue(ANativeWindow *surface){
    err = native_window_api_connect(surface, NATIVE_WINDOW_API_CPU);
    if (err != NO_ERROR) {
        ALOGE("connect bufferqueue err....");
    }
}
//4. Set buffer data
void setBufferTransaction(){
    /*
    setLayer(): Set the Z-order of the window
    setPosition(): Set the position of window display
    show(): The settings window is displayed
    apply(): Apply the set window information to the SurfaceFlinger for real effect
    */
    SurfaceComposerClient::Transaction{}
            .setLayer(mSurfaceControl, 0x7FFFFFFF)
            .setPosition(mSurfaceControl,mWidth/4,mHeight/4)
            .show(mSurfaceControl)
            .apply();
}
//5. Set buffer
void setBuffer(ANativeWindow *surface){
    //Set usage
    err = native_window_set_usage(surface, GRALLOC_USAGE_SW_WRITE_OFTEN);
    if (err != NO_ERROR) {
	ALOGE("native_window_set_usage err....");	
    }
    //Set transform
    err = native_window_set_buffers_transform(surface, NATIVE_WINDOW_TRANSFORM_ROT_90);
    if (err != NO_ERROR) {
        ALOGE("native_window_set_buffers_transform err....");
    }
    //Setting scaling_mode
    err = native_window_set_scaling_mode(
            surface, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err != NO_ERROR) {
         ALOGE("native_window_set_scaling_mode err....");
    }
}

The above code is basically implemented according to the window drawing process. Look at the setBuffer function above, which calls a series of native functions_ window_ set_ XXX function to set various information for a Surface, in fact, it is ultimately called to the Surface setXXX function, native_window_set_XXX provides an interface for external calls to operate the Surface. These functions are defined in frameworks / native / LIBS / native window / include / system / window. H. take a look at native for example_ window_ set_ Usage function

static inline int native_window_set_usage(struct ANativeWindow* window, uint64_t usage) {
    return window->perform(window, NATIVE_WINDOW_SET_USAGE64, usage);
}

The perform function of antiativewindow will be called:

int     (*perform)(struct ANativeWindow* window,
                int operation, ... );

The function pointer is assigned in the Surface constructor:

ANativeWindow::perform          = hook_perform;
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
    va_list args;
    va_start(args, operation);
    Surface* c = getSelf(window);
    int result = c->perform(operation, args);
    va_end(args);
    return result;
}

hook_ The perform function also calls the Surface's perform function:

int Surface::perform(int operation, va_list args)
{
    int res = NO_ERROR;
    switch (operation) {
    case NATIVE_WINDOW_CONNECT:
        // deprecated. must return NO_ERROR.
        break;
    case NATIVE_WINDOW_DISCONNECT:
        // deprecated. must return NO_ERROR.
        break;
    case NATIVE_WINDOW_SET_USAGE:
        res = dispatchSetUsage(args);
        break;
    case NATIVE_WINDOW_SET_CROP:
        res = dispatchSetCrop(args);
        break;
    case NATIVE_WINDOW_SET_BUFFER_COUNT:
        res = dispatchSetBufferCount(args);
        .......
   }

There are many functions in it. Call different dispatchXXX according to the passed parameters, such as native_window_set_usage passes NATIVE_WINDOW_SET_USAGE64 to call dispatchSetUsage64:

case NATIVE_WINDOW_SET_USAGE64:
        res = dispatchSetUsage64(args);
        break;

dispatchSetUsage64 calls setUsage and finally sets the data to the Surface

int Surface::dispatchSetUsage64(va_list args) {
    uint64_t usage = va_arg(args, uint64_t);
    return setUsage(usage);
}

Drawing a window has a lot of information that must be set. There are related judgments in the dequeueBuffer and queueBuffer analyzed before, such as width and height cannot be less than 0, and BufferQueue must be connected.

Compile test program: mmm frameworks/native/libs/gui/drawWindowTest/

push to mobile:
adb push out/target/product/SEOUL_ATT/system/bin/drawWindow /system/bin

Run this test program: adb shell /system/bin/drawWindow
The effect is as follows:

Through this test program combined with previous theoretical articles, you can be more familiar with Android graphics architecture.

Tags: Android Mobile Linker less

Posted on Mon, 15 Jun 2020 01:58:37 -0400 by akumakeenta