您的位置:首页 > 移动开发 > Android开发

android 系统触摸屏BUG解决过程分析

2017-06-07 11:29 766 查看
          BUG描述:添加触摸屏驱动后, apk对触摸事件没有响应.
 Linux
层驱动移植

内核根目录 make menuconfig
更改 “Device Drivers” ->“HID Devices”
下的 “/dev/hidraw” “PID device support”
和 “/dev/hiddev”
三个位置置成*
复制 “ilitek_auv3X.c”和 “usbhid.h” 到内核下的 “drivers/hid/” 文件夹中
在“drivers/hid/Makefile”
文件中添加
“obj-$(CONFIG_INPUT_ILITEK_TOUCH) +=ilitek_auv3X.o”
在drivers/hid/Kconfig
中添加
“config INPUT_ILITEK_TOUCH”
tristate “ILITEK USB
touch
screen driver”
内核根目录 make menuconfig
将 “Device Drivers”->”HID Devices”->”Special HID drivers”->ILITEK USB touch screen driver”
的状态置成 ‘*’
问题:将内核编译并烧录进目标板后报错 “hid probe creating class error”
解决:发现是BUS多次调用"hid_probe"注册同一个USB设备所导致的错误。既然是同一个USB设备就有相同的PIDVID,如是在drivers/hid/ilitek_auv3X.chid_probe
添加两个静态变量记录PIDVID,如果相同则返回不进行任何处理。然后在编译烧入进目标板,
如何是否查看注册成功
 
# ls /dev/ilitek_ctrl_usb
 
# cat /proc/bus/input/devices
   
I: Bus=0003 Vendor=222a Product=0001
Version=0110
N: Name="ILITEK Multi-Touch-V3000"
P: Phys=usb-fsl-ehci.1-1/input0
S: Sysfs=/devices/platform/fsl-ehci.1/usb2/2-1/2-1:1.0/input/input2
U: Uniq=
H: Handlers=event2

B: EV=b
B: KEY=4000000000000
B: ABS=26500000
 
# ls /dev/input/event
event0 event1  event2

android 层

修改设备权限
# dd if=uramdisk.imgof=ramdisk.img.gz skip=64 bs=1
# gunzip ramdisk.img.gz
# mkdir ramdisk; cd ramdisk
# cpio -i < ../ramdisk.img
# vim init.rc  
(modify the init.rc)
 
```
添加设备权限
chmod 0777 /dev/ilitek_ctrl_usb
chmod 0777 /dev/input/event2
```
 
 
# find . | cpio --create--format='newc' | gzip > ../ramdisk.img
# mkimage -A arm -O linux -T ramdisk-C none -a 0x70308000 -n "Android Root Filesystem" -d ./ramdisk.img./uramdisk.img
在aosp工程目录下将 ilitek_hid.idc
复制到 out/target/product/xxx/system/usr/idc/(注意此文件名应与触摸屏驱动名一致)
烧录镜像,查看反应
发现问题:
在android
进入luncher后,点击触摸屏,并没有任何反应。在终端输入
getevent
getevent
add device
1: /dev/input/event1
 name:    "mxc_power_key"
add device
2: /dev/input/event0
 name:     "mxckpd"
add device
3: /dev/input/event2
 name:     "ILITEKMulti-Touch-V3000"
能发现"ILITEK Multi-Touch-V3000"设备,点击触摸屏能获取到事件
/dev/input/event2:
0003003900000000
/dev/input/event2:
0003003000000001
/dev/input/event2:
000300350000292c
/dev/input/event2:
00030036000011a6
/dev/input/event2:
0000000200000000
/dev/input/event2:
0000000000000000
/dev/input/event2:
0003003900000000
/dev/input/event2:
0003003000000000
/dev/input/event2:
0000000200000000
/dev/input/event2:
0000000000000000
对比了官方正常驱动事件上报流程,发现一致,说明驱动并没有说明问题。
如是使用了一个触摸屏测试程序
@Override
public
booleanonTouchEvent(MotionEvent event)
应用层加上log没有任何反应,在其他android机下能实现点击,拖拽,缩放等功能。
-----------------------------------------------------------------------------------------------------------------------------------------
 
