会议教育机之另外一种触摸穿透机制,使用Android输入子系统

     专门解决类似创维光电所使用的LG触摸框,此触摸框没有触摸穿透协议,只有开关触摸协议。曾应用于mstar828、mstar848/8386、MT9632、MT9630平台。无论是amlogic平台、MTK平台还是RK平台,但凡是使用了Android平台用信号源的都可以使用。

     对于USB+串口的触摸框同样也可以适用,只不过有前他更好的方法做usb+串口的触摸穿透机制

原理如下:

一:Android输入子系统启动


Android的输入子系统是在InputManagerService中启动的,而InputManagerService是在system_server中启动。
system_server路径:
frameworks/base/services/java/com/android/server/SystemServer.java
流程:main()--SystemServer().run();--startOtherServices();

在这里完成创建InputManagerService服务,添加服务之后InputManagerService就启动了。进入到InputManagerService里面去。
路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

在InputManagerService的构造方法种调用了一个方法:nativeInit 是一个native接口位于jin当中进入到jni层

路径frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
nativeInit 中创建一个NativeInputManagera对象

在NativeInputManager构造函数中创建了一个EventHub实例,并且将这个实例作为参数来创建一个InputManager对象,这个对象会做一些初始化的操作。
InputManager:
frameworks/native/services/inputflinger/InputManager.cpp
 

 可以看到在InputManager的构造方法中先创建分发和读输入事件的对象,然后在initialize中创建分发线程和读数据线程并绑定。

接着到InputManagerService的start方法

调用nativeStart进入到NativeInputManager  再次进入到
com_android_server_input_InputManagerService.cpp 的nativeStart
 调用了im->getInputManager()->start();  
getInputManager 如下:
实际调用了InputManager的start函数

 启动两大线程InputReaderThread和InputDispatcherThread来从读取和分发键盘消息,调用它们的run方法后,就会进入threadLoop函数中,只要threadLoop函数返回true,该函数就会循环执行。

frameworks/native/services/inputflinger/InputDispatcher.cpp

frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::loopOnce() :
1:读取InputReader配置信息是否改变,如界面大小、方向、键盘布局重新加载、指针速度改变等
uint32_t changes = mConfigurationChangesToRefresh;
2:刷新配置信息
refreshConfigurationLocked(changes);
3:读取事件,从EventHub读取事件,获取输入事件和设备增删事件,count为读取的事件数量,mEventBuffer存储着事件
    //epoll来监听驱动程序上报的事件的
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
4:处理事件,判断count的值,如果有读到事件(count大于0),则调用processEventsLocked(mEventBuffer, count);
5:将事件传到InputDispatcher
mQueuedListener->flush();

事件的处理:
processEventsLocked
processEventsLocked()函数中会遍历所有的事件,分别进行处理。
其处理的事件类型分为四种:
原始输入事件、设备加载事件、设备卸载事件及FINISHED_DEVICE_SCAN事件

重点关心:processEventsForDeviceLocked

device->process(rawEvents, count);

事件传递到了InputMapper
对应了很多的子类,这里根据事件的类型进行相应的派发,处理。

TouchInputMapper
SingleTouchInputMapper
CursorInputMapper
xxInputMapper

比如:触摸屏事件 TouchInputMapper

 sync(rawEvent->when);

processRawTouches(false /*timeout*/);

cookAndDispatch:

最终调用了dispatchTouches
对于dispatchTouches中,会根据记录的上一次的触摸位置,对事件的类型进行判断,然后做相应的分发,事件类型有抬起,下落,移动等,然后对相应的事件进行分发。无论是对于何种类型的事件派发,最终被调用到的都是dispatchMotion()方法。

创建定义的地方

我们将触摸相关的事件进行包装之后,将其加入到一个ArgsQueue队列,到此,我们已经将数据加入到参数队列中

将事件传到InputDispatcher
mQueuedListener->flush();

mDispatcher->dispatchOnce();

二:InputChannel


