1. 程式人生 > >Android按鍵事件傳遞流程(一)

Android按鍵事件傳遞流程(一)

做Android開發的少不了對觸控、按鍵事件進行處理,對於手機來說,主要是手勢移動、觸控按下等,而TV主要通過遙控器、按鍵操作,按鍵事件不同於觸控事件,必須先獲得焦點,然後才能移動、選擇。

Android輸入裝置支援滑鼠、鍵盤(按鍵)、觸控式螢幕(單點、多點)、軌跡球等,這些裝置所產生的輸入事件Input Event從底層驅動開始經過input子系統核心層到達Event Handler事件層,最終把事件copy_to_user到使用者空間,然後由使用者空間層獲取這些事件進行分發、傳遞。整個過程涉及到核心層、Framework層以及應用層,核心層傳遞過程不在本文研究範圍內,本文主要對按鍵事件在Framework層、應用層的傳遞過程進行分析(本文基於Android5.1.1版本),帶著問題出發:

1.    Framework層如何獲得輸入事件

2.    Framework層獲得按鍵事件後如何處理、儲存

3.    Framework層如何分發、傳遞給應用層

4.    Framework層服務端管道和應用程式客戶端管道如何建立、註冊

5.    應用層如何從Framework層接收按鍵事件

6.    應用層接收到按鍵事件後如何傳遞

7.    特殊按鍵如何處理

8.    總結

1.    Framework層如何獲得輸入事件

要分析輸入系統,最好先從輸入事件系統服務InputManagerService入手,這是瞭解輸入系統的起點,所有其他相關類、執行緒都因此被建立或間接建立。

1.1    InputManagerService的初始化

Android系統啟動時,Android部分第一個使用者空間程序zygote(注:如果從Linux角度出發,第一個使用者空間程序是init)首先fork建立了SystemServer物件,隨後SystemServer建立了核心服務ActivityManagerService、PowerManagerService、PackageManagerService、InputManagerService等,相關程式碼在SystemServer.java的run方法中,與InputManagerService相關部分:

1
) inputManager = new InputManagerService(context);   2) wm = WindowManagerService.main(context, inputManager,                     mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,                     !mFirstBoot, mOnlyCore);   3) ServiceManager.addService(Context.INPUT_SERVICE, inputManager);   4) inputManager.setWindowManagerCallbacks(wm.getInputMonitor());   5) inputManager.start();

1.1.1    InputManagerService的構造方法

this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

建立了一個InputManagerHandler物件,引數是HandlerThread中建立的looper物件

mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

用Looper中的訊息佇列作為引數,呼叫本地方法nativeInit,返回C++中的NativeInputManager物件地址賦給mPtr,mPtr在1.5節會用到。

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

把InputManagerInternal.class和LocalService物件作為一對對映新增到ArrayMap<Class<?>, Object>中

1.1.2    nativeInit方法實現

static jlongnativeInit(JNIEnv* env, jclassclazz,
        jobjectserviceObj, jobjectcontextObj, jobjectmessageQueueObj) {
    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());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

android_os_MessageQueue_getMessageQueue方法:

sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobjectmessageQueueObj) {
    jlongptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
    return reinterpret_cast<NativeMessageQueue*>(ptr);
}

獲得了C++層NativeMessageQueue物件,NativeMessageQueue物件是在建立Looper物件時建立的,在本部落格Handler機制中已經分析過

NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
 messageQueue->getLooper());
im->incStrong(0);
return reinterpret_cast<jlong>(im);

建立了NativeInputManager物件,返回到java層的nativeInit,並將地址賦給mPtr;getLooper()獲得c++層的Looper物件,在Handler機制中,此時Looper物件也已初始化,同時建立了管道並註冊在epoll興趣列表中

NativeInputManager的建構函式:

NativeInputManager::NativeInputManager(jobjectcontextObj,
        jobjectserviceObj, 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);
}

把獲得的Looper物件傳給過來賦給mLooper,用來回調Looper物件的方法;建立c++層中全域性變數mContextObj, mServiceObj;建立EventHub物件並作為引數建立InputManager物件。

