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 touchscreen 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设备就有相同的PID VID,如是在drivers/hid/ilitek_auv3X.chid_probe 添加两个静态变量记录PID VID,如果相同则返回不进行任何处理。然后在编译烧入进目标板,

如何是否查看注册成功

# 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.img of=ramdisk.img.gz skip=64 bs=1
# gunzip ramdisk.img.gz
# mkdir ramdisk; cd ramdisk
# cpio -i < ../ramdisk.img
# vim 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 boolean  onTouchEvent(MotionEvent event)

应用层加上log没有任何反应,在其他android机下能实现点击,拖拽,缩放等功能。

-----------------------------------------------------------------------------------------------------------------------------------------

 

----------------------------------------------------------------------------------------------------------------------------------------

Android Framework

既然驱动层没问题应用层没问题,就只能在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 与 ViewWindow) 以及 ActivityManager 之间的通信。

View and Activity:接收按键并处理。

ActivityManager Service:ANR 处理

在在framework 层中信息分析输入子系统各个模块函数功能,并添加相应LOG对其验证,发现EventHub InputRead模块运行正常,在InputDispatcher中添加打印时,发现InputDispatch 没有走到最后一步startDispatchCycleLocked函数中的PublishMotionEvent. 如是向上分析发现LOG

"inbound event was dropped  because 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 
                
   //    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,
                
                
 
        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()) {
                
            }
        }
   } 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,
                
}

这已经涉及到电源管理 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