InputManagerService inputReader of Android Framework important services

This chapter will continue to explore the InputReader thread, which is responsible for obtaining events from the eventhub, converting them into EventEntry events, and adding them to the InputDispatcher's mInboundQueue.

threadLoop

After the InputReaderThread thread is started, a thredLoop method will be called. This method continuously obtains events from the eventhub and performs subsequent processing. The relevant code is located in frameworks/native/services/inputlinker/reader/inputreader cpp:

// Start InputReader and call loopOnce
status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

void InputReader::loopOnce() {
    ...
    
    // Get events from the eventhub (EVENT_BUFFER_SIZE = 256)
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
    {
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
 
        if (count) {
            // Handling events
            processEventsLocked(mEventBuffer, count);   
        }
        ...
        
        // If the input device is changed, the current input device is retrieved
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
 
    // Send notification of input device changes
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
 
    // Send events to nputDispatcher
    mQueuedListener->flush();  
} 

In the above code, there are three key nodes to focus on: meventhub->getevents, processeventslocked, and mqueedlistener->flush. The following will focus on these methods.

Meventhub->getevents [get events]

The getEvents method is mainly responsible for obtaining the event of the kernel. Here, the event includes not only input, but also add/remove and other related events of the input device.
When there is no event in the input device, it will block the epoll in the EventHub. When an event occurs, the epoll_wait will return, and then handle the changed event. The relevant codes are located in frameworks/native/services/inputlinker/reader/EventHub cpp:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ...
    
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        // Scanning equipment
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();   
            mNeedToSendFinishedDeviceScan = true;
        }
 
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            // Add device event
            event->type = DEVICE_ADDED;  
            event += 1;
        }
            ...
            
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
            // Read events from the device and put them into the readBuffer
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        // Read events
                        struct input_event& iev = readBuffer[i];  
                        // Set input_event information is encapsulated into RawEvent
                        event->when = processEventTimestamp(iev);
                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
 
                }
            ...
        }
                mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);
 
        //Release the lock before poll and wait for the input event
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }
 
    // All done, return the number of events we read.
    return event - buffer;
} 

The EventHub uses the INotify + epoll mechanism to listen to the device nodes under the directory /dev/input. The input_event structure + deviceId is converted to RawEvent structure. The declaration of RawEvent structure is located in frameworks/native/services/inputlinker/reader/include/EventHub h. Its structure is as follows:

RawEvent mEventBuffer[EVENT_BUFFER_SIZE];

struct RawEvent {
    // Time when the event happened
    nsecs_t when;
    // Time when the event was read by EventHub. Only populated for input events.
    // For other events (device added/removed/etc), this value is undefined and should not be read.
    nsecs_t readTime;
    int32_t deviceId;
    int32_t type;
    int32_t code;
    int32_t value;
};
 
struct input_event {
 struct timeval time; //Time point of the event
 __u16 type;
 __u16 code;
 __s32 value;
    }; 

processEventsLocked [process event]

The processEventsLocked method is responsible for adding, removing, and deleting devices, as well as processing events. The relevant codes are located in frameworks/av/media/libstrming/input/key_injector.cpp:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            // Handling events
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
                // Add device
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                // Remove device
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                // Device scan
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
} 

processEventsForDeviceLocked

The processeventsfordevice locked method is used to process input device events. More specifically, it verifies the relevant information of input events. This method is located in frameworks/native/services/inputlinker/reader/inputreader cpp:

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    // Use eventhubbid to find out which device sent the event
    auto deviceIt = mDevices.find(eventHubId);
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return;
    }

    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);
} 

InputDevice::process

The InputDevice::process method is responsible for mapping input events and processing all events in device order, because there may be dependencies between each device processing. The relevant codes are located in frameworks/native/services/inputlinker/reader/inputdevice cpp:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                // Handle incident escalation
                mDropUntilNextSync = false;
            } else {
                ...
                
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            // Processing event cancellation
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            // Relevant processing according to event type
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
} 

KeyboardInputMapper::process

As mentioned above, the InputDevice::process method uses different processing methods to process input events, including but not limited to touch events, keyboard events, sensor events, etc. to sum up, the following processing methods are available

class SwitchInputMapper : public InputMapper {
class VibratorInputMapper : public InputMapper {
class KeyboardInputMapper : public InputMapper {
class CursorInputMapper : public InputMapper {
class RotaryEncoderInputMapper : public InputMapper {
class TouchInputMapper : public InputMapper {
class ExternalStylusInputMapper : public InputMapper {
class JoystickInputMapper : public InputMapper { 

Among them, the keyboard event processing is the simplest and easiest to analyze, and the touch event is the most complex. Therefore, the keyboard event is taken as an example for analysis. The relevant code is located in frameworks/native/services/inputlinker/reader/mapper/keyboardinputmapper cpp:

// Keyboard event handling
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
        case EV_KEY: {
            // Handling key events
            int32_t scanCode = rawEvent->code;
            int32_t usageCode = mCurrentHidUsage;
            mCurrentHidUsage = 0;

            if (isKeyboardOrGamepadKey(scanCode)) {
                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
                           usageCode);
            }
            break;
        }
        case EV_MSC: {
            // Handle events that cannot be classified into other categories
            if (rawEvent->code == MSC_SCAN) {
                mCurrentHidUsage = rawEvent->value;
            }
            break;
        }
        case EV_SYN: {
            // Escalation events
            if (rawEvent->code == SYN_REPORT) {
                mCurrentHidUsage = 0;
            }
        }
    }
} 

KeyboardInputMapper::processKey

The KeyboardInputMapper::processKey method is used to handle key related events. The related codes are located in frameworks/native/services/inputlinker/reader/mapper/keyboardinputmapper cpp:

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
    int32_t keyCode;
    int32_t keyMetaState;
    uint32_t policyFlags;
 
