Android 按键流程(InputManagerService 以及 JNI phoneWindowManger 应用)(and5.1)

原创 2015年11月20日 11:40:45


一、 初识Android输入系统

第4章通过分析WMS详细讨论了Android的窗口管理、布局及动画的工作机制。窗口不仅是内容绘制的载体,同时也是用户输入事件的目标。本章将详细讨论Android输入系统的工作原理,包括输入设备的管理、输入事件的加工方式以及派发流程。因此本章的探讨对象有两个:输入设备、输入事件。

触摸屏与键盘是Android最普遍也是最标准的输入设备。其实Android所支持的输入设备的种类不止这两个,鼠标、游戏手柄均在内建的支持之列。当输入设备可用时,Linux内核会在/dev/input/下创建对应的名为event0~n或其他名称的设备节点。而当输入设备不可用时,则会将对应的节点删除。在用户空间可以通过ioctl的方式从这些设备节点中获取其对应的输入设备的类型、厂商、描述等信息。

当用户操作输入设备时,Linux内核接收到相应的硬件中断,然后将中断加工成原始的输入事件数据并写入其对应的设备节点中,在用户空间可以通过read()函数将事件数据读出。

Android输入系统的工作原理概括来说,就是监控/dev/input/下的所有设备节点,当某个节点有数据可读时,将数据读出并进行一系列的翻译加工,然后在所有的窗口中寻找合适的事件接收者,并派发给它。

以Nexus4为例,其/dev/input/下有evnet0~5六个输入设备的节点。它们都是什么输入设备呢?用户的一次输入操作会产生什么样的事件数据呢?获取答案的最简单的办法就是是用getevent与sendevent工具。

  getevent与sendevent工具

Android系统提供了getevent与sendevent两个工具供开发者从设备节点中直接读取输入事件或写入输入事件。

getevent监听输入设备节点的内容,当输入事件被写入到节点中时,getevent会将其读出并打印在屏幕上。由于getevent不会对事件数据做任何加工,因此其输出的内容是由内核提供的最原始的事件。其用法如下:

adb shell getevent [-选项] [device_path]

其中device_path是可选参数,用以指明需要监听的设备节点路径。如果省略此参数,则监听所有设备节点的事件。

打开模拟器,执行adb shell getevent –t(-t参数表示打印事件的时间戳),并按一下电源键(不要松手),可以得到以下一条输出,输出的部分数值会因机型的不同而有所差异,但格式一致:

[   1262.443489] /dev/input/event0: 0001 0074 00000001

松开电源键时,又会产生以下一条输出:

[   1262.557130] /dev/input/event0: 0001 0074 00000000

这两条输出便是按下和抬起电源键时由内核生成的原始事件。注意其输出是十六进制的。每条数据有五项信息:产生事件时的时间戳([   1262.443489]),产生事件的设备节点(/dev/input/event0),事件类型(0001),事件代码(0074)以及事件的值(00000001)。其中时间戳、类型、代码、值便是原始事件的四项基本元素。除时间戳外,其他三项元素的实际意义依照设备类型及厂商的不同而有所区别。在本例中,类型0x01表示此事件为一条按键事件,代码0x74表示电源键的扫描码,值0x01表示按下,0x00则表示抬起。这两条原始数据被输入系统包装成两个KeyEvent对象,作为两个按键事件派发给Framework中感兴趣的模块或应用程序。

注意一条原始事件所包含的信息量是比较有限的。而在Android API中所使用的某些输入事件,如触摸屏点击/滑动,包含了很多的信息,如XY坐标,触摸点索引等,其实是输入系统整合了多个原始事件后的结果。这个过程将在5.2.4节中详细探讨。

为了对原始事件有一个感性的认识,读者可以在运行getevent的过程中尝试一下其他的输入操作,观察一下每种输入所对应的设备节点及四项元素的取值。

输入设备的节点不仅在用户空间可读,而且是可写的,因此可以将将原始事件写入到节点中,从而实现模拟用户输入的功能。sendevent工具的作用正是如此。其用法如下:

