input key and Touch

  • Android 输入事件的一般流程
    • EventHub
    • InputRead
    • InputDispatcher
    • InputManager
  • Key
    • 常见debug 工具 或 命令
    • Android 框架Key原理
    • 常见需求
  • Touch
    • 常见debug 工具
    • Android 框架Touch原理
    • 常见需求

Android 输入事件的一般流程

EventHub

  1. EventHub 事件枢纽,可以多路复用监听/dev/input/目录 文件的变化;
  2. getEvents步骤:
    (1) . 开始循环执行,直至已经收集了任何事件,或者我们被显式地唤醒,现在返回。
    a. Reopen input devices if needed.
    b. Report any devices that had last been added/removed.
    c. scan devices if Needed, add devices / remove devices.
    d. product adding devices event if needed.
    e. product finishing scan devices event if needed.
    f. start grab the next input event really. //多路复用监听fd,
    g. readNotify()将修改设备列表,因此必须在处理所有其他事件之后完 成,以确保在关闭设备之前读取所有剩余事件。
    h. 若上一个动作g执行,Report added or removed devices immediately.
    i. 如果我们已经收集了任何事件,或者我们被显式地唤醒,现在返回。

多路复用监听事件补充:

/*man help:EPOLLIN       连接到达;有数据来临;The associated file is available for read(2) operations.EPOLLOUT      有数据要写The associated file is available for write(2) operations.EPOLLRDHUP    表示读关闭。如果有EPOLLRDHUP,检测它就可以直到是对方关闭;否则就用上面方法。EPOLLHUP 表示读写都关闭。*/if (eventItem.events & EPOLLIN) {...            } else if (eventItem.events & EPOLLHUP) {                ALOGI("Removing device %s due to epoll hang-up event.",                        device->identifier.name.string());                deviceChanged = true;                closeDeviceLocked(device);            } else {                ALOGW("Received unexpected epoll event 0x%08x for device %s.",                        eventItem.events, device->identifier.name.string());            }

EventHub getEvents 函数:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {    ALOG_ASSERT(bufferSize >= 1);    AutoMutex _l(mLock);    struct input_event readBuffer[bufferSize];    RawEvent* event = buffer;    size_t capacity = bufferSize;    bool awoken = false;    for (;;) {        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);        // Reopen input devices if needed.        if (mNeedToReopenDevices) {            mNeedToReopenDevices = false;            ALOGI("Reopening all input devices due to a configuration change.");            closeAllDevicesLocked();            mNeedToScanDevices = true;            break; // return to the caller before we actually rescan        }        // Report any devices that had last been added/removed.        while (mClosingDevices) {            Device* device = mClosingDevices;            ALOGV("Reporting device closed: id=%d, name=%s\n",                 device->id, device->path.string());            mClosingDevices = device->next;            event->when = now;            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;            event->type = DEVICE_REMOVED;            event += 1;            delete device;            mNeedToSendFinishedDeviceScan = true;            if (--capacity == 0) {                break;            }        }        if (mNeedToScanDevices) {            mNeedToScanDevices = false;            scanDevicesLocked();            mNeedToSendFinishedDeviceScan = true;        }        while (mOpeningDevices != NULL) {            Device* device = mOpeningDevices;            ALOGV("Reporting device opened: id=%d, name=%s\n",                 device->id, device->path.string());            mOpeningDevices = device->next;            event->when = now;            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;            event->type = DEVICE_ADDED;            event += 1;            mNeedToSendFinishedDeviceScan = true;            if (--capacity == 0) {                break;            }        }        if (mNeedToSendFinishedDeviceScan) {            mNeedToSendFinishedDeviceScan = false;            event->when = now;            event->type = FINISHED_DEVICE_SCAN;            event += 1;            if (--capacity == 0) {                break;            }        }        // 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;            }            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);            if (deviceIndex < 0) {                ALOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",                        eventItem.events, eventItem.data.u32);                continue;            }            Device* device = mDevices.valueAt(deviceIndex);            if (eventItem.events & EPOLLIN) {                int32_t readSize = read(device->fd, readBuffer,                        sizeof(struct input_event) * capacity);                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {                    // Device was removed before INotify noticed.                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32                            " bufferSize: %zu capacity: %zu errno: %d)\n",                            device->fd, readSize, bufferSize, capacity, errno);                    deviceChanged = true;                    closeDeviceLocked(device);                } else if (readSize < 0) {                    if (errno != EAGAIN && errno != EINTR) {                        ALOGW("could not get event (errno=%d)", errno);                    }                } else if ((readSize % sizeof(struct input_event)) != 0) {                    ALOGE("could not get event (wrong size: %d)", readSize);                } else {                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;                    size_t count = size_t(readSize) / sizeof(struct input_event);                    for (size_t i = 0; i < count; i++) {                        struct input_event& iev = readBuffer[i];                        ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",                                device->path.string(),                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,                                iev.type, iev.code, iev.value);                        // Some input devices may have a better concept of the time                        // when an input event was actually generated than the kernel                        // which simply timestamps all events on entry to evdev.                        // This is a custom Android extension of the input protocol                        // mainly intended for use with uinput based device drivers.                        if (iev.type == EV_MSC) {                            if (iev.code == MSC_ANDROID_TIME_SEC) {                                device->timestampOverrideSec = iev.value;                                continue;                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {                                device->timestampOverrideUsec = iev.value;                                continue;                            }                        }                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {                            iev.time.tv_sec = device->timestampOverrideSec;                            iev.time.tv_usec = device->timestampOverrideUsec;                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {                                device->timestampOverrideSec = 0;                                device->timestampOverrideUsec = 0;                            }                            ALOGV("applied override time %d.%06d",                                    int(iev.time.tv_sec), int(iev.time.tv_usec));                        }                        // Use the time specified in the event instead of the current time                        // so that downstream code can get more accurate estimates of                        // event dispatch latency from the time the event is enqueued onto                        // the evdev client buffer.                        //                        // The event's timestamp fortuitously uses the same monotonic clock                        // time base as the rest of Android.  The kernel event device driver                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a                        // system call that also queries ktime_get_ts().                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL                                + nsecs_t(iev.time.tv_usec) * 1000LL;                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);                        // Bug 7291243: Add a guard in case the kernel generates timestamps                        // that appear to be far into the future because they were generated                        // using the wrong clock source.                        //                        // This can happen because when the input device is initially opened                        // it has a default clock source of CLOCK_REALTIME.  Any input events                        // enqueued right after the device is opened will have timestamps                        // generated using CLOCK_REALTIME.  We later set the clock source                        // to CLOCK_MONOTONIC but it is already too late.                        //                        // Invalid input event timestamps can result in ANRs, crashes and                        // and other issues that are hard to track down.  We must not let them                        // propagate through the system.                        //                        // Log a warning so that we notice the problem and recover gracefully.                        if (event->when >= now + 10 * 1000000000LL) {                            // Double-check.  Time may have moved on.                            nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);                            if (event->when > time) {                                ALOGW("An input event from %s has a timestamp that appears to "                                        "have been generated using the wrong clock source "                                        "(expected CLOCK_MONOTONIC): "                                        "event time %" PRId64 ", current time %" PRId64                                        ", call time %" PRId64 ".  "                                        "Using current time instead.",                                        device->path.string(), event->when, time, now);                                event->when = time;                            } else {                                ALOGV("Event time is ok but failed the fast path and required "                                        "an extra call to systemTime: "                                        "event time %" PRId64 ", current time %" PRId64                                        ", call time %" PRId64 ".",                                        event->when, time, now);                            }                        }                        event->deviceId = deviceId;                        event->type = iev.type;                        event->code = iev.code;                        event->value = iev.value;                        event += 1;                        capacity -= 1;                    }                    if (capacity == 0) {                        // The result buffer is full.  Reset the pending event index                        // so we will try to read the device again on the next iteration.                        mPendingEventIndex -= 1;                        break;                    }                }            } else if (eventItem.events & EPOLLHUP) {                ALOGI("Removing device %s due to epoll hang-up event.",                        device->identifier.name.string());                deviceChanged = true;                closeDeviceLocked(device);            } else {                ALOGW("Received unexpected epoll event 0x%08x for device %s.",                        eventItem.events, device->identifier.name.string());            }        }        // readNotify() will modify the list of devices so this must be done after        // processing all other events to ensure that we read all remaining events        // before closing the devices.        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {            mPendingINotify = false;            readNotifyLocked();            deviceChanged = true;        }        // Report added or removed devices immediately.        if (deviceChanged) {            continue;        }        // Return now if we have collected any events or if we were explicitly awoken.        if (event != buffer || awoken) {            break;        }        // Poll for events.  Mind the wake lock dance!        // We hold a wake lock at all times except during epoll_wait().  This works due to some        // subtle choreography.  When a device driver has pending (unread) events, it acquires        // a kernel wake lock.  However, once the last pending event has been read, the device        // driver will release the kernel wake lock.  To prevent the system from going to sleep        // when this happens, the EventHub holds onto its own user wake lock while the client        // is processing events.  Thus the system can only sleep if there are no events        // pending or currently being processed.        //        // The timeout is advisory only.  If the device is asleep, it will not wake just to        // service the timeout.        mPendingEventIndex = 0;        mLock.unlock(); // release lock before poll, must be before release_wake_lock        release_wake_lock(WAKE_LOCK_ID);        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock        if (pollResult == 0) {            // Timed out.            mPendingEventCount = 0;            break;        }        if (pollResult < 0) {            // An error occurred.            mPendingEventCount = 0;            // Sleep after errors to avoid locking up the system.            // Hopefully the error is transient.            if (errno != EINTR) {                ALOGW("poll failed (errno=%d)\n", errno);                usleep(100000);            }        } else {            // Some events occurred.            mPendingEventCount = size_t(pollResult);        }    }    // All done, return the number of events we read.    return event - buffer;}

InputRead

  1. InputManager 启动的一个线程主要用于循环去通过EventHub监听事件

InputDispatcher

  1. inputManager 启动的一个线程主要用于循环的派发事件;

InputManager

  1. 主要用来管理InputReader 和 InputDispatcher;
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#define LOG_TAG "InputManager"//#define LOG_NDEBUG 0#include "InputManager.h"#include namespace android {InputManager::InputManager(        const sp<EventHubInterface>& eventHub,        const sp<InputReaderPolicyInterface>& readerPolicy,        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {    mDispatcher = new InputDispatcher(dispatcherPolicy);    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);    initialize();}InputManager::InputManager(        const sp<InputReaderInterface>& reader,        const sp<InputDispatcherInterface>& dispatcher) :        mReader(reader),        mDispatcher(dispatcher) {    initialize();}InputManager::~InputManager() {    stop();}void InputManager::initialize() {    mReaderThread = new InputReaderThread(mReader);    mDispatcherThread = new InputDispatcherThread(mDispatcher);}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;}status_t InputManager::stop() {    status_t result = mReaderThread->requestExitAndWait();    if (result) {        ALOGW("Could not stop InputReader thread due to error %d.", result);    }    result = mDispatcherThread->requestExitAndWait();    if (result) {        ALOGW("Could not stop InputDispatcher thread due to error %d.", result);    }    return OK;}sp<InputReaderInterface> InputManager::getReader() {    return mReader;}sp<InputDispatcherInterface> InputManager::getDispatcher() {    return mDispatcher;}} // namespace android

Key

常见debug 工具 或 命令

  1. input 自动化测试常用(命令每次重新执行通过app_process是用来调用java代码的,耗时1到2s ,可以做优化)
  2. dumpsys input 调试常用,可以查看当前按键和触屏情况;
  3. 打开开发者选项的命令:
    settings put secure user_setup_complete 1
    settings put global device_provisioned 1
    am start -n com.android.launcher/com.android.launcher2.Launcher
  4. 控制log的相关命令:
    关闭所有log: setprop persist.log.tag S
    打开指定TAG的log: setprop persist.log.tag.TAG V
  5. kill -3 PID命令获取java应用堆栈信息,一般可以用来调查APP无法接受按键和触屏事件的内部原因

Android 框架Key原理

dumpsys input:

Event Hub State:  BuiltInKeyboardId: 5  Devices:    -1: Virtual      Classes: 0x40000023      Path: <virtual>      Enabled: true      Descriptor: a718a782d34bc767f4689c232d64d527998ea7fd      Location:      ControllerNumber: 0      UniqueId: <virtual>      Identifier: bus=0x0000, vendor=0x0000, product=0x0000, version=0x0000      KeyLayoutFile: /system/usr/keylayout/Generic.kl      KeyCharacterMapFile: /system/usr/keychars/Virtual.kcm      ConfigurationFile:      HaveKeyboardLayoutOverlay: falseInput Reader State:  Device -1: Virtual    Generation: 2    IsExternal: false    HasMic:     false    Sources: 0x00000301    KeyboardType: 2    Keyboard Input Mapper:      Parameters:        HasAssociatedDisplay: false        OrientationAware: false        HandlesKeyRepeat: false      KeyboardType: 2      Orientation: 0      KeyDowns: 0 keys currently down      MetaState: 0x0      DownTime: 0

根据个人的项目经验, KeyLayoutFile条目中看到加载的按键映射文件,都是根据厂商ID和产品ID匹配对应文件的,例如vendor为0x3697,product 为0x0001,那么对应的配置文件为/system/usr/keylayout/Vendor_3697_Product_0001.kl.

常见需求

Touch

常见debug 工具

Android 框架Touch原理

常见需求

更多相关文章

  1. Android(安卓)ListView setOnItemClickListener/setOnItemSelect
  2. Android(安卓)连续多次点击事件的实现
  3. android中view手势滑动冲突的解决方法
  4. android之XMPP过程分析(连接、文件监听、登录、异常重连)
  5. Android(安卓)笔记5---事件处理和手势
  6. Android自动化测试之Monkeyrunner使用方法及实例
  7. Android之广播大全 Intent Action 事件
  8. Android超级终端文件处理命令
  9. Android(安卓)RatingBar 评分条

随机推荐

  1. Android面试系列之一
  2. 【Android】解决使用Dialog + EdiText 实
  3. 产品中,不明白与思考
  4. Android仿京东首页轮播文字(又名垂直跑马
  5. 初学Android,手势翻页效果(四十九)
  6. Android(安卓)实现书籍翻页效果---番外篇
  7. Android(安卓)(最新)控件透明度,布局透明
  8. Android休眠问题探讨(1)
  9. 【幻灯片分享】和Android源代码一起工作
  10. 给Android开发者的一封信