----------------------------------------------------------------------------------------------------------------------------------------
AndroidFramework

既然驱动层没问题应用层没问题,就只能在framework层找问题了在调试过程中需要添加log ,定位问题.

LOG级别
修改/system/core/rootdir/init.rc,把loglevel从3改为7
Android系统启动的log分为Linux内核的log和Android
Logger系统的log,
抓取的方法如下:
$ adb shell dmesg > dmesg.txt
 
$ adb logcat -d -v
time -b"main"  >  main.txt
 
$ adb logcat -d -v
time -b"system" > system.txt
 
$ adb logcat -d -v
time -b"events" > events.txt
由于log数量较多,
需要静态分析log
请使用SecureCRT之自动记录日志功能:

http://jingyan.baidu.com/article/335530da88aa0b19cb41c3b9.html
理解 android输入子系统
android 子系统主要由以下几个模块组成:
EventHub:事件的传入是从EventHub开始的,EventHub是事件的抽象结构,维护着系统设备的运行情况,并维护一个所有输入设备的文件描述符
Input Reader:
负责从硬件获取输入,转换成事件(Event),
并分发给Input Dispatcher.
Input Dispatcher:
Input Reader传送过来的Events
分发给合适的窗口,并监控ANR。
Input Manager Service:负责Input
Reader 和
Input
Dispatchor的创建,并提供Policy
用于Events的预处理。
Window Manager Service:管理Input
Manager 与
View
Window)
以及 ActivityManager
之间的通信。
View and Activity:接收按键并处理。
ActivityManager Service:ANR
处理
在在framework
层中信息分析输入子系统各个模块函数功能,并添加相应LOG对其验证,发现EventHub InputRead模块运行正常,在InputDispatcher中添加打印时,发现InputDispatch
没有走到最后一步startDispatchCycleLocked函数中的PublishMotionEvent.
如是向上分析发现LOG
"inbound event was droppedbecause the policy consumed it "
进一步分析发现
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime){
   ```
   ```
   bool done = false;
   DropReason dropReason = DROP_REASON_NOT_DROPPED;
   if (!(mPendingEvent->policyFlags &POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;
   } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
   }
}
设置为DROP_REASON_POLICY主要有两种情形:
A. 在InputReader notify InputDispatcher之前,Policy会判断不需要传递给应用的事件。
 
B. 在InputDispatcher dispatch事件前,PhoneWindowManager使用方法interceptKeyBeforeDispatching()提前consume掉一些按键事件,interceptKeyBeforeDispatching()主要对HOME/MENU/SEARCH按键的特殊处理,如果此时能被consume掉,那么在InputDispatcher
中将被丢弃。
由于本系统不使用这些按键,如是修改./system/usr/keylayout/下的.kl文件映射值。屏蔽掉它,排除HOME/MENU/SEARCH按键的可能。
验证:touchScreen
还是没反应
如是用了一狠招,将如下return
注释,使dispatcher正常执行到最后一步
boolInputDispatcher::dispatchMotionLocked( 

        nsecs_t currentTime,MotionEntry* entry, DropReason* dropReason,
nsecs_t* nextWakeupTime){ 
   ····
   ····
 
   // Cleanup if dropping the event. 

   if (*dropReason != DROP_REASON_NOT_DROPPED) { 

        setInjectionResultLocked(entry,*dropReason == DROP_REASON_POLICY 

               
?INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); 
   //   
return true; 修改方法 

   }
   
   ····
   ····
   }
验证:查看LOG没有其他异常,安装触摸屏测试APK发现点击,缩放,拖拽均没有问题
进一步分析
上述问题解决了但是没有找到具体原因,下面进一步分析.
在程序流程前面找原因:
查看前面的调用发现是由于mPendingEvent->policyFlags没有置POLICY_FLAG_PASS_TO_USER
导致dropReason = DROP_REASON_POLICY
voidInputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
        nsecs_t keyRepeatDelay,
