1. 程式人生 > >Android系統層的input裝置解析

Android系統層的input裝置解析

轉載說明:

這邊文章正好和http://blog.csdn.net/zangcf/article/details/51129867轉載互補,另一個角度講述輸入系統。

前言:這篇從2011年寫到2012年,呵呵,2012來臨了,祝大家新年快樂,心想事成。

     上一篇從linux核心角度分析input驅動,那麼android怎麼獲取input資訊呢?本文重點討論這個話題。

      在Java層,處理input型別訊息在InputManager.java檔案裡,當然首先要找到源頭,即InputManager類由誰來建立?在WindowManagerService.java這個介面視窗管理服務檔案裡。

     WindowManagerService類的建構函式:

     private WindowManagerService(Context context, PowerManagerService pm,

            boolean haveInputMethods, boolean showBootMsgs) {

           …..

        mInputManager = new InputManager(context, this);

           …..

        mInputManager.start();

           .….

    }

…..省掉與InputManager無關的部分,建立InputManager物件,然後呼叫其start方法來監控input事件。

進入InputManager.java檔案,先看看它的建構函式,之後分析start方法。

public InputManager(Context context, WindowManagerService windowManagerService) {

        this.mContext = context;

        this.mWindowManagerService = windowManagerService;

        this.mCallbacks = new Callbacks();//建立回撥物件

        Looper looper = windowManagerService.mH.getLooper();//建立looper

        Slog.i(TAG, "Initializing input manager");

        nativeInit(mContext, mCallbacks, looper.getQueue());//呼叫本地方法nativeInit來//進行C++層的初始化操作

        // Add ourself to the Watchdog monitors.

        Watchdog.getInstance().addMonitor(this);//watchdog監視器

    }

   在這裡,重點關注nativeInit。進入C++層,在com_android_server_InputManager.cpp檔案:

  static JNINativeMethod gInputManagerMethods[] = {

    /* name, signature, funcPtr */

    { "nativeInit", "(Landroid/content/Context;"

            "Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",

            (void*) android_server_InputManager_nativeInit },// nativeInit與java層的關//聯

    { "nativeStart", "()V",

       (void*) android_server_InputManager_nativeStart },// nativeStart與java層的關聯

………..

}

在這裡,分析android_server_InputManager_nativeInit函式.

static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,

        jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {

    // gNativeInputManager為空,便建立物件.

    if (gNativeInputManager == NULL) {

        sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);// sp<Looper>: Looper類強指標

        gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);//建立NativeInputManager物件

    } else {

        LOGE("Input manager already initialized.");

        jniThrowRuntimeException(env, "Input manager already initialized.");

    }

}

通過android_server_InputManager_nativeInit函式完成Looper和NativeInputManager初始化。

重點關注NativeInputManager類建構函式:

NativeInputManager::NativeInputManager(jobject contextObj,

        jobject callbacksObj, const sp<Looper>& looper) :

        mLooper(looper) {

    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);

    mCallbacksObj = env->NewGlobalRef(callbacksObj);

    {

        AutoMutex _l(mLock);

        mLocked.displayWidth = -1;

        mLocked.displayHeight = -1;

        mLocked.displayExternalWidth = -1;

        mLocked.displayExternalHeight = -1;

        mLocked.displayOrientation = DISPLAY_ORIENTATION_0;

        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;

        mLocked.pointerSpeed = 0;

        mLocked.pointerGesturesEnabled = true;

        mLocked.showTouches = false;

    }

    sp<EventHub> eventHub = new EventHub();//new一個EventHub物件

    mInputManager = new InputManager(eventHub, this, this);//建立InputManager物件

}

這個函式建立一個EventHub物件,然後把它作為引數來建立InputManager物件。特別注意,InputManager是在C++裡,具體在InputManager.cpp裡。EventHub類在EventHub.cpp裡,這個類和input事件獲取有關。

首先是去InputManager.cpp檔案,下面是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();

}

它建立了InputDispatcher物件,同時也建立了InputReader物件。並分別暫存於mDispatcher和mReader變數中。注意eventHub和mDispatcher都作為引數建立InputReader物件。後面還用initialize來初始化。下面是initialize函式的定義:

void InputManager::initialize() {

    mReaderThread = new InputReaderThread(mReader);

    mDispatcherThread = new InputDispatcherThread(mDispatcher);

}

它建立兩個執行緒,一個是InputReaderThread執行緒,負責input事件的獲取;另一個是InputDispatcherThread執行緒,負責input訊息的傳送。

先回頭解決在開始InputManager.java的mInputManager.start()這個start方法。看究竟怎麼啟動。

    public void start() {

        Slog.i(TAG, "Starting input manager");

        nativeStart();//呼叫本地方法nativeStart

        …..

    }

