Android UI system 2

Following: juejin.cn/post/700365…

As we know from the Jave layer analysis, ViewRootImpl, acting as the general manager of the entire view tree, actually posts a Callback to Choreographer when requestLayout, Since the requestLayout initiator is already in the main thread, it will call Choreographer’s scheduleVsyncLocked, Which is actually called FrameDisplayEventReceiver scheduleVsync, ultimately achieve the following:

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

The actual implementation of nativeScheduleVsync is android_view_DisplayEventReceiver:

static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
    sp<NativeDisplayEventReceiver> receiver =
            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
    status_t status = receiver->scheduleVsync();
    // ...
}
Copy the code

NativeDisplayEventReceiver inherited from DisplayEventDispatcher, scheduleVsync concrete implementation:

status_t DisplayEventDispatcher::scheduleVsync(a) {
    if(! mWaitingForVsync) {// ...
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "".this.ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
				// Request the next Vsync via DisplayEventReceiver
        status_t status = mReceiver.requestNextVsync(a);// ...
    }
    // ...
}
Copy the code

The core of requestNextVsync is to call the IDisplayEventConnection requestNextVsync method:

status_t DisplayEventReceiver::requestNextVsync(a) {
    if(mEventConnection ! =nullptr) {
        mEventConnection->requestNextVsync(a);return NO_ERROR;
    }
    // mEventConnection is constructed when the DisplayEventReceiver is initialized
    return NO_INIT;
}


Copy the code

ScheduleVsync finally calls IDisplayEventConnection requestNextVsync, asking for something to do when the next Vsync signal arrives. Moving on, you need to know what mEventConnection is, in the constructor of the DisplayEventReceiver:

DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
                                           ISurfaceComposer::ConfigChanged configChanged) {
  	// Get a reference to ISurfaceComposer from ComposerService
  	Binder handle to the SurfaceFlinger process
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if(sf ! =nullptr) {
        / / handle to the binder through SurfaceFlinger, call SurfaceFlinger createDisplayEventConnection process.
        // Create a DisplayEventReceiver object
        mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
        if(mEventConnection ! =nullptr) {
            // mDataChannel is the channel through which application processes interact with SurfaceFlinger processes
            mDataChannel = std::make_unique<gui::BitTube>();
            // Let's do the analysis below
            mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}Copy the code

Above mentioned BitTube this class is actually storing the two file descriptors: native/include/private/GUI

private:
    // ...
    mutable base::unique_fd mSendFd;
    mutable base::unique_fd mReceiveFd;
    // ...
};
Copy the code

In the BitTube constructor, the BitTube::init function is called to initialize the two descriptors:

void BitTube::init(size_t rcvbuf, size_t sndbuf) {
    int sockets[2];
    // In Linux, you can create a pair of sockets through socketPair for IPC.
    // Both sockets can be used to read and write
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) == 0) {
        // Omit some code that sets socket handles
        // Assign two socket handles to two file descriptors
        mReceiveFd.reset(sockets[0]);
        mSendFd.reset(sockets[1]);
    } else {
        mReceiveFd.reset(a);ALOGE("BitTube: pipe creation failed (%s)".strerror(errno)); }}Copy the code

MSendFd and mReceiveFd are obstructed, and the Buffer size is set to 4KB. For example, if A process (thread A) takes the mReceiveFd and there is no Buffer in the Buffer, epoll blocks. When process B writes to the Buffer using mSendFd, process A wakes up and reads the Buffer using mRecerveFd.

Continue to back to createDisplayEventConnection, note that the following here was conducted in the process of SurfaceFlinger service.

sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
    / / here is actually the default eVsyncSourceSurfaceFlinger
    // See Choreographer for details
    const auto& handle =
            vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
    return mScheduler->createDisplayEventConnection(handle, configChanged);
}
Copy the code

Call to mScheduler – > createDisplayEventConnection

createConnectionInternal(mConnections[handle].thread.get(), configChanged);
-->
eventThread->createEventConnection([&] { resync(a); }, configChanged); -->// Create an EventThreadConnection object, so EventConnection is actually an EventThreadConnection object
// In the constructor of EventThreadConnection, an mChannel object (actually a BitTube object) is created.
new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
                                     configChanged);
MEventConnection ->requestNextVsync is an EventThreadConnection method:
mEventThread->requestNextVsync(this);
// mEventThread is an EventThread object, whose core is in requestNextVsync:
// Set the connection's vsyncRequest to Single, bearing this in mind and mentioned again below
connection->vsyncRequest = VSyncRequest::Single;
Copy the code

In EventThreadConnection, onFirstRef looks like this: RefBase::onFirstRef is called the first time a reference is created:

void EventThreadConnection::onFirstRef(a) {
    mEventThread->registerDisplayEventConnection(this); } -- >status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
    std::lock_guard<std::mutex> lock(mMutex);
		// ...
    // Add the created EventThread to the mDisplayEventConnections container
    mDisplayEventConnections.push_back(connection);
    Condition_variable mCondition. Wait specifies the thread that is blocked because condition_variable mCondition
    mCondition.notify_all(a);return NO_ERROR;
}
Copy the code

