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 AP...

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.

15 June 2020, 01:58 | Views: 9150

Add new comment

For adding a comment, please log in
or create account

0 comments