Android screen refresh mechanism

Previously we talked about layout optimization and mentioned that the Android system sends a VSYNC signal every 16ms and then performs a UI rendering. If the rendering is successful, the interface is basically smooth.

Let’s take a look at how the Android system does the screen refresh mechanism, and how to ensure that every time we click or touch the screen, we can quickly process the corresponding events.

VSync comes from the reporting of the underlying hardware driver. For the interface visible to Android, it is an abstract hardware device from HAL layer hwC_comPOser_device

Basic knowledge of

The View map

This section was covered in a previous article on Android’s View drawing mechanism

In our previous code, there was no detailed explanation of the part 15-17, so how does the bottom layer generate the Vsync signal and then inform our application to refresh the screen? That is the focus of this article.

The entrance

	mChoreographer = Choreographer.getInstance();
	
	//Choreographer.java	frameworks\base\core\java\android\view
    public static Choreographer getInstance(a) {
        return sThreadInstance.get();
    }	

    private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue(a) {
            // Get the corresponding looper
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
			// Notice the VSYNC_SOURCE_APP used here
            Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = choreographer;
            }
            returnchoreographer; }};private Choreographer(Looper looper, int vsyncSource) {
		/ / create signal is VSYNC_SOURCE_APP FrameDisplayEventReceiver, APP layer request VSYNC
        mDisplayEventReceiver = USE_VSYNC? new FrameDisplayEventReceiver(looper, vsyncSource): null; . }Copy the code

Here to initialize FrameDisplayEventReceiver class inherits from DisplayEventReceiver class

    public DisplayEventReceiver(Looper looper, int vsyncSource) {
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }

        mMessageQueue = looper.getQueue();
		// Call the underlying initialization and pass in itself and the corresponding mMessageQueue
        / / frameworks/base/core/jni \ android_view_DisplayEventReceiver CPP
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,vsyncSource);

        mCloseGuard.open("dispose");
    }
Copy the code

This calls the Native layer’s methods and passes the current DisplayEventReceiver along with the queue mMessageQueue and **vsyncSource(VSYNC_SOURCE_APP)** to the bottom layer

nativeInit

//frameworks\base\core\jni\android_view_DisplayEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, jint vsyncSource) {
    // Apply for the corresponding MessageQueuesp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); ./ / create NativeDisplayEventReceiver key method 1
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverWeak, messageQueue, vsyncSource);
	/ / the key methods to initialize NativeDisplayEventReceiver 2, initialization and return the corresponding results
    status_t status = receiver->initialize();
    if (status) {// Initialization is abnormalString8 message; led to initialize display event receiver. status message.appendFormat("Fai=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }
    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

Copy the code

Here we see the NativeDisplayEventReceiver creation process.

[NativeDisplayEventReceiver create]

NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
        jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource) :
        // Inherits the DisplayEventDispatcher and passes in the corresponding Messagequeue, converting the vsyncSource to the underlying variable
        DisplayEventDispatcher(messageQueue->getLooper(),
                static_cast<ISurfaceComposer::VsyncSource>(vsyncSource)),
        mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
        mMessageQueue(messageQueue) {
    ALOGV("receiver %p ~ Initializing display event receiver.".this);
}

//DisplayEventDispatcher constructor
DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,ISurfaceComposer::VsyncSource vsyncSource) :
        // The source of Vsync is passed to the mReceiver. This is equivalent to calling the mReceiver(DisplayEventReceiver) constructor
        mLooper(looper), mReceiver(vsyncSource), mWaitingForVsync(false) {
    ALOGV("dispatcher %p ~ Initializing display event dispatcher.".this);
}


Copy the code

This will create a DisplayEventReceiver

