Android Input子系统浅谈

Android Input子系统浅谈

本文主要讲解[Android Input 子系统][6],我会从一下几个方面讲解:

  • linux kernel的input子系统框架
  • 以触摸屏驱动为例讲解内核input子系统
  • Android framework层Input子系统的框架
  • Input子系统的应用程序接口

linux kernel里面input子系统框架

  • **主要作用是维护两个重要的链表input_dev_list和input_handler_list
  • 下面这段代码便是内核里面Input子系统的框架层部分代码
    代码位置:/kernel/driver/input/input.c
    可以看到input的内核框架层也是以类似于driver的方式注册的
    这段代码便是Input子系统在内核中的核心框架;大家可能会疑惑怎么这
    么简单,看着什么也没有做,其实却是这样,这这段code里面主要建立
    了一些用于debug的节点,主要有如下节点:

class节点/sys/class/input :调用class_register生成

proc节点/proc/bus/input/devices和handles:调用input_proc_init生成

dev节点:/dev/input:调用register_chrdev_region

以上三种节点在后面会讲解分别用作什么

static int __init input_init(void)
{
    int err;

    err = class_register(&input_class);
    if (err) {
        pr_err("unable to register input_dev class\n");
        return err;
    }

    err = input_proc_init();
    if (err)
        goto fail1;

    err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                     INPUT_MAX_CHAR_DEVICES, "input");
    if (err) {
        pr_err("unable to register char major %d", INPUT_MAJOR);
        goto fail2;
    }

    return 0;

 fail2: input_proc_exit();
 fail1: class_unregister(&input_class);
    return err;
}

static void __exit input_exit(void)
{
    input_proc_exit();
    unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
                 INPUT_MAX_CHAR_DEVICES);
    class_unregister(&input_class);
}

subsys_initcall(input_init);

两个重要的链表

两个重要的链表
input_register_device():
向input_dev_list里面添加input device
调用input_attach_handler去匹配input_handler
input_register_handler():
向input_handler_list天剑input handler
调用input_attach_handler去匹配input_handler

注意上面这两个函数都提到了input_attach_handler()这意味着我们在注册我们的input device的时候会去匹配我们的input handler同时当我们去注册input handler的时候会去匹配input device
通常这两个函数是在我们的driver里面调用的

以触摸屏驱动为例讲解内核input子系统

input_dev = input_allocate_device();
    if (!input_dev) {
        err = -ENOMEM;
        dev_err(&client->dev, "[Focal][Touch] %s: failed to allocate input device\n", __func__);
        goto exit_input_dev_alloc_failed;
    }


    ftxxxx_ts->input_dev = input_dev;

    set_bit(KEY_BACK, input_dev->keybit);
    set_bit(KEY_HOME, input_dev->keybit);
    set_bit(KEY_APPSELECT, input_dev->keybit);
    //set_bit(KEY_POWER, input_dev->keybit);

    __set_bit(EV_ABS, input_dev->evbit);
    __set_bit(EV_KEY, input_dev->evbit);
    __set_bit(BTN_TOUCH, input_dev->keybit);
    __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
//  printk("maxx=%d,maxy=%d\n",ftxxxx_ts->x_max,ftxxxx_ts->y_max);
    input_mt_init_slots(input_dev, CFG_MAX_TOUCH_POINTS, 0);
//  input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, CFG_MAX_TOUCH_POINTS, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 31, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ftxxxx_ts->x_max, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ftxxxx_ts->y_max, 0, 0);
    input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, PRESS_MAX, 0, 0);

    input_dev->name = Focal_input_dev_name;
    err = input_register_device(input_dev);

上面这段代码便是我摘抄的触摸屏驱动的部分代码,主要是input device的注册过程,重点函数是input_register_device()正如我们上面所说的他会去input_handler_list里面去寻找匹配input handler,对于向触摸屏和鼠标等我们的内核已经为我们注册好了input handler代码位于:/kernel/driver/input/evdev.c:
这段代码主要注册了一个input handler,我们的触摸屏驱动匹配到的就是这个handler

/kernel/driver/input/evdev.c:
static int __init evdev_init(void)
{
    return input_register_handler(&evdev_handler);
}

static void __exit evdev_exit(void)
{
    input_unregister_handler(&evdev_handler);
}

module_init(evdev_init);
module_exit(evdev_exit);

