} 對於按鍵事件,調用mDevices[i]->layoutMap->map進行映射。映射實際是由 KeyLayoutMap::map完成的,KeyLayoutMap類裡讀取配置文件qwerty.kl,由配置 文件 qwerty.kl 決定鍵值的映射關系。你可以通過修 改./development/emulator/keymaps/qwerty.kl來改變鍵值的映射關系。
JNI 函數
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文 件中,向 JAVA提供了函數android_server_KeyInputQueue_readEvent,用於讀 取輸入設備事件。
C代碼
- static jboolean
- android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
- jobject event)
- {
- gLock.lock();
- sp 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);
-
- env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
- env->SetIntField(event, gInputOffsets.mType, (jint)type);
- env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
- env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
- env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
- env->SetIntField(event, gInputOffsets.mValue, value);
- env->SetLongField(event, gInputOffsets.mWhen,
- (jlong)(nanoseconds_to_milliseconds(when)));
-
- return res;
- }
static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
{
gLock.lock();
sp 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);
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen,
(jlong)(nanoseconds_to_milliseconds(when)));
return res;
}
readEvent調用hub->getEvent讀了取事件,然後轉換成JAVA的結構。
事件中轉線程
在frameworks/base/services/java/com/android/server/KeyInputQueue.java 裡創建了一個線程,它循環的讀取事件,然後把事件放入事件隊列裡。
Java代碼
- Thread mThread = new Thread("InputDeviceReader") {
- public void run() {
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
- try {
- RawInputEvent ev = new RawInputEvent();
- while (true) {
- InputDevice di;
-
- readEvent(ev);
-
- send = preprocessEvent(di, ev);
- addLocked(di, curTime, ev.flags, ..., me);
- }
- }
- };
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
try {
RawInputEvent ev = new RawInputEvent();
while (true) {
InputDevice di;
readEvent(ev);
send = preprocessEvent(di, ev);
addLocked(di, curTime, ev.flags, ..., me);
}
}
};
輸入事件分發線程
在frameworks/base/services/java/com/android/server/WindowManagerService.java裡創建了一個輸入事件分發線程,它負責把事件分發到相應的窗口上去。
Java代碼
- mQueue.getEvent
- dispatchKey/dispatchPointer/dispatchTrackball
mQueue.getEvent
dispatchKey/dispatchPointer/dispatchTrackball
按鍵,觸摸屏流程分析
按鍵觸摸屏流程分析:
WindowManagerService類的構造函數
WindowManagerService()
mQueue = new KeyQ();
因為 WindowManagerService.java (frameworks\base\services\java\com\android\server)中有:
private class KeyQ extends KeyInputQueue
KeyQ 是抽象類 KeyInputQueue 的實現,所以 new KeyQ類的時候實際上在 KeyInputQueue 類中創建了
一個線程 InputDeviceReader 專門用來沖設備讀取按鍵事件,代碼:
Thread mThread = new Thread("InputDeviceReader") {
public void run()
{
在循環中調用:readEvent(ev);
...
send = preprocessEvent(di, ev);
實際調用的是 KeyQ 類的 preprocessEvent 函數
...
int keycode = rotateKeyCodeLocked(ev.keycode);
int[] map = mKeyRotationMap;
for (int i=0; i<N; i+=2)
{
if (map[i] == keyCode)
return map[i+1];
} //
addLocked(di, curTime, ev.flags,RawInputEvent.CLASS_KEYBOARD,newKeyEvent(di, di.mDownTime, curTime, down,keycode, 0, scancode,...));
QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
}
}
readEvent() 實際上調用的是 com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)中的:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,jobject event)
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when);
調用的是 EventHub.cpp (frameworks\base\libs\ui)中的:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
在函數中調用了讀設備操作:res = read(mFDs[i].fd, &iev, sizeof(iev));
在構造函數 WindowManagerService()調用 new KeyQ() 以後接著調用了:
mInputThread = new InputDispatcherThread();
...
mInputThread.start();
來啟動一個線程 InputDispatcherThread
run()
process();
QueuedEvent ev = mQueue.getEvent(...)
因為WindowManagerService類中: final KeyQ mQueue;
所以實際上 InputDispatcherThread 線程實際上從 KeyQ 的事件隊列中讀取按鍵事件。
switch (ev.classType)
case RawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
===============================================================
KeyInputQueue.java (frameworks\base\services\java\com\android\server):
的線程 Thread mThread = new Thread("InputDeviceReader") 本地調用:
readEvent(ev);讀取按鍵。readEvent 調用的是文件:
com_android_server_KeyInputQueue.cpp (frameworks\base\services\jni)中的函數:
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
android_server_KeyInputQueue_readEvent中有:
hub = new EventHub;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);
hub->getEvent 調用的是
EventHub.cpp (frameworks\base\libs\ui) 文件中的函數:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
讀取按鍵。
class RefBase::weakref_impl : public RefBase::weakref_type
在系統啟動後,android 會通過
static const char *device_path = "/dev/input";
bool EventHub::openPlatformInput(void)
res = scan_dir(device_path);
通過下面的函數打開設備。
int EventHub::open_device(const char *deviceName)
{
...
fd = open(deviceName, O_RDWR);
...
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
...
ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
...
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
...
device->layoutMap->load(keylayoutFilename);
...
}
打開設備的時候,如果 device->classes&CLASS_KEYBOARD 不等於 0 表明是鍵盤。
常用輸入設備的定義有:
enum {
CLASS_KEYBOARD = 0x00000001, //鍵盤
CLASS_ALPHAKEY = 0x00000002, //
CLASS_TOUCHSCREEN = 0x00000004, //觸摸屏
CLASS_TRACKBALL = 0x00000008 //軌跡球
};
打開鍵盤設備的時候通過上面的 ioctl 獲得設備名稱,命令字 EVIOCGNAME 的定義在文件:
kernel/include/linux/input.h 中。
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
在內核鍵盤驅動文件 drivers/input/keyboard/pxa27x_keypad.c 中定義了設備名稱:pxa27x-keypad
static struct platform_driver pxa27x_keypad_driver = {
.probe = pxa27x_keypad_probe,
.remove = __devexit_p(pxa27x_keypad_remove),
.suspend = pxa27x_keypad_suspend,
.resume = pxa27x_keypad_resume,
.driver = {
.name = "pxa27x-keypad",
.owner = THIS_MODULE,
},
};
ANDROID_ROOT 為環境變量,在android的命令模式下通過 printenv 可以知道它為: system
所以 keylayoutFilename 為:/system/usr/keylayout/pxa27x-keypad.kl
pxa27x-keypad.kl 定義了按鍵映射,具體內容如下:
----------------------
# NUMERIC KEYS 3x4
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 83 POUND
key 55 STAR
# FUNCTIONAL KEYS
key 231 MENU WAKE_DROPPED
key 192 BACK WAKE_DROPPED
key 193 HOME WAKE
key 107 DEL WAKE
key 102 CALL WAKE_DROPPED
key 158 ENDCALL WAKE_DROPPED
key 28 DPAD_CENTER WAKE
key 115 VOLUME_UP
key 114 VOLUME_DOWN
----------------------
如果沒有定義鍵盤映射文件,那麼默認使用系統的 /system/usr/keylayout/qwerty.kl
可以修改 /system/usr/keylayout/qwerty.kl 文件改變Android公司的按鍵映射。
device->layoutMap->load(keylayoutFilename) 調用的是文件:
KeyLayoutMap.cpp (frameworks\base\libs\ui)中的函數:
status_t KeyLayoutMap::load(const char* filename)通過解析 pxa27x-keypad.kl
把按鍵的映射關系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
當獲得按鍵事件以後調用:
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags)
由映射關系 KeyedVector<int32_t,Key> m_keys 把掃描碼轉換成andorid上層可以識別的按鍵。