Android11重复事件上报流程

大多数的按键驱动,即使按键一直按着,也不会一直上报事件,而是只上报一个down事件,那对于事件的重复上报,自然而然就交给了Android 的输入子系统。

在InputDispatcher线程中,对于按键事件,调用dispatchKeyLocked处理

c 复制代码
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // Preprocessing.
    if (!entry->dispatchInProgress) {//dispatchInProgress默认为false
        if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&//按下
            (entry->policyFlags & POLICY_FLAG_TRUSTED) && //事件来自于InputReader
            (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {//没有设置过禁止重复上报的flag
            if (mKeyRepeatState.lastKeyEntry &&
                mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
               	//能进入到这里,说明在驱动上,已经上报了重复按下的事件,Android就不用再处理了
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
            } else {
                // Not a repeat.  Save key down state in case we do see a repeat later.
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;//keyRepeatTimeout默认是400ms
            }
            mKeyRepeatState.lastKeyEntry = entry;//将当前事件保存在lastKeyEntry 中
            entry->refCount += 1;//引用加1
        } else if (!entry->syntheticRepeat) {
            resetKeyRepeatLocked();
        }

        if (entry->repeatCount == 1) {//repeatCount 等于1时,就设置长按事件的flag
            entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
        } else {
            entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
        }

        entry->dispatchInProgress = true;

        logOutboundKeyDetails("dispatchKey - ", *entry);
    }
	//省略

这个方法主要是将当前事件保存在了mKeyRepeatState的lastKeyEntry 变量中,并设置该事件经过keyRepeatTimeout会再次上报。keyRepeatTimeout是通过JNI,调用到ViewConfiguration 类中的getLongPressTimeout方法获取的,默认是400ms

c 复制代码
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
	
	//省略

    mKeyRepeatState.lastKeyEntry = nullptr;

    policy->getDispatcherConfiguration(&mConfig);
}

//frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
    ATRACE_CALL();
    JNIEnv* env = jniEnv();

    jint keyRepeatTimeout = env->CallIntMethod(mServiceObj,
            gServiceClassInfo.getKeyRepeatTimeout);
    if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) {
        outConfig->keyRepeatTimeout = milliseconds_to_nanoseconds(keyRepeatTimeout);
    }
	//省略
}
//frameworks\base\services\core\java\com\android\server\input\InputManagerService.java
 // Native callback.
private int getKeyRepeatTimeout() {
	return ViewConfiguration.getKeyRepeatTimeout();
}
//frameworks\base\core\java\android\view\ViewConfiguration.java
public static int getKeyRepeatTimeout() {
        return getLongPressTimeout();
}
public static int getLongPressTimeout() {
	return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
                DEFAULT_LONG_PRESS_TIMEOUT);//默认是400ms
} 

上面的方法只是保存了当前事件,并设置了下次上报的时间间隔是400ms,那再次上报是在哪里上报的呢?

在InputDispatcher线程的循环体内,调用dispatchOnceInnerLocked处理

c 复制代码
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
	//省略
    if (!mPendingEvent) {
        if (mInboundQueue.empty()) {
          	//省略
            // Synthesize a key repeat if appropriate.
            if (mKeyRepeatState.lastKeyEntry) {//上次已经保存了之前的down事件
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {//如果当前时间超过了400ms,表示要再次上报了
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);//取出事件
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

	//省略

可以看出,如果lastKeyEntry不为空,而且当前时间超过了之前设置的400ms后,就会调用synthesizeKeyRepeatLocked去取出事件,进行后续的分发

c 复制代码
//frameworks\native\services\inputflinger\dispatcher\InputDispatcher.cpp
KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
    KeyEntry* entry = mKeyRepeatState.lastKeyEntry;//得到事件

    // Reuse the repeated key entry if it is otherwise unreferenced.
    uint32_t policyFlags = entry->policyFlags &
            (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
    if (entry->refCount == 1) {
        entry->recycle();
        entry->id = mIdGenerator.nextId();
        entry->eventTime = currentTime;
        entry->policyFlags = policyFlags;
        entry->repeatCount += 1;
    } else {
        KeyEntry* newEntry =
                new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
                             entry->displayId, policyFlags, entry->action, entry->flags,
                             entry->keyCode, entry->scanCode, entry->metaState,
                             entry->repeatCount + 1, entry->downTime);//根据上次的down事件,重新生成一个事件,action,keycode这些都是一样的,注意新的KeyEntry的repeatCount 会加1

        mKeyRepeatState.lastKeyEntry = newEntry;//重新设置lastKeyEntry ,以供下一次重复上报
        entry->release();

        entry = newEntry;
    }
    entry->syntheticRepeat = true;

    entry->refCount += 1;

    mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;//后面重复事件隔keyRepeatDelay就会去上报,keyRepeatDelay是通过调用ViewConfiguration的getKeyRepeatDelay得到的,默认是50ms
    return entry;
}

可以看出,这里取出的是上次保存的事件,并根据上次事件的各个参数的值,重新生成KeyEntry,去重复上报。后续的上报处理流程就和之前的流程是一样的。需要注意的是,后续重复事件上报的间隔默认为50ms

总结

  • 如果驱动已经上报了重复事件,InputDispatcher是不会去重新生成事件上报的
  • 第一次重复事件上报的间隔时间默认为400ms,后续的间隔默认为50ms
  • 每次上报的时候,repeatCount 都会增加 1
  • 当repeatCount 等于 1 的时候,就会设置长按的flag,也就是默认按键按下400ms,就会认为是长按事件。如果我们要修改长按的时间的话,修改Settings.Secure.LONG_PRESS_TIMEOUT默认值即可
相关推荐
安东尼肉店4 小时前
Android compose屏幕适配终极解决方案
android
2501_916007475 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun6 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户20187928316710 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子10 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜822710 小时前
安卓接入Max广告源
android
齊家治國平天下10 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO10 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel10 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢10 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