//DisplayEventReceiver constructor frameworks/native/libs/GUI /DisplayEventReceiver
DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
                                           ISurfaceComposer::ConfigChanged configChanged) {
    // Get the SurfaceFling service and save it in the ComposerService
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if(sf ! =nullptr) {
		/ / method 2 by binder, the last call surfaceFling createDisplayEventConnection method across processes
		// Method location isurfacecomposer. CPP frameworks\native\libs\ GUI 66331 2020/3/22 1379
        mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
        if(mEventConnection ! =nullptr) {
			3 / / method
            mDataChannel = std::make_unique<gui::BitTube>();
			4 / / method
            mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}Copy the code

The DisplayEventReceiver structure is an important class whose main purpose is to establish a connection to the SurfaceFlinger. We will do a self-study analysis of each of its methods

  • Method 1: Obtain the SurfaceFlinger service

sp sf(ComposerService::getComposerService());

ComposerService::getComposerService()
// frameworks\native\libs\gui\SurfaceComposerClient.cpp
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService(a) {
    ComposerService& instance = ComposerService::getInstance();
    Mutex::Autolock _l(instance.mLock);/ / lock
    if (instance.mComposerService == nullptr) {
		// Get the SurfaceFling service and save it in the ComposerServiceComposerService::getInstance().connectLocked(); assert(instance.mComposerService ! =nullptr);
        ALOGD("ComposerService reconnected");
    }
    return instance.mComposerService;
}

void ComposerService::connectLocked(a) {
    const String16 name("SurfaceFlinger");
	// Get the SurfaceFlinger service using the getService method and save the obtained service to the mComposerService variable
    while(getService(name, &mComposerService) ! = NO_ERROR) { usleep(250000);
    }
    // Create death callback. mDeathObserver =new DeathObserver(*const_cast<ComposerService*>(this));
    IInterface::asBinder(mComposerService)->linkToDeath(mDeathObserver);
}
Copy the code

Obtain the corresponding SurfaceFlinger service through getService method. The obtained service is saved in the mComposerService variable.

  • Creating event connections
sf->createDisplayEventConnection
    virtual sp<IDisplayEventConnection> createDisplayEventConnection(VsyncSource vsyncSource,ConfigChanged configChanged) {
        Parcel data, reply;
        sp<IDisplayEventConnection> result;
		/ / binder mechanism called SurfaceFling createDisplayEventConnection method
		//SurfaceFlinger.cpp	frameworks\native\services\surfaceflinger
        int err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
        data.writeInt32(static_cast<int32_t>(vsyncSource));
        data.writeInt32(static_cast<int32_t>(configChanged)); err = remote()->transact( BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, data, &reply); . result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());return result;
    }
Copy the code

As you can see, the Binder mechanism is used and the provider of the service is SurfaceFlinger.

// Create a display event connection
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
    //makeResyncCallback is a method defined in eventThread.h. using ResyncCallback = std::function
      
       ;
      ()>
    // Create a resyncCallback
    auto resyncCallback = mScheduler->makeResyncCallback([this] {
        Mutex::Autolock lock(mStateLock);
        return getVsyncPeriod();
    });
	// Depending on the Vsync type passed in, different handlers are returned. If registered in an application, mAppConnectionHandle is returned
    const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
	/ / call createDisplayEventConnection, introduced the corresponding handle, mScheduler is Scheduler. The CPP structures
    return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
                                                    configChanged);
}

Copy the code

Returns a specific Handler based on the vsyncSource type passed in. Because of the type of application we used here, the Handle is mAppConnectionHandle.

The corresponding connection is then created through mScheduler.

Here we need a footnote for Handle

Supplementary notes:

The creation of the Handler is done in SurfaceFlinger’s initialization method init()

void SurfaceFlinger::init(a) {... mAppConnectionHandle = mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
                                     mPhaseOffsets->getOffsetThresholdForNextVsync(),
                                     resyncCallback,
                                     impl::EventThread::InterceptVSyncsCallback()); . }sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
        ResyncCallback resyncCallback,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    // The corresponding ID, cumulative
    const int64_t id = sNextId++;
	// Create an EventThread with the name of the incoming connectionName
    std::unique_ptr<EventThread> eventThread =
            makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
                            offsetThresholdForNextVsync, std::move(interceptCallback));
	/ / create EventThreadConnection
    auto eventThreadConnection = createConnectionInternal(eventThread.get(), std::move(resyncCallback),
                                     ISurfaceComposer::eConfigChangedSuppress);
	// Create ConnectionHandle with id,
	// Then store the created connection into the map. The key is the id.
    mConnections.emplace(id,
                         std::make_unique<Connection>(new ConnectionHandle(id),
                                                      eventThreadConnection,
                                                      std::move(eventThread)));
    return mConnections[id]->handle;
}
Copy the code