1.1.3    EventHub.cpp的建構函式

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
//    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
 
    mINotifyFd = inotify_init();
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s.  errno=%d",
            DEVICE_PATH, errno);
 
    struct epoll_eventeventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
 
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
 
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
 
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);
 
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);
 
    eventItem.data.u32 = EPOLL_ID_WAKE;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
 
    int major, minor;
    getLinuxRelease(&major, &minor);
    // EPOLLWAKEUP was introduced in kernel 3.5
    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
}

初始化必要的變數,採用epoll機制系統呼叫介面epoll_create建立epoll物件,返回檔案描述符mEpollFd,代表epoll物件;採用inotify機制監聽檔案或目錄的移動、讀取、寫入或刪除等事件,inotify機制主要包含2個系統呼叫介面:inotify_init, inotify_add_watch

inotify_init:建立一個inotify物件,如果成功,返回一個檔案描述符,作為該物件;返回-1表示出錯

int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE)

inotify_add_watch:把監控項新增到mINotifyFd物件的監控列表中,第二個引數DEVICE_PATH就是被監控物件,該物件一般是檔案或目錄,本文中被監控的DEVICE_PATH就是/dev/input目錄;第三個引數是一個位掩碼,表示被監控物件上發生的具體事件,可以由1個或多個掩碼位或門組成。IN_DELETE:當被監控目錄內刪除檔案或目錄時觸發該事件;IN_CREATE:當被監控目錄內建立檔案或目錄時觸發該事件。比如,插入、拔出滑鼠時,就會觸發該事件。

如果inotify_add_watch執行成功,返回一個非負監控描述符(假如為wd),代表被監控的項,如果將來不需要再監聽了,可以使用inotify_rm_watch(fd, wd)刪除所新增的監控項wd。

struct epoll_eventeventItem;
memset(&eventItem, 0, sizeof(eventItem));
eventItem.events = EPOLLIN;
eventItem.data.u32 = EPOLL_ID_INOTIFY;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

把inotify物件mINotifyFd新增到epoll物件的興趣列表中,此處採用inotify與epoll機制結合起來檢查檔案

eventItem.data.u32 = EPOLL_ID_WAKE;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);

建立了管道物件讀取端檔案描述符mWakeReadPipeFd並新增到epoll物件的興趣列表中

讀到此處,提出2個問題:

Q1    epoll機制一般有3個系統呼叫介面,而截止到此處,只提到epoll_create, epoll_ctl,還缺epoll_wait,猜測在程式碼某處肯定有epoll_wait,否則epoll機制無法工作

Q2    如果找到了epoll_wait,呼叫程序處於等待狀態,那麼肯定還有一處用來喚醒呼叫程序的程式碼,比如Looper的wake函式或者其他地方呼叫了write系統呼叫

記住這兩個問題,有助於在分析多程序控制時的程式碼走向

1.1.4    InputManager.cpp的構造器

mInputManager = new InputManager(eventHub, this, this);
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();
}

第一個引數是剛建立的EventHub物件,第二、三個引數都是this,當前NativeInputManager物件,傳遞過去後分別是InputReaderPolicyInterface、InputDispatcherPolicyInterface型別,這是為何?因為NativeInputManager繼承了InputReaderPolicyInterface、InputDispatcherPolicyInterface類,實際型別還是NativeInputManager

用這兩個傳遞過去的物件型別作為引數分別建立了InputDispatcher和InputReader物件,再呼叫initialize方法分別建立了與InputDispatcher和InputReader對應的執行緒InputDispatcherThread和InputReaderThread物件

1.1.5    InputDispatcher的建構函式

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    mLooper = new Looper(false);
 
    mKeyRepeatState.lastKeyEntry = NULL;
 
    policy->getDispatcherConfiguration(&mConfig);
}

把傳遞過來的NativeInputManager物件賦給mPolicy,該NativeInputManager物件又是InputDispatcherPolicyInterface介面型別,然後又初始化了很多變數,其中包括:

mLooper = new Looper(false);