在touch driver里面调用input_register_device()会有如下关键的一段代码:
将input device添加进input_dev_list,并且对于每一个注册好的input handler调用input_attach_handler()去匹配device(在这里使我们的触摸屏)

list_add_tail(&dev->node, &input_dev_list);
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler);
。。。

下面便是匹配的代码:对于我们的touch 这里匹配到的就是我们在evdev.c里面注册好的handler,紧接着调用handler->connect(handler, dev, id);
会到evdev.c里面会知道这里的connect是evdev_connect()

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
    const struct input_device_id *id;
    int error;

    id = input_match_device(handler, dev);
    if (!id)
        return -ENODEV;

    error = handler->connect(handler, dev, id);
    if (error && error != -ENODEV)
        pr_err("failed to attach handler %s to device %s, error: %d\n",
               handler->name, kobject_name(&dev->dev.kobj), error);

    return error;
}

这个函数里面用我们的设备号作为标号作为设备名:
dev_set_name(&evdev->dev, “event%d”, dev_no);
紧接着得到我们的input device在这里就是我们的touch:
evdev->handle.dev = input_get_device(dev);
还有之前在input.c里面注册的input_class:
evdev->dev.class = &input_class;
还记得我们之前在input.c里面register_chrdev_region吗:
evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);这里的INPUT_MAJOR就是我们在那里注册的主设备号
下面紧接着注册字符设备:此时注册的字符设备在/dev/input/event%d:
/dev/input:这个是我们在我们的input.c里面注册的,这里的event%d是以次设备号作为标号命名的,

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
             const struct input_device_id *id)
{
    struct evdev *evdev;
    。。。。。
    。。。。。
    dev_set_name(&evdev->dev, "event%d", dev_no);

    evdev->handle.dev = input_get_device(dev);
    evdev->handle.name = dev_name(&evdev->dev);
    evdev->handle.handler = handler;
    evdev->handle.private = evdev;

    evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
    evdev->dev.class = &input_class;
    evdev->dev.parent = &dev->dev;
    evdev->dev.release = evdev_free;
    device_initialize(&evdev->dev);

    error = input_register_handle(&evdev->handle);
    if (error)
        goto err_free_evdev;

    cdev_init(&evdev->cdev, &evdev_fops);
    evdev->cdev.kobj.parent = &evdev->dev.kobj;
    error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
    if (error)
        goto err_unregister_handle;

    error = device_add(&evdev->dev);
    if (error)
        goto err_cleanup_evdev;

    return 0;

 err_cleanup_evdev:
    evdev_cleanup(evdev);
 err_unregister_handle:
    input_unregister_handle(&evdev->handle);
 err_free_evdev:
    put_device(&evdev->dev);
 err_free_minor:
    input_free_minor(minor);
    return error;
}

到此我们的touch有关的内核注册函数讲解完毕,下面讲解,当我们的手指触摸touch的时候内核里面的数据是如何上报的
当我们的手指触摸touch的时候会触发中断:下面是我贴出来的focal的touchdriver里面的中断函数:首先在中断里面会调用ftxxxx_read_Touchdata()读取手指数据(一般为i2c读取),这里不是我们关注的重点;紧接着调用ftxxxx_report_value():上报读到的数据

static irqreturn_t ftxxxx_ts_interrupt(int irq, void *dev_id)
{

            ret = ftxxxx_read_Touchdata(ftxxxx_ts); 
            。。。。。
            ftxxxx_report_value(ftxxxx_ts);
            。。。。。
    return IRQ_HANDLED;
}

这个函数里面会调用一系列的内核input子系统提供的API如:input_report_abs(),input_mt_slot(),input_report_key()等这些函数主要是按照一定的格式将我们读到的touch手指触摸信息写入我们在evdev.c注册好的/dev/input/event0:这里我们假设我们的touch对应的次设备号是0
然后我们的用户空间就可以读取这个节点的数据得到touch的触摸事件。这个在Android 的framwork层里面会讲解,其实不只是Android其他的只要是使用linux内核的只要使用input子系统都应该是类似的在用户空间读取这个节点

