Android Input
Android Input refers to input events, mainly touch swipe, and of course also includes inputs similar to Bluetooth peripherals. The main modules involved in Input
- EventHub: Map input events
- InputReader: Collect input events
- InputDispatcher: Distributes events to the upper layer
- InputManager: Receiving and distributing input events in framework
- WMS: manage windows, collect and distribute input events
This article mainly deals with the framework's perspective to debug the input problem. There is already a lot of information introducing the input, so it does not talk about the input transmission process and mechanism, but only looks at how to solve the problem.
From the perspective of framework, we first need to troubleshoot the problem of input driver. For example, if you input from the screen, it is the input driver of the display; if it is input from the Bluetooth peripheral, you need to find the BT driver layer.
adb shell getEvent
Then enter it to see if the key value is normal. If the getEvent is not received, it does not fall into the category of framework.
After confirming that there is no problem with the driver, you can turn on the debug log dynamically or statically. The commands of switch logs of different manufacturers are somewhat different, and the contents of the logs are also different.
Here we directly take local debug as an example, refer to the Android T version of common code to add the key log by yourself, and then start reproducing the problem and checking the log at the time point of the problem. By the way, you can use the following command to display the time to seconds, which is convenient for the corresponding log time when reproducing the problem.
adb shell settings put secure clock_seconds 1
Step1. Check whether ViewRootImpl has received the input event
/frameworks/base/core/java/android/view/
@UnsupportedAppUsage void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); if (event instanceof MotionEvent) { MotionEvent me = (MotionEvent) event; if (() == MotionEvent.ACTION_CANCEL) { (EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Motion - Cancel", getTitle().toString()); } } else if (event instanceof KeyEvent) { KeyEvent ke = (KeyEvent) event; if (()) { (EventLogTags.VIEW_ENQUEUE_INPUT_EVENT, "Key - Cancel", getTitle().toString()); } } // Always enqueue the input event in order, regardless of its time stamp. // We do this because the application or the IME may inject key events // in response to touch events and we want to ensure that the injected keys // are processed in the order they were received and we cannot trust that // the time stamp of injected events are monotonic. QueuedInputEvent last = mPendingInputEventTail; if (last == null) { mPendingInputEventHead = q; mPendingInputEventTail = q; } else { = q; mPendingInputEventTail = q; } mPendingInputEventCount += 1; (Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName, mPendingInputEventCount); //Add log to print key information (">_<!!","enqueueInputEvent: event = " + event + " ,this = " + this); if (processImmediately) { doProcessInputEvents(); } else { scheduleProcessInputEvents(); } }
Here you only need to view two parameters according to the added log. The event will print out the action and keyCode of the KeyEvent. We need to see if there is any disorder in the action and keyCode here. If the input does not correspond to the get, the driver still needs to coordinate it. This printed later is this ViewRootImpl object. For details, you can see its toString method.
We only need to observe whether this sentence is printed in the final log. If it is printed, it means that the input event has been successfully sent to the application. Skip the following steps and check Step5 directly. If this log is not printed, then look at Step2
Step2. Check whether the inputDispatcher has received the input event
/frameworks/native/services/inputflinger/dispatcher/
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { // This is the log mechanism of AOSP, no need to add logs logOutboundKeyDetails("dispatchKey - ", *entry); } void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { //if (DEBUG_OUTBOUND_EVENT_DETAILS) { if (true) { ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, prefix, , , , , , , , , , , , ); } }
Here, the AOSP log has been added very comprehensively, we just need to manually set the printing condition to true. This log can also correspond to the action and keyCode, but the c++ code prints hexadecimal, but it also corresponds to the strings printed in the above java code. If we can finally search for this log, it means that the inputDispatcher has received the input event, then fast forward to Step 4 to check whether the inputDispatcher status is normal. If you haven't viewed this log, then look at Step 3
Step3. Check whether there is a keycode in the inputreader thread
/frameworks/native/services/inputflinger/reader/mapper/
int32_t usageCode) { int32_t keyCode; int32_t keyMetaState; uint32_t policyFlags; if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState, &policyFlags)) { keyCode = AKEYCODE_UNKNOWN; keyMetaState = mMetaState; policyFlags = 0; } if () { policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); getListener().notifyKey(&args); ALOGI("device: %s, keyCode=%d, scanCode=%d, eventTime = %lld, action=0x%x,duwnTime=%lld",getDeviceName().c_str(), keyCode, scanCode, args,eventTime, . ); }
It is a tool added after Android R. If it is an older version, you need to add logs. Here you can confirm that the input event has been sent to the inputReader. The value here is read from getEvent. If the value of getEvent is correct, but no log is printed here, you need to print the callstack of the cpp file to see which step in the process is wrong.
Step4. Check whether the status of inputDispatcher is normal
You can use the adb command to view the status of the inputDispatcher
adb shell dumpsys input
/frameworks/native/services/inputflinger/dispatcher/
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled)); dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen)); dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled)); dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId);
DispatcherEnabled must be 1, and DispatcherFrozen must be 0. If there is a problem with the inputDispatcher status, you need to check in the code where the inputDispatcher status is modified mDispatchEnabled, mDispatchFrozen, and find the place where the state will be modified to analyze the problem. If the printed FocusedDisplayId or FocusedApplications does not meet expectations, it is a display or WMS-related issue, and has nothing to do with the input process.
Step5. Check which page is the final input consumption event
/frameworks/base/core/java/android/view/
public boolean dispatchKeyEvent(KeyEvent event) { if (mInputEventConsistencyVerifier != null) { (event, 0); } (">_<!!","dispatchKeyEvent event:" + event + " to :" + v); // Give any attached key listener a first crack at the event. //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && != null && (mViewFlags & ENABLED_MASK) == ENABLED && (this, (), event)) { //Indicates that the input has been consumed (">_<!!","Event:" + event+ " handle in: " + v + " ,ListenerInfo = " + ()); return true; } if ((this, mAttachInfo != null ? : null, this)) { return true; } if (mInputEventConsistencyVerifier != null) { (event, 0); } return false; }
The log here can indicate that the input event is being dispatched in sequence according to the view level and is eventually consumed by which view? If this view is not the expected view, then you need to check why it is consumed on this view. Is there a transparent boundary in the layout area? The expected view does not exist, there are many possibilities, and you can think about the details later. If the view here meets expectations, then the question goes back to the application layer to see if there is any exception in the application layer's response to this input event.
The above is the detailed content of Android development input problem solving analysis. For more information on Android input problem solving, please follow my other related articles!