android4.4电源管理——Input系统(Power键处理)

2019-07-13 22:14发布

在开始叙述各部分的功能之前,我们还是先说说更个Input系统的来龙去脉,一方面能够知道Input系统从哪儿来,另一方面能对整个系统有个大概的了解,使我们不至于迷失在浩瀚的Android源码中。在Android系统中一说到重要的服务,基本都是要从systemserver进程开始说起,因为他是Android世界的开拓者,创建了Android世界所需要个基础。同样,Input系统也是从systemserver中开始说起,首先创建一个InputManagerService对象,为这个对象设置与WindowManagerService相关的回调函数,然后调用InputManagerService的start函数。
SystemServer.java     public void initAndLoop() {             Slog.i(TAG, "Power Manager");
            power = new PowerManagerService();             Slog.i(TAG, "Display Manager");
            display = new DisplayManagerService(context, wmHandler);
            Slog.i(TAG, "Input Manager");
            inputManager = new InputManagerService(context, wmHandler);//阶段一,初始化输入服务
            wm = WindowManagerService.main(context, power, display, inputManager,
                    wmHandler, factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
                    !firstBoot, onlyCore);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

            ActivityManagerService.self().setWindowManager(wm);
            inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
            inputManager.start();//阶段二、启动输入服务             display.setWindowManager(wm);
            display.setInputManager(inputManager);
    } 阶段一、InputManager的初始化
WindowManagerService.java     public static WindowManagerService main(final Context context,
            final PowerManagerService pm, final DisplayManagerService dm,
            final InputManagerService im, final Handler wmHandler,
            final boolean haveInputMethods, final boolean showBootMsgs,
            final boolean onlyCore) {
        final WindowManagerService[] holder = new WindowManagerService[1];
        wmHandler.runWithScissors(new Runnable() {
            @Override
            public void run() {
                holder[0] = new WindowManagerService(context, pm, dm, im,
                        haveInputMethods, showBootMsgs, onlyCore);
            }
        }, 0);
        return holder[0];
    }     private WindowManagerService(Context context, PowerManagerService pm,
            DisplayManagerService displayManager, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {         mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerService = displayManager;

    } 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());//通过JNI调用来启动native层的input系统,然后把返回值存放在mPtr中
    } 从代码可以看出,InputManagerService的构造是很简单的,只是在最后通过JNI方法初始化了native层的Input系统。接下来我们就看看在native层都做了些什么,代码如下:
com_android_server_input_InputManagerService.cp    在这里,重点关注nativeInit,进入C++层 static JNINativeMethod gInputManagerMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit",
            "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)I",
            (void*) nativeInit },
    { "nativeStart", "(I)V",
            (void*) nativeStart }, ………..     } 在这里,分析android_server_InputManager_nativeInit函数. static jint nativeInit(JNIEnv* env, jclass clazz,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);// sp: Looper类强指针
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
//这里实例化了NativeInputManagerService的一个对象,使用的Java层的MessageQueue的Looper,意味着Java层消息和Native消息是在同一个MessageQueue中的     NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);     return reinterpret_cast(im);//把新建的NativeInputManager强制转换,返回给Java层 
} 在native层初始化的时候,创建了一个名叫NativeInputMnager的对象,这个对象是很重要的,因为它主要负责和系统的其他模块交互,而且InputReader和InputDispatcher都是只运行在Native层中,如果需要调用Java函数也是通过这个对象进行的,另外他实现了InputReaderPolicyInterface和InputDispatcherPolicyInterface,是一个重要的Policy。 NativeInputManager在构造过程中,完成了InputManager在native基本运行组件的创建: 1、创建了EventHub对象,它是事件的Android系统的起源地,所有的事件都是它从驱动中读取出来的; 2、创建了InputReaderThread线程用来执行InputReader的功能;InputDispatcherThread用来执行InputDispatcher的功能; 3、创建了InputManager来管理EventHub,InputReader,InputReaderThread,InputDispatcher,InputDispatcherThread这些Native运行的基本对象。 这些对象的创建过程中并没有非常重要的调用,这里略过代码。不过要注意一点的是NativeInputManager是InputReaderPolicyInterface和InputDispatcherPolicyInterface的子类, 因此在构造InputReader和InputDispatcher的时候要用到NativieInputManager对象。   在对象构建完成后,开始执行start方法,让之前创建的这些对象运行起来。start方法也是比较简单的,就是通过JNI调用让native层的Input系统运行起来,然后在Java层把自己列入WatchDog的监视范围内。 之后定义下自己需要接受的外部通知等。那么到这里位置,整个Input系统就运行起来了,至于其中具体的功能我们再逐步分析。这部分内容叙述完毕。 com_android_server_input_InputManagerService.cpp 重点关注NativeInputManager类构造函数: NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp& 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 = 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(
        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);
} 它创建两个线程: 1、InputReaderThread线程,负责input事件的获取; 2、nputDispatcherThread线程,负责input消息的发送。 阶段二、InputManager的启动 开始InputManager.java的mInputManager.start()这个start方法。看究竟怎么启动。 InputManagerService.java
    public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);//调用本地方法nativeStart

        // 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();
    } com_android_server_input_InputManagerService.cpp
