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 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui