浅谈framework之InputManagerService_星空梦想plus的博客-程序员ITS301

技术标签: android系统学习轨迹  

1. 前言

InputManagerService为书写方便,下文皆简称为IMS。
IMS在前面的文章有一笔带过,这篇文章重点来学习一下这个系统服务,也是对自己学习过程的一个记录,下文时序图由visio工具所绘制。(主要还是因为想了解特殊键值的处理过程,以方便理解键值定制原理,哈哈哈哈~)

2. 服务的启动

说到IMS,这里还是从服务的启动开始讲起,依然是老生常谈的模式,它是由SystemServer在开机的时候给拉起来的:
/frameworks/base/services/java/com/android/server/SystemServer.java

 inputManager = new InputManagerService(context, wmHandler);  //1
 ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); //2
 inputManager.start();  //3

3. 特殊按键的处理

特殊键值这里指的是遥控器的按键,通常做系统开发可能都会知道,遥控器的键值信号数据,会通过信号接收器,通过底层一层一层映射到上层framwork进行处理。

代码位置:framworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
键值入队之前处理,处理函数:interceptKeyBeforeQueueing
键值分发之前处理,处理函数:interceptKeyBeforeDispatching

interceptKeyBeforeQueueing函数已处理了绝大多数的键值事件,而interceptKeyBeforeDispatching函数主要处理HOME,菜单,搜索等这几个键值。

下面将会从IMS一步步调用到这两个函数分别进行说明。

3.1 interceptKeyBeforeQueueing

上面代码我已截取到关键部分,首先来看注释1处代码,然后我就跳转到InputManagerService类的构造函数中:
/frameworks/base/services/java/com/android/server/input/InputManagerService.java

    public InputManagerService(Context context, Handler handler) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(handler.getLooper());

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

然后直接看关键点,注释4处的代码,可想而知是调用了本地nativeInit函数:

    private static native int nativeInit(InputManagerService service,
            Context context, MessageQueue messageQueue);

我找到它的实现所在:
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp

static jint 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,
            messageQueue->getLooper());  //5
    im->incStrong(0);
    return reinterpret_cast<jint>(im);
}

找到这个函数是根据jni知识,不懂的话可以后面去补一下这部分知识,这里直接看上面函数实现注释5处代码,可以看到它实例化了一个NativeInputManager对象,那我先去找找这个类的构造实现:

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper) {
    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();  //6
    mInputManager = new InputManager(eventHub, this, this);  //7
}

在上面的函数中有两点需要注意的,其中注释6处是实例化了一个EventHub对象,这个后面会讲到它的作用,那我们继续往下看注释7中InputManager这个类的构造函数实现:
frameworks/base/services/input/InputManager.cpp

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);   //8
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);  //9
    initialize();  //10
}

这里先看注释10处的函数实现代码:

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

这里回到注释3处看调用的start函数实现代码:


    public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();

        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                updatePointerSpeedFromSettings();
                updateShowTouchesFromSettings();
            }
        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);

        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
    }

上面代码的关键处其实就是调用到nativeStart函数,这个


static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

可以看到这里InputManagerService保存的mPtr传下来的变量强制转成NativeInputManager,调用调用器getInputManager函数,也就是其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;
}

这里可以看到,注释11,12的两个线程实际是在这里被启动了。先来看注释11处的代码,由于调用了线程的run方法,则最终会调用到下面的函数:

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

再然后就是loopOnce函数的实现:

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        AutoMutex _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

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

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

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

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

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

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }

    mQueuedListener->flush();   //14
}

注释12处代码就是从EventHub监控并读取/dev/input下的数据,初步封装成rawEventData给注释13处的函数处理,并在注释14处函数开始分发键值。

这里直接先看注释14处的代码:

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);  //15
        delete args;
    }
    mArgsQueue.clear();
}

关键代码为注释15,最终调用的是NotifyKeyArgs的notify函数:

void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

而这个listener,是新建QueuedInputListener的时候传进来的

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
}

listener是InputReader里面传过来的,并且我们知道InputReader是在InputManager中创建的