重點關注nativeStart。進入C++層,在com_android_server_InputManager.cpp檔案:

  static JNINativeMethod gInputManagerMethods[] = {

    /* name, signature, funcPtr */

    { "nativeInit", "(Landroid/content/Context;"

            "Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",

            (void*) android_server_InputManager_nativeInit },// nativeInit與java層的關//聯

    { "nativeStart", "()V",

       (void*) android_server_InputManager_nativeStart },// nativeStart與java層的關聯

………..

}

關注android_server_InputManager_nativeStart函式:

    static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {

    if (checkInputManagerUnitialized(env)) {

        return;

    }

    status_t result = gNativeInputManager->getInputManager()->start();

    if (result) {

        jniThrowRuntimeException(env, "Input manager could not be started.");

    }

          先看getInputManager:

inline sp<InputManager> getInputManager() const { return mInputManager; }

getInputManager是InputManager類,它的start方法繼承InputManager類的方法(注意是InputManager.cpp的InputManager類)。

status_t InputManager::start() {

    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

    if (result) {

        LOGE("Could not start InputDispatcher thread due to error %d.", result);

        return result;

    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

    if (result) {

        LOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();

        return result;

    }

    return OK;

}

      它主要是啟動InputDispatcherThread和InputReaderThread這兩個執行緒。前面提到了建立這兩個執行緒。也建立了InputDispatcher和InputReader物件。下面就這兩個物件做分解.

       下面是class InputDispatcherThread : public Thread {

public:

    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);

    ~InputDispatcherThread();

private:

    virtual bool threadLoop();

    sp<InputDispatcherInterface> mDispatcher;

};

由於它是Thread子類,於是繼承它的run方法,進入run方法後會呼叫threadLoop(),在Thread類中它是虛擬函式,得由子類來複寫,如下所示:

bool InputDispatcherThread::threadLoop() {

    mDispatcher->dispatchOnce();

    return true;

}

啟動mDispatcher->dispatchOnce();

void InputDispatcher::dispatchOnce() {

    nsecs_t nextWakeupTime = LONG_LONG_MAX;

    { // acquire lock

        AutoMutex _l(mLock);

        dispatchOnceInnerLocked(&nextWakeupTime);

        if (runCommandsLockedInterruptible()) {

            nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately

        }

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

}

函式功能:dispatchOnceInnerLocked函式處理input輸入訊息,mLooper->pollOnce是等待下一次輸入事件。

mLooper->pollOnce(timeoutMillis):

這個請看Looper.cpp檔案中的Looper::pollOnce()函式。Looper裡主要通過linux管道方式實現程序間通訊,通過epoll機制實現外界事件請求作出響應。

接著,來分析InputReaderThread的啟動。

class InputReaderThread : public Thread {

public:

    InputReaderThread(const sp<InputReaderInterface>& reader);

    virtual ~InputReaderThread();

private:

    sp<InputReaderInterface> mReader;

    virtual bool threadLoop();//loop

};

在這裡直接到InputReader.cpp檔案

bool InputReaderThread::threadLoop() {

    mReader->loopOnce();

    return true;

}

往下走,

void InputReader::loopOnce() {

    int32_t timeoutMillis;

    { // acquire lock

        …….

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

        ……

}

這個函式主要通過EventHub的getEvents方法來獲取input事件。

接下來進入到EventHub.cpp檔案。一睹getEvents方法。

bool EventHub::getEvent(RawEvent* outEvent)

{

    outEvent->deviceId = 0;

    outEvent->type = 0;

    outEvent->scanCode = 0;

    outEvent->keyCode = 0;

    outEvent->flags = 0;

    outEvent->value = 0;

    outEvent->when = 0;

    // Note that we only allow one caller to getEvent(), so don't need

    // to do locking here...  only when adding/removing devices.

    if (!mOpened) {

        mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;

        mOpened = true;

        mNeedToSendFinishedDeviceScan = true;

    }

    for (;;) {

        // Report any devices that had last been added/removed.

        if (mClosingDevices != NULL) {

            device_t* device = mClosingDevices;

            LOGV("Reporting device closed: id=0x%x, name=%s\n",

                 device->id, device->path.string());

            mClosingDevices = device->next;

            if (device->id == mFirstKeyboardId) {

                outEvent->deviceId = 0;

            } else {

                outEvent->deviceId = device->id;

            }

            outEvent->type = DEVICE_REMOVED;

            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

            delete device;

            mNeedToSendFinishedDeviceScan = true;

            return true;

        }

        if (mOpeningDevices != NULL) {

            device_t* device = mOpeningDevices;

            LOGV("Reporting device opened: id=0x%x, name=%s\n",

                 device->id, device->path.string());

            mOpeningDevices = device->next;

            if (device->id == mFirstKeyboardId) {

                outEvent->deviceId = 0;

            } else {

                outEvent->deviceId = device->id;

            }

            outEvent->type = DEVICE_ADDED;

            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

            mNeedToSendFinishedDeviceScan = true;

            return true;

        }

        if (mNeedToSendFinishedDeviceScan) {

            mNeedToSendFinishedDeviceScan = false;

            outEvent->type = FINISHED_DEVICE_SCAN;

            outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

            return true;

        }

        // Grab the next input event.

        for (;;) {

            // Consume buffered input events, if any.

            if (mInputBufferIndex < mInputBufferCount) {

                const struct input_event& iev = mInputBufferData[mInputBufferIndex++];

                const device_t* device = mDevices[mInputDeviceIndex];

                LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),

                     (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);

                if (device->id == mFirstKeyboardId) {

                    outEvent->deviceId = 0;

                } else {

                    outEvent->deviceId = device->id;

                }

                outEvent->type = iev.type;

                outEvent->scanCode = iev.code;

                if (iev.type == EV_KEY) {

                    status_t err = device->layoutMap->map(iev.code,

                            & outEvent->keyCode, & outEvent->flags);

                    LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",

                        iev.code, outEvent->keyCode, outEvent->flags, err);

                    if (err != 0) {

                        outEvent->keyCode = AKEYCODE_UNKNOWN;

                        outEvent->flags = 0;

                    }

                } else {

                    outEvent->keyCode = iev.code;

                }

                outEvent->value = iev.value;

                // Use an event timestamp in the same timebase as

                // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()

                // as expected by the rest of the system.

                outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);

                return true;

            }

            // Finish reading all events from devices identified in previous poll().

            // This code assumes that mInputDeviceIndex is initially 0 and that the

            // revents member of pollfd is initialized to 0 when the device is first added.

            // Since mFDs[0] is used for inotify, we process regular events starting at index 1.

            mInputDeviceIndex += 1;

            if (mInputDeviceIndex >= mFDCount) {

                break;

            }

            const struct pollfd& pfd = mFDs[mInputDeviceIndex];

            if (pfd.revents & POLLIN) {

                int32_t readSize = read(pfd.fd, mInputBufferData,

                        sizeof(struct input_event) * INPUT_BUFFER_SIZE);

                if (readSize < 0) {

                    if (errno != EAGAIN && errno != EINTR) {

                        LOGW("could not get event (errno=%d)", errno);

                    }

                } else if ((readSize % sizeof(struct input_event)) != 0) {

                    LOGE("could not get event (wrong size: %d)", readSize);

                } else {

                    mInputBufferCount = readSize / sizeof(struct input_event);

                    mInputBufferIndex = 0;

                }

            }

        }

#if HAVE_INOTIFY

        // readNotify() will modify mFDs and mFDCount, so this must be done after

        // processing all other events.

        if(mFDs[0].revents & POLLIN) {

            readNotify(mFDs[0].fd);

            mFDs[0].revents = 0;

            continue; // report added or removed devices immediately

        }

#endif

        mInputDeviceIndex = 0;

        // Poll for events.  Mind the wake lock dance!

        // We hold a wake lock at all times except during poll().  This works due to some

        // subtle choreography.  When a device driver has pending (unread) events, it acquires

        // a kernel wake lock.  However, once the last pending event has been read, the device

        // driver will release the kernel wake lock.  To prevent the system from going to sleep

        // when this happens, the EventHub holds onto its own user wake lock while the client

        // is processing events.  Thus the system can only sleep if there are no events

        // pending or currently being processed.

        release_wake_lock(WAKE_LOCK_ID);

        int pollResult = poll(mFDs, mFDCount, -1);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);

        if (pollResult <= 0) {

            if (errno != EINTR) {

                LOGW("poll failed (errno=%d)\n", errno);

                usleep(100000);

            }

        }

    }

}

函式功能:如果是第一次進入到這個函式中時,並且成員變數mOpened的值為false,於是就會呼叫openPlatformInput函式來開啟系統輸入裝置。打開了這些輸入裝置檔案後,就可以對這些輸入裝置進行是監控了。如果不是第一次進入到這個函式,那麼就會分析當前有沒有input事件發生,如果有,就返回這個事件,否則就會進入等待狀態,等待下一次input事件的發生。這個函式很關鍵,上次分析input核心驅動,通過這個函式讀取(開啟裝置檔案形式)由input驅動傳送過來的事件資訊。

     這一節分解到這裡,下回再分解。