sendevent <节点路径> <类型><代码> <值>

可以看出,sendevent的输入参数与getevent的输出是对应的,只不过sendevent的参数为十进制。电源键的代码0x74的十进制为116,因此可以通过快速执行如下两条命令实现点击电源键的效果:

adb shell sendevent /dev/input/event0 1 116 1 #按下电源键

adb shell sendevent /dev/input/event0 1 116 0 #抬起电源键

执行完这两条命令后,可以看到设备进入了休眠或被唤醒,与按下实际的电源键的效果一模一样。另外,执行这两条命令的时间间隔便是用户按住电源键所保持的时间,所以如果执行第一条命令后迟迟不执行第二条,则会产生长按电源键的效果——关机对话框出现了。很有趣不是么?输入设备节点在用户空间可读可写的特性为自动化测试提供了一条高效的途径。[1]

现在,读者对输入设备节点以及原始事件有了直观的认识,接下来看一下Android输入系统的基本原理。

  Android输入系统简介

上一节讲述了输入事件的源头是位于/dev/input/下的设备节点,而输入系统的终点是由WMS管理的某个窗口。最初的输入事件为内核生成的原始事件,而最终交付给窗口的则是KeyEvent或MotionEvent对象。因此Android输入系统的主要工作是读取设备节点中的原始事件,将其加工封装,然后派发给一个特定的窗口以及窗口中的控件。这个过程由InputManagerService(以下简称IMS)系统服务为核心的多个参与者共同完成。

输入系统的总体流程和参与者如图5-1所示。


图 5-1 输入系统的总体流程与参与者

图5-1描述了输入事件的处理流程以及输入系统中最基本的参与者。它们是:

·  Linux内核,接受输入设备的中断,并将原始事件的数据写入到设备节点中。

·  设备节点,作为内核与IMS的桥梁,它将原始事件的数据暴露给用户空间,以便IMS可以从中读取事件。

·  InputManagerService,一个Android系统服务,它分为Java层和Native层两部分。Java层负责与WMS的通信。而Native层则是InputReader和InputDispatcher两个输入系统关键组件的运行容器。

·  EventHub,直接访问所有的设备节点。并且正如其名字所描述的,它通过一个名为getEvents()的函数将所有输入系统相关的待处理的底层事件返回给使用者。这些事件包括原始输入事件、设备节点的增删等。

·  InputReader,I是IMS中的关键组件之一。它运行于一个独立的线程中,负责管理输入设备的列表与配置,以及进行输入事件的加工处理。它通过其线程循环不断地通过getEvents()函数从EventHub中将事件取出并进行处理。对于设备节点的增删事件,它会更新输入设备列表于配置。对于原始输入事件,InputReader对其进行翻译、组装、封装为包含了更多信息、更具可读性的输入事件,然后交给InputDispatcher进行派发。

·  InputReaderPolicy,它为InputReader的事件加工处理提供一些策略配置,例如键盘布局信息等。

·  InputDispatcher,是IMS中的另一个关键组件。它也运行于一个独立的线程中。InputDispatcher中保管了来自WMS的所有窗口的信息,其收到来自InputReader的输入事件后,会在其保管的窗口中寻找合适的窗口,并将事件派发给此窗口。

·  InputDispatcherPolicy,它为InputDispatcher的派发过程提供策略控制。例如截取某些特定的输入事件用作特殊用途,或者阻止将某些事件派发给目标窗口。一个典型的例子就是HOME键被InputDispatcherPolicy截取到PhoneWindowManager中进行处理,并阻止窗口收到HOME键按下的事件。

·  WMS,虽说不是输入系统中的一员,但是它却对InputDispatcher的正常工作起到了至关重要的作用。当新建窗口时,WMS为新窗口和IMS创建了事件传递所用的通道。另外,WMS还将所有窗口的信息,包括窗口的可点击区域,焦点窗口等信息,实时地更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派发到指定的窗口。

