一, 先看一下 Android HAL
  Class EventHub 在  $(ANDROID_DIR)/frameworks/base/include/ui/eventhub。h 定义。
  i。 scan_dir(const char *dirname) // dirname = "/dev/input"
  扫描 dirname 目录, 该目录下有 event0, event1 …, 等设备。
  ii。  open_device(devname);
  打开 /dev/input/event0, /dev/input/event1 等设备。

  这里以打开 /dev/input/event0 设备为例, 分析按键的底层处理。

for (attempt = 0; attempt < 10; attempt++) {
fd = open(deviceName, O_RDWR);
if (fd >= 0) break;
usleep(100);
}

首先会打开传进来的设备. 然后会获取version, id等信息.

if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
//fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
name[0] = '\0';
}

获取 driver name, 在这里也就是 /dev/input/evevnt0, 也就是要到 Driver 里面去读取。
  这个名字很重要, 之后要与 keyboard map 相匹配。
  这里返回的值是: name = "wayland_m_ebook_key_input"
  为什么会返回这个值? 请看 event0 的 linux driver。
  wayland_m_ebook_keypad_probe() 函数中,有以下语句:
  gpio_key_input->name = "wayland_m_ebook_key_input"。
  所以这个值是在这个时候设置的。

int devid = 0;
while (devid < mNumDevicesById) {
if (mDevicesById[devid].device == NULL) {
break;
}
devid++;
}
if (devid >= mNumDevicesById) {
device_ent* new_devids = (device_ent*)realloc(mDevicesById,
sizeof(mDevicesById[0]) * (devid + 1));
if (new_devids == NULL) {
LOGE("out of memory");
return -1;
}
mDevicesById = new_devids;
mNumDevicesById = devid+1;
mDevicesById[devid].device = NULL;
mDevicesById[devid].seq = 0;
}

分配 new device, 将 device 信息保存至 mDeviceById[] 数组中。
  mNumDevicesById: device 的数量
  mDevicesById: devive 的信息
  new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1));
  new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1));
  为 new_mFDs, mFDs 分配空间, 以备之后保存每个 event(x) 的fd。
  mFDs[mFDCount]。fd = fd;
  mFDs[mFDCount]。events = POLLIN;
  将 fd 放到 mFDs 数组中。
// See if this is a keyboard, and classify it.
uint8_t key_bitmask[(KEY_MAX+1)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
LOGV("Getting keys...");
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {
//LOGI("MAP\n");
//for (int i=0; i<((KEY_MAX+1)/8); i++) {
// LOGI("%d: 0x%02x\n", i, key_bitmask[i]);
//}
for (int i=0; i<((BTN_MISC+7)/8); i++) {
if (key_bitmask[i] != 0) {
device->classes |= CLASS_KEYBOARD;
break;
}
}
if ((device->classes & CLASS_KEYBOARD) != 0) {
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
} else {
delete device;
LOGE("out of memory allocating key bitmask");
return -1;
}
}
}

如果是 keyboard 的设备.

if (test_bit(BTN_TOUCH, key_bitmask)) {
uint8_t abs_bitmask[(ABS_MAX+1)/8];
memset(abs_bitmask, 0, sizeof(abs_bitmask));
LOGV("Getting absolute controllers...");
if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0)
{
if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
device->classes |= CLASS_TOUCHSCREEN;
}
}
}

继续分析 Android 是如何进行 keyboard 映射的:

char tmpfn[sizeof(name)];
char keylayoutFilename[300];
// a more descriptive name
device->name = name;
// replace all the spaces with underscores
strcpy(tmpfn, name);
for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
*p = '_';
// find the .kl file we need for this device
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
bool defaultKeymap = false;
if (access(keylayoutFilename, R_OK)) {
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}

ANDROID_ROOT 一般都设置为 /system
  tmpfn 也就是 name 中的内容。而 name = wayland_m_ebook_key_input
  所以 keylayoutFilename = /system/usr/keylayout/wayland_m_ebook_key_input。kl。
  这个文件保存有按键的信息。 可以在这个文件中修改按键的键值。
  如果没有这个文件则会去读 define 的 qwerty。kl。
  device->layoutMap->load(keylayoutFilename);
  load /system/usr/keylayout/wayland_m_ebook_key_input。kl 这个文件进行分析。
  KeyLayoutMap::load 在 KeyLayoutMap。cpp 中实现。
  把按键的映射关系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
  iii。 EventHub::getEvent()
  主要通过 read() 函数读取按键事件 及进行 Map 键值映射。

二、再来看看 jni 层

看看其中几行的代码:

static JNINativeMethod gInputMethods[] = {
/* name, signature, funcPtr */
{ "readEvent", "(Landroid/view/RawInputEvent;)Z",
(void*) android_server_KeyInputQueue_readEvent },

可以看出, 上层(即 framework层)调用的接口是 readEvent, 实现函数是本文件的android_server_KeyInputQueue_readEvent()。
  这个函数调用了 getEvent 读取事件。也就是 EventHub。cpp 中的 EventHub::getEvent()。
  readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。

三、再看看 jni 层对应的 java 层(经常封装成 。jar 档案)

  KeyInputQueue。java

  在frameworks/base/services/java/com/android/server/KeyInputQueue。java 里创建了一个  InputDeviceReader 线程,它循环的读取事件,然后把事件放入事件队列里,包括 key / touch panel。

  四、事件分发

  输入事件分发线程 在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;

Android 读取按键及Touch Panel相关推荐

  1. Android Keyboard/Touch Panel分析

    分析一下 Android 是如何读取按键及Touch Panel 的驱动的.主要在 $(ANDROID_DIR)/frameworks/base/libs/ui/EventHub.cpp 这个文件中, ...

  2. 怎么看android底层源码,Android 底层按键获取

    与用户交互的输入设备(触摸屏,键盘等)是获取用户意图的来源.由于硬件本身的物理特性及由各大硬件厂商的标准不一,这将导致我们从设备上获取到的键值存在一定的差异性,为了让系统能够正确处理用户的操作,我们就 ...

  3. Android 默认按键音开关

    android 默认按键音开关的值存储位置在 frameworks/base/packages/SettingsProvider/res/values/defaults.xml , <!-- D ...

  4. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

  5. android 读取json数据(遍历JSONObject和JSONArray)

    android 读取json数据(遍历JSONObject和JSONArray) public String getJson(){ String jsonString = "{\" ...

  6. android mp3 lrc歌词文件utf-8歌词显示为乱码,Android读取本地json文件的方法(解决显示乱码问题)...

    本文实例讲述了Android读取本地json文件的方法.分享给大家供大家参考,具体如下: 1.读取本地JSON ,但是显示汉字乱码 public static String readLocalJson ...

  7. android 读取 网页,Android读取网页内容

    1.修改AndroidManifest.xml文件 2.网页读取类 package com.neohope.android.web; import java.io.ByteArrayOutputStr ...

  8. android读取assets中的html文件,android读取assets文件.htm

    android读取assets文件 android读取assets文件 style='FONT-SIZE: 14px; FONT-FAMILY: verdana, "ms song" ...

  9. android 读取excel数据并保存为xml文件

    今天,简单讲讲android如何  读取excel数据并保存为xml文件. 最近,我这边需要把客户翻译的Excel字符资源作为xml字符资源,当时自己是一个一个的复制,发现效率太低.后来,在网上搜 ...

  10. android 读取assets文件夹下的文件资源

    今天,简单讲讲如何读取assets文件夹下的文件. Android资源文件大致可以分为两种: 第一种是res目录下存放的可编译的资源文件: 这种资源文件系统会在R.Java里面自动生成该资源文件的ID ...

最新文章

  1. Pycharm 基本快捷键
  2. PHP数据库操作分页类
  3. 汇编:ZF(zero flag)标志位
  4. eclipse目录出现重复情况 解决
  5. 使用约束控件创建界面
  6. 数据库每日一题 2020.04.29
  7. [XJOI]noip44 T3还有这种操作
  8. 【C++笔记】字符串、向量和数组
  9. 理解JavaScript中的多态
  10. 精准验码,昂视助力锂电生产管理追溯
  11. 如何免费将一个PDF拆分成多个文件?
  12. 密度计算机公式,相对密度计算公式
  13. android monkey,Android Monkey搭建 你不用了解的
  14. 深度学习分类分到同一个类
  15. 计算机技术协会主持词,协会表彰大会主持词
  16. 计算机带不起大型游戏怎么回事,电脑带不动原神怎么办
  17. 使用微软Office组件读取Excel文件
  18. 零基础能学大数据开发吗 可以从哪些方面入手
  19. 小程序挂服务器,小程序帮微信开挂_服务器x86服务器-中关村在线
  20. 关于在杭州滨江区租房的一些建议

热门文章

  1. Learun框架的入门问题
  2. aliplayer播放rtmp视频流
  3. FIT2CLOUD飞致云成为Kubernetes认证服务提供商(KCSP)
  4. python中os关于目录创建和文件移动操作
  5. 留学时在海外如何解决国内手机验证码问题?
  6. php require失败,关于php:致命错误:require_once()[function.require]:要求打开失败
  7. dll依赖查看工具-depends
  8. linux 神舟z7,老船长带你神舟战神Z7使用进阶
  9. 北理工慕课 嵩天 Python零基础入门 笔记整理
  10. Excel函数大全-12统计函数