Android系统层的input设备解析

                 

前言:这篇从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 = android_os_MessageQueue_getLooper(env, messageQueueObj);// sp: 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) :

        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 = 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& eventHub,

        const sp& readerPolicy,

        const sp& 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 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& dispatcher);

    ~InputDispatcherThread();

 

private:

    virtual bool threadLoop();

 

    sp 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& reader);

    virtual ~InputReaderThread();

 

private:

    sp 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驱动传送过来的事件信息。

     这一节分解到这里,下回再分解。

 

你可能感兴趣的:(Android)