Android输入系统(一)

来源:互联网 发布:iphone吉他调音软件 编辑:程序博客网 时间:2024/06/05 04:38

Android输入系统(一)

首先我们明白,在PC或者手机上我们都支持热插拔,比如现在有个键盘,现在键盘插入USB接口,就会检测到。那么系统怎么知道有设备插入,又怎么识别这个设备呢?


  • Android输入系统一
    • 设备识别的两种方式
    • 储备知识一监测文件
      • 使用inotify函数流程
      • 测试方法
      • 使用epoll函数流程
      • 测试方法
    • 储备知识二通信
      • 使用socketpair函数流程
      • 测试方法

设备识别的两种方式

一.hotplug机制    1.内核发现键盘接入或者拔出    2.启动一个进程(hotplug进程)    3.进程发送消息告诉输入系统    4.这个输入系统处理这个消息二.inotify机制    系统检测/dev/input目录    发生变化进行对应处理

当然Android系统就是使用inotify机制,而使用inotify机制的核心是:

  • inotify函数(用于监测目录中文件是否有删除,增加等)
  • epoll函数(用于监测多个文件内容的变化)
    • 有无数据读入
    • 有无数据读出

储备知识一(监测文件)

使用inotify函数流程

@(参考frameworks\native\services\inputflinger\EventHub.cpp)
- fd=inotify_init()—用于初始化
- inotify_add_watch(目录名称,监测类型)—用于查看什么东西
- read(fd,…)—用于读取监测结果,返回值是inotify_event结构体

#include <unistd.h>#include <stdio.h>#include <sys/inotify.h>#include <string.h>#include <errno.h>int read_process_inotify_fd(int fd){    int res;    char event_buf[512];    int event_size;    int event_pos = 0;    struct inotify_event *event;    /* 此时read函数为阻塞式 */      res = read(fd, event_buf, sizeof(event_buf));    if(res < (int)sizeof(*event)) {        if(errno == EINTR)            return 0;        printf("could not get event, %s\n", strerror(errno));        return -1;    }    /*      * 读到的数据是1个或多个inotify_event     * 由于结构体的长度不一样     * 所以逐个处理     */    while(res >= (int)sizeof(*event)) {        event = (struct inotify_event *)(event_buf + event_pos);        if(event->len) {            if(event->mask & IN_CREATE) {                printf("create file: %s\n", event->name);            } else {                printf("delete file: %s\n", event->name);            }        }        event_size = sizeof(*event) + event->len;        res -= event_size;        event_pos += event_size;    }    return 0;}int main(int argc, char **argv){    int mINotifyFd;    int result;    if (argc != 2)    {        printf("Usage: %s <dir>\n", argv[0]);        return -1;    }    /* inotify_init */    mINotifyFd = inotify_init();    /* add watch */    result = inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE);    /* read */    while (1)    {        read_process_inotify_fd(mINotifyFd);    }    return 0;}

测试方法

$ gcc -o inotify inotify.c$ mkdir tmp$ ./inotify tmp &$ echo > tmp/1$ echo > tmp/2$ rm tmp/1 tmp/2

使用epoll函数流程

@(参考frameworks\native\services\inputflinger\EventHub.cpp)
- epoll_create()—创建fd
- 对每个文件执行epoll_ctl(…,EPOLL_CTL_ADD,…)—表示监测
- 执行epoll_wait(等待某个文件可用)
- 不再想监测某文件,可以执行epoll_ctl(…,EPOLL_CTL_DEL,…)

#include <sys/epoll.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#if 0typedef union epoll_data {   void        *ptr;   int          fd;   uint32_t     u32;   uint64_t     u64;} epoll_data_t;#endif#define DATA_MAX_LEN 500/* usage: epoll <file1> [file2] [file3] ... */int add_to_epoll(int fd, int epollFd){    int result;    struct epoll_event eventItem;    memset(&eventItem, 0, sizeof(eventItem));    eventItem.events = EPOLLIN;//当文件有数据的时候就可以监测到    eventItem.data.fd = fd;    result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem);    return result;}/*从epoll中删除需要监测的文件*/void rm_from_epoll(int fd, int epollFd){    epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);}int main(int argc, char **argv){    int mEpollFd;    int i;    char buf[DATA_MAX_LEN];    // Maximum number of signalled FDs to handle at a time.    static const int EPOLL_MAX_EVENTS = 16;    // The array of pending epoll events and the index of the next event to be handled.    struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS];    if (argc < 2)    {        printf("Usage: %s <file1> [file2] [file3] ...\n", argv[0]);        return -1;    }    /* epoll_create */    mEpollFd = epoll_create(8);    /* for each file:     * open it     * add it to epoll: epoll_ctl(...EPOLL_CTL_ADD...)     */    for (i = 1; i < argc; i++)       {        //int tmpFd = open(argv[i], O_RDONLY|O_NONBLOCK);        int tmpFd = open(argv[i], O_RDWR);        add_to_epoll(tmpFd, mEpollFd);    }    /* epoll_wait */    while (1)    {        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1);        for (i = 0; i < pollResult; i++)        {            printf("Reason: 0x%x\n", mPendingEventItems[i].events);            int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN);            buf[len] = '\0';            printf("get data: %s\n", buf);            //sleep(3);        }    }    return 0;}

