Android的触摸机制
kernel部分:
http://doc.okbase.net/u010245383/archive/211207.html 这个文章研究的蛮详细,可供参考。
首先保证I2C移植成功并正常工作,如下:
系统启动后通过串口或adb shell进入系统命令行窗口,查询/sys/bus/i2c/devices目录下是否有0-0038信息,查询/sys/bus/i2c/drivers目录下是否存在‘ft5x16’设备名;先保证i2c能够正常通信;
setup_arch->setup_machine_fdt+unflatten_device_tree->arch_initcall_sync(arm64_device_init)
以上是设备驱动的执行,开始执行设备驱动。
触摸驱动部分的加入:以ft5X0X系列的驱动为例分析。
module_init(tpd_driver_init);->
tpd_driver_init->tpd_driver_add
这个地方是MTK的适配机制吧,不同的驱动挂载进去。通过硬件适配,比如I2C设备总线与从机地址通信,没有的话就会报错返回。
late_initcall(tpd_device_init);
直接执行到probe的探测函数。
g_tpd_drv->tpd_local_init();通过名字查找,直接执行tpd_local_init,以此执行驱动的probe函数。
这里初始化两个部分,tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);
这里的触摸分两个区域,一个是虚拟按键,就是常用手机的home,返回,和查看活动列表的。
#define key_1 250,1950
#define key_2 900,1950
#define TPD_KEY_COUNT 3
#define TPD_KEYS {KEY_MENU,KEY_HOMEPAGE, KEY_BACK}
#ifdef TPD_HAVE_BUTTON
tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);// initialize tpd button data
#endif
tpd_probe:也是基本常用的,一是配置中断口,复位按键和设备节点。中断函数。
mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM, EINTF_TRIGGER_FALLING, tpd_eint_interrupt_handler, 1);
中断触发函数,中断来临时,触发
有点搞不明白,硬中断触发后,进入到touch_event_handler线程中去。
thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);
然后在touch_event_handler中数据上报。
系统挂载和使用:
systemserver.java中调用:
inputManager = new InputManagerService(context);
进入到InputManagerService.java的构造函数。
->nativeInit
nativeInit的定义在com_android_server_input_InputManagerService.cpp中,这里就是Android的JNI的调用了。
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
执行到NativeInputManager的构造函数。
sp eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
然后执行到InputManager的构造函数。
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
这里终于见到了是哪个主角,eventhub的主要作用是从/dev/input/eventX中读取插入,拔出,中断产生的数据。这里主要是开机初始化干的事情,如果是event则是执行add,如果是键盘,产生FINISHED_DEVICE_SCAN加载键盘配置文件。
inputreader则是调用getevent函数获得raw_events构造数据,通过socket管道通知inputdipatcher分发出去。
initialize();则是分别创造除非死机,否则不会撤销的线程。
将windowmanagerservice与inputmanagerservice建立连接。具体以后分析。
那么线程的执行是如何开启的呢?
如下:继续回到systemserver.java中去。
inputManager.start();
执行到inputmanagerservice.java的start
nativeStart();进入到com_android_server_input_InputManagerService.cpp中执行。
进入到inputmanager.cpp中去执行start。
mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
开始各自的线程执行。
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
->
dispatchOnce
->
至于怎么执行到这里,需要研究inputreader的函数,一会详细交代。
mLooper->pollOnce(timeoutMillis);
pollOnce();
->
result = pollInner(timeoutMillis);
->
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
这里标志着阻塞在这里,等着inputreader队列里消息。
最终唤起这个进程的是inputdispatcher的
if (needWake) {
mLooper->wake();
}
wake();->
nWrite = write(mWakeWritePipeFd, "W", 1);
这里还要插入一知识点,为什么在EventHub的构造函数里初始化的是mWakeReadPipeFd,但这里写入的是mWakeWritePipeFd,这里就是Linux的pipe的知识了,
int wakeFds[2];
result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
写入到mWakeWritePipeFd,然后mWakeReadPipeFd读取到,唤醒这里的阻塞,真的很棒。
终于这里调用过程接上道了。
进入到inputreader的分析。
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
先执行的是inputreader的线程,然后合成构造数据后,通过管道通知inputdispatcher执行。
进入到inputreader的分析中。
mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
进入到eventhub中执行。
getEvents中主要分析了三种情况,一种是设备的插入记载,remove记载,和中断数据产生记载。
for (;;) 也是进入到for循环中。
不过这里分析下epoll和inotify的机制,细节不叙述。
inotify是Linux的文件系统变化监听机制,创建,删除,读写。
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
监听dev/input/下的所有文件变化。
但监听时却没有主动的上报,只是记载,因为用epoll用来监听上报。
int epfd = epoll_create(intsize); 创造一个句柄,最大监听大小是size。
删除和添加监听的fb
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
这个是阻塞,触发就把mPendingEventItems加入队列,然后就清空,否则一直等待这里。
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
分别监听event的数据和管道,用来通知reader和dispatcher。
然后再次进入到getevent中进行分析。
scanDevicesLocked();这是进入到打开设备中,open event。
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
唤醒epoll的金监控,继续进入到mReader->loopOnce进行分析。
inputreader线程在getevents数据后的处理过程:
这个调用过程中,会分为add device ,remove device,和中断数据处理。
其中add和remove其实就是device->addMapper(new SwitchInputMapper(device))的过程。
processEventsLocked->processEventsForDeviceLocked;
device->process(rawEvents, count);
这里是个virtual基类,调用的是派生类的函数,因为会进入到类似void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent)
这里插句话:raw_event中的数据就是code代表的坐标。
类似如下处理,说明一个raw_event里只是一个坐标。这个设计会不会太操蛋。也可能是我没理解到位。
一个最初级的触摸需要四个raw_event,(x1,y1),(x2,y2).
触摸事件一共三种:down up move .
down类型的touch事件需要四个RawEvent来完成,第一个是X坐标(ABS_X),第二个是Y坐标(ABS_Y),第三个代表方向(ABS_PRESSURE)(0的时候是up,1的时候是down,所以这里应该是1),第四个是结束标志(SYN_REPORT)。
move类型的touch事件需要三个RawEvent来完成,第一个是X坐标,第二个是Y坐标,第三个是结束标志。up类型的touch事件需要两个RawEvent来完成,第一个代表方向(0的时候是up,1的时候是down,所以这里应该是0),第四个是结束标志。可能你已经注意到了up事件是没有坐标信息的,它的坐标信息与down(没有move时)或最后一个move(down和up之间有move事件产生)事件的坐标相同
switch (rawEvent->code) {
case ABS_MT_POSITION_X:
slot->mInUse = true;
slot->mAbsMTPositionX = rawEvent->value;
break;
case ABS_MT_POSITION_Y:
slot->mInUse = true;
slot->mAbsMTPositionY = rawEvent->value;
break;
mQueuedListener->flush();创建NotifyArgs类型的元素,将数据压入队列中。
flush();->
args->notify(mInnerListener);
这里执行以motion为例。
void NotifyMotionArgs::notify(const sp& listener) const {
listener->notifyMotion(this);
}
大家关注下mInnerListener是如何实例化的。
QueuedInputListener::QueuedInputListener(const sp& innerListener) :
mInnerListener(innerListener) {
}
InputReader.cpp中构造函数中mQueuedListener = new QueuedInputListener(listener);
在inputmanager.cpp中构造函数。
InputManager::InputManager(
const sp& eventHub,
const sp& readerPolicy,
const sp& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
这里可以看到mInnerListener就是InputDispatcher的实例,其实就是对象拷贝。good!!!
进入到InputDispatcher::notifyMotion
mLooper->pollOnce(timeoutMillis);
pollOnce();
->
result = pollInner(timeoutMillis)
if (needWake) {
mLooper->wake();接上开始分析了。
}唤醒dispatchOnce
继续下一轮的执行mDispatcher->dispatchOnce();
接下来分析inputdispacther如何把事件上报给APP的。
->dispatchOnceInnerLocked
这里会根据type进入到不通的switch 下执行。
case EventEntry::TYPE_KEY
case EventEntry::TYPE_MOTION
->dispatchMotionLocked
最后所有的不同事件,都会执行到
dispatchEventLocked(currentTime, entry, inputTargets);
->prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
->enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
->startDispatchCycleLocked(currentTime, connection);
这里的主要作用则是加入到共享内存中去。
case EventEntry::TYPE_MOTION:
{
status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
motionEntry->deviceId, motionEntry->source,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
xOffset, yOffset,
motionEntry->xPrecision, motionEntry->yPrecision,
motionEntry->downTime, motionEntry->eventTime,
motionEntry->pointerCount, motionEntry->pointerProperties,
usingCoords);
}
publishMotionEvent:
执行写入到共享内存中,并发消息通知
for (uint32_t i = 0; i < pointerCount; i++) {
msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
/// M: input systrace @{
if (ATRACE_ENABLED()) {
char buffer[50];
sprintf(buffer, "pub_x : %d, pub_y : %d, seq=%u",
(int)pointerCoords[i].getX(), (int)pointerCoords[i].getY(), seq);
ATRACE_BEGIN(buffer);
ATRACE_END();
}
/// @}
}
return mChannel->sendMessage(&msg);
->nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
这里的符号蛮奇葩的哈,表示send函数的作用域无限大。这里表示调用的是全局函数。
这里的send难道是接着
pollInner();
->
int callbackResult = response.request.callback->handleEvent(fd, events, data);
这里又是socket通信,Android果然脱离不开Linux的本质呀。
looper中会监控socket fd的变化。
回调当时注册的函数NativeInputEventReciever的handleEvent的。
->
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
->
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
这里返回consumeEvents的for循环中,执行JNI的调用JAVA -dispatchInputevent
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
这里是调用到了JAVA世界的dispatchInputEvent。