我们看下InputManager的构造函数,发现传进来的是InputDispatcher

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

所以最后这个listener是InputDispatcher,因此最后就是调用的InputDispatcher的notifyKey函数。

这个listener实际上就是InputDispatcher,故:

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS
    ALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, "
            "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",
            args->eventTime, args->deviceId, args->source, args->policyFlags,
            args->action, args->flags, args->keyCode, args->scanCode,
            args->metaState, args->downTime);
#endif
    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;
    }
    if (policyFlags & POLICY_FLAG_ALT) {
        metaState |= AMETA_ALT_ON | AMETA_ALT_LEFT_ON;
    }
    if (policyFlags & POLICY_FLAG_ALT_GR) {
        metaState |= AMETA_ALT_ON | AMETA_ALT_RIGHT_ON;
    }
    if (policyFlags & POLICY_FLAG_SHIFT) {
        metaState |= AMETA_SHIFT_ON | AMETA_SHIFT_LEFT_ON;
    }
    if (policyFlags & POLICY_FLAG_CAPS_LOCK) {
        metaState |= AMETA_CAPS_LOCK_ON;
    }
    if (policyFlags & POLICY_FLAG_FUNCTION) {
        metaState |= AMETA_FUNCTION_ON;
    }

    policyFlags |= POLICY_FLAG_TRUSTED;

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

    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); //16

    if (policyFlags & POLICY_FLAG_WOKE_HERE) {
        flags |= AKEY_EVENT_FLAG_WOKE_HERE;
    }

    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* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, args->keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

上面到注释16处mPolicy实际上是NativeInputManager对象,所以最终通过下面的interceptKeyBeforeQueueing函数实现,实现反射调用到java层的interceptKeyBeforeQueueing函数进行键值处理的操作:


void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime();
        bool isScreenOn = this->isScreenOn();
        bool isScreenBright = this->isScreenBright();

        JNIEnv* env = jniEnv();
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags, isScreenOn);
            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;
        }

        if (!(policyFlags & POLICY_FLAG_INJECTED)) {
            if (!isScreenOn) {
                policyFlags |= POLICY_FLAG_WOKE_HERE;
            }

            if (!isScreenBright) {
                policyFlags |= POLICY_FLAG_BRIGHT_HERE;
            }
        }

        handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
    } else {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    }
}

3.2 interceptKeyBeforeDispatching

我们再回到注释12处代码,来分析启动java处interceptKeyBeforeDispatching函数的过程。

同理会走到InputDispatcherThread的threadLoop:

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

下一步则跳转到InputDispatcher类的dispatchOnce函数:


void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        AutoMutex _l(mLock);
        //唤醒等待线程,monitor()用于监控dispatcher是否发生死锁
        mDispatcherIsAliveCondition.broadcast();
        if (!haveCommandsLocked()) {
            //当mCommandQueue不为空时处理
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    }
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    //进入epoll_wait
    mLooper->pollOnce(timeoutMillis);
}

当线程被唤醒的时候,并且mCommandQueue不为空的情况下执行dispatchOnceInnerLocked函数


void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    if (!mPolicy->isKeyRepeatEnabled()) {
        resetKeyRepeatLocked();
    }
    if (mDispatchFrozen) {
#if DEBUG_FOCUS
        ALOGD("Dispatch frozen.  Waiting some more.");
#endif
        return;
    }

    // Optimize latency of app switches.
    // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has
    // been pressed.  When it expires, we preempt dispatch and drop all other pending events.
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    // Ready to start a new event.
    // If we don't already have a pending event, go grab one.
    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);
        }

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

由于这里分析的是特殊键值的处理过程,故上面会走switch的EventEntry::TYPE_KEY分支,并最终会调用到dispatchKeyLocked


bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
			......
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            ......
}

上面代码已经大量删减,直接看下面:


void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
        CommandEntry* commandEntry) {
    KeyEntry* entry = commandEntry->keyEntry;

    KeyEvent event;
    initializeKeyEvent(&event, entry);

    mLock.unlock();

    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);

    mLock.lock();

    if (delay < 0) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry->interceptKeyWakeupTime = now() + delay;
    }
    entry->release();
}