nsecs_t*nextWakeupTime) {
   nsecs_t currentTime = now();
 
   ````
   ````
 
   // Now we have an event to dispatch.
   assert(mPendingEvent != NULL);
   bool done = false;
   DropReason dropReason = DROP_REASON_NOT_DROPPED;
   if (!(mPendingEvent->policyFlags &POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;
   } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
   }
再往上面找mPendingEvent->policyFlags
再往上找mPendingEvent->policyFlags
voidInputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout,
        nsecs_t keyRepeatDelay,
nsecs_t*nextWakeupTime) {
 
 mInboundQueue.dequeue(entry);
mPendingEvent = entry;
 
}
再找mInboundQueue
void InputDispatcher::dispatchOnce() {
   nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
   nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
 
   nsecs_t nextWakeupTime = LONG_LONG_MAX;
   { //acquire lock
        AutoMutex _l(mLock);
       dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, &nextWakeupTime);
 
        if(runCommandsLockedInterruptible()) {
            nextWakeupTime =LONG_LONG_MIN; 
// force next poll to wake upimmediately
        }
   } //release lock
 ``
 ``
 }
再找dispatchOnceInnerLocked
void InputDispatcher::dispatchOnce() {
   nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout();
   nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay();
 
   nsecs_t nextWakeupTime = LONG_LONG_MAX;
   { //acquire lock
        AutoMutex _l(mLock);
       dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, &nextWakeupTime);
 
        if(runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN; 
// force next poll to wake up immediately
        }
   } //release lock
 
 ``
 ``
}
再找dispatchOnceInnerLocked
void InputDispatcher::notifyMotion(nsecs_teventTime,
int32_t deviceId, int32_t source,
        uint32_t policyFlags,
int32_taction, int32_t flags, int32_t metaState,
int32_tedgeFlags,
        uint32_t pointerCount,
constint32_t* pointerIds, const PointerCoords* pointerCoords,
        float xPrecision,
floatyPrecision, nsecs_t downTime) {
``
``
   policyFlags |= POLICY_FLAG_TRUSTED;
//注意此处赋值
   mPolicy->interceptGenericBeforeQueueing(eventTime,
/*byref*/ policyFlags);
//执行后并不会影响 policyFlags的值依然为
POLICY_FLAG_TRUSTED  这就便造成了上述出现的直接返回的问题
    bool needWake;
   { //acquire lock
        AutoMutex _l(mLock);
 
        // Attempt batching and streaming ofmove events.
        if (action ==AMOTION_EVENT_ACTION_MOVE) {
NoBatchingOrStreaming:;
        }
 
        // Just enqueue a new motion event.
        MotionEntry* newEntry =mAllocator.obtainMotionEntry(eventTime,
                deviceId,
source, policyFlags, action,flags, metaState, edgeFlags,
               
xPrecision, yPrecision,downTime,
               
pointerCount, pointerIds,pointerCoords);
 
        needWake =enqueueInboundEventLocked(newEntry);
//在此处进队列
   } //release lock
 
   if (needWake) {
        mLooper->wake();
   }
}
在InputReader notifyMotion
中被调用
void TouchInputMapper::dispatchTouch(nsecs_twhen,
uint32_t policyFlags,
        TouchData* touch, BitSet32 idBits,
uint32_tchangedId, uint32_t pointerCount,
        int32_t motionEventAction) {
   int32_t pointerIds[MAX_POINTERS];
   PointerCoords pointerCoords[MAX_POINTERS];
   int32_t motionEventEdgeFlags =
0;
   float xPrecision, yPrecision;
   ```
   ```
        xPrecision = mLocked.orientedXPrecision;
        yPrecision =mLocked.orientedYPrecision;
   } //release lock
 
   getDispatcher()->notifyMotion(when, getDeviceId(), getSources(),policyFlags,
            motionEventAction,
0,getContext()->getGlobalMetaState(), motionEventEdgeFlags,
            pointerCount, pointerIds,pointerCoords,
            xPrecision, yPrecision, mDownTime);
}
通过上述分析是void InputDispatcher::notifyMotion中的mPolicy->interceptGenericBeforeQueueing(eventTime,/byref/
policyFlags);没有更改了policyflag所导致的,于是添加Log查看没有变化前后数值一致
POLICY_FLAG_TRUSTED = 0x0200000, 注意后面0的个数
分析interceptGenericBeforeQueueing的定义mPolicy的interceptGenericBeforeQueueing实现在 frameworks\base\services\jni\com_android_server_InputManager.cpp中
void NativeInputManager::interceptGenericBeforeQueueing(nsecs_twhen,
uint32_t& policyFlags) {
#if DEBUG_INPUT_DISPATCHER_POLICY
   LOGD("interceptGenericBeforeQueueing- when=%lld, policyFlags=0x%x", when, policyFlags);
#endif
 
   //Policy:
   // -Ignore untrusted events and pass them along.
   // - Nospecial filtering for injected events required at this time.
   // -Filter normal events based on screen state.
   // - Fornormal events brighten (but do not wake) the screen if currently dim.
   if ((policyFlags & POLICY_FLAG_TRUSTED) &&!(policyFlags & POLICY_FLAG_INJECTED)) {
        if (isScreenOn()) {
            policyFlags |=POLICY_FLAG_PASS_TO_USER;
 
            if (!isScreenBright()) {
               
policyFlags |= POLICY_FLAG_BRIGHT_HERE;
            }
        }
   } else {
        policyFlags |=POLICY_FLAG_PASS_TO_USER;
   }
}
policyFlags 前后都为POLICY_FLAG_TRUSTED
因此可以断定是 isScreenOn()
为false
所导致,
继续分析isScreenOn()
android_server_PowerManagerService_isScreenOn
 
--> gScreenOn
-->android_server_PowerManagerService_nativeSetPowerState
 接着提供函数给JNI层调用
-->nativeSetPowerState
分析nativeSetPowerState
在base\services\java\com\android\server\PowerManagerService.java中
private
void updateNativePowerStateLocked() {
        nativeSetPowerState(
               (mPowerState & SCREEN_ON_BIT) !=
0,
               
(mPowerState &SCREEN_BRIGHT) == SCREEN_BRIGHT);
}
这已经涉及到电源管理 framework.
由mPowerState的值决定nativeSetPowerState函数的实参 mPowerState是private的经过详细分析由setPowerState 或者 synchronized或者systemReady() 或者acquireWakeLockLocked , 于是在应用直接请求 wakelock。 发现没有用。
后来与同事讨论,发现linux层在电源层管理做了某些处理,还有一部分并没有完善。android的电源管理框架在底层调用中可能会出现异常,这就造成Android framework一直以为屏幕是关闭的, 所以触摸事件一直被intercept无法上传到应用层。获取wakelock也起不到作用。考虑到系统不会进行自动息屏休眠, 于是在\frameworks\base\services\jni\com_android_server_InputManager.cpp
文件中的interceptGenericBeforeQueueing函数中强制 policyFlags |= POLICY_FLAG_PASS_TO_USER。最终Android apk应用可以接收到触摸屏事件。

总结

此次bug的解决,通过一层一层的分析,最终找到问题的根源. 涉及到Linux驱动层,android  input framework和powermanagerframework. 以及应用层相关监控事件的调用. 关联到的代码量非常大, 不建议直接阅读代码去找问题,因为分析的信息量太大,一不小心走了点弯路,可能就会浪费时间.建议先多阅读安卓官方文档,以及framework 的UML图.将问题规模缩小,然后详细分析代码.
参考:
http://www.360doc.com/content/14/0329/00/10366845_364576767.shtml
http://www.tuicool.com/articles/be2qI3v
http://www.linuxidc.com/Linux/2011-11/47721p2.htm
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