异常现象
android开启触摸白点模式后, 屏幕上出现白点不消失,存在一个长按down事件
分析
经过研究,发现多点触摸协议存在type A和type B。出现此异常的TP使用的是Type B的多点触摸协议,支持轨迹跟踪。
通过sendevent发送input event事件,发现该异常与ABS_MT_SLOT,ABS_MT_TRACKING_ID事件关系密切。先简单介绍下这2个事件。
多点触摸协议的事件
ABS_MT_SLOT这个事件的SLOT值由TP IC传递给主机.
ABS_MT_TRACKING_ID这个事件的值由Input Driver生成(也可以由TP Driver中hardCode)。在input driver中它的值分为2种情况正值(代表与之关联的SLOT的down事件),负值(-1代表与之关联的SLOT的UP事件);不同的SLOT匹配不同的ABS_MT_TRACKING_ID值,ABS_MT_TRACKING_ID值是一个递增的值。
本文之前提到的问题与ABS_MT_TRACKING_ID事件密切相关
异常的可能性
TP Driver没有发送对应的ABS_MT_SLOT和ABS_MT_TRACKING_ID(-1)的事件
TP Driver仅仅发送了ABS_MT_SLOT,没有发送ABS_MT_TRACKING_ID的正值;
第一类问题很好处理,仅仅需要在合适的时候增加一个UP的事件组合即可,第二类问题相对复杂。
第二类问题,可能是驱动本身遗漏了ABS_MT_TRACKING_ID的正值,相对容易排查;也可能是因为input event的异步发送导致的问题,这个问题相对复杂。经过调试input driver,我获取了一些信息。
对于常用的ABS_MT_SLOT常用通道(1~5)发生异常,很容易通过人为的多点触摸恢复,因为在新的多点触摸中驱动必然由down和up事件生成,那么此时异常旧恢复了,但是对于ABS_MT_SLOT通道值较大(10~15)的异常,旧很难通过人为的手动操作恢复了,没那么多手指去触摸呀。
我调试的是cyttsp的一款触摸板,通过手动触摸和sendevent命令同时操作很容易就触发这个bug。
经过调试我发现,cyttsp的驱动的sync_report插入了sendevent发送的事件中间,导致sendevent发送的事件添加了一个ABS_MT_SLOT为15的触摸事件集合,并且这个事件集合中没有ABS_MT_TRACKING_ID事件。最终这个点发送除去,并且被framework作为一个down事件处理。然而在最后释放的时候这个ABS_MT_SLOT为15对应的点确没有UP事件。
这个测试说明:缺少ABS_MT_TRACKING_ID的触摸down事件可以被framework处理,但是kernel没办法触发对应的
dev=8
function touch_point()
{
adb shell sendevent /dev/input/event${dev} 3 ABS_MT_SLOT $1
adb shell sendevent /dev/input/event${dev} 3 ABS_MT_TRACKING_ID $2
adb shell sendevent /dev/input/event${dev} 3 53 $3
adb shell sendevent /dev/input/event${dev} 3 54 $4
}
sendevent发送了ABS_MT_TRACKING_ID后,cyttsp发送了UP事件,完成了此次点击。后面2条命令继续发送,产生了以仅仅包含ABS_MT_SLOT的触摸事件。ABS_MT_SLOT的值为15是因为cyttsp驱动在每次事件处理后会遍历ABS_MT_SLOT并释放。
通过分析input模块代码,在tp driver或者user space空间向input driver发送input event时,
如果发送的事件ACTION_1包含(ABS_MT_SLOT,ABS_MT_POSITION_X,ABS_MT_POSITION_Y, ABS_MT_PRESSURE)且不含ABS_MT_TRACKING_ID时,
tp driver即使发送了ACTION_2(对应的ABS_MT_SLOT和ABS_MT_TRACKING_ID=-1),但是在input driver 模块处理event时,因为在input_handle_abs_event函数中逻辑如下:
static int input_handle_abs_event(struct input_dev *dev,
unsigned int code, int *pval)
{
struct input_mt *mt = dev->mt;
bool is_mt_event;
int *pold;
if (code == ABS_MT_SLOT) {
//input_event函数发送ABS_MT_SLOT消息时,实际上并不将对应的事件添加到队列中
//仅仅完成了赋值操作
if (mt && *pval >= 0 && *pval < mt->num_slots)
mt->slot = *pval;
return INPUT_IGNORE_EVENT;
}
is_mt_event = input_is_mt_value(code);
if (!is_mt_event) {
pold = &dev->absinfo[code].value;
} else if (mt) {
//因为ACTION_1中没有ABS_MT_TRACKING_ID的值,因此这个值一直为-1
//当发送ACTION_2事件时,这个pold的值为-1
pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
} else {
/*
* Bypass filtering for multi-touch events when
* not employing slots.
*/
pold = NULL;
}
if (pold) {
*pval = input_defuzz_abs_event(*pval, *pold,
dev->absinfo[code].fuzz);
//pold和pval都为-1,因此不处理这个事件,丢失抬起事件
if (*pold == *pval)
return INPUT_IGNORE_EVENT;
*pold = *pval;
}
/* Flush pending slot event */
if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
//input_event函数发送ABS_MT_SLOT消息时,实际上并不将对应的事件添加到队列中
//在使用input_event发送其他消息时,根据需要在返回值中增加INPUT_SLOT,
//然后在input_handle_event函数中把ABS_MT_SLOT数据添加发送队列中
return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
}
return INPUT_PASS_TO_HANDLERS;
}
出现白点不消失后,需要再次给对应的ABS_MT_SLOT,发送一条包含ABS_MT_TRACKING_ID的值(需要为正值),然后再次发送释放point的input event(对应的ABS_MT_SLOT和ABS_MT_TRACKING_ID=-1)