The Handler created here holds the corresponding EventThread, and the eventThreadConnection is created through the EventThread. Once an eventThreadConnection is created, it is saved to the map and the corresponding key is the ID information.

The connection handler: ConnectionHandle is an object that holds an ID.

Let’s go back to the main story…

mScheduler->createDisplayEventConnection
//	frameworks\native\services\surfaceflinger\Scheduler\Scheduler.cpp

sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
        ISurfaceComposer::ConfigChanged configChanged) {
    RETURN_VALUE_IF_INVALID(nullptr);
	// Handle.id is passed. Indicates whether the connection is app or surfaceFlinger
    return createConnectionInternal(mConnections[handle->id]->thread.get(),
                                    std::move(resyncCallback), configChanged);
}

sp<EventThreadConnection> Scheduler::createConnectionInternal( EventThread* eventThread, ResyncCallback&& resyncCallback, ISurfaceComposer::ConfigChanged configChanged) {
    // Call the EventThread method to create the event connector
    return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
}

Copy the code

Let’s look at the creation of the event connector EventThreadConnection

sp<EventThreadConnection> EventThread::createEventConnection( ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
    return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
                                     configChanged);
}


EventThreadConnection::EventThreadConnection(EventThread* eventThread,
                                             ResyncCallback resyncCallback,
                                             ISurfaceComposer::ConfigChanged configChanged)
      : resyncCallback(std::move(resyncCallback)),
        configChanged(configChanged),
        mEventThread(eventThread),
        mChannel(gui::BitTube::DefaultSize) {}

Copy the code

The most important constructor of EventThreadConnection is the creation of an mChannel, which is of type GUI ::BitTube

//	frameworks\native\libs\gui\BitTube.cpp
BitTube::BitTube(size_t bufsize) {
    init(bufsize, bufsize);
}


void BitTube::init(size_t rcvbuf, size_t sndbuf) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        size_t size = DEFAULT_SOCKET_BUFFER_SIZE;
		// Create a pair of sockets: 0 and 1, one for reading and one for writing.
        setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
        setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
        // since we don't use the "return channel", we keep it small...
        setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
        setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
        fcntl(sockets[0], F_SETFL, O_NONBLOCK);
        fcntl(sockets[1], F_SETFL, O_NONBLOCK);
		// Bind the mReceiveFd file to the socket. When Vsync arrives, mSendFd file will be used to write messages. By writing messages to the file, the Vsync signal is monitored
        mReceiveFd.reset(sockets[0]);
        mSendFd.reset(sockets[1]);
    } else {
        mReceiveFd.reset();
    }
}

Copy the code

In the initialization method, a pair of sockets are created and then mReceiveFd and mSendFd are bound. When Vsync arrives, a message is written to mSendFd and the APP can then listen for changes to the file.

The onFirstRef method is automatically called when the EventThreadConnection object is created.

void EventThreadConnection::onFirstRef(a) {
    mEventThread->registerDisplayEventConnection(this);
}

status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
    std::lock_guard<std::mutex> lock(mMutex);

    // this should never happen
    auto it = std::find(mDisplayEventConnections.cbegin(),
            mDisplayEventConnections.cend(), connection);
    if(it ! = mDisplayEventConnections.cend()) {
        ALOGW("DisplayEventConnection %p already exists", connection.get());
        mCondition.notify_all(a);return ALREADY_EXISTS;
    }
	// Put the connection into the list of notifications.
    mDisplayEventConnections.push_back(connection);
	// When a new connection is made, you need to wake up the AppEventThread thread to enable Vsync.
    mCondition.notify_all(a);return NO_ERROR;
}

Copy the code

The connection we created is put into mDisplayEventConnections managed by EventThread, and the AppEventThread thread is awakened to enable the Vsync signal

The whole step 2 is actually to guide the corresponding listener from APP according to the incoming vsyncSource, and then create a pair of socket connections for inter-process communication.

Let’s go back to the main line and trace

DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
                                           ISurfaceComposer::ConfigChanged configChanged) {
    // Get the SurfaceFling service and save it in the ComposerService
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if(sf ! =nullptr) {
		/ / method 2 by binder, the last call surfaceFling createDisplayEventConnection method across processes
		// Method location ISurfaceComposer. CPP frameworks\native\libs\ GUI
        mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
        if(mEventConnection ! =nullptr) {
			// Method 3 gets the GUI ::BitTube object created in method 2
            mDataChannel = std::make_unique<gui::BitTube>();
			4 / / method
            mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}Copy the code

Method 3 gets the corresponding GUI ::BitTube object. Let’s focus on method four.

Method four calls The stealReceiveChannel of EventThreadConnect

status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
    outChannel->setReceiveFd(mChannel.moveReceiveFd());
    return NO_ERROR;
}

Copy the code

The mChannel for this is GUI ::BitTube. The Fd created in the event connector EventThreadConnection is copied to outChannel. MDataChannel for DisplayEventReceiver.

The app process has mReceivedFd and the surfaceFlinger process has mSendFd. At this time, communication can be carried out through the socket.

The purpose of the DisplayEventReceiver is to create a socket and the corresponding file, and then implement two-way communication with the SurfaceFlinger.

Here, we are so far, we just create NativeDisplayEventReceiver.

So there’s a follow-up

receiver->initialize()

status_t DisplayEventDispatcher::initialize(a) {
	// Exception detection
    status_t result = mReceiver.initCheck(a);if (result) {
        ALOGW("Failed to initialize display event receiver, status=%d", result);
        return result;
    }
	// Looper is the main thread Looper of the app process. This step is the BitTube channel that will be created
	//fd adds listener to Looper.
    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
            this.NULL);
    if (rc < 0) {
        return UNKNOWN_ERROR;
    }
    return OK;
}

Copy the code

We were able to tap in here because of our

The whole method here is simple: check for the exception and add the FD file created in Step 1 to Looper’s listener.

So far, the whole process has been worked out.

The Java layer creates a connection between the application layer and SurfaceFlinger through a pair of sockets corresponding to mReceiveFd and mSendFd, using the nativeInit function of DisplayEventReceive. The application layer adds the mReceiveFd listener to the native Looper and waits for the mSendFd to be written.

So when and how does mSendFd get written to the application layer?

While we are drawing the page refresh, how do we register for listening on Vsync

    @UnsupportedAppUsage
    void scheduleTraversals(a) {... mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable,null); . }public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

    public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

    private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {...// It needs to be drawn immediatelyscheduleFrameLocked(now); . }private void scheduleFrameLocked(long now) {... scheduleVsyncLocked(); . }private void scheduleVsyncLocked(a) {
        // Perform synchronization and draw once. A VSYNC event listener is registered, if any
        mDisplayEventReceiver.scheduleVsync();
    }

    public void scheduleVsync(a) {.. nativeScheduleVsync(mReceiverPtr); . }Copy the code

**nativeScheduleVsync()** is how the application layer registers with the native layer to listen for the next Vsync signal.

nativeScheduleVsync

//base\core\jni\android_view_DisplayEventReceiver.cpp 8492 2020/9/14 96
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
    sp<NativeDisplayEventReceiver> receiver =
            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
    // call the scheduling method from Recivier
    status_t status = receiver->scheduleVsync(a); }Copy the code

The receiver, here is NativeDisplayEventReceiver. And NativeDisplayEventReceiver is inherited from DisplayEventDispatcher

DisplayEventDispatcher->scheduleVsync();

/ / scheduling Vsync
status_t DisplayEventDispatcher::scheduleVsync(a) {
	// If you are currently waiting for a Vsync signal, return directly
    if(! mWaitingForVsync) {nsecs_t vsyncTimestamp;
        PhysicalDisplayId vsyncDisplayId;
        uint32_t vsyncCount;
		// Key method 1 handles the corresponding preparation event, which returns true if a Vsync signal is obtained
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "".this.ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
		// Key method 2 requests the next Vsync signal
        status_t status = mReceiver.requestNextVsync(a); .// Setup is waiting for Vsync signal
        mWaitingForVsync = true;
    }
    return OK;
}

Copy the code

So let’s track method 1 here

DisplayEventDispatcher::processPendingEvents
bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    // Get the corresponding event
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        ALOGV("dispatcher %p ~ Read %d events.".this.int(n));
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:/ / Vsync type
                // Get the latest Vsync signal and save the timestamp and other information
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break; .return gotVsync;
}

Copy the code

The corresponding event type is retrieved using the getEvents method, and the Vsync signal is returned.

DisplayEventReceiver::getEvents
//	native\libs\gui\DisplayEventReceiver.cpp

ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,size_t count) {
	// mDataChannel is created in init to receive Vsync signals
    return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}
ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
        Event* events, size_t count)
{
    return gui::BitTube::recvObjects(dataChannel, events, count);
}

//native\libs\gui\BitTube.cpp
    static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }

ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {
    char* vaddr = reinterpret_cast<char*>(events);
	// Read data through the socket
    ssize_t size = tube->read(vaddr, count * objSize);
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
// Read data
ssize_t BitTube::read(void* vaddr, size_t size) {
    ssize_t err, len;
    do {
		// Place the data received by mReceiveFd into the size vaddr buffer. And returns len, the actual received data size
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        // If the receive is abnormal, return 0
        return 0;
    }
    return err == 0 ? len : -err;
}

Copy the code

After the received data is put into the corresponding buffer and the data is returned, the specific data type returned is verified.

status_t DisplayEventReceiver::requestNextVsync(a) {
	// Verify that the current connection exists
    if(mEventConnection ! =nullptr) {
		// Request the next Vsync signal through the connection. The mEventConnection. Is created when the DisplayEventReceiver is initialized
		// Specify EventThreadConnection (located in EventThread)
        mEventConnection->requestNextVsync(a);return NO_ERROR;
    }
    return NO_INIT;
}

void EventThreadConnection::requestNextVsync(a) {
    ATRACE_NAME("requestNextVsync");
    mEventThread->requestNextVsync(this);
}

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        connection->resyncCallback(a); }// Thread locking mechanism
    std::lock_guard<std::mutex> lock(mMutex);
	//vsyncRequest defaults to None. Defined in the eventThread.h file
    if (connection->vsyncRequest == VSyncRequest::None) {
		// The reason Vsync is one-time is that we set this field to Single when we are currently None.
		// This method will not be executed when the hardware receives a Vsync signal
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}


Copy the code

Here, a notify_all() is called when a Vsync signal arrives. This method wakes up all threads that have performed the **wait()** method.

So who will this wake up?

This brings us to the EventThread creation process.

EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src),
        mVSyncSourceUnique(std::move(uniqueSrc)),
        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
        mThreadName(threadName) {
    ...
	// Create mThread thread
    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
		The threadMain function is called when the thread is created
        threadMain(lock); }); . }Copy the code

When An EventThread is created, a thread is created and the threadMain method is called.

// This method is called when EventThread is created. It's going to iterate over and over again
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;
	// Keep iterating through the loop as long as there is no exit
    while(mState ! = State::Quit) { std::optional<DisplayEventReceiver::Event> event; .// Whether there is a Vsync request
        bool vsyncRequested = false; .// query all connections, actually a connection is a listener
        auto it = mDisplayEventConnections.begin(a);while(it ! = mDisplayEventConnections.end()) {
            if (const auto connection = it->promote()) { vsyncRequested |= connection->vsyncRequest ! = VSyncRequest::None;// traverse, putting the listeners that need to be notified into consumers
                if (event && shouldConsumeEvent(*event, connection)) {
                    consumers.push_back(connection);
                }

                ++it;
            } else {
                it = mDisplayEventConnections.erase(it); }}if(! consumers.empty()) {
			// Perform event distribution. Will call GUI: : BitTube: : sendObjects function
            dispatchEvent(*event, consumers);
            consumers.clear(a); } State nextState;if (mVSyncState && vsyncRequested) {
            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
        } else {
            ALOGW_IF(! mVSyncState,"Ignoring VSYNC request while display is disconnected");
            nextState = State::Idle;
        }

        if(mState ! = nextState) {if (mState == State::VSync) {
                mVSyncSource->setVSyncEnabled(false);
            } else if (nextState == State::VSync) {
                mVSyncSource->setVSyncEnabled(true);
            }

            mState = nextState;
        }

        if (event) {
            continue;
        }

        // Wait for the event request
        if (mState == State::Idle) {
            mCondition.wait(lock);
        } else{... }}}Copy the code

