原文网址:http://www.cnblogs.com/cnhome/archive/2009/12/22/1629435.html
按键事件
对于按键事件,调用mDevices->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;
}
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;
// block, doesn't release the monitor
readEvent(ev);
boolean send = false;
boolean configChanged = false;
if (false) {
Log.i(TAG, "Input event: dev=0x"
+ Integer.toHexString(ev.deviceId)
+ " type=0x" + Integer.toHexString(ev.type)
+ " scancode=" + ev.scancode
+ " keycode=" + ev.keycode
+ " value=" + ev.value);
}
if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
mDevices.put(ev.deviceId, di);
configChanged = true;
}
}
......
}
}
}
};
按键、触摸屏流、轨迹球程分析
输入事件分发线程
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
按键触摸屏流程分析:
WindowManagerService类的构造函数
WindowManagerService()
mQueue = new KeyQ();
因为 WindowManagerService.java (frameworks\base\services\java\com\android\server)中有:
private class KeyQ extends KeyInputQueue implements KeyInputQueue.FilterCallback
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 == 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.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 的事件队列中读取按键事件,在process() 方法中进行处理事件。
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;
case RawInputEvent.CLASS_TRACKBALL:
dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
break;
===============================================================
补充一些内容:
在写程序时,需要捕获KEYCODE_HOME、KEYCODE_ENDCALL、KEYCODE_POWER这几个按键,但是这几个按键系统做了特殊处理,
在进行dispatch之前做了一些操作,HOME除了Keygaurd之外,不分发给任何其他APP,ENDCALL和POWER也类似,所以需要我们系统
处理之前进行处理。
我的做法是自己定义一个FLAG,在自己的程序中添加此FLAG,然后在WindowManagerServices.java中获取当前窗口的FLAG属性,如果是我
们自己设置的那个FLAG,则不进行特殊处理,直接分发按键消息到我们的APP当中,由APP自己处理。
这部分代码最好添加在
@Override
boolean preprocessEvent(InputDevice device, RawInputEvent event)
方法中,这个方法是KeyInputQueue中的一个虚函数,在处理按键事件之前的一个“预处理”。
PS:对HOME键的处理好像必需要修改PhoneWindowManager.java中的interceptKeyTi方法,具体可以参考对KeyGuard程序的处理。
===============================================================
系统底层事件处理过程
在系统启动后,android 会通过
static const char *device_path = "/dev/input";
bool EventHub::penPlatformInput(void)
res = scan_dir(device_path);
通过下面的函数打开设备。
int EventHub::pen_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上层可以识别的按键。
分享到:
相关推荐
### Android键盘映射详解 #### 一、概览 Android系统中的键盘映射是指系统如何将物理键盘(或虚拟键盘)上的按键与操作系统内部表示的键值进行匹配的过程。这一机制对于实现用户输入功能至关重要,同时也为开发者...
Android 模拟器和PC物理键盘映射,文档里有的,不要钱
本文将详细讲解如何在Android Studio中进行键盘映射设置,并说明这些设置同样适用于其他基于IntelliJ IDEA的产品。 首先,打开Android Studio,进入“File”菜单,然后选择“Settings”(在Mac系统中是...
在Android系统中,按键映射是开发者理解和处理设备硬件按键输入的关键部分。这些按键映射对应表详细列举了各种按键的码值,使得程序能够识别并响应不同的用户操作。码值是Android系统用来识别按键的一种编码方式,...
在Android平台上,屏幕映射(Screen Mirroring)是一种将手机屏幕实时投射到其他设备上显示的技术,这对于演示、教学或者协作非常有用。在你提到的场景中,使用这种工具可以使观众在大屏幕上清晰地看到演讲者手机上...
在Android系统中,"android串口键盘"是指通过串行通信接口(UART)与设备进行交互,实现键盘功能的一种技术。这种技术常用于智能电视、机顶盒等嵌入式设备,因为它们可能没有物理键盘,但需要用户输入指令。在本简例...
在Android平台上,自定义全键盘是一项常见的需求,特别是在开发输入法或者特定应用时。这个“android自定义全键盘随机(包含字母+标点符号+数字)”项目提供了一个解决方案,它能够适应各种Android手机系统,为用户...
通过MonkeyRunner,我们可以编写程序,使电脑键盘的按键映射为Android设备上的相应操作,实现远程控制。 Python作为强大的脚本语言,拥有丰富的库和社区支持,使得键盘事件的捕获和解析变得相对简单。我们可以利用...
在 Android 系统中,我们可以使用键盘映射表来将事件值映射到对应的键盘值。键盘映射表是一个查表,它将事件值与键盘值之间建立映射关系。通过使用键盘映射表,我们可以将事件值映射到对应的键盘值。 捕获 Android ...
可以实现安卓游戏中的虚拟键盘映射到实体键上,可以编辑映射的虚拟键位置,使其对位在屏幕的虚拟控制键上,实体按钮才能产生作用。然后可以再手机上玩Xperia Play专属游戏,不过进入游戏前和过程中不能点击返回键,...
`AdbKeyMonkey`的核心在于它能将PC键盘输入的键值映射为Android设备上的触摸事件。当你在PC键盘上按下按键时,`AdbKeyMonkey`会捕获这个输入,然后通过`adb`发送相应的触摸事件到连接的Android设备上。这样,你可以...
Android的PMEM(Physical Memory Block)驱动用于管理内存映射区域,使得用户空间可以直接访问物理内存。PMEM驱动通常与GPU、VPU、CPU等硬件组件相交互。PMEM服务涉及到了linux/android_pmem.h和drivers/android/...
在Android平台上,开发一款车牌号输入法涉及到许多技术细节和用户体验设计。首先,我们要了解车牌号输入法的主要功能,即为用户提供快速、准确输入车辆识别号码(VIN)或地方规定的车牌号码的方式。以下是一些相关的...
安装完成后,首次启动Android Studio,会引导你进行一些基本设置,如导入现有设置、选择主题、设置键盘映射等。然后,系统会检查并更新Android SDK组件,这是必要的步骤,以确保能支持最新的Android版本。 接下来,...
2.3.3 键盘映射与模拟器控制 13 2.3.4 横屏与竖屏切换 14 第3章 第一个Android程序 15 3.1 HelloAndroid 15 3.1.1 在Eclipse中创建项目 15 3.1.2 编写程序项目代码 17 3.1.3 运行HelloAndroid 18 3.1.4 ...
至于"IntelliJ IDEA Global Settings",这是IntelliJ IDEA(Android Studio基于此平台)的全局设置备份,可能包括了你的个性化配置,如插件、代码风格、键盘绑定等。 总的来说,使用代码高亮插件能显著提高Android ...
标题中的“GeminiKCM”指的是一个专为Gemini PDA设计的键盘映射测试工具。Gemini PDA是一款结合了手机和平板功能的设备,它具有物理键盘,旨在提供类似传统PDA(个人数字助手)的使用体验。这个项目可能是为了优化或...
在Linux系统中,输入设备(如键盘)通常会被映射到`/dev/input`目录下的事件设备文件,例如`/dev/input/event0`。这些事件文件代表了系统中不同的输入设备,你可以通过读取这些文件来获取设备发送的事件信息,包括...
#### 一、Android中键盘驱动及按键映射机制 在深入了解Android系统中如何实现全局快捷键之前,我们需要先理解Android操作系统中的键盘驱动及其工作原理。 ##### 1. Linux内核中的按键定义 在Linux内核层面,按键...
对于游戏爱好者来说,夜神模拟器的一大亮点是支持键盘映射功能。用户可以将键盘按键映射到游戏中的触摸操作,实现更精确、更快速的游戏控制,这对于需要精细操作的手机游戏尤其有帮助。此外,模拟器还支持多开功能,...