在系统启动过程中,会加载驱动程序,初始化硬件设备,会进入bool EventHub::openPlatformInput(void)这个函数,该函数主要功能是扫描/dev/input该目录,获取输入设备。如何获取呢?通过linux API res = scan_dir(device_path); 该函数叫
while((de = readdir(dir))) {
strcpy(filename, de->d_name);
open_device(devname);
}
不断读取目录文件,然后通过open_device()打开设备。具体打开设备函数是fd = open(deviceName, O_RDWR);以读写方式打开,该函数会调用驱动里file_operations里的实现函数。
此时所有输入设备已经打开。
在WindowManagerService服务类运行的时候,在构造函数中会创建内部类KeyQ对象. 该类继承之KeyInputQueue类。当然要进入该类的构造函数。在KeyInputQueue类的构造函数中会启动Thread mThread=new Thread("InputDeviceReader")这个匿名内部类线程,有该线程读驱动事件并把它放到消息队列中。具体实现是:
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
RawInputEvent ev = new RawInputEvent();
while (true) {
InputDevice di;
readEvent(ev);
}
}
该线程持续运行,循环通过readEvent(); 该函数是个native方法,具体实现为
static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
{
gLock.lock();
sp<EventHub> hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);
}
以上步骤获得了事件QueuedEvent。
获得事件后通过
private void addLocked(InputDevice device, long when, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;
QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
QueuedEvent p = mLast.prev;
while (p != mFirst && ev.when < p.when) {
p = p.prev;
}
ev.next = p.next;
ev.prev = p;
p.next = ev;
ev.next.prev = ev;
ev.inQueue = true;
}
该函数加到消息队列中,该消息队列就是个双向连表,头和尾分别是 final QueuedEvent mFirst;
final QueuedEvent mLast; 该函数就是把新消息插到尾的前面。
消息队列有了。还需要读队列。在WindowManagerService.java 类中同时又开了个内部线程
mInputThread = new InputDispatcherThread();
mInputThread.start();
该线程和刚才的写队列线程是并行的。该线程起来后,通过
public void run() {
while (true) {
try {
process();
} catch (Exception e) {
Log.e(TAG, "Exception in input dispatcher", e);
}
}
}
这个process()函数来读消息。具体实现是
private void process() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
while (true) {
QueuedEvent ev = mQueue.getEvent(
(int)((!configChanged && curTime < nextKeyTime)
? (nextKeyTime-curTime) : 0));
}
该getEvent()函数的具体实现是
QueuedEvent getEvent(long timeoutMS) {
long begin = SystemClock.uptimeMillis();
final long end = begin+timeoutMS;
long now = begin;
synchronized (mFirst) {
while (mFirst.next == mLast && end > now) {
QueuedEvent p = mFirst.next;
mFirst.next = p.next;
mFirst.next.prev = mFirst;
p.inQueue = false;
return p;
}
}
持续的读消息,如果没消息就阻塞,有消息,就读取消息,所谓读取消息就得到引用,然后把该消息从双向连表中删除。得到消息后根据消息输入设备类型把消息发送到具体AP 中。如
switch (ev.classType) {
case RawInputEvent.CLASS_KEYBOARD:
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
case RawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
break;
case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
configChanged = true;
break;
default:
mQueue.recycleEvent(ev);
break;
}
当然这其中涉及很多细节,有兴趣可以看看。同时读写队列的互斥机制也值得学习。