·  ViewRootImpl,对于某些窗口,如壁纸窗口、SurfaceView的窗口来说,窗口即是输入事件派发的终点。而对于其他的如Activity、对话框等使用了Android控件系统的窗口来说,输入事件的终点是控件(View)。ViewRootImpl将窗口所接收到的输入事件沿着控件树将事件派发给感兴趣的控件。

简单来说,内核将原始事件写入到设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件作出响应,更新自己的画面、执行特定的动作。所有这些参与者以IMS为核心,构建了Android庞大而复杂的输入体系。

Linux内核对硬件中断的处理超出了本书的讨论范围,因此本章将以IMS为重点,详细讨论除Linux内核以外的其他参与者的工作原理。


二、 代码讲解

2.1 输入系统的初始化,启动流程

先从java层的InputManagerService的创建开始,它是在SystemServer中创建:

            inputManager = new InputManagerService(context);

            Slog.i(TAG, "Window Manager");
            wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

            mActivityManagerService.setWindowManager(wm);

            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();

创建后,将自己传给windowManagerService, 然后调用了start函数。再来看看InputManagerService的构造函数:

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

        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());//注意这个mPtr是NativeInputManager对象指针

        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }

构造函数里面还是主要看下nativeInit这个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;
    }

    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,//注意传下来的serviceObj对象
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

nativeInit中主要是new了一个NativeInputManager对象,而NativeInputManager对象中的serviceObj是java层的InputManagerService对象。

而NativeInputManager对象的构造函数如下:

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

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

    {
        AutoMutex _l(mLock);
        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
    }

    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

创建了一个InputManager对象,并把自己传给了它。再来看看InputManager对象的构造函数:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);//前面把NativeInputManager的对象传了进来,就是dispatcherPolicy和readerPolicy
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

InputManager对象的构造函数中,创建了两个JNI层最重要的对象InputDispatcher,InputReader这两个对象有什么作用上面一节已经说过了。

initialize函数把reader和dispatch对象传进去了,建了两个线程。

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

2.2 InputReader InputDispatcher线程的run函数

初始化后就是start,java层的start比较简单。我们主要看下JNI的nativeStart

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.");
    }
}

ptr就是java层InputManagerService保存的mPtr,这就到InputManager的start函数了。

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;
}

InputManager的start函数没啥别的就是启动了两个线程。我们再来看看这两个线程:

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}

这种写法是ndk中的thread的写法,return true,就是继续循环。调用了InputReader的loopOnce函数:

void InputReader::loopOnce() {
............

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

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

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

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        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);
    }

    mQueuedListener->flush();
}

这个函数挑重点的讲,第一个重要的函数getEvents是读取每个设备的数据形成RawEvent,放入buffer中,如果没有输入事件,就epoll_wait函数阻塞等待。

因此InputReaderThread大部分的时间在epoll_wait上,有输入设备事件到来。唤醒线程,读数据封装成RawEvent,放在mEventBuffer中然后调用processEventsLocked。具体的getEvents函数就不看了,也比较长。

看下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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", 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;
    }
}

这个函数处理RawEvent分成两类,一类是设备发生变化的Event,包括增加,移除,扫描设备结束;

另一类是设备自身产生的Event,比如按键的Event。这类Event,会从RawEvent的mEventBuffer中不断读取出来然后调用processEventsForDeviceLocked函数处理。

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;
    }

    device->process(rawEvents, count);
}

processEventsForDeviceLocked函数根据deviceId得到InputDevice对象,然后调用其process函数

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
.........
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
.........
}

process函数处理RawEvent会对每个RawEvent调用它的所有的InputMaper对象的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函数:最后会调用getListener()->notifyKey(&args);

这边的这个listener是QueuedInputListener,而processKey最后调用的notifyKey,就是把数据保存在mArgsQueue中。

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