Choreographer requests the next Vsync signal and eventually passes the requestNextVsync method of EventThread. An EventThreadConnection object will be created during the call. Represents the connection object to EventThread, and sets the vsyncRequest of EventThreadConnection to Single.

Let’s look at the creation of EventThread:

After SurfaceFlinger process started, will call SurfaceFlinger: : init is initialized, the call to SurfaceFlinger: : processDisplayHotplugEventsLocked function, The Scheduler is initialized with a call to initScheduler.

void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
    // ...
    // start the EventThread
    mScheduler =
            getFactory().createScheduler([this] (bool enabled) { setPrimaryVsyncEnabled(enabled); },
                                         *mRefreshRateConfigs, *this);
    mAppConnectionHandle =
            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
                                         impl::EventThread::InterceptVSyncsCallback());
    / / create EventConnection
    mSfConnectionHandle =
            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
                                         [this] (nsecs_t timestamp) {
                                             mInterceptor->saveVSyncEvent(timestamp);
                                         });
    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
    // ...
}

Scheduler::ConnectionHandle Scheduler::createConnection(
        const char* connectionName, nsecs_t phaseOffsetNs,
        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
    // Create a Vsync source
    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
    // Create an EventThread and pass the Vsync source to the EventThread
    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                           std::move(interceptCallback));
    return createConnection(std::move(eventThread));
}
Copy the code

The STD ::make_unique library function passes the entry parameter to the EventThread constructor, where:

EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
                         InterceptVSyncsCallback interceptVSyncsCallback)
      : mVSyncSource(std::move(vsyncSource)),
        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
        mThreadName(mVSyncSource->getName()) {
    mVSyncSource->setCallback(this);
		// Create a thread and the thread calls threadMain
    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
        threadMain(lock);
    });
	// ...
}
Copy the code

The logic in threadMain is quite long and can be analyzed as follows:

void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;
		// An infinite loop
    while(mState ! = State::Quit) { std::optional<DisplayEventReceiver::Event> event;/ /... Let's deal with some previous events
        if(! mPendingEvents.empty()) {
            event = mPendingEvents.front(a); mPendingEvents.pop_front(a);// ...
        }
        bool vsyncRequested = false;

        // Find the connection that needs to consume the event
        auto it = mDisplayEventConnections.begin(a);while(it ! = mDisplayEventConnections.end()) {
            if (const auto connection = it->promote()) { vsyncRequested |= connection->vsyncRequest ! = VSyncRequest::None;// shouldConsumeEvent, if the event type is Vsync and the connection vsyncRequest is Single
                // We mentioned above that connection.vsyncrequest is set to Single during requestNextVsync
                // Return true and set Connection. vsyncRequest back to None.
                if (event && shouldConsumeEvent(*event, connection)) {
                    // Place these connections that require consumption events in the Consumer container
                    consumers.push_back(connection);
                }
                ++it;
            } else {
                it = mDisplayEventConnections.erase(it); }}// There is a consumer Connection
        if(! consumers.empty()) {
            // Distribute the event
            dispatchEvent(*event, consumers);
            consumers.clear(a); }// ...
        if (event) {
            continue;
        }
        // If EventThread is idle, it is blocked here
        // Wait until there is a call to mCondition. NotifyXXX and then wake up
        // In requestNextVsync, we call notify to wake up the blocked EventThread
        if (mState == State::Idle) {
            mCondition.wait(lock);
        } else {
            // ...}}}Copy the code

Once a connection is found that can consume Vsync signals, the event is distributed to them:

void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
                                const DisplayEventConsumers& consumers) {
    for (const auto& consumer : consumers) {
        // Call postEvent to distribute events to each consumer. Consumer is an EventThreadConnection object
        switch (consumer->postEvent(event)) {
            // ...
            default:
                // Remove the consumer from mDisplayEventConnections
                // So a connection consumes only one Vsync event.
                removeDisplayEventConnectionLocked(consumer); }}}Copy the code

EventThreadConnection: : postEvent implementation is as follows:

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: : sendEvents final call to:
ssize_t DisplayEventReceiver::sendEvents(gui::BitTube* dataChannel,
        Event const* events, size_t count)
{
    // Seems to be connected!!
    // In constructing DisplayEventReceiver, we created a DataChannel BitTube object
    return gui::BitTube::sendObjects(dataChannel, events, count);
}
// BitTube's sendObjects method implements:
ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
    const char* vaddr = reinterpret_cast<const char*>(events);
    // The SurfaceFlinger process writes data to the Socket using BitTube write.
    // Then the read end of the Socket in the application process can wake up and read data.
    ssize_t size = tube->write(vaddr, count * objSize);
		// ...
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
Copy the code

Socket allows application processes and SurfaceFlinger processes to establish Socket channels for communication.

Back to the DisplayEventReceiver construction process:

DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource,
                                           ISurfaceComposer::ConfigChanged configChanged) {
  	// Get a reference to ISurfaceComposer from ComposerService
  	Binder handle to the SurfaceFlinger process
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if(sf ! =nullptr) {
        // Create a DisplayEventReceiver object with SurfaceFlinger's binder handle
        mEventConnection = sf->createDisplayEventConnection(vsyncSource, configChanged);
        if(mEventConnection ! =nullptr) {
            // mDataChannel is the channel through which application processes interact with SurfaceFlinger processes
            mDataChannel = std::make_unique<gui::BitTube>();
            // After mDataChannel is created, call stealReceiveChannel to complete the establishment of the communication between the two processes
            mEventConnection->stealReceiveChannel(mDataChannel.get()); }}}// stealReceiveChannel implements the following: outChannel is a BitTube object created in the application process,