建立並初始化了Looper物件,在Looper構造方法建立了管道並採用epoll機制把管道加入到興趣列表中。

注意,此處建立的Looper物件是在InputDispatcher中的,與主執行緒中的Looper沒有關係。

Q3    既然有了epoll_ctl,那肯定某處會呼叫epoll_wait

1.1.6    InputReader的建構函式

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
 
    { // acquire lock
        AutoMutex_l(mLock);
 
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

第一個引數是EventHub物件,第二個policy賦值給mPolicy,policy是InputReaderPolicyInterface介面型別,實際是NativeInputManager物件型別。第三個listener實際是InputDispatcher型別,因為InputDispatcher實現了InputDispatcherInterface,InputDispatcherInterface又實現了InputListenerInterface,因此policy也是InputListenerInterface物件型別

mQueuedListener = new QueuedInputListener(listener);

把InputListenerInterface物件型別listener作為引數建立QueuedInputListener物件,傳遞過去後賦給mInnerListener變數

1.1.7    InputManager.cpp的initialize函式

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

用InputReader物件作為引數建立InputReaderThread執行緒物件,用InputDispatcher作為引數建立InputDispatcherThread執行緒物件,InputReaderThread的建構函式:

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
    Thread(/*canCallJava*/ true), mReader(reader) {
}

把InputReader物件賦給mReader變數

InputDispatcherThread的建構函式:

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
    Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

把InputDispatcher物件賦給mDispatcher變數

程序執行到此處,InputManagerService的初始化就完成,整個過程都是在建立、初始化各種物件。

Q4    既然建立了執行緒物件,那麼就應該在某處啟動執行緒,否則執行緒無法執行,在哪裡啟動執行緒

1.2    WindowManagerService的main方法

wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
public static WindowManagerServicemain(final Contextcontext,
 final InputManagerServiceim,
 final boolean haveInputMethods, final boolean showBootMsgs,
 final boolean onlyCore) {
    final WindowManagerService[] holder = new WindowManagerService[1];
    DisplayThread.getHandler().runWithScissors(new Runnable() {
        @Override
 public void run() {
    holder[0] = new WindowManagerService(context, im,
 haveInputMethods, showBootMsgs, onlyCore);
 }
    }, 0);
 return holder[0];
}
mInputManager = inputManager;

main方法建立了WindowManagerService物件,第二個引數是InputManagerService物件,傳遞到WindowManagerService的建構函式中賦給了mInputManager

1.3    ServiceManager的addService方法

ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

把InputManagerService服務註冊到ServiceManager中

1.4    InputManagerService的setWindowManagerCallbacks方法

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

把InputMonitor物件傳遞到InputManagerService中去,方便回撥,比如7.2節通過該引數回撥InputMonitor中的interceptKeyBeforeQueueing方法

1.5     InputManagerService的start方法

start方法中有兩句:

nativeStart(mPtr);
Watchdog.getInstance().addMonitor(this);

nativeStart的本地實現:

static void nativeStart(JNIEnv* env, jclassclazz, jlongptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
    status_tresult = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

ptr在1.1.1節已經提到過,是NativeInputManager物件地址,只不過是長整形,此處再轉化成NativeInputManager型指標

Watchdog是看門狗,一個單例類,addMonitor方法把InputManagerService物件新增到Watchdog,便於回撥

Q5    建立了看門狗,有什麼用?

nativeStart的傳遞過程: nativeStart —-> com_android_server_input_InputManagerService.cpp的nativeStart  —->  InputManager.cpp的start()

1.5.1    InputManager.cpp的start

status_tInputManager::start() {
    status_tresult = 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;
}

首先啟動分發器執行緒InputDispatcherThread,呼叫run方法後,開始執行threadLoop方法,如果threadLoop返回true,就再次執行threadLoop方法直到requestExit方法停止執行緒;再呼叫InputReaderThread的run方法啟動接收器執行緒。這回答了1.1.7節Q4問題,只要建立了執行緒,那就應該有啟動執行緒的地方。

threadLoop中呼叫了mDispatcher->dispatchOnce()