回到前面的loopOnce函数,最后调用了mQueuedInputListener->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();
}

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}
最后还是调用了InputDispatcher的notifyKey函数
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
...........

    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);

    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);//最后是调到phoneWindowManager中去对按键进行过滤
	

    bool needWake;
    { // acquire lock
        mLock.lock();

        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
				ALOGE("kangchen filterInputEvent return");
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        int32_t repeatCount = 0;
        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) {
		ALOGE("kangchen needWake.");
        mLooper->wake();
    }
}
这个函数我们也主要挑重点说,我们先看mPolicy->interceptKeyBeforeQueueing,这里的mPolicy就是nativeInputManager

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    // Policy:
    // - Ignore untrusted events and pass them along.
    // - Ask the window manager what to do with normal events and trusted injected events.
    // - For normal events wake and brighten the screen if currently off or dim.
    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,//直接是到phoneWindowManager中的interceptKeyBeforeQueueing函数
                    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;
        }
    }
}

再来看看handleInterceptActions函数

void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {//这位是1
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    } else {//也就是说wmActions如果没有WM_ACTION_PASS_TO_USER这标志位就不会传到应用的线程去了。
        ALOGD("kangchen handleInterceptActions: Not passing key to user.");
    }
}

再来看看phoneWindowManager中的interceptKeyBeforeQueueing函数中的一段,如果是power,result返回值就是~ACTION_PASS_TO_USER

            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
		    Log.i(TAG, "PowerKey down, interactive = " + interactive);
                    interceptPowerKeyDown(event, interactive);
                } else {
		    Log.i(TAG, "PowerKey up.");
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }

            case KeyEvent.KEYCODE_SLEEP: {
                result &= ~ACTION_PASS_TO_USER;
                if (!mPowerManager.isInteractive()) {
                    useHapticFeedback = false; // suppress feedback if already non-interactive
                }
                mPowerManager.goToSleep(event.getEventTime(),
                        PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                isWakeKey = false;
                break;
            }

            case KeyEvent.KEYCODE_WAKEUP: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = true;
                break;
            }
结合整个InputDispatcher的notifyKey函数会先调用到PhoneWindowManager,把返回值result的标志位~ACTION_PASS_TO_USER。后面如果是这个标志位应该就不会传给应用的线程了。(这段代码较多比较复杂就不分析了)。继续看InputDispatcher的notifyKey函数

        needWake = enqueueInboundEventLocked(newEntry);//enqueueInboundEventLocked函数把keyEntry放在mInboundQueue
        mLock.unlock();
    } // release lock

    if (needWake) {
		ALOGE("kangchen needWake.");
        mLooper->wake();
    }

最后再调用mLooper->wake()来唤醒线程。looper机制的话wake是往管道写数据,epoll_wait监听这个管道的read端。这样就把线程唤醒了。
这样InputReader就分析好了,下面再来看InputDispatcher


InputDispatcherThread线程开启后,一直调用mDispatcher->dispatchOnce

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

我们来看看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);//线程阻塞了,具体看looper机制。后面会分析
}

也就是InputReader线程有发送数据后调用mLooper的wake函数,唤醒InputDispatcher线程来发送给应用线程信息。其中dipatchOnceInnerLocked就是分发信息的。


调用dipatchOnceInnerLocked的流程较多,这里就列下调用关系:

dispatchOnce -> dispatchOnceInnerLocked -> dispatchKeyLocked -> dispatchEventLocked -> prepareDispatchCycleLocked -> enqueueDispatchEntriesLocked

最后再调到startDispatchCycleLocked函数,当然这其中肯定有把phoneWindowManager过滤的按键处理的过程,如果是被phoneWindowManager处理的按键就不会发送到应用线程那里了。

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
..........		
        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // Publish the key event.
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
        }
		........
		}
}

然后又调用了connection->inputPublisher.publishKeyEvent。我们再来跟下:


2.3 InputChannel的JNI层

这个connection从InputManagerService的registerInputChannel函数跟起:

    public void registerInputChannel(InputChannel inputChannel,
            InputWindowHandle inputWindowHandle) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null.");
        }

        nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
    }

在 nativeRegisterInputChannel函数中又调用了,NativeInputManager的registerInputChannel,然后又到InputDispatcher的registerInputChannel函数

 status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);