然后又是相同套路,即调用了NativeInputManager类的interceptKeyBeforeDispatching函数,具体实现如下:


nsecs_t NativeInputManager::interceptKeyBeforeDispatching(
        const sp<InputWindowHandle>& inputWindowHandle,
        const KeyEvent* keyEvent, uint32_t policyFlags) {
    nsecs_t result = 0;
    if (policyFlags & POLICY_FLAG_TRUSTED) {
        JNIEnv* env = jniEnv();

        // Note: inputWindowHandle may be null.
        jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle);
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        if (keyEventObj) {
            jlong delayMillis = env->CallLongMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeDispatching,
                    inputWindowHandleObj, keyEventObj, policyFlags);
            bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching");
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
            if (!error) {
                if (delayMillis < 0) {
                    result = -1;
                } else if (delayMillis > 0) {
                    result = milliseconds_to_nanoseconds(delayMillis);
                }
            }
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
        }
        env->DeleteLocalRef(inputWindowHandleObj);
    }
    return result;
}

通过上面的一些函数跳转,最终通过反射调用到java处的interceptKeyBeforeDispatching函数。

4. 普通按键的处理

普通按键这里指的是点击屏幕时候的普通按钮产生的事件,这里直接从屏幕点击事件传递上面dispatchOnceInnerLocked函数中,EventEntry::TYPE_MOTION switch分支进行说明:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
     case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        ...
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
}

可以看到实际处理触摸事件的是dispatchMotionLocked函数:

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    Vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {
        //关键点1: 找到目标Window
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    ...
    //关键点2: 消息派发到目标窗口中
    dispatchEventLocked(currentTime, entry, inputTargets);  //20
    return true;
}

触摸事件会首先通过findTouchedWindowTargetsLocked()函数找到目标Window,进而通过dispatchEventLocked()将消息发送到目标窗口。

/frameworks/native/services/inputflinger/InputDispatcher.cpp

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    enum InjectionPermission {
        INJECTION_PERMISSION_UNKNOWN,
        INJECTION_PERMISSION_GRANTED,
        INJECTION_PERMISSION_DENIED
    };
 
    nsecs_t startTime = now();
 
    ......
 
    // Ensure all touched foreground windows are ready for new input.
    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
        const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
            // Check whether the window is ready for more input.
            String8 reason = checkWindowReadyForMoreInputLocked(currentTime,
                    touchedWindow.windowHandle, entry, "touched");
            if (!reason.isEmpty()) {
                injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                        NULL, touchedWindow.windowHandle, nextWakeupTime, reason.string());
                goto Unresponsive;
            }
        }
    }
    ......
    for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
        const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                touchedWindow.pointerIds, inputTargets);
    }
 
    ......
    return injectionResult;
}

获取能够处理这个事件的forceground window,如果这个window不能够继续处理事件,就是说这个window的主线程被某些耗时操作占据,我们继续看handleTargetsNotReadyLocked这个方法。

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
    const EventEntry* entry,
    const sp<InputApplicationHandle>& applicationHandle,
    const sp<InputWindowHandle>& windowHandle,
    nsecs_t* nextWakeupTime, const char* reason) {
    if (applicationHandle == NULL && windowHandle == NULL) {
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
            mInputTargetWaitStartTime = currentTime; //当前时间
            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
            mInputTargetWaitTimeoutExpired = false;
            mInputTargetWaitApplicationHandle.clear();
        }
    } else {
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
            nsecs_t timeout;
            if (windowHandle != NULL) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; // 5s
            }

            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime; //当前时间
            mInputTargetWaitTimeoutTime = currentTime + timeout;
            mInputTargetWaitTimeoutExpired = false;
            mInputTargetWaitApplicationHandle.clear();

            if (windowHandle != NULL) {
                mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
            }
            if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) {
                mInputTargetWaitApplicationHandle = applicationHandle;
            }
        }
    }

    if (mInputTargetWaitTimeoutExpired) {
        return INPUT_EVENT_INJECTION_TIMED_OUT; //等待超时已过期,则直接返回
    }

    //当超时5s则进入ANR流程
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);

        *nextWakeupTime = LONG_LONG_MIN; //强制立刻执行轮询来执行ANR策略
        return INPUT_EVENT_INJECTION_PENDING;
    } else {
        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
            *nextWakeupTime = mInputTargetWaitTimeoutTime; //当触发超时则强制执行轮询
        }
        return INPUT_EVENT_INJECTION_PENDING;
    }
}