// mChannel is the BitTube object created by the SurfaceFlinger process, which contains the Socket read and write fd created by the SurfaceFlinger process
status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
    // This sets the read end of the application Socket and the read end of the SurfaceFlingerSocket to the same fd
    // If the SurfaceFlinger process writes data to the write end of the Socket, the application process can read the data from the read end of the Socket
    outChannel->setReceiveFd(mChannel.moveReceiveFd());
    outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
    return NO_ERROR;
}
Copy the code

So far, we know how SurfaceFlinger tells the application process that the Vsync signal has received this event, but how does the application process receive this event?

Let’s take a look at the application process Choreographer object creation:

    private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        / / Java layer DisplayEventReceiver object, USE_VSYNC is true, then created is FrameDisplayEventReceiver
        // where looper is the main thread looper.
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
       	// ...
    }

		/ / FrameDisplayEventReceiver inherited from DisplayEventReceiver
    public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) {
				// Get the MessageQueue for the main thread of the application process
        mMessageQueue = looper.getQueue();
        // mReceiverPtr? It is passed when scheduleVsyncLock requests the next Vsync signal
        // enter the Native processing logic, where the MessageQueue of the main thread is passed to the nativeInit method
        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
                vsyncSource, configChanged);

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

NativeInit method:

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, jint vsyncSource, jint configChanged) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    // ...
  	/ / NativeDisplayEventReceiver creation process will messageQueue assigned to mMessageQueue fields
    / / so NativeDisplayEventReceiver can direct access to the main thread of the message queue (Native layer message queue)
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverWeak, messageQueue, vsyncSource, configChanged);
    status_t status = receiver->initialize(a);// ...
}
// Receiver inherits from DisplayEventDispatcher, receiver->initialize() as follows:
status_t DisplayEventDispatcher::initialize(a) {
    // ...
    if(mLooper ! =nullptr) {
        The core is to add a file descriptor to the main thread's Looper.
        // Is the SocketPair created by SurfaceFlinger above and returned to the Read end of the application process
        int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this.NULL);
        if (rc < 0) {
            returnUNKNOWN_ERROR; }}return OK;
}
Copy the code

Moving on to the message mechanism, we can take a look at what the file descriptor does in Looper:

int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
}

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
  // Create a Request and bind it to the Epoll mechanism.
  // The Epoll mechanism can wake up the blocking Looper to fetch the Request when data is written to the incoming FD.
  Request request;
	request.fd = fd;
	request.events = events;
	request.callback = callback;
  // Add or replace the Request structure created above Looper's mRequests
}
Copy the code

As we know from the previous analysis of Android message mechanism, In Looper’s pollInner function, it processes the Request list of the Native side, first pushes it into the mResponse list, and then:

    for (size_t i = 0; i < mResponses.size(a); i++) { Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
						// Execute the handleEvent method for Callback
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            // If 0 is returned to remove the fd from the Looper, subsequent loopers will no longer listen to the fd
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }
						// ...}}return result;
}
Copy the code

Who is the implementation of handleEvent? Here is the NativeDisplayerEventReceiver (inherited from DisplayEventDispatcher) handleEvent should be:

int DisplayEventDispatcher::handleEvent(int.int events, void*) {
    // ...
    // If there is a vsync event Receiver, then this is true
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
       	// ...
        // Distribute the Vsync event
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }
		// Return 1 in the hope that Looper will continue to listen to the fd
    return 1; // keep the callback
}
/ / dispatchVsync as follows
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
                                               uint32_t count) {
    JNIEnv* env = AndroidRuntime::getJNIEnv(a);ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
    if (receiverObj.get()) {
        ALOGV("receiver %p ~ Invoking vsync handler.".this);
        / / call the Java layer DisplayEventReceiver. Java objects dispatchVsync method.
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
        ALOGV("receiver %p ~ Returned from vsync handler.".this);
    }

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

Back at the Java layer, dispatchVsync is called to onVsync, and Choreographer can then know that the Vsync signal has arrived and begin to trigger the re-rendering of the view.