The threadMain function loops over and over again. If an EventThreadConnection is found that can consume the event, dispatchEvent is called to distribute the event. If the current state is idle, the thread is put into wait, waiting to wake up.

That’s what we called arousal earlier.

When a Vsync signal arrives, the dispatchEvent method is called for distribution

void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                const DisplayEventConsumers& consumers) {
    // Where DisplayEventConsumers is a vector and holds an EventThreadConnection internally.
    for (const auto& consumer : consumers) {
        switch (consumer->postEvent(event)) {
            case NO_ERROR:
                break;
            case -EAGAIN:
                ALOGW("Failed dispatching %s for %s".toString(event).c_str(),
                      toString(*consumer).c_str());
                break;
            default:
                // Treat EPIPE and other errors as fatal.
                removeDisplayEventConnectionLocked(consumer); }}}Copy the code

Let’s look at the postEvent method

//EventThread.cpp	frameworks\native\services\surfaceflinger\Scheduler	
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
    ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
    return size < 0 ? status_t(size) : status_t(NO_ERROR);
}

//DisplayEventReceiver.cpp	frameworks\native\libs\gui	
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
        Event const* events, size_t count)
{
    return gui::BitTube::sendObjects(dataChannel, events, count);
}

ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
        Event const* events, size_t count)
{
	// The Vsync signal is sent to the corresponding BitTube
    return gui::BitTube::sendObjects(dataChannel, events, count);
}

//BitTube.h	frameworks\native\libs\gui\include\private\gui	
    static ssize_t sendObjects(BitTube* tube, T const* events, size_t count) {
        return sendObjects(tube, events, count, sizeof(T));
    }

ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
    const char* vaddr = reinterpret_cast<const char*>(events);
	// Write data to vaddr. When mSendFd writes to the file, the corresponding mReceiveFd receives the data.
	// Then mReceiveFd calls the corresponding callback function
    ssize_t size = tube->write(vaddr, count * objSize); .return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}

Copy the code

When sendObjects writes to mSendFd, mReceiveFd can receive the message. In nativeInit, mReceiveFd is added to the Handler’s epoll for listening. So when data is written, the corresponding handleEvent callback is called. This callback is registered with mReceiveFd when it is added

The callback process

//mReceiveFd can receive the written data and then call this method.
int DisplayEventDispatcher::handleEvent(int.int events, void*) {
    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
        ALOGE("Display event receiver pipe was closed or an error occurred. "
                "events=0x%x", events);
        return 0; // remove the callback
    }

    nsecs_t vsyncTimestamp;
    PhysicalDisplayId vsyncDisplayId;
    uint32_t vsyncCount;
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
                ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d".this.ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
		// A Vsync message has been obtained here, so it will be waiting for the Vsync flag position to be false.
        mWaitingForVsync = false;
		// Make a distribution. The concrete is now DisplayEventDispater (android_view_DisplayEventReceiver) defined in subclasses NativeDisplayEventReceiver
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }

    return 1; // keep the callback
}


//android_view_DisplayEventReceiver.cpp	frameworks\base\core\jni	
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
                                               uint32_t count) {
    // The JNI context
    JNIEnv* env = AndroidRuntime::getJNIEnv(a);// Where mReceiverWeakGlobal
    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
    if (receiverObj.get()) {
        ALOGV("receiver %p ~ Invoking vsync handler.".this);
		// The JNI method is used to call dispatchVsync with the corresponding timestamp, display screen and the number of vsyncs
		// Is actually the dispatchVsync method of the DisplayEventReceiver
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
        ALOGV("receiver %p ~ Returned from vsync handler.".this);
    }

    mMessageQueue->raiseAndClearException(env, "dispatchVsync");
}

Copy the code

Our dispatchVsync method in Java will eventually be called.

   //DisplayEventReceiver.java	frameworks\base\core\java\android\view	
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }

Copy the code

Finally back to our main line…

So we’ve got the underlined part. The rest of the Java layer callback processing, we explained in the previous View drawing, if you are interested.

reference

Dandanlove.com/2018/04/25/…

Blog.csdn.net/stven_king/…

Blog.csdn.net/litefish/ar…

Android VSync signal generation and propagation structure details

Blog.csdn.net/qq_34211365…

Blog.csdn.net/qq_34211365…

This article is published by Kaiken!

Synchronous public number [open Ken]