该系列文章总纲链接:专题分纲目录 Android Framework 输入子系统
本章节只需要关注➕读取 & 流程,同时对于代码分析,横向表示纵向思维,一层层递进调用,纵向表示步骤,是并列的执行顺序。下面这张图是本模块放大的部分,如下所示:
这样就会清晰好多。
1 宏观分析InputReader
这里从InputManager的start函数,延续上一章节的分析,代码如下:
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
//...
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
//...
return OK;
}
InputManager启动了一个InputReaderThread和InputDispatcherThread来读取和分发输入消息,调用它们的run方法后,会进入threadLoop函数(只要threadLoop函数返回true,该函数就会循环执行)。因此接下来直接分析InputReaderThread的threadLoop函数,实现如下:
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
这里继续分析loopOnce,实现如下:
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector inputDevices;
//...
//关键点1 通过EventHub获取事件列表
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) {
//关键点2 对事件进行 加工处理
processEventsLocked(mEventBuffer, count);
}
//...
} // release lock
// Send out a message that the describes the changed input devices.
if (inputDevicesChanged) {
mPolicy->notifyInputDevicesChanged(inputDevices);
}
//关键点3 发布事件到InputDispatcher中
mQueuedListener->flush();
}
因此对于读取数据的环节,我们需要注重的就是3个关键点:
接下来从这3个关键点出发,详细分析Inputreader的流程。
2 读取原始输入事件
2.1 EventHub的构造器
这里首先从EventHub的构造器代码开始分析,看初始化了些什么?代码如下:
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);
//这里直接参考IMS分析的第一章节:inofify和epoll机制即可。
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
mINotifyFd = inotify_init();
int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
//创建了名为wakeFds的匿名管道,并将管道读取端的描述符的可读事件注册到epoll对象中。
int wakeFds[2];
result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
//...
}
这里说明下:为什么使用管道?
因为InputReader在执行getEvents()时会因无事件而导致其线程阻塞在epoll_wait()的调用里,然而有时希望能够立刻唤醒InputReader线程使其处理一些请求。此时只需向wakeFds管道的写入端写入任意数据,此时读取端有数据可读,使得epoll_wait()得以返回,从而达到唤醒InputReader线程的目的。
2.2 EventHub->getEvents获取事件
接下来看EventHub->getEvents(),它的代码实现如下:
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
//...
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;//这是元事件指针,可以指向一系列的事件,这些事件按照数组的方式存放的
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
//mNeedToReopenDevices = false; mClosingDevices = 0;
//mNeedToSendFinishedDeviceScan = false; mOpeningDevices = 0
//mNeedToScanDevices = true
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
//...
// Grab the next input event.
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
//...这里省略对于其他的epoll类型的处理。
//如果是EPOLLIN类型的事件,意味着epoll监视的文件描述符中有写入事件,这类事件是输入事件
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)) {
deviceChanged = true;
closeDeviceLocked(device);
} else if //...
} 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];
//这里主要是对于事件时间戳的设定,考虑的因素挺多,但这不是本次分析的重点。
event->deviceId = deviceId;
//...其他属性赋值
event += 1;
capacity -= 1;
}
if (capacity == 0) {
mPendingEventIndex -= 1;
break;
}
}
} else
//...
}
//...
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//pollResult处理
}
return event - buffer;
}
总结一下,EventHub负责打开/dev/input/目录下的所有设备,然后为每一个设备创建一个Device,并把这个Device放入EventHub所定义的数组们Device中。之后把这个设备纳入监视范围。接下来等待事件发生,一旦有事件发生,就从产生input_event事件的设备中读取出这些设备,把这些事件转化为RawEvent类型放入InputReader提供的事件数组中,之后返回。同时这里简单说明下RawEvent结构体,它的定义如下:
struct RawEvent {
nsecs_twhen; /* 发生事件时的时间戳 */
int32_tdeviceId;/* 产生事件的设备Id,由EventHub自行分配,InputReader根据它从EventHub中获取此设备的详细信息 */
int32_ttype; /* 事件的类型 */
int32_tcode; /* 事件代码 */
int32_tvalue; /* 事件值 */
};
特殊说明:RawEvent也用来表示设备增删事件,为此EventHub定义了三个特殊的事件类型DEVICE_ADD、DEVICE_REMOVED以及FINISHED_DEVICE_SCAN,用以与原始输入事件进行区别。同时这里列出 Android支持的设备类型:
getEvent函数是通过inotify和epoll机制对/dev/input/下的事件 进行获取并封装成一个个RawEvent事件。
接下来重点分析扫描设备 scanDevicesLocked 和epoll_wait两个关键点。
2.2.1 扫描设备
关键方法scanDevicesLocked实现如下:
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();
}
}
继续分析scanDirLocked,代码实现如下:
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;
}
函数调用readdir函数扫描/dev/input下的文件,然后调用openDeviceLocked打开这些设备文件。代码如下:
status_t EventHub::openDeviceLocked(const char *devicePath) {
...
InputDeviceIdentifier identifier;
// 获取设备的名字,如果成功获取到设备的名字,把它存入InputDeviceIdentifier中
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);
}
...
//构造EventHub所需要的对象Device,这里的fd是刚刚打开的设备的文件描述符
int32_t deviceId = mNextDeviceId++;//从这里可以看出,deviceId是与设备无关的,和打开顺序有关
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
// 测试设备能够产生的事件的类型,这里就是Android支持的事件类型,是Kernel的一个子集
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);
...
//根据前面获取到的设备属性,检测设备是鼠标,键盘,手柄等,然后把这些信息继续存入Device
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;
}
...
// 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.
//加载kl和kcm文件
keyMapStatus = loadKeyMapLocked(device);
}
...
//将前面打开的设备的文件描述符加入到epoll监听中
if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
该函数打开扫描到的/dev/input下的设备文件,然后获取该设备文件的name,bus,product,vendor,version,location, uniqueId, descriptor以及设备类型,如是否是键盘、虚拟按键等。如果是INPUT_DEVICE_CLASS_KEYBOARD 这个类型,则调用loadKeyMapLocked来加载kl文件。
这里继续分析loadKeyMapLocked,代码如下:
status_t EventHub::loadKeyMapLocked(Device* device) {
return device->keyMap.load(device->identifier, device->configuration);
}
继续分析load实现,代码如下:
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration) {
// Use the configured key layout if available.
if (deviceConfiguration) {
String8 keyLayoutName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
keyLayoutName)) {
status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName);
//...
}
String8 keyCharacterMapName;
if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
keyCharacterMapName)) {
status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName);
//...
}
//...
}
//...
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) { return OK;}
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {return OK;}
// Try the Virtual key map as a last resort.
if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) {return OK;}
return NAME_NOT_FOUND;
}
这里说明下查找逻辑(以上代码 递进分析):
这里对于按键类设备,驱动层有一组 对按键的定义,而android在IMS中又有一组对按键值的定义,这是为什么呢?
这是因为Android 系统是建立在Linux内核基础上的,但如果Linux内核中 输入事件的定义值发生了变化,android系统也要跟着改,而这样就很被动,因此android系统采用了配置文件*.kl 和*.kcm,这样只要配置就可以了。而接下来谈谈kl和kcm文件的差异:
kl文件仅仅是值的映射,比如:
# This is an example of a key layout file for basic system controls,
# such as volume and power keys which are typically implemented as GPIO pins
# the device decodes into key presses.
key 114 VOLUME_DOWN
key 115 VOLUME_UP
key 116 POWER
像前面114、115这些数字都是在kernel*/include/uapi/linux/input.h中定义的,kernel报上来的键值就是114或115,而kl文件是对这个键值的映射,在android系统中代表的是VOLUMEDOWN、VOLUMEUP。
kcm文件则有规则限制,比如:
key A {
label: 'A'
base: 'a'
shift, capslock: 'A'
ctrl, alt, meta: none
}
在上面的例子中,label
属性被分配了 'A'
行为(驱动中对应行为)。同样ctrl
、alt
和 meta
属性同时被分配了 none
行为。
按键声明包括关键字 key
,后跟 Android 按键代码名称、左大括号、一组属性和行为以及一个右大括号。每个按键属性都会建立从按键到行为的映射。为了使按键字符映射文件更加紧凑,可以将多个属性(用逗号分隔)映射到同一个行为。
2.2.2 epoll_wait等待事件简要说明
从epoll_wait()中得到新的事件后会重新循环,对新事件进行处理,返回本次getEvents()调用所读取的事件数量,之后会调用processEventsLocked来处理RawEvent。
2.3 总结
获取输入事件的目的:从驱动中将input事件独取出来转换成 RawEvent事件,同时对于输入设备,抽象成一个EventHub中的Device结构体并存储。
3 处理事件
processEventsLocked的代码实现如下:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
size_t batchSize = 1;
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;
}
//关键点1:有输入设备产生的事件,在这个方法中处理
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {//设备添加类的事件在这里处理
case EventHubInterface::DEVICE_ADDED:
//这个方法中创建了InputReader所必须的一些数据结构
//添加设备
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;
}
}
3.1 processEventsForDeviceLocked处理设备的输入事件,代码如下:
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
const RawEvent* rawEvents, size_t count) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
//...
InputDevice* device = mDevices.valueAt(deviceIndex);
//...
device->process(rawEvents, count);
}
这里根据deviceId获取到InputDevice,然后调用InputDevice的process函数
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
size_t numMappers = mMappers.size();
for (const RawEvent* rawEvent = rawEvents; count--; 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];
mapper->process(rawEvent);
}
}
}
}
这里的mMappers成员变量保存了一系列输入设备事件处理对象,是在InputReader类的成员函数createDevice中创建的。这里查询每一个InputMapper对象是否要对当前发生的事件进行处理。这里后面以按键处理为例,发生键盘事件,真正会对该事件进行处理的只有KeyboardKeyMapper对象。接下来对KeyboardKeyMapper的process进行分析:
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)) {
int32_t keyCode;
uint32_t flags;
if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
keyCode = AKEYCODE_UNKNOWN;
flags = 0;
}
processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
}
break;
}
//...
}
}
继续分析processKey,代码实现如下:
void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
if (down) {
// 根据屏幕方向的不同调整键盘码
// Add key down.
ssize_t keyDownIndex = findKeyDown(scanCode);
if (keyDownIndex >= 0) {
// key repeat, be sure to use same keycode as before in case of rotation
} else {
// key down
}
mDownTime = when;
} else {
// Remove key down.
if (keyDownIndex >= 0) {
// key up, be sure to use same keycode as before in case of rotation
} else {
return;
}
}
//...
NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
//说明:这里getListener是InputReader初始化时传入的对象,即InputDispatcher
getListener()->notifyKey(&args);
}
通过对getListener()的分析了解到,这里返回的是mQueuedListener对象 。(在构造器中定义 mQueuedListener = new QueuedInputListener(listener); 这句话中定义,同时这里传递的listerner便是InputDispatcher)。notifyKey代码实现如下所示:
void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
mArgsQueue.push(new NotifyKeyArgs(*args));
}
3.2 添加/删除设备
3.2.1 addDeviceLocked添加设备
void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
//...
//获取厂商信息和设备类型
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
uint32_t classes = mEventHub->getDeviceClasses(deviceId);
int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
//创建InputDevice
InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
//使用inputReader中保存的配置信息对新Inputdevice进行策略配置,执行reset进行设备重制
device->configure(when, &mConfig, 0);
device->reset(when);
//...
//添加到列表mDevices中
mDevices.add(deviceId, device);//
bumpGenerationLocked();
}
这里继续分析createDeviceLocked,代码实现如下:
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);
}
//...
// 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;
}
//...
// Joystick-like devices.
if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
device->addMapper(new JoystickInputMapper(device));
}
return device;
}
这里主要创建InputDevice设备,并根据class给device添加了各种能够支持的Mapper。最后添加到mDevices的Vector容器中。
3.2.2 removeDeviceLocked删除设备
void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
InputDevice* device = NULL;
ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
//...
device = mDevices.valueAt(deviceIndex);
mDevices.removeItemsAt(deviceIndex, 1);
bumpGenerationLocked();
//...
device->reset(when);
delete device;
}
根据传递进来的deviceId找到对应的deviceIndex,然后从mDevices的Vector容器中删除该device。(说明:这里的mDevices是InputDevice类型的,和之前在EventHub中的Device结构体并不相同,EventHub中的Device结构体主要是记录。这里的InputDevice主要是封装了处理事件各种Mapper,便于对输入事件的处理)。
4 将事件交付给InputDispatcher
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();
}
之前处理事件中,执行了mArgsQueue.push操作将NotifyKeyArgs放进Vector中。在flush时直接取出来执行NotifyKeyArgs的notify方法,这里代码如下:
void NotifyKeyArgs::notify(const sp& listener) const {
listener->notifyKey(this);
}
这里调用传递进来的listener执行notifyKey,实际上就是调用了InputDispatcher的notifyKey函数。
代码实现如下:
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
//validateKeyEvent来判断是否是有效按键事件,按键则为Up/Down
//构造KeyEvent对象
KeyEvent event;
event.initialize(args->deviceId, args->source, args->action,
flags, keyCode, args->scanCode, metaState, 0,
args->downTime, args->eventTime);
//最后会调用到java层的PhoneWindowManagerService函数
mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
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);
将输入事件加入队列,如果返回true,则调用后面mLooper.wake函数
needWake = enqueueInboundEventLocked(newEntry);
mLock.unlock();
} // release lock
if (needWake) {
//唤醒等待的InputDispatcher,进行输入事件分发。
mLooper->wake();
}
}
这里有2个关键点:一个是interceptKeyBeforeQueueing,这里最终会调用到java层的PhoneWindowManager,而另一个是enqueueInboundEventLocked,将时间队列加入到mInboundQueue中。
4.1 分析 interceptKeyBeforeQueueing
这里看实现接口,最终找到NativeInputManager::interceptKeyBeforeQueueing,代码实现如下:
void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
uint32_t& policyFlags) {
if (mInteractive) {
policyFlags |= POLICY_FLAG_INTERACTIVE;
}
if ((policyFlags & POLICY_FLAG_TRUSTED)) {
nsecs_t when = keyEvent->getEventTime();
JNIEnv* env = jniEnv();
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
jint wmActions;
if (keyEventObj) {
wmActions = env->CallIntMethod(mServiceObj,
gServiceClassInfo.interceptKeyBeforeQueueing,
keyEventObj, policyFlags);
if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
android_view_KeyEvent_recycle(env, keyEventObj);
env->DeleteLocalRef(keyEventObj);
} else {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
wmActions = 0;
}
handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
} else {
if (mInteractive) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
}
}
}
这里会通过JNI机制(对应com_android_server_input_InputManagerService)文件中回调 java层的PhoneWindowManager里的同名方法interceptKeyBeforeQueueing,实现如下:
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
//...
int result;
boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
|| event.isWakeKey();
//...
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
//...
if (down) {
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null) {
if (telecomManager.isRinging()) {
telecomManager.silenceRinger();
result &= ~ACTION_PASS_TO_USER;
break;
}
//...
}
}
break;
}
//...
}
//...
return result;
}
这里interceptKeyBeforeQueueing中以打电话 按power键为例,这里直接截断并将result标志位置为非,即不发给用户层。实际上这里把三类按键(system Key | Global Key | User Key)中前两项都进行了相应的处理。
4.2 分析enqueueInboundEventLocked
enqueueInboundEventLocked的代码实现如下:
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
mInboundQueue.enqueueAtTail(entry);
switch (entry->type) {
case EventEntry::TYPE_KEY: {
KeyEntry* keyEntry = static_cast(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
//...这里忽略Motion,因为情况类似
}
return needWake;
}
这里依然以按键事件为例,将EventEntry加入到mInboundQueue中,该函数两种情况下会返回true:
如果不是这两种情况,那么就说明InputDispatcherThread线程现在正在处理前面的键盘事件,不需要被唤醒。