InputMonitor笔记

文章仅记录自己的一点理解,仅供自己参考。

1、mInputFocus

WMS.addWindow()-->WMS.finishUpdateFocusedWindowAfterAssignLayersLocked()-->InputMonitor.setInputFocusLw()-->mInputFocus = newWindow;

add一个window的时候会重新寻找焦点窗口,并把焦点窗口保存在WMS.mCurrentFocus中,这个焦点窗口也会保存到InputMonitor.mInputFocus 中。关键代码:

<span style="font-size:18px;"><span style="font-size:18px;">addWindow(){
            ……
            boolean focusChanged = false;
            if (win.canReceiveKeys()) {
                focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                        false /*updateInputWindows*/);
                if (focusChanged) {
                    imMayMove = false;
                }
            }

            if (imMayMove) {
                moveInputMethodWindowsIfNeededLocked(false);
            }

            assignLayersLocked(displayContent.getWindowList());
            // Don't do layout here, the window must call
            // relayout to be displayed, so we'll do it there.

            if (focusChanged) {
                finishUpdateFocusedWindowAfterAssignLayersLocked(false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);
           …………
}
</span></span>

这几行代码虽短,逻辑也很简单,但是调用了很多方法。对于canReceiveKeys()函数:

<span style="font-size:18px;">    public final boolean canReceiveKeys() {
        return isVisibleOrAdding()
                && (mViewVisibility == View.VISIBLE)
                && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0);
    }</span>

如果窗口设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE属性,那么该窗口是不能获得焦点的,即canReceiveKeys()会返回false,也就是说焦点窗口是不会更改的。对于悬浮的窗口一般来说是不需要获得焦点的,故一般设置WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE属性,不然会存在一些问题。如果该窗口可以接受key事件(这个key事件有点歧义,并不是只接受按键事件),那么就调用updateFocusedWindowLocked(),这个以后再研究。如果焦点窗口发生变更,那么就调用moveInputMethodWindowsIfNeededLocked(false)来移动输入法窗口到合适的位置。

mInputFocus中保存的窗口在调用InputMonitor.updateInputWindowsLw()时最终设置到InputDispatcher.mFocusedWindowHandle中去了。

2、mInputDispatchFrozen

冻结InputDispatcher标志,“ When true, prevents input dispatch from proceeding until set to false again.”。

①WMS.startFreezingDisplayLocked()-->InputMonitor.freezeInputDispatchingLw()-->mInputDispatchFrozen = true; updateInputDispatchModeLw();

②WMS.stopFreezingDisplayLocked()-->InputMonitor.thawInputDispatchingLw()-->mInputDispatchFrozen = false; updateInputDispatchModeLw();-->InputManagerService.setInputDispatchMode()-->InputDispatcher.setInputDispatchMode(bool enabled,
bool frozen)-->mDispatchFrozen = frozen;

<span style="font-size:18px;">void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();

    // Reset the key repeat timer whenever we disallow key events, even if the next event
    // is not a key.  This is to ensure that we abort a key repeat if the device is just coming
    // out of sleep.
    if (!mPolicy->isKeyRepeatEnabled()) {
        resetKeyRepeatLocked();
    }

    // If dispatching is frozen, do not process timeouts or try to deliver any new events.
    if (mDispatchFrozen) {
#if DEBUG_FOCUS
        ALOGD("Dispatch frozen.  Waiting some more.");
#endif
        return;
}
……….
}
</span>

调用dispatchOnceInnerLocked()分发事件时,直接return掉。上面的调用逻辑可以知道冻结/解冻显示屏时,会冻结InputDispatcher的分发流程。

3、mInputDispatchEnabled

一个java层控制InputDispatcher的开关。

①WMS.setEventDispatching()-->InputMonitor.setEventDispatchingLw()

②WMS.performEnableScreen()-->InputMonitor.setEventDispatchingLw()-->mInputDispatchEnabled = enabled;updateInputDispatchModeLw();-->InputManagerService.setInputDispatchMode()-->InputDispatcher.setInputDispatchMode(bool enabled,
bool frozen)-->mDispatchEnabled = enabled;