测试方法

$ gcc -o -epoll epoll.c$ mkfifo tmp/2$ ./epoll tmp /1 tmp/2 tmp/3$ echo aa>tmp/1$ echo bb>tmp/2

储备知识二(通信)

首先思考一个问题,如果让你自己设计一个输入系统会怎么做呢?

  • 首先创建一个进程,用于读取分发那些事件
  • 写出应用程序,用于处理应用事件,并对进程状态做出回馈

@(可以看出读取进程和APP进程之间是需要双向通信)

此时我们知道Binder系统在Android中承担大部分的进程通信角色
但是在输入系统中,是不是照样适用呢?答案是否定的。
因为Binder是单向通信,Clinet发出指令,Server接收指令,Server不能主动
如果需要双向通信,Binder的任何一端口都是Server+Client,这样会使得系统很复杂。所以输入系统中用的是SocketPair

这里写图片描述|center

缺点:(只适用如下情况:)

  • 适用于线程通信
  • 具有亲缘关系的进程间通信

如果我们要在两个APP之间进行通讯,那就必须把其中的一个文件句柄比如fd2通过Binder传递给另一个APP

使用socketpair函数流程

@(参考frameworks\native\libs\input\InputTransport.cpp)
- pthread_create—创建线程1
- read—读数据: 线程1发出的数据
- sprintf—main thread向thread1 发出: Hello, thread1
- 子线程sprintf— 向 main线程发出: Hello, main thread
- read—读取数据(main线程发回的数据)

#include <pthread.h>#include <unistd.h>#include <stdio.h>#include <sys/types.h>         #include <sys/socket.h>#define SOCKET_BUFFER_SIZE      (32768U)void *function_thread1 (void *arg){    int fd = (int)arg;    char buf[500];    int len;    int cnt = 0;    while (1)    {        /* 向 main线程发出: Hello, main thread  */        len = sprintf(buf, "Hello, main thread, cnt = %d", cnt++);        write(fd, buf, len);        /* 读取数据(main线程发回的数据) */        len = read(fd, buf, 500);        buf[len] = '\0';        printf("%s\n", buf);        sleep(5);    }    return NULL;}int main(int argc, char **argv){    int sockets[2];    socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);    int bufferSize = SOCKET_BUFFER_SIZE;    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));    /* 创建线程1 */    pthread_t threadID;    pthread_create(&threadID, NULL, function_thread1, (void *)sockets[1]);    char buf[500];    int len;    int cnt = 0;    int fd = sockets[0];    while(1)    {        /* 读数据: 线程1发出的数据 */        len = read(fd, buf, 500);        buf[len] = '\0';        printf("%s\n", buf);        /* main thread向thread1 发出: Hello, thread1 */        len = sprintf(buf, "Hello, thread1, cnt = %d", cnt++);        write(fd, buf, len);    }}

测试方法

gcc -o socketpair socketpari.c -lpthread./socketpari

既然我们上面说了,socketpair不能在不同APP之间通信,那么怎么做才达到在不同APP之间通信的目的呢?
@(要明白这个道理,就必须了解文件句柄与进程和线程的关系)

我们知道task_struct表示进程结构体,在进程结构体中有struct files_struct * files;
struct files_struct中有struct file __ruc **fd;数组这个数组的下标就是对应的fd。
同样对于进程2也会有一个数组
此时如果我要把APP1进程里面的fd传递给APP2,此时就会先在APP2的进程的file__ruc数组中找出一个空项,然后让这个空项指向APP1对应的file__ruc[X]
使用binder传输file\__ruc[X]

0 0
原创粉丝点击