一 ,问题现象
长按power键会有很大的概率不弹出关机,重启,紧急呼叫的选项窗口,正常情况下应该是弹出选项窗口供用户进一步操作 拿到问题现场后,第一时间先把log拉出来看一下有没有有用的log,很不幸没有有用的log,找一台机器操作一下很快就复现了问题 ,接下来可以进一步分析log了。
二 ,问题分析
1279-3041/system_process D/WindowManager: interceptKeyTq keycode=0 interactive=true keyguardActive=false policyFlags=22000000
1279-3041/system_process D/WindowManager: interceptKeyTq keycode=0 interactive=true keyguardActive=false policyFlags=22000000
1279-3040/system_process D/WindowManager: interceptKeyTi keyCode=0 down=true repeatCount=0 keyguardOn=false canceled=false
1279-3040/system_process D/WindowManager: interceptKeyTi keyCode=0 down=false repeatCount=0 keyguardOn=false canceled=false
1279-3040/system_process D/WindowManager: Unhandled key: inputToken=android.os.BinderProxy@cecba52, title=com.google.android.setupwizard/
1279-3040/system_process D/WindowManager: No fallback.
1279-3040/system_process D/WindowManager: Unhandled key: inputToken=android.os.BinderProxy@cecba52, title=com.google.android.setupwizard/
1279-3040/system_process D/WindowManager: No fallback.
1279-3041/system_process D/WindowManager: interceptKeyTq keycode=26 interactive=true keyguardActive=false policyFlags=22000000 // 打印keycode值26即为power键值
1279-1279/system_process D/WindowManager: powerLongPress: eventTime=383451 mLongPressOnPowerBehavior=1 // 处理power键长按逻辑
1279-6569/system_process W/WindowManager: Changing focus fromWindow{6286edd u0 com.google.android.setupwizard/com.google.android.setupwiz
1279-1344/system_process D/WindowManager: update navigation bar window=Window{d6725b6 u0 ActionsDialog},disableFlags=0,appearance=0,this=
1279-3041/system_process D/WindowManager: interceptKeyTq keycode=26 interactive=true keyguardActive=false policyFlags=22000000
从日志可以看出,正常情况下日志会打印出 power键的keycode值26,并且打印 powerLongPress
日志处理power长按事件
进而弹出选项框,这个逻辑处理是在PhoneWindowManager里面的
但是出问题的时候不会打印这些关键日志,说明这个时候按键事件可能因为某种原因没有传到上层,通过现有日志无法进一步分析日志,要进一步打开input的debug日志才能获取更多的信息,在 /frameworks/native/services/inputflinger/reader/Macros.h
文件中 找到 DEBUG_RAW_EVENTS
把值改为1,再重新编译系统烧入手机即可debug input系统获取更多的日志。
打开日志开关后,获取到了更多的日志,果然有所收获
1313-1421/system_process D/InputReader: BatchSize: 2 Count: 2
1313-1421/system_process D/InputReader: Input event: device=5 type=0x0001 code=0x0074 value=0x00000001 when=301409902000
1313-1421/system_process I/InputReader: processKey down:1 scanCode:116 keyCode:26 // power键长按事件
1313-1421/system_process D/InputReader: Input event: device=5 type=0x0000 code=0x0000 value=0x00000000 when=301409902000
1313-1421/system_process D/WindowManager: interceptKeyTq keycode=26 interactive=true keyguardActive=false policyFlags=22000000
1313-1421/system_process I/SingleKeyGesture: Intercept key by rule KeyCode=KEYCODE_POWER, LongPress=true, VeryLongPress=false, MaxMultiPressCount=1
1313-1421/system_process D/InputReader: BatchSize: 4 Count: 4
1313-1421/system_process D/InputReader: Input event: device=1 type=0x0001 code=0x00fb value=0x00000001 when=301457624000
1313-1421/system_process I/InputReader: processKey down:1 scanCode:251 keyCode:0 // 未知code down事件
1313-1421/system_process D/InputReader: Input event: device=1 type=0x0000 code=0x0000 value=0x00000000 when=301457624000
1313-1421/system_process D/InputReader: Input event: device=1 type=0x0001 code=0x00fb value=0x00000000 when=301457654000
1313-1421/system_process I/InputReader: processKey down:0 scanCode:251 keyCode:0 // 未知code up 事件
1313-1421/system_process D/InputReader: Input event: device=1 type=0x0000 code=0x0000 value=0x00000000 when=301457654000
1313-1421/system_process D/WindowManager: interceptKeyTq keycode=0 interactive=true keyguardActive=false policyFlags=22000000
1313-1421/system_process I/SingleKeyGesture: Press another key KEYCODE_UNKNOWN // 未知code按下
1313-1421/system_process D/WindowManager: interceptKeyTq keycode=0 interactive=true keyguardActive=false policyFlags=22000000
1313-1420/system_process D/WindowManager: interceptKeyTi keyCode=0 down=true repeatCount=0 keyguardOn=false canceled=false
1313-1420/system_process D/WindowManager: interceptKeyTi keyCode=0 down=false repeatCount=0 keyguardOn=false canceled=false
1313-1420/system_process D/WindowManager: Unhandled key: inputToken=android.os.BinderProxy@8910c7a, title=com.google.android.setupwizard/com.google.android.setupwizard.user.WelcomeActivity, action=0, flags=8, keyCode=0, scanCode=251, metaState=0, repeatCount=0, policyFlags=1644167168
1313-1420/system_process D/WindowManager: No fallback.
1313-1420/system_process D/WindowManager: Unhandled key: inputToken=android.os.BinderProxy@8910c7a, title=com.google.android.setupwizard/com.google.android.setupwizard.user.WelcomeActivity, action=1, flags=8, keyCode=0, scanCode=251, metaState=0, repeatCount=0, policyFlags=1644167168
1313-1420/system_process D/WindowManager: No fallback.
一个关键字映入眼帘KEYCODE_UNKNOWN
,未知code,长安下power键后为什么回打印出一个KEYCODE_UNKNOWN的日志,再分析一下日志,可以看出一点问题的端倪,从日志中可以看到以下三句关键日志
1313-1421/system_process I/InputReader: processKey down:1 scanCode:116 keyCode:26
1313-1421/system_process I/InputReader: processKey down:1 scanCode:251 keyCode:0
1313-1421/system_process I/InputReader: processKey down:0 scanCode:251 keyCode:0
InputReader是有读取到 keycode:26 也就是 power键按下事件的,但是紧接着又读取到了keycode:0,这个0代表的就是未知code也就是KEYCODE_UNKNOWN
,而且不但读取到了keycode:0还同时读取到了keycode:0的down事件和up事件,在这里先解释一下日志中的几个关键词
InputReader
:底层线程负责在底层读取事件
down
:也就是手指按下抬起的事件,down:1 代表按下,down:0 代表抬起
scanCode
:代表的是事件没有传到上层之前底层的扫描码,例如这里 power按键在底层的扫描码就是116
keyCode
:代表的是scanCode经过转换之后给上层的键盘码,power按键的键盘码即为26
scanCode具体的定义是在xxx.kl文件中的,xxx.kl文件在系统中存在不止一个,而是有很多个
如果项目没有特殊配置一般都是使用的默认的Generic.kl,如下图
那么这么多的xxx.kl文件我们要找到系统具体使用的那个kl文件岂不是和大海捞针差不多,别害怕,google已经替我们想好了
你只需要使用dumpsys input
命令即可查看系统当前使用的是那个kl文件
keycode 是定义在KeyEvent.java文件中的
三,添加日志
好了了解了相关知识后,我们再来继续分析问题,从以上的日志分析我们可以知道问题的原因,底层读取到的power按键scanCode:116还没有来得及传到上层就被scanCode:251给覆盖了,而且这个251并没有在系统的Generic.kl文件中定义,所以上层转换后识别不了就是keycode:0也就是日志中打印的KEYCODE_UNKNOWN,这个只是查清楚了framework层的原因,底层根音并没有查清楚,为什么长按power键后没有任何其他的动作底层会上报scanCode:251而且还把down和up事件一起上报了,为了查清楚问题的底层原因,我们必须要把power按键由底层到上层的处理流程搞清楚这样才能顺藤摸瓜找到底层的调用逻辑,先来一张图看一下总体的流程
从图中我们可以看出底层的处理流程大致就是
EventHub—>InputReader–>InputDevice–>KeyboardInputMapper–>InputDispatcher
我们可以在这些关键类的方法中加一些日志去验证我们的猜想,不过打蛇打七寸,我们可以先只在一开始读取事件的地方去加日志验证一下,如果这个地方没有问题那么说明问题可能就出在framework层的input逻辑处理中,如果这个地方打印的日志有问题
那说明底层的驱动层上报的事件本身就有问题,也就排除了framework层的嫌疑,那么应该在哪里加呢,答案就是 EventHub::getEvents
方法,我们先来看看EventHub这个类是干什么的,EventHub主要有以下三个作用
- 监控/dev/input/目录的iNotify文件mINotifyFd
- 接收Kernel驱动事件(/dev/input/eventX)的文件描述符
- 用来唤醒InputReader线程的管道读文件
如下图所示在红框处添加日志打印iev.code
和iev.value
这两个值分别代表的是底层原始的 scancode 和 down 值
1313-1421/system_process I/EventHub: EventHub_getEvent down:1 scanCode:116
1313-1421/system_process I/EventHub: tEventHub_getEvent down:0 scanCode:0
1313-1421/system_process I/EventHub: EventHub_getEvent down:1 scanCode:251
1313-1421/system_process I/EventHub: EventHub_getEvent down:0 scanCode:0
1313-1421/system_process I/EventHub: EventHub_getEvent down:0 scanCode:251
1313-1421/system_process I/EventHub: EventHub_getEvent down:0 scanCode:0
四,getevent工具
通过添加日志之后我们可以清楚的看到,底层的原始数据有问题,上报给framewrok层的就是错误的scanCode,也就是说这个问题的根因在驱动层而非framework层。现在确认了问题出在驱动层但是还是没有确认到具体在驱动层哪里有问题,而且再往下层走也不清楚怎么分析了,无从下手了,不要着急这里还有个大杀器没有祭出,就是getevent
命令,如果对getevent命令比较熟悉,甚至都不用在framework层加日志,敲几个命令就能定位出问题,现在我们来使用神奇的getevent
命令,先做个大概介绍,getevent工具能够在终端设备上运行,并且能够提供关于Android终端输入设备和Linux内核输入事件的实时转储关系,可以确保设备驱动程序报告各个输入设备的一系列预期功能并生成输入事件所需的信息流。
- getevent –h查看该工具的用法
- getevent:显示当前有那些输入设备,数量与 /dev/input 目录下相同
可以看到在/dev/input/
下有event0~event6 这几个设备节点 。
输入getevent命令后,我们在操作复现一下问题
可以看到有00fb的数据打印出来,这里使用的是16进制 0074 也就是power键的scancode转换成十进制也就是116 同理 00fb也就是scancode为251的未知扫描码
- getevent –l 查看详细的事件类型
可以看到 power键按下后紧接着上报了 00fb的down和up事件,和日志打印出来的结果是一致的
- getevent –p 查看event节点的详细信息
由上图我们可以看出,这个未知扫描码 251 是由event6上报的,我们来看看event6具体是干什么的
至此我们就找到问题的所在了,这个uinput-fpsensor
节点在长按power键后上报了 扫描码251的down和up事件,进而导致了问题的出现,这个看起来应该是一个和指纹相关的sensor为什么会定义在这里,和负责指纹的同事沟通后,确认了问题的原因,原来这个单独的节点是适配指纹导航功能的,而这个项目的指纹功能又是集成在power键上的,所以产生了冲突,项目刚刚启动,问题还很多,还没有处理这些冲突事件,和同事确认了下解决方案,项目目前还不需要指纹导航的功能,这个功能会去掉,等FAE更新库文件以后问题就会修复。
五,总结
在平时开发和解决问题的时候,多了解和使用android的工具,有时候可以达到事半功倍的效果。平时要有意识的去总结这些命令工具的使用方法。