所以还是直接看

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
..........
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);//创建了一个connection

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }

        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();
    return OK;
}

registerInputChannel函数中创建了一个connection。然后加入了mConnectionsByFd列表中。也就是存在多个connection。而在dispatchKeyLocked的时候调用函数findFocusedWindowTargetsLocked得到拥有焦点窗口的inputChannel,然后调用getConnectionIndexLocked函数得到mConnectionsByFd列表中和inputChannel关联的Connection的index。

ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
    if (connectionIndex >= 0) {
        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
        if (connection->inputChannel.get() == inputChannel.get()) {
            return connectionIndex;
        }
    }

    return -1;
}

得到index也就得到了connection,在startDispatchCycleLocked中connection->inputPublisher.publishKeyEvent

status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {
...............

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

最后调用了mChannel->sendMessage,这个mChannel是穿件Connection对象时的参数。在nativeRegisterInputChannel函数中,创建Connection的时候传入的。而这个InputChannel在nativeRegisterInputChannel函数中创建,调用android_view_InputChannel_getInputChannel函数得到的inputChannel对象。

sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
    return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
}

android_view_InputChannel_getNativeInputChannel函数会JNI反调JAVA层InputManagerService中的mPtr,InputChannel的Java层对象就保存在mPtr中。

static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}

那最后是调用了java层的InputChannel对象的native层的NativeInputChannel来sendMessage。但是这个java层的InputChannel又是在哪创建的呢?看下面一节

2.4 InputChannel java层

我们先来看ViewRootImpl的setView方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();//新建Channel
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
                } 

setView方法中会新建一个InputChannel,然后通过mWindowSession.addToDisplay方法传到WindowManagerService中。最总会调用WMS的addWindow方法。

public int addWindow(Session session, IWindow client, int seq,
		WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
		Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
..............			
			if (outInputChannel != null && (attrs.inputFeatures
					& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
				String name = win.makeInputChannelName();
				InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
				win.setInputChannel(inputChannels[0]);
				inputChannels[1].transferTo(outInputChannel);

				mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
			}
................
}

addWindow方法中先调用了InputChannel.openInputChannelPair方法返回一个InputChannel的数组,然后将inputChannel[0]设置到win对象中。接着将inputChannel[1]装换成和客户端传过来的outInputChannel对象。最后再调用InputManagerService中的registerInputChannel方法。所以最后JNI层用的InputChannel就是这个inputChannel[0]。

那我们接下来看下openInputChannel方法:

    public static InputChannel[] openInputChannelPair(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name must not be null");
        }

        if (DEBUG) {
            Slog.d(TAG, "Opening input channel pair '" + name + "'");
        }
        return nativeOpenInputChannelPair(name);
    }

看JNI层

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    String8 name(nameChars);
    env->ReleaseStringUTFChars(nameObj, nameChars);

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

先来看看InputChannel::openInputChannelPair函数,这个方法调用了soketpair创建了一对sokcet。然后用这两个socket创建了两个InputChannel对象。一个serverChannel一个clientChannel。

status_t InputChannel::openInputChannelPair(const String8& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.string(), errno);
        outServerChannel.clear();
        outClientChannel.clear();
        return result;
    }

    int bufferSize = SOCKET_BUFFER_SIZE;
    setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));

    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

接下来继续拿这两个Channel各自新建了NativeInputChannel,然后传给android_view_InputChannel_createInputChannel方法:

static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
        NativeInputChannel* nativeInputChannel) {
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);
    if (inputChannelObj) {
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    }
    return inputChannelObj;
}

这个方法新建了一个java层的InputChannel对象,就是最后返回到java层的inputChannels[0]和inputChannels[1],然后再来看看android_view_InputChannel_setNativeInputChannel方法。

static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
        NativeInputChannel* nativeInputChannel) {
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}

原来这个方法就是把NativeInputChannel传给java层InputChannel的mPtr成员变量中去。也就是最后用来sendMessage的对象就是java层传下来的InputChannel对象中的mPtr成员。

