1. 程式人生 > >InputDispatcher中按鍵分發之notifyKey之後流程詳解

InputDispatcher中按鍵分發之notifyKey之後流程詳解

  該篇文章僅分析notifyKey之後的流程,InputReader怎麼讀取之類的本文不關心.本文重點關注InputDispatcher和java層的互動,包括呼叫interceptKeyBeforeQueueing和interceptKeyBeforeDispatching方法,以及有輔助服務攔截按鍵時的處理.

1.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);

    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, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

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

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

  主函式中有四個重要的呼叫,我們需要分別分析:

  1.mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);//最終會呼叫到PWM中對應的方法

  2.shouldSendKeyToInputFilterLocked(args)//和輔助服務相關

  3.needWake = enqueueInboundEventLocked(newEntry);//把按鍵加入佇列

  4.mLooper->wake();//喚醒執行緒,從佇列中去除按鍵事件執行分發

  首先分析第一步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.
    bool interactive = mInteractive.load();
    if (interactive) {
        policyFlags |= POLICY_FLAG_INTERACTIVE;
    }
    if ((policyFlags & POLICY_FLAG_TRUSTED)) {
        nsecs_t when = keyEvent->getEventTime();
        JNIEnv* env = jniEnv();
        jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
        jint wmActions;
        if (keyEventObj) {
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }

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

  其中會主要是呼叫env->CallIntMethod(mServiceObj,gServiceClassInfo.interceptKeyBeforeQueueing,keyEventObj, policyFlags).該方法會呼叫InputManagerService的interceptKeyBeforeQueueing,InputManagerService又會呼叫PhoneWindowManager的interceptKeyBeforeQueueing,然後會有一個返回值賦給wmActions,而接下來handleInterceptActions會根據wmActions的值來更新policyFlags:

void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    } else {
#if DEBUG_INPUT_DISPATCHER_POLICY
        ALOGD("handleInterceptActions: Not passing key to user.");
#endif
    }
}

  也就意味著我們在PhoneWindowManager中可以通過返回值來控制policyFlags的值,事件分發過程中多次判斷該值,決定要不要繼續分發.

  第二步shouldSendKeyToInputFilterLocked,當有輔助服務配置接收按鍵的話,該方法返回true,進入判斷,然後呼叫如下程式碼:

            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

  邏輯很簡單,呼叫NativeInputManager的filterInputEvent方法,如果該返回值返回false則notifyKey方法直接返回,實際上只要shouldSendKeyToInputFilterLocked走進來的話,如果沒有異常產生filterInputEvent總是返回false的,那麼事件怎麼分發傳遞的?這就是我們接下來要分析的:

bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
    // The callee is responsible for recycling the event.
    jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent,
            inputEventObj, policyFlags);
    if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) {
        pass = true;
    }
    env->DeleteLocalRef(inputEventObj);
    return pass;
}

  可以看出沒有異常發生的話,返回的值為InputManagerService中返回的值:

    // Native callback.
    final boolean filterInputEvent(InputEvent event, int policyFlags) {
        synchronized (mInputFilterLock) {
            if (mInputFilter != null) {
                try {
                    mInputFilter.filterInputEvent(event, policyFlags);
                } catch (RemoteException e) {
                    /* ignore */
                }
                return false;
            }
        }
        event.recycle();
        return true;
    }

  在InputManagerService中,可以看到只要mInputFilter不為空就返回false,而上篇文章分析過了,如果系統存在接收按鍵的輔助服務,AccessibilityManagerService會呼叫setInputFilter,會更新mInputFilter,該值不會為空.所以可以得出結論,只要有接收按鍵的輔助服務,則notifyKey不會傳遞,但是,實際情況按鍵還是有效的,到底哪裡把這些按鍵傳遞了呢?我們回到上篇文章分析過得mInputManager.setInputFilter(filter)方法裡面去:

    public void setInputFilter(IInputFilter filter) {

            if (filter != null) {
                mInputFilter = filter;
                mInputFilterHost = new InputFilterHost();
                try {
                    filter.install(mInputFilterHost);
                } catch (RemoteException re) {
                    /* ignore */
                }
            }

            nativeSetInputFilterEnabled(mPtr, filter != null);
    }

  我們看見,有一個filter.install(mInputFilterHost)的操作,奧祕就在這裡,InputFilter的filterInputEvent方法同時會呼叫到InputFilterHost的sendInputEvent方法,其中的呼叫讀者可以自己去看,限於篇幅,不作分析,很簡單.

        @Override
        public void sendInputEvent(InputEvent event, int policyFlags) {
            if (event == null) {
                throw new IllegalArgumentException("event must not be null");
            }

            synchronized (mInputFilterLock) {
                if (!mDisconnected) {
                    nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
                            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
                            policyFlags | WindowManagerPolicy.FLAG_FILTERED);
                }
            }
        }

  謎底已經揭開,nativeInjectInputEvent最終回撥用InputDispatcher的injectInputEvent方法,該方法和notifyKey有相同的呼叫,就是我們上面分析的3,4步

  3.needWake = enqueueInboundEventLocked(newEntry);//把按鍵加入佇列

  4.mLooper->wake();//喚醒執行緒,從佇列中去除按鍵事件執行分發

  第三步比較簡單,只是呼叫mInboundQueue.enqueueAtTail(entry),把事件放入佇列.

  重點是第四步,相當複雜,開頭就很複雜mLooper->wake()怎麼就能喚醒執行緒了呢?這個我們要細細捋一捋了,裡面牽扯到native的執行緒和looper的pollOnce,wake.

  SystemServer在啟動的時候會去建立InputManagerService,在InputManagerService的建構函式中回撥用nativeInit來初始化native層的InputManager,最終呼叫的的程式碼如下:  

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

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

  其中建立了一個InputDispatcherThread物件

(未完待續)