findTouchedWindowTargetsLocked,如果没有发生ANR,则addWindowTargetLocked()将该事件添加到inputTargets。

void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
    inputTargets.push();

    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget& target = inputTargets.editTop();
    target.inputChannel = windowInfo->inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.scaleFactor = windowInfo->scaleFactor;
    target.pointerIds = pointerIds;
}

将当前聚焦窗口mFocusedWindowHandle的inputChannel传递到inputTargets。

回到注释20处的代码,看消息在目标窗口的传递过程:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    //向mCommandQueue队列添加doPokeUserActivityLockedInterruptible命令
    pokeUserActivityLocked(eventEntry);

    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);
        //
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            //找到目标连接
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        }
    }
}

代码逐层往下看会发现最后会调用到InputChannel的sendMessage函数,最会通过socket发送到APP端(Socket怎么来的接下来会分析)

在这里插入图片描述
这个Socket是怎么来的呢?或者说两端通信的一对Socket是怎么来的呢?其实还是要牵扯到WindowManagerService,在APP端向WMS请求添加窗口的时候,会伴随着Input通道的创建,窗口的添加一定会调用ViewRootImpl的setView函数:

/frameworks/native/services/inputflinger/ViewRootImpl.cpp

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
                ...
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    //创建InputChannel容器    
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    //添加窗口,并请求开辟Socket Input通信通道
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } ...
                //监听,开启Input信道
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

在IWindowSession.aidl定义中 InputChannel是out类型,也就是说需要服务端进行填充,那么接着看服务端WMS如何填充的呢?

public int addWindow(Session session, IWindow client, int seq,
        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
        InputChannel outInputChannel) {            
          ...
        if (outInputChannel != null && (attrs.inputFeatures
                & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
            String name = win.makeInputChannelName();
            <!--关键点1创建通信信道 -->
            InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
            <!--本地用-->
            win.setInputChannel(inputChannels[0]);
            <!--APP端用-->
            inputChannels[1].transferTo(outInputChannel);
            <!--注册信道与窗口-->
            mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
        }

WMS首先创建socketPair作为全双工通道,并分别填充到Client与Server的InputChannel中去;之后让InputManager将Input通信信道与当前的窗口ID绑定,这样就能知道哪个窗口用哪个信道通信了;最后通过Binder将outInputChannel回传到APP端,下面是socketPair的创建代码:

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;
        ...
        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));
    <!--填充到server inputchannel-->
    String8 serverChannelName = name;
    serverChannelName.append(" (server)");
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
     <!--填充到client inputchannel-->
    String8 clientChannelName = name;
    clientChannelName.append(" (client)");
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

这里socketPair的创建与访问其实是还是借助文件描述符,WMS需要借助Binder通信向APP端回传文件描述符fd,这部分只是可以参考Binder知识,主要是在内核层面实现两个进程fd的转换,窗口添加成功后,socketpair被创建,被传递到了APP端,但是信道并未完全建立,因为还需要一个主动的监听,毕竟消息到来是需要通知的,先看一下信道模型
在这里插入图片描述
APP端的监听消息的手段是:将socket添加到Looper线程的epoll数组中去,一有消息到来Looper线程就会被唤醒,并获取事件内容,从代码上来看,通信信道的打开是伴随WindowInputEventReceiver的创建来完成的。

在这里插入图片描述
信息到来,Looper根据fd找到对应的监听器: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;
    }
  ...