简单理解就是把sokcetpair中的一个socket注册到JNI层,另一个给应用。

再来看看如何把inputChannels[1]传给应用的:

    public void transferTo(InputChannel outParameter) {
        if (outParameter == null) {
            throw new IllegalArgumentException("outParameter must not be null");
        }
        
        nativeTransferTo(outParameter);
    }

JNI层:

static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
        jobject otherObj) {
    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Other object already has a native input channel.");
        return;
    }

    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}

这个函数的功能就是把调用对象的mPtr中的值,放到参数对象的mPtr中,并把调用对象的mPtr置为NULL。

这样客户端调用完addToDisplay后,作为入参传递的outInputChannel将会被赋值。也就是另一个socket传到客户端了。

这样整个JNI层的两个thread,再到DispatchThread给应用发按键消息都通了。

JNI的两个Thread还是在SystemServer进程中的,而通过socketpair这种进程间的通信方式给客户端。


2.5 客户端的接收

前面JNI层最后调用了InputChannel的sendMessge,最后是通过socket发送的。那我们再来看看客户端的接收。

前面说客户端调用addToDisplay后得到InputChannel对象后,接着又创建了WindowInputEventReceiver对象。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
	.......
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
	.....

再来看看WindowInputEventReceiver父类的构造函数

    public InputEventReceiver(InputChannel inputChannel, Looper looper) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null");
        }
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }

        mInputChannel = inputChannel;
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }

看看nativeInit函数

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }

    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }

    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }

    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(receiver.get());
}

nativeInit函数取得了java层的InputChannel对象和MessageQueue在native层对象的指针,然后用它们创建了NativeInputEventReceiver对象,调用了NativeInputEventReceiver::initialize函数。

status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}

再来看看setFdEvents函数,把InputChannel里的fd放到looper中去,也就是将客户端的socket放入looper的epoll机制中,如果JNI层发通过socket发信息过来。就会用epoll机制唤醒线程,并且处理其事件。

void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}


下面我们来温习下looper机制:

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
............

    int epollEvents = 0;
    if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;

    { // acquire lock
        AutoMutex _l(mLock);

        Request request;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;

        struct epoll_event eventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);//将当前fd加入到epoll中
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}

addFd函数将参数fd加入epoll,然后在mRequests列表中加入一个Request对象,这个对象的events成员带有EPOLLIN标记,然后它的callBack成员变量指向NativeInputEventReceiver。以前分析looper机制的时候知道,线程处理消息循环会调用looper的poolOnce函数,而这个函数又会调用pollInner函数:

int Looper::pollInner(int timeoutMillis) {
..........
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);//事件到来
..........
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeReadPipeFd) {//管道事件,就是调用wakeup函数的
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
            }
        } else {//其它事件,就包括InputChannel的socket
            ssize_t requestIndex = mRequests.indexOfKey(fd);//获取request的index
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));//加入mResponses列表
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
............

    // Invoke all response callbacks.处理所有的mResponses元素
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            int callbackResult = response.request.callback->handleEvent(fd, events, data);//调用回调事件
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}

看上面代码的注释,把客户端socket的fd加入客户端线程消息机制中的epoll,当有事件过来,最总会调用回调。也就会调用NativeInputEventReceiver::handleEvent函数:

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
..........

    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
	..........
}

consumeEvents函数如下,先读取socket的信息然后反调java层WindowInputEventReceiver的dispatchInputEvent方法

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
......
    for (;;) {
	    uint32_t seq;
        InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,//读取InputChannel的socket信息
                consumeBatches, frameTime, &seq, &inputEvent);
		........
		      if (inputEventObj) {//反调java层的WindowInputEventReceiver的dispatchInputEvent方法
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
.....
}

这是父类的方法,然后再到子类WindowInputEventReceiver的onInputEvent方法

    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

WindowInputEventReceiver 类是在ViewRootImpl.java中是一个内部类:

    final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }

enqueueInputEvent函数

    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        Log.e(TAG, "kangchen enqueueInputEvent");
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

        // Always enqueue the input event in order, regardless of its time stamp.
        // We do this because the application or the IME may inject key events
        // in response to touch events and we want to ensure that the injected keys
        // are processed in the order they were received and we cannot trust that
        // the time stamp of injected events are monotonic.
        QueuedInputEvent last = mPendingInputEventTail;
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

这边都直接带过了,doProcessInputEvents函数中调用了deliverInputEvent函数

    void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            Log.e(TAG, "kangchen mPendingInputEventHead != null");
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            deliverInputEvent(q);
        }

        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
      

deliverInputEvent函数,进去上层InputState流程。

    private void deliverInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
        }

        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }

        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

InputState流程就不多分析了,处理流程从NativeRreImeInputStage -> ViewPreImeStage -> ImeStage -> EarlyPostImeStage -> NativePostImeStage -> ViewPostImeInputStage -> SyntheticInputStage。

而我们分析下ViewPostImeInputStage:

InputStage的deliver函数会调用onProcess函数,我们就分析下ViewPostImeInputStage的onProcess函数

        public final void deliver(QueuedInputEvent q) {
            if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                apply(q, onProcess(q));
            }
        }


    final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);//处理key事件
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {//触屏事件
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {//轨迹球
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }

processKeyEvent函数

        private int processKeyEvent(QueuedInputEvent q) {
            final KeyEvent event = (KeyEvent)q.mEvent;

            if (event.getAction() != KeyEvent.ACTION_UP) {
                // If delivering a new key event, make sure the window is
                // now allowed to start updating.
                handleDispatchDoneAnimating();
            }

            // Deliver the key to the view hierarchy.
            if (mView.dispatchKeyEvent(event)) {
                return FINISH_HANDLED;
            }
.....
}

processKeyEvent函数会调用mView.dispatchKeyEvent函数,如果一个应用调的话,会优先调到应用自己实现的Activity中。

下面随便举一个应用Activity的一个例子的一个dispatchKeyEvent函数:

    public boolean dispatchKeyEvent(KeyEvent event) {
        int curpos = mTrackList.getSelectedItemPosition();
        if (mPlaylist != null && !mPlaylist.equals("recentlyadded") && curpos >= 0 &&
                event.getMetaState() != 0 && event.getAction() == KeyEvent.ACTION_DOWN) {
            switch (event.getKeyCode()) {//对KeyEvent的keycode处理
                case KeyEvent.KEYCODE_DPAD_UP:
                    moveItem(true);
                    return true;
                case KeyEvent.KEYCODE_DPAD_DOWN:
                    moveItem(false);
                    return true;
                case KeyEvent.KEYCODE_DEL:
                    removeItem();
                    return true;
            }
        }

        return super.dispatchKeyEvent(event);//最后调用了父类的dispatchKeyEvent函数
    }

我们再来看看Acitivity的dispatchKeyEvent函数

    public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();

        // Let action bars open menus in response to the menu key prioritized over
        // the window handling it
        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }

        Window win = getWindow();
        if (win.superDispatchKeyEvent(event)) {
            return true;
        }
        View decor = mDecor;
        if (decor == null) decor = win.getDecorView();
        return event.dispatch(this, decor != null
                ? decor.getKeyDispatcherState() : null, this);
    }