如何找出目标应用程序,输入系统和应用程序如何建立联系,APP是通过InputChannel跟输入子系统进行Connection。
安卓系统都是运行着多个应用程序,但是只有屏幕最前面的应用程序才可以接收到输入事件,谁来告诉输入事件哪个是运行在屏幕最前面的应用程序呢?

1:InputChannel的创建是在 ViewRootImpl中setView方法中。

Frameworks/base/core/java/android/view/ViewRootImpl.java

首先是创建了一个InputChannel,然后将其调用了WindowSession的addToDisplay方法将其作为参数传递。然后将InputChannel添加到WindowManagerService中创建socketpair(一对socket)用来发送和接受事件。
addToDisplay将会把InputChannel添加到WindowManagerService中。会调用WMS的addWindow方法。

对于每一个应用程序,WindowServiceManager都有一个结构体WindowSate来表示该应用程序。假设新启动一个APP,通过binder通信,调用addToDisplay,会导致AddWindow被调用,AddWindow创建了WindowSate表示该应用程序,接着创建一个socketpair得到两个文件句柄fd0和fd1,fd1直接返回给应用程序,fd0将其封装为InputChannel类,一方面InputChannel会放进该APPWindowState中,另外一方面,他会将InputChannel注册给InputDispatch。
Frameworks/base/services/core/java/com/android/server/wm/Session.java

 mService 就是WindowManagerService

Frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

对于InputChannel的相关处理调用了WindowState的openInputChannel方法。

   public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
……

……

}
Frameworks/base/services/core/java/com/android/server/wm/WindowState.java

调用了InputChannel的openInputChannelPair方法,该方法调用了InputChannel的native方法nativeOpenInputChannelPair,创建了两个InputChannel。
openInputChannelPair的实现在Frameworks/native/libs/input/InputTransport.cpp


使用socketpair产生的文件描述符是一对socket,该系统调用能创建一对已连接的(UNIX族)无名socket。在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写。 

2:开启输入事件的监听接受事件
在之前的setView中,我们创建了InputChannel之后,开启了对于InputChannel中输入事件的监听。

创建WindowInputEventReceiver

WindowInputEventReceiver继承自InputEventReceiver

到了InputEventReceiver中这里调用了native方法来做初始化

Frameworks/base/core/jni/android_view_InputEventReceiver.cpp
根据传入的InputChannel和MessageQueue,创建一个NativeInputEventReceiver,然后调用其initialize方法。

在initialize()方法中,只调用了一个函数setFdEvents

从InputConsumer中获取到channel的fd,然后调用Looper的addFd方法。

该方法所执行的操作就是对传递的fd添加epoll监控,当有消息到来后,根据消息类型做一些判断处理,然后调用其相关的callback。在这里当有数据到来,我们便会执行相应的回调。这里对于InputChannel的回调是在调用了NativeInputEventReceiver的handleEvent方法。


 

对于Event的处理,这里调用consumeEvents来对事件进行处理。
1:调用consume来进行接受事件
2:执行Java层的InputEventReceiver.dispachInputEvent 就会跑Android那套input事件的分发流程

     对于创维的触摸穿透,和擦板触摸的开关就是这个文件来操作,在jni中可以操作/dev/hidraw节点。但是/dev/hidraw 节点有好几个,并且如果有usb设备接入或者拔插这个节点也会改变。这里就采用了通过USB 设备的描述符当中有一个唯一的数值来确定是不是创维双USB的节点。 

1:如果已经打开了该设备节点,先关闭 避免多次重复打开造成泄漏。

2:判断当前触摸框是否是创维LG的触摸框

3:通过USB的唯一标识符来查找创维触LG摸框

有两个版本的触摸框固件,一个是0x0087 另外一个是0x0097

 数据定义:

        创维LG的双USB触摸穿透机制:是在consumeEvents这个接口中来操作,通过getInputChannelName(),来得到这个InputChannel 是谁,InputChannel对应的就是上层应用设置的title。如果这个InputChannel 是信源的,那么把触摸打开,如果不是就把触摸关闭掉。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑吧撸码兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值