static void ftxxxx_report_value(struct ftxxxx_ts_data *data)
{   
    .....
    /*protocol B*/

    filter_touch_point = event->touch_point;
    for (i = 0; i < event->touch_point; i++) {
        report_point=true;
        if(!report_point)
            continue;
        input_mt_slot(data->input_dev,event->au8_finger_id[i]);
        if (event->au8_touch_event[i]== 0 || event->au8_touch_event[i] == 2) {
            input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,true);
            input_report_abs(data->input_dev, ABS_MT_PRESSURE, event->pressure[i]);
            input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, event->area[i]);
            input_report_abs(data->input_dev, ABS_MT_POSITION_X, event->au16_x[i]);
            input_report_abs(data->input_dev, ABS_MT_POSITION_Y, event->au16_y[i]);
        } else {
            uppoint++;
            input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,false);

        }

    }

    if((last_touchpoint>0)&&(event->Cur_touchpoint==0))
    {
        for(i=0;i<CFG_MAX_TOUCH_POINTS;i++)
        {
            input_mt_slot(data->input_dev,i);
            input_mt_report_slot_state(data->input_dev,MT_TOOL_FINGER,false);
        }
        last_touchpoint=0;
    }
    if(filter_touch_point == uppoint) {
        input_report_key(data->input_dev, BTN_TOUCH, 0);
    } else {
        input_report_key(data->input_dev, BTN_TOUCH, event->touch_point > 0);
    }
    input_sync(data->input_dev);
    last_touchpoint=event->Cur_touchpoint;
}

我们以input_report_key()为例看看我们的数据是如何写入event0的。
这里我省略了很多判断,只留下关键部分

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
    input_event(dev, EV_KEY, code, !!value);
}

void input_event(struct input_dev *dev,
         unsigned int type, unsigned int code, int value)
{
    unsigned long flags;

    if (is_event_supported(type, dev->evbit, EV_MAX)) {

        spin_lock_irqsave(&dev->event_lock, flags);
        input_handle_event(dev, type, code, value);
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}

static void input_handle_event(struct input_dev *dev,
                   unsigned int type, unsigned int code, int value)
{
    int disposition;
    。。。。。
    if (disposition & INPUT_FLUSH) {
        if (dev->num_vals >= 2)
            input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    } else if (dev->num_vals >= dev->max_vals - 2) {
        dev->vals[dev->num_vals++] = input_value_sync;
        input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    }

}


static void input_pass_values(struct input_dev *dev,
                  struct input_value *vals, unsigned int count)
{
    struct input_handle *handle;
    。。。。。
    handle = rcu_dereference(dev->grab);
    if (handle) {
        count = input_to_handler(handle, vals, count);
    } else {
        list_for_each_entry_rcu(handle, &dev->h_list, d_node)
            if (handle->open)
                count = input_to_handler(handle, vals, count);
    }
    。。。。。
}

static unsigned int input_to_handler(struct input_handle *handle,
            struct input_value *vals, unsigned int count)
{
    struct input_handler *handler = handle->handler;
    。。。。。
    if (handler->events)
        handler->events(handle, vals, count);
    else if (handler->event)
        for (v = vals; v != end; v++)
            handler->event(handle, v->type, v->code, v->value);
    return count;
}

到这里调用到了input_to_handler();这个函数里面调用handler->events;
对于touch这里的handler就是我们在evdev.c里面注册的因策这里的events函数就是:evdev.c里面的evdev_events():
evdev_events函数紧接着就会调用evdev_pass_values()

static void evdev_events(struct input_handle *handle,
             const struct input_value *vals, unsigned int count)
{
    struct evdev *evdev = handle->private;
    struct evdev_client *client;
    ktime_t time_mono, time_real;

    time_mono = ktime_get();
    time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());

    rcu_read_lock();

    client = rcu_dereference(evdev->grab);

    if (client)
        evdev_pass_values(client, vals, count, time_mono, time_real);
    else
        list_for_each_entry_rcu(client, &evdev->client_list, node)
            evdev_pass_values(client, vals, count,
                      time_mono, time_real);

    rcu_read_unlock();
}

static void evdev_pass_values(struct evdev_client *client,
            const struct input_value *vals, unsigned int count,
            ktime_t mono, ktime_t real)
{
    struct evdev *evdev = client->evdev;
    const struct input_value *v;
    struct input_event event;
    bool wakeup = false;

    event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
                      mono : real);

    /* Interrupts are disabled, just acquire the lock. */
    spin_lock(&client->buffer_lock);

    for (v = vals; v != vals + count; v++) {
        event.type = v->type;
        event.code = v->code;
        event.value = v->value;
        __pass_event(client, &event);
        if (v->type == EV_SYN && v->code == SYN_REPORT)
            wakeup = true;
    }

    spin_unlock(&client->buffer_lock);

    if (wakeup)
        wake_up_interruptible(&evdev->wait);
}

evdev_pass_value()会调用__pass_event()将我们的touch数据写入event0的buffer,这里我们就不继续往下看了,最后调用wake_up_interruptible()唤醒用户空间读取这里数据的进程对于Android就是我们framwork层的input子系统后面会讲

Android framework层Input子系统的框架

现在请大家跟着我的思绪,想象假设我们的kernel已经跑起来然后跑进了第一个init进程,init进程会启动zygote进程,而zygote进程会启动systemserver进程,这里的细节之后有时间我会在其他的博文里面讲解,在这里我们重点关注和input子系统相关的部分。现在我们假设系统跑到了zygote启动systemserver,之所以讲这里是由于我们的input子系统是systemserver里面的一个service:代码如下:
在systemserver进程启动的过程中会调用startOtherService(),这个函数里面会new InputManagerService(context);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
inputManager.start();这三个函数分别创建inputmanager,将inputmanager注册进binder机制(android 的进程间通信机制)
最后调用inputmanager.start();启动inputmanager;这里我们重点分析inputmanager的创建以及启动

位置:
/framwork/base/services/java/com/android/server/systemserver.java:
private void startOtherServices() {
    。。。。。
    inputManager = new InputManagerService(context);
    Slog.i(TAG, "Window Manager");
    wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
            mActivityManagerService.setWindowManager(wm);   inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();
            。。。。。
}

public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

        LocalServices.addService(InputManagerInternal.class, new LocalService());
}
这里有一个jni调用nativeInit()会调用到/framwork/base/services/core/jni/com_android_server_input_InputManagerService.cpp里面的nativeInit()代码如下

static jlong nativeInit(JNIEnv* env, jclass clazz,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast(im);
}

调用到了
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }
     sp eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

两个重点函数 EventHub()和InputManager()第一个参数是eventhub
主要创建InputReader和InputDispatcher以及在initialize里面创建InputReaderThread和InputDispatcherThread两个线程,不过此时线程并没有启动
代码位于:/framwork/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp& eventHub,
        const sp& readerPolicy,
        const sp& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}
void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

下面我们返回到syetemserver.java里面inputManager.start();
这个函数里面又是一个jni调用这里调用到/framwork/base/services/core/jni/com_android_server_input_InputManagerService.cpp里面的nativeStart()

 public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);
        。。。。。
}
这里的im->getInputManager()->start()调用的是/framwork/native/services/inputflinger/InputManager.cpp的start
static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeInputManager* im = reinterpret_cast(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}
在这里会启动刚才常见的两个线程
status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }
    return OK;
}

下面我们重点分析:
mReaderThread->run(“InputReader”, PRIORITY_URGENT_DISPLAY);
这里会调用到/framwork/native/services/inputflinger/InputReader.cpp里面的thread_loop();

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}
下面函数中的mEventHub->getEvents会依次打开/dev/input/目录底下的节点阻塞等待,其中就包括我们的touch,加入这是我们的手指触摸touch则会触发中断上报数据,会唤醒eventhub读取数据
void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        AutoMutex _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();
}

这里我裁剪贴出了部分getevent代码
scanDevicesLocked()函数里面会依次打开我们的/dev/input底下的节点
并且将文件描述符fd添加进mEpollFd监控(这里类似于linux的select系统调用)
最后getevent会阻塞在epoll_wait()要么超时返回要么在/dev/input下的节点有事件要读取。关于linux的阻塞IO机制我觉得很重要后面有时间我会专门有一节讲解

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    。。。。。
    for (;;) {
        。。。。。
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

       .....

       ......

        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }

            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                .....
            }
            .....
        }
      .....
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

       .....
    return event - buffer;
}

写到这里我们已经进入framwork层的input子系统,不过我不打算向下写了,因为这里有一篇写的不错的文章,这里有别人的一篇写的很好我这里给出连接:
android5.0 Lollipop input子系统

你可能感兴趣的:(Android)