    // Convert event scancode to keycode
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }
 
    if (down) {
        ...
    
        mDownTime = when;
    } else {
        ...
        
    }
    // Create NotifyKeyArgs object, when record eventTime, downTime record pressing time
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    
     // Send key event
    getListener()->notifyKey(&args); 
} 

EventHub::mapKey

EventHub::mapKey is responsible for converting the event scancode into keycode. The relevant codes are located in frameworks/native/services/inputlinker/reader/eventhub cpp:

status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
                          int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
    std::scoped_lock _l(mLock);
    Device* device = getDeviceLocked(deviceId);
    status_t status = NAME_NOT_FOUND;

    if (device != nullptr) {
        // Check the key character map first.
        const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
        if (kcm) {
            if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                *outFlags = 0;
                status = NO_ERROR;
            }
        }

        // Check the key layout next.
        if (status != NO_ERROR && device->keyMap.haveKeyLayout()) {
            if (!device->keyMap.keyLayoutMap->mapKey(scanCode, usageCode, outKeycode, outFlags)) {
                status = NO_ERROR;
            }
        }

        if (status == NO_ERROR) {
            if (kcm) {
                kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
            } else {
                *outMetaState = metaState;
            }
        }
    }

    if (status != NO_ERROR) {
        *outKeycode = 0;
        *outFlags = 0;
        *outMetaState = metaState;
    }

    return status;
} 

end of document

If you want to be an architect, you should not be limited to coding and business. You should be able to select, expand and improve your programming thinking. In addition, good career planning is also very important. The habit of learning is very important, but the most important thing is to persevere. Any plan that cannot be implemented is empty talk.

If you have no direction, here is a set of advanced notes on the eight Android modules prepared by Ali senior architects to help you systematize the messy, scattered and fragmented knowledge, so that you can systematically and efficiently master all the knowledge points of Android development.

Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

1, Necessary skills for architects to build foundations

1. Deep understanding of Java generics
2. Notes in simple terms
3. Concurrent programming
4. Data transmission and serialization
5. Principles of Java virtual machine
6. High efficiency IO
......

2, Source code analysis of top 100 Android frameworks

1.retro 2.0 source code analysis
2.Okhttp3 source code analysis
3.ButterKnife source code analysis
4.mpadroidchart source code analysis
5.Glide source code analysis
6.Leakcanary source code analysis
7. Universal lmage loader source code analysis
8.EventBus 3.0 source code analysis
9.zxing source code analysis
10.Picasso source code analysis
11.LottieAndroid usage details and source code analysis
12.Fresco source code analysis - picture loading process

3, Practical analysis of Android performance optimization

  • Tencent Bugly: some understanding of string matching algorithm
  • Iqiyi: Android APP crash capture scheme - xCrash
  • Byte skipping: deeply understand one of the Gradle frameworks: Plugin, Extension, buildSrc
  • Baidu APP Technology: Android H5 first screen Optimization Practice
  • Alipay client architecture analysis: Android client startup speed optimization "garbage collection"
  • Ctrip: component architecture practice from the perspective of Zhixing Android project
  • Netease News Construction Optimization: how to make your construction speed "like lightning"?
  • ...

4, Advanced kotlin enhanced combat

1. Getting started with Kotlin
2. Kotlin practical pit avoidance Guide
3. Project practice "Kotlin Jetpack practice"

  • Start with a Demo of worshiping the great God

  • What was Kotlin's experience writing Gradle scripts?

  • Three realms of Kotlin programming

  • Kotlin higher order function

  • Kotlin generics

  • Kotlin extension

  • Entrusted by Kotlin

  • Debugging skills of "unknown" coordination process

  • Graphic collaboration: suspend

5, Advanced decryption of Android advanced UI open source framework

1. Use of smartrefreshlayout
2.Android PullToRefresh control source code analysis
3. Basic usage of Android pulltorefresh pull-down refresh Library
4.LoadSir- efficient and easy-to-use loading feedback page management framework
5. Detailed explanation of Android general LoadingView loading framework
6.mpadroidchart implements LineChart (line chart)
7. Hellocharts Android User Guide
8.SmartTable User Guide
9. introduction to the open source project Android uitableview
10.ExcelPanel User Guide
11. Deep analysis of Android open source project SlidingMenu
12.MaterialDrawer User Guide

6, NDK module development

1. NDK module development
2. JNI module
3. Native development tools
4. Linux Programming
5. Bottom picture processing
6. Audio and video development
7. Machine learning

7, Advanced shuttle Technology

1. Overview of Flutter cross platform development
2. Setup of the fluent development environment in Windows
3. Write your first shuttle app
4. Setup and debugging of Flutter development environment
5. Basic grammar of Dart Grammar (1)
6. The use and source code analysis of the collection of Dart Grammar (2)
7. Set operator functions and source code analysis in Dart Grammar (3)
...

8, Wechat applet development

1. Applet overview and introduction
2. Applet UI development
3. API operation
4. Shopping mall project practice

Full set of video materials:

1, Interview collection

2, Source code analysis collection


3, Open source framework collection


You are welcome to support three links with one button. If you need the information in the text, you can directly click on the official certified wechat card of CSDN at the end of the text to receive [guarantee 100% free] ↓↓

Tags: Android Framework

Posted by vaan on Wed, 01 Jun 2022 04:00:28 +0530