再来看看KeyEvent的dispatch函数,这个receiver就只Activity本身,如果是应用调的话就是应用的Activity。
    public final boolean dispatch(Callback receiver) {
        return dispatch(receiver, null, null);
    }

    public final boolean dispatch(Callback receiver, DispatcherState state,
            Object target) {
        switch (mAction) {
            case ACTION_DOWN: {
                mFlags &= ~FLAG_START_TRACKING;
                if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
                        + ": " + this);
                boolean res = receiver.onKeyDown(mKeyCode, this);
                if (state != null) {
                    if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
                        if (DEBUG) Log.v(TAG, "  Start tracking!");
                        state.startTracking(this, target);
                    } else if (isLongPress() && state.isTracking(this)) {
                        try {
                            if (receiver.onKeyLongPress(mKeyCode, this)) {
                                if (DEBUG) Log.v(TAG, "  Clear from long press!");
                                state.performedLongPress(this);
                                res = true;
                            }
                        } catch (AbstractMethodError e) {
                        }
                    }
                }
                return res;
            }
            case ACTION_UP:
                if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
                        + ": " + this);
                if (state != null) {
                    state.handleUpEvent(this);
                }
                return receiver.onKeyUp(mKeyCode, this);
            case ACTION_MULTIPLE:
                final int count = mRepeatCount;
                final int code = mKeyCode;
                if (receiver.onKeyMultiple(code, count, this)) {
                    return true;
                }
                if (code != KeyEvent.KEYCODE_UNKNOWN) {
                    mAction = ACTION_DOWN;
                    mRepeatCount = 0;
                    boolean handled = receiver.onKeyDown(code, this);
                    if (handled) {
                        mAction = ACTION_UP;
                        receiver.onKeyUp(code, this);
                    }
                    mAction = ACTION_MULTIPLE;
                    mRepeatCount = count;
                    return handled;
                }
                return false;
        }
        return false;
    }
最总回到应用Activity的onKeyDown,onKeyUp中去。

如此整个按键流程从InputManagerService到JNI到应用分析完了。





版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

Android6.0 按键流程(五)无线鼠标右键无效 -- hal层

最近在我们的手机上碰到这样一个问题,用otg连接无线鼠标,发现右键没反应。 于是先用getEvent命令:查看其扫描码,然后再去kl文件中,结果没找到这个扫描码对应的键盘码。 于是结合之前分析的按...

android Generic.kcm的使用

最近项目要用到组合键打开一个apk的功能,大家都知道,如果是单个键盘打开APK,那就很简单了,比如按F11打开浏览器,或者其他,自己 Generic.kl中增加,并在上层做简单的修改就行了,但是组合键...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

e科士威创业这样一个绝佳的创业机会

很庆幸能遇到e科士威创业这样一个绝佳的创业机会,"个人创业"是当今很流行的一个词,可是传统行业的生意,动轧几万几十万的创业资本,使得那些有创业理想可是囊中羞涩的年轻人望而却步。 天降大任于斯人也,必先...

android添加按键(二) 添加按键流程、出现问题

Android 系统对加载键盘布局文件(Generic.kl ). 在 开发板的文件目录 /system/usr/idc 中有一系列的 *.idc  框架层会根据驱动中的配置,自动生成设备的idc 文...

Android之硬键盘字符映射------KeyCharacterMap

Android之硬键盘映射----字符映射KeyCharacterMap 预备基础知识: http://source.android.com/porting/keymaps_keyboard_...

价格大战局面,沈阳餐饮业如何应对?

最近,沈阳团购网站负责人告诉记者,沈阳餐饮各大商家已经开始主动要求参加入沈阳团购网,从春节到现在为止,我们网站已经接单了几千个餐饮团购。与过去不同的是,商家主动给出了较低的折扣。   沈阳大部分餐饮...

Android6.0 按键kl文件加载过程分析

在之前按键过程分析的几篇博客中,我分析过关于按键kl文件的加载,但是讲的不是非常详细,这篇博客主要把kl文件加载过程单独拉出来分析下。 1. 获取InputDeviceIdentifier的nam...

android 添加按键(一) kl文件 kcm文件

在Android 系统中定义了大量的物理和虚拟按键,比如返回键,home键,数字键等。   在添加按键之前,需要了解Andorid 系统的按键软件架构,以物理按键为例。   1. 驱动层       ...

android kl文件

android kl(key layout)文件是一个映射文件,是标准linux与anroid的键值映射文件,kl文件可以有很多个,但是它有一个使用优先级:/system/usr/keylayout/...

android input进程(模拟按键)

我们可以在手机adb shell中,使用input来模拟按键,和之前的sm类似,input也是一个进程,在framework/base/cmds目录下。 一、Input源码 下面我们先看下input的...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)