static JNINativeMethod gInputManagerMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit",
            "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)I",
            (void*) nativeInit },
    { "nativeStart", "(I)V",
            (void*) nativeStart },
} static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) {
    NativeInputManager* im = reinterpret_cast(ptr);

    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
} 先看getInputManager:
  inline sp getInputManager() const { return mInputManager; }//初始时构造mInputManager对象 InputManager.cpp
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;
} 它主要是启动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);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);//事件分发
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {//事件执行
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // 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的启动。 在InputManagerService中start方法会通过JNI调用,启动Native层的InputReaderThread,InputDispatcherThread线程,从而开始Input系统的运行。InputReaderThread主要是执行和InputReader相关的内容,主要是从EventHub中读取事件,预处理事件,然会是根据policy来处理此事件,最后发送一个消息到InputDispatcher中通知事件的产生。紧接着InputDispatcher会开始事件的分发,通过InputChannel把事件分发给WindowManager或者应用程序。说以一个事件的流程是从 Eventhub  ---> InputReader  ---> InputDispatcher  ---> InputPublisher  ---> InputChannel  ---> InputConsumer  --->  WindowManager or Application.这就是整个事件分发的大致流程。
InputReader的功能,以及执行的流程   从前面的内容我们可以知道,在InputManager的start方法被调用会,会执行两个线程,分别是InputReaderThread和InputDispatcherThread,虽然它们的启动在代码上有先后之分,但是在实际执行过程中是没有先后的,所以先从哪个线程开始解析Input系统不是很重要的。不过,我是按照从事件的产生到分发开始解析的,所以这里我是选择从InputReader开始。InputReader是Android系统中重要的部分,根据Android文档中的描述,主要功能就是: (1) 从EventHub读取事件,这些事件是元事件,即没有经过加工或者仅仅是简单加工的处理的事件; (2)把这些事件加工处理,生成inputEvent事件,这样封装之后的事件,可以满足Android系统的一些需求; (3)把这些事件发送到事件监听器,即QueuedInputListener,这个监听器可以把事件传递给InputDispatcher。 下面我们就从线程开始执行的地方一步一步分析这些功能的实现。既然要看InputReader的功能,我就从InputReader的构造函数说起。前面在说到构造InputManager的时候,就创建了InputReader,当时没有介绍起功能和构造方法,我们从这里开始: InputReader::InputReader(const sp& eventHub,
        const sp& policy,
        const sp& listener) :
        mContext(this), mEventHub(eventHub), mPolicy(policy),
        mGlobalMetaState(0), mGeneration(1),
        mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);//在这里创建了一个QueuedInputListener,注意其参数是listener是InputDispatcher
    { // acquire lock
        AutoMutex _l(mLock);
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
} 在InputReader创建的时候,这里把InputDispatcher作为参数传递进来,然后以InputDispatcher作为参数构造出了QueuedInputListener对象。所以现在有这么一个关系:InputReader持有一个QueuedInputListener,而QueuedInputListener持有InputDispatcher对象。
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; } 往下走,在这里补充一点内容: Android系统在Native层中实现了一个类似于Java中的线程对象,即C++中的Thread类。这个线程类有个特点就是,当线程开始执行后,不一直重复执行threadLoop方法,知道这个线程的强引用计数变为零为止。所以,这里的threadLoop函数会不停地执行下去,也即是mReader->loopOnce()会循环执行下去,每循环一次就能从EventHub中读取出若干事件。下面我们就以一次循环过程为例,分析此线程的执行,loopOnce的代码如下: void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector inputDevices;
    ...
    //如果系统刚刚启动,或者有新的设备加入的话,timeoutMillis一般为0,意味着无需等待,可以立即返回;timeoutMillis一般为-1,意味着无限等待
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//阶段一,通过EventHub的getEvents方法来获取input事件。

    {
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            processEventsLocked(mEventBuffer, count);//阶段二,开始处理读取出来的元事件
        }

        ...
    }

    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    mQueuedListener->flush();//阶段三,把QueuedInputListener中的消息全部都开始处理
} void QueuedInputListener::flush() {     size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        args->notify(mInnerListener);//mlnnerListener是InputDispatcher对象         delete args;
    }
    mArgsQueue.clear();
} 这个函数主要包括三个阶段: 1、通过EventHub的getEvents方法来获取input事件。 2、从EventHub中读取出若干事件,然会对这些事件进行预处理。 3、把QueuedInputListener中的事件分发出去。 这个方法中包含了InputReader的主要功能,所以此线程每循环一次,都会执行完成一次InputReader的主要功能。接下来依次介绍着三个阶段: 阶段一,从EventHub获取事件
先简单介绍下EvenHub,这个类的主要功能就是主动监视Input驱动的变化,一旦有事件产生,就从产生事件相应的驱动中读取出这个事件。实现这个监视驱动功能,是通过Linux提供的epoll机制来实现。epoll机制简单地说就是高效地I/O多路复用机制,使用epoll_wait来监听所需要的文件描述符的变化,关于epoll的介绍有很多文章,man中也有详细的介绍。EventHub的主要功能是通过epoll_wait来实现的,所以EventHub所在的线程应该会阻塞在epoll_wait方法中,一直等到epoll_wait设置的超时时间。现在我们开始看看EventHub的实现,在EventHub的构造函数中,建立了一个管道,并把这个管道的读端和写端的文件描述符添加到epoll的监视之下,以便于其他的线程或者进程能够使EventHub所在的线程从epoll_wait的阻塞中返回。EventHub在创建完成之后,第一个被调用的方法就是getEvents,而且这个方法也是EventHub的主要功能,对于这个方法需要仔细分析,我们把getEvents方法也分成了三个部分去解析,分别是:打开设备部分;事件读取部分;等待部分。这三个部分中,以事件的读取部分为重点。设备打开部分一般发生在Input系统建立的时候调用,所以在系统启动完成,稳定之后,这部分内容应该不会再被执行的;而等待部分较为简单。不过这些作为系统必不可少的部分,还是要一一说明的,先说设备打开部分吧,代码如下: size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ...
    struct input_event readBuffer[bufferSize];
    //这是元事件指针,可以指向一系列的事件,这些事件按照数组的方式存放的
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        //mNeedToReopenDevices = false;
        //mClosingDevices = 0;         //mNeedToSendFinishedDeviceScan = false;         //mOpeningDevices = 0         //mNeedToScanDevices = true
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
    ... EventHub对象在初始化的时候,有很多变量都已经赋值,所以我把代码中判断不成立的代码块暂时都拿掉了,只留下了在Input系统启动时候会执行的内容, 也就是scanDevicesLocked方法。在这个方法执行之后,肯定会产生一些设备添加,移除之类的事件,到时候在一一分析。 在这个方法中,使用了一个结构体叫RawEvent,使用这个结构体简单地表明事件发生的基本信息,代码如下: struct RawEvent {
    nsecs_t when;//事件发生的时间,在getEvents中对于事件时间的处理也是值得关注的
    int32_t deviceId;//产生这个事件对应的设备的ID,与具体的硬件无关,其数值和设备打开的顺序有关
    int32_t type;//事件的类型
    int32_t code;//事件对应的事件码
    int32_t value;//事件的内容
};
RawEvent来自两种,一种是在打开设备时自己赋值,不如设备的添加,移除等,这些事件对应的RawEvent都是getEvents自己赋值的,便于InputReader处理; 还有一种是来自驱动的产生的事件,由驱动产生的这类事件,在内容中有其自己的定义的类型,就是input_event。 getEvents可以根据input_event产生相应的RawEvent便于InputReader处理。 这里要额外说明一点的就是RawEvent的type,如果是由输入设备产生的事件,那么这个type是和输入设备本身的特性相关的, 下面列举出Linux中支持的事件类型: EV_SYN 用于标识独立的事件,这些独立的事件时在时间或者空间上是可以分离的,比如在多点触摸中 EV_KEY 用于标识按键,按钮或者类似按键的设备状态的变化 EV_REL 用于描述 对于轴线相对变化量,如鼠标向左移动5个单位 EV_ABS 用于描述 对于轴线的绝对变化量, 比如在触摸屏上的触摸点的坐标 EV_SW 标识二进制的开关状态 EV_LED 表示设备上的LED是开or关 EV_SND 用于标识发送声音到设备 EV_REP 表示自动重复的设备 V_FF 用于标识发送强制要回馈的命令到设备 EV_PWR 对于Power键的一个特殊状态或者切换输入 EV_FF_STATUS 用于收到需要强制回馈的设备状态 EV_MSC 如果不是这些已存在的状态,那么就用这个标识 这个表格来自于Linux内核文档中的Document/input/event-codes.txt,如果以上有翻译不恰当的地方,可以去参考原文档。上面这些类型是Linux支持的所有的事件类型,一般的一类设备可以支持这些类型中的一个或几个。 在Android系统中,常用的设备由触摸屏,键盘或者鼠标等,这些设备一般是能够产生如下类型的事件: 多点触屏    大多是EV_ABS, EV_KEY, EV_SYN,有的还设置了EV_MSC 键盘         EV_KEY, EV_SW 鼠标        EV_REL, EV_KEY, EV_ABS 这个表格仅仅是一般性而言,具体情况还需要参考相应的设备驱动文件。这里之所以介绍这些东西,是因为在InputReader在预处理这些事件的时候会使用type这个类型。了解了这些之后,继续看EventHub是如何打开这些设备的。 EventHub是通过扫描/dev/input/目录下所有可用的设备,然后逐一打开这些设备,打开这些设备过程中,EventHub又做了一些Input系统必要的工作,比如构造Device对象,把这些设备加入到epoll的监视队列中等,时间戳的设定等。在构造Device对象的时候,是通过InputDeviceIdentifier来构造的,主要思路就是通过ioctl函数从内容中读取出一些必要的信息,然后把这些信息经过InputDeviceIdentifier存入Device中,然后再通过ioctl函数测试设备的属性,把这些属性信息也存入Device中。代码如下: static const char *DEVICE_PATH = "/dev/input";
void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);
    if(res < 0) {
        ALOGE("scan dir failed for %s ", DEVICE_PATH);
    }
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}
status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    *filename++ = '/';
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '