Android 事件处理(1):Framework层如何获得输入事件与储存
Android 事件处理(1):Framework层如何获得输入事件与储存
背景
这篇写得不够明白,虽然很多知识都很全面,但是就是太乱了。
在QMMI开发,遇到key-testcase
里面的按power键测试无法通过的问题。最后才知道,原来powerkey是特殊的按键,一般不对应用层发放。
改完这个以后,趁着有时间,我就展开了对于Android这块的学习。
分析代码版本:Android 9
参考:
- https://blog.csdn.net/cheris_cheris/article/details/53306833
- https://blog.csdn.net/u013891715/article/details/52805056
- https://blog.csdn.net/lin20044140410/article/details/78032007
基本概念
对于Android上层(Native framework/framework, c++/java)的分析,我一般采用的是由上而下的分析,即从其初始化(main,构造,onFirstRef())开始, 通常在其初始化时候,会重一些很重要的上下层的连接,如果由下往上看,会麻烦点,
按键分类
一个Android产品所有的按键分为三大类:
- global key :通用按键,例如KEYCODE_TV,这些key我们可以修改
frameworks/base/core/res/res/xml/global_keys.xm
自己指定。 - system key:系统按键,例如KEYCODE_HOME(home键) 、KEYCODE_VOLUME_DOWN(音量键)。
- user key: 用户按键,例如KEYCODE_A、KEYCODE_B、KEYCODE_1等。
做Android开发的少不了对触摸、按键事件进行处理,对于手机来说,主要是手势移动、触摸按下等,而TV主要通过遥控器、按键操作,按键事件不同于触摸事件,必须先获得焦点,然后才能移动、选择。
按键输入事件主要包括按键(keyEvent),触屏(motionEvent)
Android框架
Android输入设备支持鼠标、键盘(按键)、触摸屏(单点、多点)、轨迹球等,这些设备所产生的输入事件Input Event从底层驱动开始经过input子系统核心层到达Event Handler事件层,最终把事件copy_to_user到用户空间,然后由用户空间层获取这些事件进行分发、传递。整个过程涉及到内核层、Framework层以及应用层。
拓展阅读:Android系统架构
输入事件的投递流程
事件投递的过程分为几个部分,源信息的采集->WindowManagerService分配->应用程序处理。
1、内核驱动上报事件
Android设备中的各种输入设备,例如键盘都需要编写驱动来进行控制支持。
有内核驱动基础的读者,可以看看下列的文章,没有涉及很深的驱动知识:
一句话概括来说,就是:驱动中控制并识别出了外设的某种输入情况,将这种情况通过input_report_key(按键)
/input_report_abs(坐标)
等接口进行上报。此后,允许应用层阶段将这种内容读取出来。
2、Framework层接收事件、对应用层发送
3、应用程序处理输入事件
接下来,我们就按键事件为例,看看按键事件是如何在Framework层、应用层进行传递的。
在startOtherServices中启动input服务
要分析输入系统,最好先从输入事件系统服务InputManagerService
入手,这是了解输入系统的起点,所有其他相关类、线程都因此被创建或间接创建。
输入系统的核心服务是
InputManagerService
,由SystemServer启动。
路径:frameworks/base/services/java/com/android/server/SystemServer.java
Android系统启动时,Android部分第一个用户空间进程zygote首先fork创建了SystemServer对象,随后SystemServer创建了核心服务ActivityManagerService
、PowerManagerService
、PackageManagerService
、InputManagerService
等,相关代码在SystemServer.java
的run方法中,与InputManagerService
相关部分:
private void startOtherServices() {
final Context context = mSystemContext;
WindowManagerService wm = null;
InputManagerService inputManager = null;
// 1
inputManager = new InputManagerService(context);
// 2
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
// 3
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
// 4
inputManager.start();
}
InputManagerService的初始化
路径:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
构造InputManagerService时做了什么
public InputManagerService(Context context) {
this.mContext = context;
// 创建了一个InputManagerHandler对象,参数是HandlerThread中创建的looper对象
this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack =
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
// 用Looper中的消息队列作为参数,调用本地方法nativeInit,
// 返回C++中的NativeInputManager对象地址赋给mPtr,mPtr在1.5节会用到。
mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
String doubleTouchGestureEnablePath = context.getResources().getString(
R.string.config_doubleTouchGestureEnableFile);
mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
new File(doubleTouchGestureEnablePath);
// 把InputManagerInternal.class和LocalService对象作为一对映射添加到`ArrayMap<Class<?>, Object>`中
LocalServices.addService(InputManagerInternal.class, new LocalService());
}
nativeInit方法
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
// 获得了C++层NativeMessageQueue对象(NativeMessageQueue对象是在创建Looper对象时创建的)
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
// 创建了NativeInputManager对象,返回到java层的nativeInit,并将地址赋给mPtr;
// getLooper()获得c++层的Looper对象。
// 注:在Handler机制中,此时Looper对象也已初始化,同时创建了管道并注册在epoll兴趣列表中
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
新建了一个NativeInputManager对象,并且把这个对象返回了保存在了InputManagerService的mPtr对象中。
这只是保存了c层对象的地址,所以只要使用long保存地址就行了。
android_os_MessageQueue_getMessageQueue
路径:frameworks/base/core/jni/android_os_MessageQueue.cpp
sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast<NativeMessageQueue*>(ptr);
}
NativeInputManager
路径:framework/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true)
// 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
JNIEnv* env = jniEnv();
// 创建c++层中全局变量mContextObj, mServiceObj
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;
mLocked.pointerCapture = false;
}
mInteractive = true;
// 创建EventHub对象并作为参数创建InputManager对象
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
待会我们就分析一下EventHub
以及InputManager
都做了什么初始化。
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
WindowManagerService的main方法
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
从这里看出,InputManagerService和WindowManagerService是有联系的,因为事件的分发要先传到WindowManagerservice来分配给具体窗口,所以他们之间必然要有实例引用。
WindowManagerService是窗口的管理者,记录了系统中所有窗口的状态信息;所以它能够判断应该把事件投递到哪个应用进程处理,按键事件直接发给最前端的窗口,触摸消息要先计算这个触目点落在那个区域,然后传给相应的窗口单元。
路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
WindowManagerPolicy policy) {
DisplayThread.getHandler().runWithScissors(() ->
sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
onlyCore, policy), 0);
return sInstance;
}
main方法做了2件事情:
1、创建了WindowManagerService对象
2、将第二个参数InputManagerService对象,传递到WindowManagerService的构造函数中赋给了mInputManager
调用ServiceManager的方法
addService
ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
把InputManagerService服务注册到ServiceManager中
setWindowManagerCallbacks
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
把InputMonitor对象传递到InputManagerService中去,方便回调。
最终会通过该参数回调InputMonitor中的interceptKeyBeforeQueueing方法(7.2)
这个InputMonitor
实现的是InputManagerService
中的接口WindowManagerCallbacks
,是WindowManagerService
(WMS)跟InputManagerService
(IMS)中InputDispatch
之间沟通的桥梁,InputDispatch
是负责事件分发的。
比如,一个key事件的拦截,首先是InputDispatch(cpp)的函数dispatchKeyLocked,然后经有NativeInputManager(cpp)调用到InputManagerService.java(java)中的interceptKeyBeforeDispatching,再由InputManagerService.java中那个回调就是wm.getInputMonitor(),也即是mWindowManagerCallbacks,这样就到了WindowManagerService.java,InputMonitor是WMS的一个成员变量,最后由InputMonitor.java转到具体的窗口管理策略PhoneWindowManager.java中,执行实际的拦截处理。
InputManagerService的start方法
路径:framework/base/services/core/java/com/android/server/input/InputManagerService.java
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
start方法中有两句调用值得我们关心:
nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);
通过nativeStart 找到对应的c实现
nativeStart的本地实现:
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
// ...
}
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
mptr
是NativeInputManager
的对象地址,只不过是长整形;此处再转化成NativeInputManager
型指针。
要彻底理解mPtr
,就要看看InputManagerService
的构造函数。
public InputManagerService(Context context){
mPtr= nativeInit(this, mContext, mHandler.getLooper().getQueue());
}
这里也是通过naive方法nativeInit完成的mPtr的赋值,接着看native实现
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
// 创建一个native类型的NativeInputManager实例
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);
}
nativeStart的传递过程: nativeStart
—-> com_android_server_input_InputManagerService.cpp
的nativeStart
—-> InputManager.cpp
的start()
Watchdog
Watchdog.getInstance().addMonitor(this);
Watchdog是看门狗,一个单例类,addMonitor方法把InputManagerService对象添加到Watchdog,便于回调。
Q:这里的创建了看门狗,有什么用?
A:最终我们会知道,在getEvents
中,这个看门狗是为了确保epoll不会因为超时而退出。
NativeInputManager中的2个重要对象
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true)
// 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
// ...
// 创建EventHub对象并作为参数创建InputManager对象
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
EventHub的本质是一个pipe(管道),它通过读取/dev/input/event
文件来判断是不是有新的事件,然后通知InputReader
。
所以,InputReader
并不是直接去读设备节点,而是通过与EventHub
来完成读取事件。
EventHub
EventHub监听/dev/input/
目录中的变化,并对应地更新好设备的输入事件变化。
在构造函数中进行监听目录变化
路径:framework/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
// INotify机制 初始化
mINotifyFd = inotify_init();
// 监控dev/input目录
/*
第一个参数:把监控项添加到mINotifyFd对象的监控列表中,
第二个参数:DEVICE_PATH就是被监控对象,该对象一般是文件或目录,本文中被监控的DEVICE_PATH就是/dev/input目录;
第三个参数:一个位掩码,表示被监控对象上发生的具体事件,可以由1个或多个掩码位或门组成。
IN_DELETE:当被监控目录内删除文件或目录时触发该事件;
IN_CREATE:当被监控目录内创建文件或目录时触发该事件。比如,插入、拔出鼠标时,就会触发该事件。
*/
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d",
DEVICE_PATH, errno);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
// 将这个iNotify的fd加入
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno);
int wakeFds[2];
//创建了管道,用来唤醒epoll
result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
eventItem.data.u32 = EPOLL_ID_WAKE;
// 把inotify对象mINotifyFd添加到epoll对象的兴趣列表中
// 此处采用inotify与epoll机制结合起来检查文件
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
int major, minor;
getLinuxRelease(&major, &minor);
// EPOLLWAKEUP was introduced in kernel 3.5
mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}
在构造函数中,初始化必要的变量(记住这些变量的值,待会在getEvents
的时候会用到),使用了INotify机制监控下文件的创建和删除动作,采用inotify机制监听/dev/input
下文件或目录的移动、读取、写入或删除等事件,并且将INotify的fd加入epoll机制;还创建了一个pipe,将read端的fd加入了epoll,用来唤醒epoll。
从这里我们也看出,一共注册了mINotifyFd以及pipe的读写fd(mWakeReadPipeFd和mWakeWritePipeFd,用来实现唤醒通信)。
要使epoll_wair能继续执行,管道肯定要被写入数据才可以,因为管道有数据了,表明发生了I/O事件,epoll机制才会触发,也就意味着InputRead线程被唤醒。
读到此处,提出2个问题:
- Q1:epoll机制一般有3个系统调用接口,而截止到此处,只提到
epoll_create
,epoll_ctl
,还缺epoll_wait
(否则epoll机制无法工作),那么epoll_wait
的调用会在哪里呢? - Q2 :如果找到了
epoll_wait
,调用进程处于等待状态,那么肯定还有一处用来唤醒调用进程的代码,比如Looper的wake函数或者其他地方调用了write系统调用
记住这两个问题,有助于在分析多进程控制时的代码走向。
在openDeviceLocked中获取设备的属性
EventHub在构造时,进行了扫描目录,其中就调用了这个方法。
openDeviceLocked会调用一些的ioctl
判断这个设备是否支持某些特性,并记录这些特性。
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
InputDeviceIdentifier identifier;
// Get device name.
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.name.setTo(buffer);
}
// Get device driver version.
int driverVersion;
if(ioctl(fd, EVIOCGVERSION, &driverVersion)) {
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
// Get device identifier.
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
identifier.bus = inputId.bustype;
identifier.product = inputId.product;
identifier.vendor = inputId.vendor;
identifier.version = inputId.version;
// Get device unique id.
if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) {
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);
}
// Fill in the descriptor.
assignDescriptorLocked(identifier);
// Allocate device. (The device object takes ownership of the fd at this point.)
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
" version %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
ALOGV(" name: \"%s\"\n", identifier.name.string());
ALOGV(" location: \"%s\"\n", identifier.location.string());
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
ALOGV(" driver: v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
// Load the configuration file for the device.
loadConfigurationLocked(device);
// Figure out the kinds of events the device reports.
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
// See if this is a keyboard. Ignore everything in the button range except for
// joystick and gamepad buttons which are handled like keyboards for the most part.
bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
sizeof_bit_array(KEY_MAX + 1));
bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
sizeof_bit_array(BTN_MOUSE))
|| containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK),
sizeof_bit_array(BTN_DIGI));
if (haveKeyboardKeys || haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this is a cursor device such as a trackball or mouse.
if (test_bit(BTN_MOUSE, device->keyBitmask)
&& test_bit(REL_X, device->relBitmask)
&& test_bit(REL_Y, device->relBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_CURSOR;
}
// See if this is a rotary encoder type device.
String8 deviceType = String8();
if (device->configuration &&
device->configuration->tryGetProperty(String8("device.type"), deviceType)) {
if (!deviceType.compare(String8("rotaryEncoder"))) {
device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER;
}
}
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
&& test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is
// a touch screen.
if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
}
// Is this an old style single-touch driver?
} else if (test_bit(BTN_TOUCH, device->keyBitmask)
&& test_bit(ABS_X, device->absBitmask)
&& test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_TOUCH;
// Is this a BT stylus?
} else if ((test_bit(ABS_PRESSURE, device->absBitmask) ||
test_bit(BTN_TOUCH, device->keyBitmask))
&& !test_bit(ABS_X, device->absBitmask)
&& !test_bit(ABS_Y, device->absBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to reserve those so we
// can fuse it with the touch screen data, so just take them back. Note this means an
// external stylus cannot also be a keyboard device.
device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this device is a joystick.
// Assumes that joysticks always have gamepad buttons in order to distinguish them
// from other devices such as accelerometers that also have absolute axes.
if (haveGamepadButtons) {
uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i <= ABS_MAX; i++) {
if (test_bit(i, device->absBitmask)
&& (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
device->classes = assumedClasses;
break;
}
}
}
// Check whether this device has switches.
for (int i = 0; i <= SW_MAX; i++) {
if (test_bit(i, device->swBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_SWITCH;
break;
}
}
// Check whether this device supports the vibrator.
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
}
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
// Load the virtual keys for the touch screen, if any.
// We do this now so that we can make sure to load the keymap if necessary.
status_t status = loadVirtualKeyMapLocked(device);
if (!status) {
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
}
// Load the key map.
// We need to do this for joysticks too because the key layout may specify axes.
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.
keyMapStatus = loadKeyMapLocked(device);
}
// Configure the keyboard, gamepad or virtual keyboard.
if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) {
// Register the keyboard as a built-in keyboard if it is eligible.
if (!keyMapStatus
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycodeLocked(device, AKEYCODE_Q)) {
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this device has a DPAD.
if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) &&
hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) {
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
// See if this device has a gamepad.
for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) {
if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) {
device->classes |= INPUT_DEVICE_CLASS_GAMEPAD;
break;
}
}
}
// If the device isn't recognized as something we handle, don't monitor it.
if (device->classes == 0) {
ALOGV("Dropping device: id=%d, path='%s', name='%s'",
deviceId, devicePath, device->identifier.name.string());
delete device;
return -1;
}
// Determine whether the device has a mic.
if (deviceHasMicLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_MIC;
}
// Determine whether the device is external or internal.
if (isExternalDeviceLocked(device)) {
device->classes |= INPUT_DEVICE_CLASS_EXTERNAL;
}
if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD)
&& device->classes & INPUT_DEVICE_CLASS_GAMEPAD) {
device->controllerNumber = getNextControllerNumberLocked(device);
setLedForControllerLocked(device);
}
if (registerDeviceForEpollLocked(device) != OK) {
delete device;
return -1;
}
configureFd(device);
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
toString(mBuiltInKeyboardId == deviceId));
addDeviceLocked(device);
return OK;
}
InputManager
路径:frameworks/native/services/inputflinger/InputManager.cpp
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true)
// 把获得的Looper对象传给过来赋给mLooper,用来回调Looper对象的方法
{
// ...
// 创建EventHub对象并作为参数创建InputManager对象
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
构造
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
// 新建了InputDispatcher和InputReader两个对象
// 将InputReader和InputDispatch建立了关联
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
// 然后调用了initialize函数
initialize();
}
第一个参数是刚创建的EventHub对象,第二、三个参数都是this。
Q:当前NativeInputManager对象,传递过去后分别是InputReaderPolicyInterface、InputDispatcherPolicyInterface类型,这是为何?
A:因为NativeInputManager继承了InputReaderPolicyInterface、InputDispatcherPolicyInterface类,实际类型还是NativeInputManager。
继续看。
构造InputDispatcher对象
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
mPolicy(policy),
mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED),
// ...
mNextUnblockedEvent(NULL),
mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
mKeyRepeatState.lastKeyEntry = NULL;
policy->getDispatcherConfiguration(&mConfig);
}
把传递过来的NativeInputManager对象赋给mPolicy(该NativeInputManager对象又是InputDispatcherPolicyInterface接口类型);
然后又初始化了其他变量;
并调用了mLooper = new Looper(false);
创建并初始化了Looper对象,在Looper构造方法创建了管道并采用epoll机制把管道加入到兴趣列表中。
注意,此处创建的Looper对象是在InputDispatcher中的,与主线程中的Looper没有关系。
Q3 既然有了epoll_ctl
,那肯定某处会调用epoll_wait
构造InputReader对象
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
{ // acquire lock
AutoMutex_l(mLock);
refreshConfigurationLocked(0);
updateGlobalMetaStateLocked();
} // release lock
}
第一个参数是EventHub对象,
第二个policy赋值给mPolicy,policy是InputReaderPolicyInterface接口类型,实际是NativeInputManager对象类型。
第三个listener实际是InputDispatcher类型,因为InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此policy也是InputListenerInterface对象类型
mQueuedListener = new QueuedInputListener(listener);
把InputListenerInterface对象类型listener作为参数创建QueuedInputListener对象,传递过去后赋给mInnerListener变量。
initialize初始化2个线程对象
void InputManager::initialize() {
// 这两个变量就是两个线程我们就不看其构造函数了。
// 在InputReaderThread这个线程中,不断调用InputReader的loopOnce函数,这个函数会调用EventHub的getEvents函数
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
一个InputReaderThread负责从驱动节点读取Event,一个InputDispatcherThread负责把event分发出去。
Q:既然initialize
创建了2个线程对象,那么会在哪里启动线程呢?
A:InputManagerService调用Start函数后,会在InputManager对象中,开启InputReaderThread,和InputDispatcherThread线程。
在start中启动线程
// frameworks/native/services/inputflinger/InputManager.cpp
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;
}
既然InputReaderThread
是继承了Android Framework中的Thread
类,那么执行run
方法的时候,最终会调用到threadLoop()
方法,以一直循环处理数据(直到 threadLoop中
返回return false;
为止)。
这里参考了:Android Framework 中的Thread 类
Thread被创建 ->Thread中的
run
被调用->__threadLoop()
被调用,readyToRun()
被调用, 然后循环调用threadLoop()
。并且在threadLoop()返回false时,可以退出循环。
// frameworks/native/services/inputflinger/InputReader.h
/* Reads raw events from the event hub and processes them, endlessly. */
class InputReaderThread : public Thread {
public:
explicit InputReaderThread(const sp<InputReaderInterface>& reader);
virtual ~InputReaderThread();
private:
sp<InputReaderInterface> mReader;
virtual bool threadLoop();
};
InputDispatcherThread调用dispatchOnce
分发器线程InputDispatcherThread
:
bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
InputReaderThread调用loopOnce
// frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
// 一直调用InputReader的loopOnce函数
mReader->loopOnce();
return true;
}
InputReaderThread
轮询设备节点是不是有新的事件发生
关于上述的2个线程对象所调用的对应方法,篇幅毕竟长,我们接下来进行分析。
InputManager中的2个重要线程对象
从上面我们知道,InputManager::initialize
初始化的2个线程对象会在start
方法中通过run
以后执行threadLoop
中的内容。
InputReader的loopOnce
InputReaderThread轮询设备节点是不是有新的事件发生。
这个函数先从EventHub中通过调用getEvents
获取事件,再调用processEventLocked
进行处理。
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices; // 描述输入设备特征和特性的类
//...
// 获取事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
// 处理事件
processEventsLocked(mEventBuffer, count);
}
// ...
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
// on another thread similarly waiting to acquire the InputReader lock thereby
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
mQueuedListener->flush();
}
在getEvents中获取各个输入事件
getEvents
完成了对于设备事件的读取与处理。
分析getEvents
的时候要动态去分析:
- 当没有事件的时候,它做了什么
- 当事件来临以后,它又做了什么
先看第一次执行吧,也就是输入事件还没来的时候;代码是怎么样的。
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
// 这里的 event 以及 buffer 都 看成是 数组
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// 1、打开设备
// mNeedToReopenDevices,mClosingDevices一开始都为false,不执行
if (mNeedToReopenDevices) {
// ... pass;
}
while (mClosingDevices) {
// ... pass;
break;
}
// 2、扫描设备
// mNeedToScanDevices的初始值为true,会执行scanDevicesLocked
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked(); // 注意这个函数。
mNeedToSendFinishedDeviceScan = true;
}
// mOpeningDevices不为空,进入到while循环
while (mOpeningDevices != NULL) {
/*
该while循环把所有设备Device的内容保存到RawEvent结构体中
*/
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;
// 事件被成功添加到RawEvent
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;
}
}
// 3、监听设备
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
break;
// ...
// 虽然现在没有执行到这里,但是需要留意一下这段代码
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;
}
// ... pass;
}
// 扫描到设备的添加或者删除
// mPendingINotify初始化为false,跳过这个if
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked(); // 读取INotify的内容,然后执行相应的打开设备或者关闭设备。
deviceChanged = true;
}
// 如果设备改变了,添加或者删除设备了,就要重新循环
// 第一次时,deviceChanged也为false,跳过这个if
if (deviceChanged) {
continue;
}
// 此时条件不成立
if (event != buffer || awoken) {
break;
}
mPendingEventIndex = 0;
mLock.unlock();
release_wake_lock(WAKE_LOCK_ID);
// 等待,数据加入mPendingEventItems
// epoll将内容放入mPendingEventItems,等下次循环处理
/* 一开始,队列等待中的事件都为0(mPendingEventIndex = 0);
调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,陷入等待
当有事件以后,epoll_wait返回发生事件的个数赋给pollResult,
此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环
*/
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock();
if (pollResult == 0)
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
mPendingEventCount = 0;
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
mPendingEventCount = size_t(pollResult);
}
} // end: for(;;;)
return event - buffer;// 返回读取的事件个数
}
这个函数实现比较长,我们分为几个部分来看。
第二个参数RawEvent* buffer:存储底层上报的输入事件,因为包含各种事件,没有过滤,称为原始事件
第三个参数bufferSize:已分配input_event的buffer大小,256字节,input_event是所获取事件的结构体:
struct input_event {
struct timevaltime;
__u16type;
__u16code;
__s32value;
};
time:事件的执行时间
type:事件类型,EV_KEY(按键),EV_ABS(绝对坐标,如触摸屏),EV_REL(相对坐标,轨迹,如鼠标),除此之外,还有EV_LED(LED),EV_FF(力反馈)等
code:如果type是按键,code表示原始按键码值;如果是EV_REL,code表示轨迹类型,包括REL_X(X轴方向),REL_Y(Y轴方向);如果是EV_ABS,code表示坐标类型,ABS_X(X轴坐标),ABS_Y(Y轴坐标)
value:事件值,如果type是按键,按下时value为1,松开时为0;如果是EV_REL,value表示x,y方向上的偏移量;如果是EV_ABS,value表示x,y轴坐标值
打开设备
虽然在第一次调用这个方法时不执行下列的语句,但是在此后配置发生改变时,标记重新打开所监听的设备。
if (mNeedToReopenDevices) { // 第一次调用时,mNeedToReopenDevices => false
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked(); // 如果之前打开了设备,则关闭所有的设备,准备重新扫描
mNeedToScanDevices = true;
break;
}
// 回收注册的资源
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;
}
}
扫描目录
第一次循环中,由于mNeedToScanDevices 的初始值是 true,那么会进行目录扫描。
因为InputReaderThread是循环执行的,进程又一次又一次进入到getEvents函数中,开始执行for循环,
当
scanDevicesLocked
完成以后,就不再执行扫描获取设备,除非遇到了设备的插拔。
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked(); // 注意这个函数。
mNeedToSendFinishedDeviceScan = true;
}
注意scanDevicesLocked
这个函数:调用了scanDirLocked
函数扫描/dev/input
下的所有设备,然后调用openDeviceLocked
函数
void EventHub::scanDevicesLocked() {
status_t res = scanDirLocked(DEVICE_PATH);
if(res < 0) {
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
}
if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
createVirtualKeyboardLocked();
}
}
status_t EventHub::scanDirLocked(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}
openDeviceLocked
至于openDeviceLocked
,使用ioctrl获取设备的各种参数,并标记设备对应的属性,最后将设备注册进去。
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
// devicePath为传过来的设备节点路径,比如按键为:/dev/input/event0
// ...... 调用ioctl,
/*
获取device的各种参数,其中包括类型检查,比如:
检查该设备是按键或键盘,就把设备对象的classes属性标为INPUT_DEVICE_CLASS_KEYBOARD,
表示当前发生输入事件的设备是按键或键盘;
然后做了一系列动作:
获取设备名称、检查是否已经包含在容器mExcludedDevices中、获取该设备的驱动版本、
获取设备身份信息保存到inputid结构体中并赋给InputDeviceIdentifier对象中的变量、
获取设备的物理地址和设备唯一id号,设置文件描述符fd为非阻塞模式O_NONBLOCK属性等。
*/
// 添加设备
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
ALOGV("add device %d: %s\n", deviceId, devicePath);
//...
/*
从底层获取了该设备的所有信息后,创建临时Device对象代表该设备,比如,代表按键设备节点对象
在这段代码之后,系统做了很多事情,
比如,继续判断设备是不是轨迹球或鼠标、多点触摸屏、单点触摸屏、操作杆、开关、振动器、虚拟按键;
如果是键盘、操纵杆等,就配置其布局。
这一过程的代码与本文关联性不大,没有贴上,如果研究鼠标、触摸屏等,可参考这段代码。
*/
addDeviceLocked(device);//加入mDevices
return 0;
}
addDeviceLocked
void EventHub::addDeviceLocked(Device* device) {
mDevices.add(device->id, device);
device->next = mOpeningDevices;
mOpeningDevices = device;
}
把设备id、设备对象作为一对映射保存到mDevices容器中,并设device->next = mOpeningDevices
,
在一开始构建EventHub对象的时候,mOpeningDevices
初始化为0,表示下一个设备还没有;而此时,已经设mOpeningDevices为当前设备对象。
监听设备的事件
此时,由于没有事件发生,因此mPendingEventIndex < mPendingEventCount
不成立。
bool deviceChanged = false;
// 第一次时,不执行,跳过;
// 但是在有了输入事件以后
while (mPendingEventIndex < mPendingEventCount) {
// ...
}
只有到有输入事件来临的时候,这个while (mPendingEventIndex < mPendingEventCount)
才会执行。
什么时候会被唤醒
在getEvents
中,epoll监听mWakeReadPipeFd
的变化,而待会我们就会知道,由于wake
方法会往这个pipe写入数据,从而唤醒epoll_wait
。
wake()
会写入'w'
,因此mWakeReadPipeFd
从这个管道中读取的字符有且只有'w'
;
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
// ...
int wakeFds[2];
result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
// ...
eventItem.data.u32 = EPOLL_ID_WAKE;
// 添加进 epoll 监听
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
// ...
// 等待
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// ...
}
void EventHub::wake() {
ALOGV("wake() called");
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1 && errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
我们先不在乎谁调用了wake
唤醒了epoll_wait
的阻塞;当唤醒了以后,int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
返回发生事件的个数赋给pollResult。
此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环。
// 等待,数据加入mPendingEventItems
// epoll将内容放入mPendingEventItems,等下次循环处理
/* 一开始,队列等待中的事件都为0(mPendingEventIndex = 0);
调用epoll_wait机制监控epoll对象mEpollFd的兴趣列表中的事件,陷入等待
当有事件以后,epoll_wait返回发生事件的个数赋给pollResult,
此时pollResult肯定大于0,mPendingEventCount也大于0,继续for循环
*/
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mLock.lock();
if (pollResult == 0)
mPendingEventCount = 0;
break;
}
if (pollResult < 0) {
mPendingEventCount = 0;
if (errno != EINTR) {
ALOGW("poll failed (errno=%d)\n", errno);
usleep(100000);
}
} else {
mPendingEventCount = size_t(pollResult);
}
我们需要从for
循环的头部重新开始看了。
唤醒之后做了什么
bool deviceChanged = false;
// 有了输入事件以后,满足这个条件
while (mPendingEventIndex < mPendingEventCount) {
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
注意接下来的3个if,都是用来判断此时的事件是什么。
为什么不写成
switch-case
呢?
/* 判断此时的事件是什么*/
// 1、如果是设备的插拔,那么跳过。
if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
// notify监测到有新设备,或者设备拔出,跳过。
if (eventItem.events & EPOLLIN) {
mPendingINotify = true;
}
continue;
}
// 2、如果是被wake唤醒,那么就继续循环
if (eventItem.data.u32 == EPOLL_ID_WAKE) {
if (eventItem.events & EPOLLIN) {
/*
从管道中读取数据,该数据就是一个字符w,
执行完后continue跳出循环,又执行到epoll_wait,InputReader又被阻塞等待;
然后,
wake()函数又调用write写入字符w触发了epoll_wait,
唤醒了InputReader线程,然后又开始执行for循环
*/
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));
}
continue;
}
// ...(省略的这部分是 准备处理真正的设备输入事件)
}
我们看到,如果是通过调用wake
唤醒的epoll_wait,其实最终会因为重新进入循环而导致InputReader
又被阻塞等待。
Q:这样循环下去的意义何在?
A:循环的意义:确保InputReader线程始终处于活动状态,能及时获取输入事件。
因为按键、触摸等输入事件还没有发生时,InputReader线程处于阻塞状态,当一定的时间过去后,仍然没有输入事件,epoll_wait可能超时返回0,因此,下段语句就会成立,跳出for循环返回了:
if (pollResult == 0) {
// Timed out.
mPendingEventCount = 0;
break;
}
也就是说,如果此时有事件发生的话,因为epoll_wait
还没有执行到,可能会漏掉事件处理。为了保障InputReader线程能够及时获取事件,系统创建了看门狗线程,每隔一段时间向管道中写入数据唤醒InputReader线程去读取事件,这样,Q5就有了答案,看门狗WatchDog实际上也是一个线程,只不过会定时发送消息给InputReader线程读取输入事件。
我们继续看上段代码中被省略的部分;到了这里,就是准备处理真正的设备输入事件了。
当有输入事件时
根据设备id从容器中取出设备对象,如果发生了可读事件,调用read从该设备文件中读取事件保存到readBuffer中
// 获取设备ID(此时eventItem.data.u32的值是设备id)
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
if (deviceIndex < 0) {
continue;
}
// 分析是否是对应设备的事件,并填充event值
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
// 从设备文件中读取 输入子系统的事件 到 readBuffer
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
/* 出错判断与处理*/
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
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 {
/* 成功执行以后的处理 */
/*
把所有的事件并取出来,
并逐一保存到input_event结构体变量iev中,
同时,然后计算相应的事件执行时间,
再把input_event结构体变量中的内容保存到原始事件RawEvent中,
*/
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);
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;
}
}
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);
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;
}
}
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
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());
}
此后,由于事件是设备输入事件,那么下列语句被跳过。
if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
mPendingINotify = false;
readNotifyLocked();
deviceChanged = true;
}
// Report added or removed devices immediately.
if (deviceChanged) {
continue;
}
那么,来到了if (event != buffer || awoken)
这里。
由于event指向的内存现在已经保存了事件,而buffer还是指向首地址,两者必定不相等。
// 当有内容可读时,event指针向前走,所以event 不等于 buffer
if (event != buffer || awoken) {
break; //跳出 for 循环
}
之后,执行函数末尾的return event - buffer;
,返回读取事件的个数。
C语言功底好的读者都知道,如果两个指针向同一个数组,它们就可以相减,其为结果为两个指针之间的元素数目。
在processEventsLocked中处理事件与储存
把获取的原始按键码值转化成应用层按键值,对按键事件进行检查,处理特殊按键,最后把按键事件封装成EventEntry后临时存储到InboundQueue队列中等待处理。
processEventLocked
处理了2件事情:
- 设备的输入事件,以及调用mapper完成一些特殊处理。
- 处理关于设备本身的2个事件:设备增加 与 设备移除。
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type; // 表示事件的类型
size_t batchSize = 1;
// 正常的事件都小于EventHubInterface::FIRST_SYNTHETIC_EVENT
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
int32_t deviceId = rawEvent->deviceId;
while (batchSize < count) {
if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT
|| rawEvent[batchSize].deviceId != deviceId) {
break;
}
batchSize += 1;
}
#if DEBUG_RAW_EVENTS
ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
// 处理设备事件
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED: //设备增加
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED: //设备移除
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
default:
ALOG_ASSERT(false); // can't happen
break;
}
}
count -= batchSize;
rawEvent += batchSize;
}
}
processEventsForDeviceLocked
处理设备对应的事件
rawEvents
相当于EventHub::getEvents
方法中的buffer
,即Linux 应用层调用read
系统调用获取底层驱动所获取的输入子系统数据。
// frameworks/native/services/inputflinger/EventHub.h
struct RawEvent {
nsecs_t when;
int32_t deviceId;
int32_t type;
int32_t code;
int32_t value;
};
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
// 获取对应的设备
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex < 0) {
ALOGW("Discarding event for unknown deviceId %d.", deviceId);
return;
}
InputDevice* device = mDevices.valueAt(deviceIndex);
if (device->isIgnored()) {
//ALOGD("Discarding event for ignored deviceId %d.", deviceId);
return;
}
// 执行process进行 input子系统 数据处理
device->process(rawEvents, count);
}
根据mapper进行process
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
// Process all of the events in order for each mapper.
// We cannot simply ask each mapper to process them in bulk because mappers may
// have side-effects that must be interleaved. For example, joystick movement events and
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().string());
mDropUntilNextSync = true;
reset(rawEvent->when);
} else {
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
// 调用mappers的process方法。
mapper->process(rawEvent);
}
}
--count;
}
}
这个时候,mapper对应的process就会针对本次输入事件做额外的处理。
processKey
我们以最熟悉的按键事件来说吧,按键事件对应的是KeyboardInputMapper对象。
因此,执行到了KeyboardInputMapper的process方法,该方法中,会把获得原始按键值转化成framework层按键值(暂时这么称呼)。
调用顺序是
device->process(rawEvents, count)
—->mapper->process(rawEvent)
—->KeyboardInputMapper::processKey
void KeyboardInputMapper::process(const RawEvent* rawEvent) {
switch (rawEvent->type) {
case EV_KEY: {
int32_t scanCode = rawEvent->code;
int32_t usageCode = mCurrentHidUsage;
mCurrentHidUsage = 0;
if (isKeyboardOrGamepadKey(scanCode)) {
// 注意这里
processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
}
break;
}
case EV_MSC: {
if (rawEvent->code == MSC_SCAN) {
mCurrentHidUsage = rawEvent->value;
}
break;
}
case EV_SYN: {
if (rawEvent->code == SYN_REPORT) {
mCurrentHidUsage = 0;
}
}
}
}
再根据按键类型,执行到KeyboardInputMapper::processKey
函数
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
int32_t keyCode;
int32_t keyMetaState;
uint32_t policyFlags;
// ...主要代码在最后部分
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
getListener()->notifyKey(&args);
}
NotifyKeyArgs
NotifyKeyArgs类用来描述一个按键事件,包括特性属性和notify函数;倒数第二句调用构造函数初始化特征属性,NotifyKeyArgs特征属性包括:
- nsecs_t eventTime:按键事件的发生时间
- int32_t deviceId:按键的设备id
- uint32_t source:输入源类型,此处由1.5.3.1-2节设置为AINPUT_SOURCE_KEYBOARD,表示按键或键盘
- uint32_t policyFlags:按键功能标志位,比如,如果是外插键盘,应该具备唤醒设备的功能,就把policyFlags设为POLICY_FLAG_WAKE,如果是内置设备,就不设置该标志。
- int32_t action:按键动作标志,是按下按键还是松开按键
- int32_t flags:操作按键的主体是谁,如果是用户操作,就设置为AKEY_EVENT_FLAG_FROM_SYSTEM
- int32_t keyCode:按键值,按键值在底层和上层值不一样,上层的值一般在KeyEvent.java中设置
- int32_t scanCode:原始按键值,就是从底层获取到的按键值
- int32_t metaState:功能按键状态,比如如果有ALT、SHIFT、CTRL按键按下,就会设置一个具体的状态,如果是普通按键,比如方向键,数字键等,此变量就设为0
- nsecs_t downTime:按键按下的时间,与eventTime不一样,eventTime从底层传过来,downTime是在framework层计算的变量
getListener
//frameworks/native/services/inputflinger/InputReader.cpp
InputListenerInterface* InputReader::ContextImpl::getListener() {
return mReader->mQueuedListener.get();
}
mQueuedListener创建InputReader时创建的,创建QueuedInputListener时期参数listener实际类型是InputDispatcher,而InputDispatcher实现了InputDispatcherInterface,InputDispatcherInterface又实现了InputListenerInterface,因此listener也是InputListenerInterface对象类型。
而notifyKey在InputDispatcher中有实现,这样进程就执行到了InputDispatcher的notifyKey函数。
如果在InputDispatcher中没有实现,就要查看InputDispatcherInterface, InputListenerInterface中是否有。
notifyKey
检查这个key,必要时将这个进行key进行特殊处理;如果可以,唤醒InputDispatcherThread::pollOnce
// frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
// 对传递过来的按键事件进行检查、验证
if (!validateKeyEvent(args->action)) {
return;
}
uint32_t policyFlags = args->policyFlags;
int32_t flags = args->flags;
int32_t metaState = args->metaState;
// 检查按键是否按下、松开
if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {
policyFlags |= POLICY_FLAG_VIRTUAL;
flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
}
//设置flags标致为AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY,虚拟硬件
// 比如手机上除了屏幕显示外的MENU、HOME、BACK键
if (policyFlags & POLICY_FLAG_FUNCTION) {
metaState |= AMETA_FUNCTION_ON;
}
// 设置metaState为meta键标志
// 设置该标志表示按键事件是可信任的,
// 比如,一个直接与Android机器相连接的按键设备
policyFlags |= POLICY_FLAG_TRUSTED;
int32_t keyCode = args->keyCode;
if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {
int32_t newKeyCode = AKEYCODE_UNKNOWN;
if (keyCode == AKEYCODE_DEL) {
newKeyCode = AKEYCODE_BACK;
} else if (keyCode == AKEYCODE_ENTER) {
newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
AutoMutex _l(mLock);
struct KeyReplacement replacement = {keyCode, args->deviceId};
mReplacedKeys.add(replacement, newKeyCode);
keyCode = newKeyCode;
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
} else if (args->action == AKEY_EVENT_ACTION_UP) {
// In order to maintain a consistent stream of up and down events, check to see if the key
// going up is one we've replaced in a down event and haven't yet replaced in an up event,
// even if the modifier was released between the down and the up events.
AutoMutex _l(mLock);
struct KeyReplacement replacement = {keyCode, args->deviceId};
ssize_t index = mReplacedKeys.indexOfKey(replacement);
if (index >= 0) {
keyCode = mReplacedKeys.valueAt(index);
mReplacedKeys.removeItemsAt(index);
metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
}
}
// 把参数传递给KeyEvent对象event
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
android::base::Timer t;
// 拦截按键进行特殊处理,比如HOME、音量键、拨号键,电源等
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
std::to_string(t.duration().count()).c_str());
}
bool needWake;
{ // acquire lock
mLock.lock();
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
return; // event was consumed by the filter
}
mLock.lock();
}
int32_t repeatCount = 0;
// 创建KeyEntry对象并把按键事件作为该对象的内容
KeyEntry* newEntry = new KeyEntry(args->eventTime,
args->deviceId, args->source, policyFlags,
args->action, flags, keyCode, args->scanCode,
metaState, repeatCount, args->downTime);
// 根据输入事件的类型作进一步处理
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
mLooper->wake();
}
}
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
// 一开始没有数据为空
bool needWake = mInboundQueue.isEmpty();
// 把按键事件对象KeyEntry插入到mInboundQueue队尾
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
// 用来切换app,如果是HOME、挂号按键,就设为true,表示可以唤醒InputDispatcherThread来分发按键事件,
// 否则,不唤醒InputDispatcherThread,那么按键就不会处理
switch (entry->type) {
case EventEntry::TYPE_KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
// ...
}
return needWake;
}
wake函数会调用write系统调用向管道中写入数据唤醒读端线程,读端线程就是InputDispatcherThread
线程(执行到了Looper对象的pollOnce函数时处于阻塞状态),此时epoll机制检测到管道中有数据写入事件发生。
读端线程InputDispatcherThread被唤醒,InputDispatcherThread线程从dispatchOnce返回到threadLoop,threadLoop的返回值为true,表明是线程是循环执行的,又一次调用dispatchOnce继续执行。唤醒了InputDispatcherThread。
addDeviceLocked
我们来看看mapper这些东西是如何添加进去的。
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
if (deviceIndex >= 0) {
ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
return;
}
// 获取设备的ID
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
// 获取设备的类
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
//创建 输入设备
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
device->configure(when, &mConfig, 0);
device->reset(when);
if (device->isIgnored()) {
ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
identifier.name.string());
} else {
ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
identifier.name.string(), device->getSources());
}
mDevices.add(deviceId, device);
bumpGenerationLocked();
if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
notifyExternalStylusPresenceChanged();
}
}
removeDeviceLocked
只不过是addDeviceLocked
的反向操作而已,不再介绍了。
获取设备Class
看看如何获取到设备的类(实际上,id也是通过相同的方式获取的)。
之前注册的设备都绑定了一个ID,用于寻找设备。
identifier与classes的赋值在
EventHub::openDeviceLocked()
中就已经完成了。
这个class
属性会用在创建设备时用于设置设备的属性以及mapper。
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == NULL) return InputDeviceIdentifier();
return device->identifier;
}
uint32_t EventHub::getDeviceClasses(int32_t deviceId) const {
AutoMutex _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == NULL) return 0;
return device->classes;
}
EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const {
if (deviceId == BUILT_IN_KEYBOARD_ID) {
deviceId = mBuiltInKeyboardId;
}
ssize_t index = mDevices.indexOfKey(deviceId);
return index >= 0 ? mDevices.valueAt(index) : NULL;
}
添加mapper
根据classes所支持的属性而增加各种Mapper。
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, uint32_t classes) {
InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
controllerNumber, identifier, classes);
// External devices.
if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
device->setExternal(true);
}
// Devices with mics.
if (classes & INPUT_DEVICE_CLASS_MIC) {
device->setMic(true);
}
// ...
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
// ...
// Cursor-like devices.
if (classes & INPUT_DEVICE_CLASS_CURSOR) {
device->addMapper(new CursorInputMapper(device));
}
// Touchscreens and touchpad devices.
if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
device->addMapper(new MultiTouchInputMapper(device));
} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
device->addMapper(new SingleTouchInputMapper(device));
}
// Joystick-like devices.
if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
device->addMapper(new JoystickInputMapper(device));
}
// External stylus-like devices.
if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
device->addMapper(new ExternalStylusInputMapper(device));
}
return device;
}
void InputDevice::addMapper(InputMapper* mapper) {
mMappers.add(mapper);
}
当我们在InputReader增加设备后,就可以在processEventsForDeviceLocked
中来进行处理啦。
现在,按键事件读取后的处理已经完成,cpu就把控制权从InputReaderThread交给了InputDispatcherThread线程,开始分发事件。
InputDispatcher的dispatchOnce
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
AutoMutex _l(mLock);
mDispatcherIsAliveCondition.broadcast();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
mLooper->pollOnce(timeoutMillis);
}
一开始,CommandQueue队列中没有任何命令,haveCommandsLocked
为false,if语句成立,执行dispatchOnceInnerLocked
。
dispatchOnceInnerLocked
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
// ...
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
/* mPendingEvent初始化为空,mInboundQueue队列也为空,满足此条件,执行*/
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
// Synthesize a key repeat if appropriate.
if (mKeyRepeatState.lastKeyEntry) {
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
*nextWakeupTime = mKeyRepeatState.nextRepeatTime;
}
}
}
// Nothing to do if there is no pending event.
if (!mPendingEvent) {
return;
}
} else {
// Inbound queue has at least one entry.
mPendingEvent = mInboundQueue.dequeueAtHead();
traceInboundQueueLengthLocked();
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}
// Get ready to dispatch the event.
resetANRTimeoutsLocked();
}
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if we intend to drop them.
ALOG_ASSERT(mPendingEvent != NULL);
bool done = false;
DropReason dropReason = DROP_REASON_NOT_DROPPED;
if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
dropReason = DROP_REASON_POLICY;
} else if (!mDispatchEnabled) {
dropReason = DROP_REASON_DISABLED;
}
if (mNextUnblockedEvent == mPendingEvent) {
mNextUnblockedEvent = NULL;
}
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
break;
}
case EventEntry::TYPE_DEVICE_RESET: {
DeviceResetEntry* typedEntry =
static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
break;
}
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEventLocked(typedEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DROP_REASON_NOT_DROPPED) {
dropReason = DROP_REASON_APP_SWITCH;
}
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
dropReason = DROP_REASON_APP_SWITCH;
}
if (dropReason == DROP_REASON_NOT_DROPPED
&& isStaleEventLocked(currentTime, typedEntry)) {
dropReason = DROP_REASON_STALE;
}
if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DROP_REASON_BLOCKED;
}
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
default:
ALOG_ASSERT(false);
break;
}
if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}
mPendingEvent初始化为空,mInboundQueue队列也为空,紧接着来到这个if语句:
if (isAppSwitchDue) {
// The inbound queue is empty so the app switch key we were waiting
// for will never arrive. Stop waiting for it.
resetPendingAppSwitchLocked(false);
isAppSwitchDue = false;
}
如果isAppSwitchDue为true,表明切换按键(比如HOME、拨号键)的执行时间还没有到,系统就自动放弃这一次的按键处理,并把isAppSwitchDue设置false。
紧接着看这里:
if (mKeyRepeatState.lastKeyEntry) { // mKeyRepeatState.lastKeyEntry初始化为false,跳过
这条语句,继续:
if (!mPendingEvent) {
return;
}
mPendingEvent初始化为空,表示还没有等待处理的输入事件,if语句为true,执行return返回
runCommandsLockedInterruptible
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
一开始,没有命令待处理,runCommandsLockedInterruptible
返回false,跳过语句;如果有命令的话,就取出来处理
pollOnce
mLooper->pollOnce(timeoutMillis);
看到熟悉的语句了,pollOnce
一直执行到epoll_wait
监控管道读取端:
- 如果管道有I/O事件发生,epoll_wait就会返回发生事件的个数;
- 如果还没有事件,进程InputDispatcherThread在此等待,
这就回答了在
构造InputDispatcher
中所new
的Looper
。
既然有Looper的pollOnce
方法,就有对应的唤醒方法,在哪里呢?
这2条线程是如何协作的
现在我们来讨论inputDispatchThread
和InputReaderThread
的协作。
建立关联
前面说过这两个线程是在frameworks/native/services/inputflinger/InputManager.cpp
中创建的。
InputManager::InputManager(...){
mDispatcher= new InputDispatcher(dispatcherPolicy);
mReader= new InputReader(eventHub, readerPolicy, mDispatcher);
}
在创建时,就将InputReader和InputDispatch建立了关联。
Listener
在InputReader的looponce中,事件是通过InputDispatch
来送到Listener的。
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices; // 描述输入设备特征和特性的类
//... 省略掉获取事件与处理事件
// 这里的 mQueuedListener实际是对InputDispatch的一个封装
mQueuedListener->flush();
}
这样InputDispatch就可以持续获取到设备中发生的事件。并且可以向InputReader注册监听多种事件。
class QueuedInputListener : public InputListenerInterface {
protected:
virtual ~QueuedInputListener();
public:
explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
// 向InputReader注册监听多种事件
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
virtual void notifyKey(const NotifyKeyArgs* args);
virtual void notifyMotion(const NotifyMotionArgs* args);
virtual void notifySwitch(const NotifySwitchArgs* args);
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
void flush();
private:
sp<InputListenerInterface> mInnerListener;
Vector<NotifyArgs*> mArgsQueue;
};
注册监听
我们以key事件为例,看下是怎么注册这个监听的。
源头在EventHub,我们知道EventHub负责原始事件的读取,然后通知到InputReader,EventHub还监听设备的插拔,当然这也是事件。
void InputReader::loopOnce() {
//...
// 获取事件
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
if (count) {
// 处理事件
processEventsLocked(mEventBuffer, count);
}
// ...
}
getEvents是EventHub很重要的一个方法,就是获取原事件,接着 processEventsLocked
开始处理事件。
如果是添加了输入设备,event->type
会等于 DEVICE_ADDED
,而FIRST_SYNTHETIC_EVENT = DEVICE_ADDED
,同时我们知道系统肯定是要创建一个输入设备的,那么processEventsLocked
会执行if
的else
分支:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
//这里实际是一个循环,处理读取到的所有原始事件。
RawEvent*rawEvent = rawEvents;
// ...处理读取到的所有原始事件。
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
// ..
} else {
// 执行这里,此时 type == EventHubInterface::FIRST_SYNTHETIC_EVENT
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED: //设备增加
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
}
}
// ...
}
我们直接找往InputReader注册监听的地方,我们知道如果这个设备的class
会被配置,并添加对应的mapper。
比如,键盘设备添加到了Vector<InputMapper*>mMappers;
通知
接下来processEventsLocked
会执行 mMappers
的所有设备的process()
方法,当然也会执行到KeyboardInputMapper
的process
方法,最终调用到了processKey
。
void KeyboardInputMapper::process(constRawEvent* rawEvent) {
// ...
if (isKeyboardOrGamepadKey(scanCode)) {
processKey(rawEvent->when, rawEvent->value != 0, scanCode,usageCode);
}
}
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
int32_t usageCode) {
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
downTime);
// 获取的listener实际就是InputReader中mQueuedListener,我们看mQueuedListener的处理。
getListener()->notifyKey(&args);
}
这里获取的listener实际就是InputReader中mQueuedListener,我们看mQueuedListener的处理。
// frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
// 把NotifyKeyArg 这个实例 添加到 Vector<NotifyArgs*> mArgsQueue 中。
mArgsQueue.push(new NotifyKeyArgs(*args));
}
还记得前面说InputReader的looponce中,把事件通知给监听这是通过mQueuedListener->flush();
就是下面的这个方法,
void QueuedInputListener::flush() {
size_t count = mArgsQueue.size();
for (size_t i = 0; i < count; i++) {
NotifyArgs* args = mArgsQueue[i];
args->notify(mInnerListener);
delete args;
}
mArgsQueue.clear();
}
在这个方法中会回调具体实例的notify函数,比如 NotifyKeyArgs的notify函数。
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
listener->notifyKey(this);
}
注意这里的 mInnerListener来自QueuedInputListener的构造函数,
QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
mInnerListener(innerListener) {
}
QueuedInputListener
又是在InputReader的构造函数实例化的,同时传入的参数是const sp<InputListenerInterface>& listener
,实际就是InputDispatcher,因此,最终调用的是InputDispatch::notifyKey
class InputDispatcher : public InputDispatcherInterface
class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface;
通过上面的类声明,我们看到InputDispatcher
间接继承了 InputListenerInterface
。
也就是这样,InputReader
就把事件传到了InputDispatcher
。
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/