InputManagerService源码分析一:准备工作


一、写在前面的话

作为一个应用程序开发,开始着手学习android源码,经常会被android海量的代码和庞大的知识图谱吓得望而却步,止步不前。放弃学习后,偶尔又心血来潮,重新出发。继续与放弃、快乐与痛苦,就这样循环交替、往复进行。每一次的重温也是对知识图谱的重新构建,对UML类图、时序图的完善。总归缺少些文字记录、缺少系统性的阐述,最近有构思将7年内关于android源码的学习与理解整理写成博客,记录下来。网上相关的介绍文章很多,总有些老生常谈,也多亏网上资料丰富,才能解决一个又一个源码阅读路上的拦路虎。最近正好有个项目需要客制化InputManagerService,遂决定以InputManagerService为始,记录下源码学习。学习InputManagerService首先需要了解:输入设备数据的获取、输入设备的挂载与删除。
InputManagerService主要职责是负责android系统输入设备事件(按压、滑动)的分发。主要功能可以分两部分介绍:1)第一部分就是事件的监听,2)第二部分就是事件的分发。本章主要介绍InputManagerService C++层关于事件读取、设备增加使用的Linux机制—— epoll、iNotify。

二、epoll读取android Input事件

android Input系统C++层使用epoll监听设备输入事件的变化。本段落主要介绍通过在使用Linux epoll机制,获取android应用屏幕输入。
本文使用的硬件环境是芯驰x9h,android软件版本为android 10。可以通过adb shell进入android系统,通过ls命令

ls /dev/input

获取系统输入设备,测试机器上显示如下

x9hp_ref:/dev/input # ls  
event0 event1 

我们的设备有两个设备event0 event,通过getevent命令可以获取设备输入详细信息

127|x9hp_ref:/dev/input # getevent                                                                            
add device 1: /dev/input/event1
  name:     "Semidrive Safe TouchScreen"
add device 2: /dev/input/event0
  name:     "Semidrive Safe TouchScreen"

从上述信息,可知设备主要有两块屏幕。接下来我们主要介绍通过监听event0设备获取屏幕数据。

2.1 获取屏幕摄入设备句柄

通过linux API open获取屏幕设备文件的句柄。

#define DEVPATH "/dev/input/event0" 
fd = open(DEVPATH, O_RDONLY);

2.2 通过ioctl获取设备信息

获取了屏幕设备文件句柄后,可以通过ioctl接口获取屏幕设备的相关信息:设备名称、版本号等。

    char buffer[80];
    if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        LOGE("could not get device name\n");
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
    }
    LOGE("device name : %s\n", buffer);

    if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) {
        LOGE("could not get location\n");
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
    }
    LOGE("location is : %s\n", buffer);
    struct input_id inputId;
    if (ioctl(fd, EVIOCGID, &inputId)) {
        LOGE("could not get device input id\n");
    } else {
        LOGE("device bustype: %d,product: %d,vendor: %d,version: %d", inputId.bustype,
             inputId.product, inputId.vendor, inputId.version);
    }
    uint8_t keyBitmap[(KEY_MAX + 1) / 8];
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBitmap)), sizeof(keyBitmap));
    LOGE("keybitmask :%0x",keyBitmap[0]);

2.3 创建屏幕设备句柄的epoll监听

android选择epoll监听Input设备输入信息。

int epoll_fd = epoll_create(MAX_EVENTS);
if (epoll_fd == -1) {
    LOGE("epoll create failed");
    return 0;
}
int opts = fcntl(fd, F_GETFL);
if (opts < 0) {
    LOGE("fcntl F_GETFL error: %s fd: %d", strerror(errno), fd);
    return -1;
}
opts |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, opts) < 0) {
    LOGE("fcntl F_SETFL error: %s", strerror(errno));
    return -1;
}
struct epoll_event ev;
int ret;
ev.events = EPOLLIN;
ev.data.fd = fd;
do {
    ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
} while (ret < 0 && errno == EINTR);

2.4 修改挂载设备的读取权限

由于seLinux机制,非系统应用无法读取/dev/input/event0事件。通过命令可以看到文件event0的权限属性为

crw-rw----  1 root input 13,  64 1970-01-01 08:00 event0

修改event0属性

crw-rw-rw-  1 root input 13,  64 1970-01-01 08:00 event0

可以读到屏幕数据。