具体是怎么使能InputDispatcher的?

<span style="font-size:18px;"> if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
    }
</span>

在dispatchOnceInnerLocked()中会设置dropReason(放弃原因),如果dropReason不为0表示输入事件将被放弃掉,不会分发。

google源码是这样解释的:“ When true, input dispatch proceeds normally.  Otherwise all events are dropped. Initially false, so that input does not get dispatched until boot is finished at which point the ActivityManager will enable dispatching.”

4、mUpdateInputWindowsNeeded

是否需要更新窗口信息到InputDispatcher中的标志。在非强制性更新窗口信息到InputDispatcher中去时,必须先调用InputMonitor.setUpdateInputWindowsNeededLw()设置该标志,然后再调用InputMonitor.updateInputWindowsLw()。如果强制性更新窗口信息到InputDispatcher中去时就不必设置该变量了。

5、notifyInputChannelBroken()

InputManager通知WMS输入通道破裂函数。

    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
        if (inputWindowHandle == null) {
            return;
        }

        synchronized (mService.mWindowMap) {
            WindowState windowState = (WindowState) inputWindowHandle.windowState;
            if (windowState != null) {
                Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
                mService.removeWindowLocked(windowState.mSession, windowState);
            }
        }
    }

函数处理逻辑非常简单,就是将输入通道破裂的窗口直接调用removeWindowLocked()进行移除。

调用流程:InputDispatcher.startDispatchCycleLocked()--> InputDispatcher.abortBrokenDispatchCycleLocked()--> InputDispatcher.onDispatchCycleBrokenLocked()-->InputDispatcher.doNotifyInputChannelBrokenLockedInterruptible()-->NativeInputManager.notifyInputChannelBroken()-->InputManagerService.notifyInputChannelBroken()-->InputMonitor.notifyInputChannelBroken();

startDispatchCycleLocked()中在publish event不成功,就会走下面的逻辑:

        if (status) {
            if (status == WOULD_BLOCK) {
                if (connection->waitQueue.isEmpty()) {
                    ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                            "This is unexpected because the wait queue is empty, so the pipe "
                            "should be empty and we shouldn't have any problems writing an "
                            "event to it, status=%d", connection->getInputChannelName(), status);
                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                } else {
                    // Pipe is full and we are waiting for the app to finish process some events
                    // before sending more events to it.
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
                            "waiting for the application to catch up",
                            connection->getInputChannelName());
#endif
                    connection->inputPublisherBlocked = true;
                }
            } else {
                ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
                        "status=%d", connection->getInputChannelName(), status);
                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
            }
            return;
        }

在正常pipe full的情况下是不会调用abortBrokenDispatchCycleLocked()的,其他情况则调用abortBrokenDispatchCycleLocked()来处理。

void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, bool notify) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
            connection->getInputChannelName(), toString(notify));
#endif

    // Clear the dispatch queues.
    drainDispatchQueueLocked(&connection->outboundQueue);
    traceOutboundQueueLengthLocked(connection);
    drainDispatchQueueLocked(&connection->waitQueue);
    traceWaitQueueLengthLocked(connection);

    // The connection appears to be unrecoverably broken.
    // Ignore already broken or zombie connections.
    if (connection->status == Connection::STATUS_NORMAL) {
        connection->status = Connection::STATUS_BROKEN;

        if (notify) {
            // Notify other system components.
            onDispatchCycleBrokenLocked(currentTime, connection);
        }
    }
}

函数中清理outboundQueue和waitQueue两个队列中的消息,然后调用onDispatchCycleBrokenLocked()-->.....来通知WMS移除对应的窗口。

outboundQueue:Queue of events that need to be published to the connection.

waitQueue:Queue of events that have been published to the connection but that have not yet received a "finished" response from the application.

输入事件只有收到应用回复的finished消息才算真正分发成功。

6、notifyANR()

源码对这个函数的注释已经非常清楚:

<span style="font-size:18px;">    /* Notifies the window manager about an application that is not responding.
     * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
     *
     * Called by the InputManager.
     */</span>