之后会进一步读取事件,并封装成Java层对象,传递给Java层,进行相应的回调处理:

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,  
                consumeBatches, frameTime, &seq, &inputEvent);  
        ...
        <!--处理touch事件-->
      case AINPUT_EVENT_TYPE_MOTION: {
        MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
        if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
            *outConsumedBatch = true;
        }
        inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
        break;
        } 
        <!--回调处理函数-->
       if (inputEventObj) {
                    env->CallVoidMethod(receiverObj.get(),
                            gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                    env->DeleteLocalRef(inputEventObj);
                }

所以最后就是触摸事件被封装成了inputEvent,并通过InputEventReceiver的dispatchInputEvent(WindowInputEventReceiver)进行处理,这里就返回到我们常见的Java世界了。

/frameworks/base/core/java/android/view/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);
        }
   ...     
  }
  void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
       ...
        if (processImmediately) {
            doProcessInputEvents();
        }
        ...
  }
  void doProcessInputEvents() {
      ...
      deliverInputEvent(q);
      ...
  }
  private void deliverInputEvent(QueuedInputEvent q) {
        ...
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }

        if (stage != null) {
            stage.deliver(q);
        } 
        ...
   }
   final class ViewPostImeInputStage extends InputStage {
        ...
        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                // If delivering a new non-key event, make sure the window is
                // now allowed to start updating.
                handleDispatchWindowAnimationStopped();
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                }
                ...
            }
        }
        ...
        private int processPointerEvent(QueuedInputEvent q) {
            ...
            boolean handled = mView.dispatchPointerEvent(event);
            ...
        }
    }     

processPointerEvent()方法通过调用mView的dispatchPointerEvent()方法来做进一步的操作,变量mView是一个DecorView类型的对象,它的父类View来实现这个方法,这个调用最终就会进入它的父类View的dispatchPointerEvent()方法中。

/frameworks/base/core/java/android/view/View.java

public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
}

整个过程参考网图如下,我就不画了,比较形象生动:
在这里插入图片描述

5. 总结

上面所述还不够完善,可以通读几遍,仅仅为一个理解思路。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/angelsmiling/article/details/103248039

智能推荐

【产品】 产品设计:ID设计和MD设计详解_产品人卫朋的博客-程序员ITS301_md设计

1、ID(Industry Design)工业设计包含外观、材质、手感、颜色配搭,主要界面的实现与及色彩等方面的设计。2、MD(Mechanical Design)结构设计以手机为例:前壳、后壳、手机的摄像镜头位置的选择、固定的方式、电池如何连接和手机的厚薄程度等。如果是滑盖手机,如何让手机滑上去,怎样实现自动往上弹,SIM卡怎样插和拔的安排,这些都是手机结构设计的范畴。繁...

oracle ora 604,ORA-600 [17285] Error Followed By ORA-604 and/or ORA-7445 Errors_吴季玄的博客-程序员ITS301

:ORA-600 [17285] Error Followed By ORA-604 and/or ORA-7445 Errors:464607.1类型:PROBLEM上次修订日期:02-MAR-2009状态:MODERATEDIn this DocumentThis document is being delivered to you via Oracle Support's(RaV) proc...

网络编程——InetAddress类_煎丶包的博客-程序员ITS301_inetaddress类

InetAddress类InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取InetAddress实例public static InetAddress getLocalHost()public static InetAddress getByName(String host)InetAddress提供了如下几个常用的方法:public String getHostAddress():返回 IP 地址字符串(以文本表现形式)。public String getHo

《PHASEN:A Phase and Harmonics-Aware Speech Enhancement Network》Pytorch代码学习_qq_39108486的博客-程序员ITS301

PHASEN结构源码地址:https://github.com/huyanxin/phasenPHASEN是一个双流网络,其中幅值流和相位流分别专门用于幅值和相位预测。幅值流主要由卷积操作,频域变换模块(FTB,后文介绍)以及双向 LSTM 组成,而相位流为纯卷积网络。强度流的预测结果为幅值掩膜 M,其取值为正的实数,相位流的预测结果是相位谱。具体细节不一一介绍,直接从每个模块的代码入手。...

Java中定时任务的6种实现方式,你知道几种?_程序新视界的博客-程序员ITS301_java定时任务实现方式

几乎在所有的项目中,定时任务的使用都是不可或缺的,如果使用不当甚至会造成资损。还记得多年前在做金融系统时,出款业务是通过定时任务对外打款,当时由于银行接口处理能力有限,外加定时任务使用不当,导致发出大量重复出款请求。还好在后面环节将交易卡在了系统内部,未发生资损。所以,系统的学习一下定时任务,是非常有必要的。这篇文章就带大家整体梳理学习一下Java领域中常见的几种定时任务实现。线程等待实现先从最原始最简单的方式来讲解。可以先创建一个thread,然后让它在while循环里一直运行着,通过sleep方法

DeepLearning | 经典卷积神经网络Alex_Net (完整模型与tensorflow代码讲解,附数据集)_Liangjun_Feng的博客-程序员ITS301_alex卷积神经网络模型

最近复现了几个经典的卷积网络,拿自己的数据集试了了试,不得不承认的是卷积神经网络在图像处理方面确实有着得天独厚的优势,写这篇博客讲述早期最经典的卷积神经网络Alex_Net以及附上一些自己的理解2012年,Hinton的学生在ILSVRC 2012中以显著的优势赢得了比赛,top-5的错误率降低至了16.4%,相比于第二名的26.2%有了巨大的提升,这可以说是神经网络在低谷期以后的第一次发声,...

随便推点

wifipineapple插件:codeInject的使用_dianyan5615的博客-程序员ITS301

  如果在中间人攻击的时候, 能在用户的网页中插入任意代码, 可以说已经完成一半了  wifipineapple有个codeInject插件, 可以让连上这个网络所有设备浏览网页的时候, 在网页中插入任意html代码  但是目前我用的wifipineapple版本是MK4, 官方很久不维护这个版本的wifipineapple  使用scp把本地的codeinject传到wif...

十大企业级Linux服务器安全防护要点_click_idc的博客-程序员ITS301

随着开源系统Linux的盛行,其在大中型企业的应用也在逐渐普及,很多企业的应用服务都是构筑在其之上,例如Web服务、数据库服务、集群服务等等。因此,Linux的安全性就成为了企业构筑安全应用的一个基础,是重中之重,如何对其进行安全防护是企业需要解决的一个基础性问题,基于此,本文将给出十大企业级Linux服务器安全防护的要点。1、强化:密码管理设定登录密码是一项非常重要的安全措施,如果用户的密码设定...

Linux下MySQL服务的安装与配置_田哥coder的博客-程序员ITS301_linux mysql服务

安装MySQL服务因MySQL数据库在Linux上使用率很高,所以目前主流的Linux系统版本基本都默认安装了MySQL,但由于版本的问题,建议删除预装的旧版本,再行安装新的版本以及配套的辅助工具。要使用MySQL服务,必须安装以下数据包:mysql.x86_64mysql-server.x86_64mysql-devel.x86_64MySQL服务的配置文件及相关目...

Kotlin系列之1-Kotlin中的JvmField JvmStatic_源源配蔡的博客-程序员ITS301

官方的说明如下伴生函数:在 “companion object” 中的公共函数必须用使用 @JvmStatic 注解才能暴露为静态方法。如果没有这个注解,这些函数仅可用作静态 Companion 字段上的实例方法。伴生常量:在 companion object 中的公共、非 const 的属性 实际上为常量 必须用 @JvmField 注解才能暴露为静态字段。如果没有这个注解,这些属性只能作为静态 Companion 字段中奇怪命名的 ‘getters’ 实例。而只使用 @JvmSt

Ubuntu apt 安装 nodejs_迦蓝叶的博客-程序员ITS301_apt install nodejs

安装nodejs在windows下,都是到Nodejs 官方网站上下载压缩包。然后在环境变量中配置Nodejs 的环境变量,但是到了Ubuntu下 这种情况可能会改变,因为有强大的APT包管理器。

推荐文章

热门文章

相关标签