2.5 通过epoll_wait获取屏幕输入数据

int n = 0;
struct input_event event;
LOGD("run...");
while (1) {
 n = epoll_wait(epoll_fd, events, MAX_EVENTS, EPOLL_SLEEP);
 if (n == -1) {
     LOGE("epoll wait error %s", strerror(errno));
     continue;
 }
 for (int i = 0; i < n; ++i) {
     if (events[i].data.fd == fd) {
         res = read(fd, &event, sizeof(event));
         if (res < (int) sizeof(event)) {
             LOGE("could not get event");
         }
     }
     LOGE("\n%04x %04x %08x\n", event.type, event.code, event.value);
 }
}

可以读到屏幕按压、滑动、抬起事件如下

08-25 13:58:03.286 5233-5262/com.example.blan.nativeandroid I/input: 0003 0035 00000265
    0003 0036 00000202
    0003 0000 00000265
    0003 0001 00000202
    0000 0000 00000000
08-25 13:58:03.299 5233-5262/com.example.blan.nativeandroid I/input: 0003 0035 00000260
    0003 0036 0000021f
    0003 0000 00000260
    0003 0001 0000021f
    0000 0000 00000000
08-25 13:58:03.312 5233-5262/com.example.blan.nativeandroid I/input: 0003 0035 00000259
    0003 0036 00000238
    0003 0000 00000259
    0003 0001 00000238
    0000 0000 00000000
08-25 13:58:03.326 5233-5262/com.example.blan.nativeandroid I/input: 0003 0039 ffffffff
    0001 014a 00000000

三、inotify监听文件的变化

3.1 添加指定目录下的文件添加、删除监听

#define FILE_PATH "/system/etc/"
int mNotifyFd;
int mFileWatchFd;
int mEpollFd;
mNotifyFd = inotify_init();
mFileWatchFd = inotify_add_watch(mNotifyFd, FILE_PATH, IN_DELETE | IN_CREATE);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.events = EPOLLIN;
event.data.fd = mNotifyFd;
int epoll_add = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mNotifyFd, &event);
LOGD("mNotifyFd file fd: %d", mNotifyFd);
LOGD("observe file fd: %d", mFileWatchFd);
LOGD("mEpollFd  file : %d", mEpollFd);
LOGD("epoll_add result %d", epoll_add);
struct epoll_event eventItem;
while (1) {
    int pollResult = epoll_wait(mEpollFd, &eventItem, 16, 1000);
    LOGD("result is %d and eventItem.data.fd is %d\n", pollResult, eventItem.data.fd);
    if (eventItem.data.fd == mNotifyFd && (eventItem.events & EPOLLIN)) {
        readNotifyLocked();
    } else {
        LOGD("unKnow event\n");
    }
}

3.2 添加设备信息解析

void readNotifyLocked() {
    char event_buf[512];
    int res;
    int event_size;
    int event_pos = 0;
    struct inotify_event *notify_event;
    LOGD("notify file change\n");
    res = read(mNotifyFd, event_buf, sizeof(event_buf));
    if (res < (int) sizeof(*notify_event)) {
        LOGD("could no get event\n");
        return;
    }
    while (res >= (int) sizeof(*notify_event)) {
        notify_event = (struct inotify_event *) (event_buf + event_pos);
        if (notify_event->len) {
            if (notify_event->wd == mFileWatchFd) {
                if (notify_event->mask & IN_CREATE) {
                    LOGD("notify add file name is: %s\n", notify_event->name);
                } else {
                    LOGD("notify remove file name is: %s\n", notify_event->name);
                }
            } else {
                break;
            }
        } else {
            break;
        }
        event_size = sizeof(*notify_event) + notify_event->len;
        res -= event_size;
        event_pos += event_size;
        LOGD("next loop");
    }
}

在/etc/system目录下创建blan.txt文件,应用程序收到通知,通知日志如下:

08-25 14:09:01.207 5308-5334/com.example.blan.nativeandroid I/Notify Test: notify file change
    notify add file name is: blan.txt
    next loop

可以获取到新增的文件的名称,删除亦可收到相似的通知。通过string拼接,可以获取新增、删除设备挂载路径。继而使用epoll接口,可以添加、删除设备的输入监听。

四、总结

InputManagerService C++层的事件监听已经介绍完毕,下一章介绍InputManagerService初始化流程。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值