调用流程:InputDispatcher.handleTargetsNotReadyLocked()--> InputDispatcher.onANRLocked()-->InputDispatcher.doNotifyANRLockedInterruptible()-->NativeInputManager.notifyANR()-->InputManagerService.notifyANR()-->InputMonitor.notifyANR();

时间: 2024-12-29 05:30:27

InputMonitor笔记的相关文章

【安全牛学习笔记】

弱点扫描 ╋━━━━━━━━━━━━━━━━━━━━╋ ┃发现弱点                                ┃ ┃发现漏洞                                ┃ ┃  基于端口五福扫描结果版本信息(速度慢)┃ ┃  搜索已公开的漏洞数据库(数量大)      ┃ ┃  使用弱点扫描器实现漏洞管理            ┃ ╋━━━━━━━━━━━━━━━━━━━━╋ [email protected]:~# searchsploit Usage:

51CTO持续更新《通哥的运维笔记》

<通哥的运维笔记>将持续在51CTO网站更新,希望大家多多关注.互相学习,后期,我将会退出<通哥的运维笔记>系列视频教程,希望带给大家最大的收获,帮助大家更好的学习.进步.<通哥的运维笔记>主要从linux系统管理.虚拟化.cloudstack云平台以及网络管理之CCNA.CCNP.CCIE,等等方面深入讲解.

WPF笔记整理 - Bitmap和BitmapImage

项目中有图片处理的逻辑,因此要用到Bitmap.而WPF加载的一般都是BitmapImage.这里就需要将BitmapImage转成Bitmap 1. 图片的路径要用这样的,假设图片在project下的Images目录,文件名XXImage.png. pack://application:,,,/xxx;component/Images/XXImage.png 2. 代码: Bitmap bmp = null; var image = new BitmapImage(new Uri(this.X

java String 类 基础笔记

字符串是一个特殊的对象. 字符串一旦初始化就不可以被改变. String s = "abc";//存放于字符串常量池,产生1个对象 String s1=new String("abc");//堆内存中new创建了一个String对象,产生2个对象 String类中的equals比较字符串中的内容. 常用方法: 一:获取 1.获取字符串中字符的个数(长度):length();方法. 2.根据位置获取字符:charAt(int index); 3.根据字符获取在字符串中

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

学习笔记之邮件发送篇

用脚本语言发送邮件是系统管理员必备技能 对系统定期检查或者当服务器受到攻击时生成文档和报表. 发布这些文档最快速有效的方法就是发送邮件. python中email模块使得处理邮件变得比较简单 发送邮件主要用到了smtplib和email两个模块,这里首先就两个模块进行一下简单的介绍: 本段摘录于    http://www.cnblogs.com/xiaowuyi/archive/2012/03/17/2404015.html 1.smtplib模块 smtplib.SMTP([host[, p

15.1-全栈Java笔记:Java事件模型是什么?事件控制的过程有哪几步??

应用前边两节上一章节的内容,大家可以完成一个简单的界面,但是没有任何的功能,界面完全是静态的,如果要实现具体功能的话,必须要学习事件模型. 事件模型简介及常见事件模型 对于采用了图形用户界面的程序来说,事件控制是非常重要的. 一个源(事件源)产生一个事件并把它(事件对象)送到一个或多个监听器那里,监听器只是简单地等待,直到它收到一个事件,一旦事件被接收,监听器将处理这些事件. 一个事件源必须注册监听器以便监听器可以接收关于一个特定事件的通知. 每种类型的事件都有其自己的注册方法,一般形式为: v

Java设计模式学习笔记,一:单例模式

开始学习Java的设计模式,因为做了很多年C语言,所以语言基础的学习很快,但是面向过程向面向对象的编程思想的转变还是需要耗费很多的代码量的.所有希望通过设计模式的学习,能更深入的学习. 把学习过程中的笔记,记录下来,只记干货. 第一部分:单例模式的内容 单例模式:类只能有一个实例. 类的特点:1.私有构造器:2.内部构造实例对象:3.对外提供获取唯一实例的public方法. 常见的单例模式实现有五种形式: 1.饿汉式. 2.懒汉式. 3.双重检查锁式. 4.静态内部类式. 5.枚举式. 以下分别

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu