【问题分析】InputDispatcher无焦点窗口ANR问题【Android 14】

news/2024/4/28 7:12:26/文章来源:https://blog.csdn.net/ukynho/article/details/137067233

在这里插入图片描述

1 问题描述

Monkey跑出的无焦点窗口的ANR问题。

特点:

1)、上层WMS有焦点窗口,为Launcher。

2)、native层InputDispacher无焦点窗口,上层为”recents_animation_input_consumer“请求了焦点,但是”recents_animation_input_consumer“最终没有成为焦点窗口,原因是”NOT_VISIBLE“。

2 log分析

总共有两个项目报了类似的问题,log分析如下:

第一份log:

在这里插入图片描述

第2份log:

在这里插入图片描述

从log中能看到,这两份log的相同点都是在调起Recents界面的时间点的附近启动了几个“快速启动又销毁”的Activity,复现的场景比较相似,并且后续都是“recents_animation_input_consumer”取得了焦点后,又莫名的丢失了焦点:

在这里插入图片描述

在这里插入图片描述

只知道“recents_animation_input_consumer”丢失焦点的原因是“NOT_VISIBLE”,即它对应的Layer被认为是不可见的,但是具体的原因是什么呢?

3 复现ANR

这里经过多次尝试,终于根据第二份log的场景,复现了ANR:

为了模拟问题场景,这类我们总共需要启动4个Activity,并这为4个Activity设置:

android:screenOrientation="reversePortrait"

这个属性,用来模拟Monkey中出现ROTATION_180的场景。

具体场景为:

1)、MainActivity连续启动ActivityA、ActivityB、ActivityC,并且MainActivity调用finish:

        startActivity(new Intent(MainActivity.this, ActivityA.class));startActivity(new Intent(MainActivity.this, ActivityB.class));startActivity(new Intent(MainActivity.this, ActivityC.class));finish();

2)、接着输入KeyEvent.KEYCODE_RECENT_APPS,调起Recents界面,此时“recents_animation_input_consumer”会拿到焦点。

3)、让ActivityC在失去top resumed状态过后一段时间,调用finish:

    public void onTopResumedActivityChanged(boolean isTopResumedActivity) {super.onTopResumedActivityChanged(isTopResumedActivity);if (!isTopResumedActivity) {Handler handler = new Handler();handler.postDelayed(new Runnable() {@Overridepublic void run() {finish();}}, 2000);}}

4)、ActivityC销毁回到ActivityB、ActivityA后,让他们也调用finish:

    @Overrideprotected void onStart() {super.onStart();finish();}

以上代码写好后,就可以复现ANR了,直接输入命令:

adb shell am start -n com.example.demoapp/.MainActivity && adb shell input keyevent 312

即可复现焦点从“recents_animation_input_consumer”离开的现象了:

在这里插入图片描述

再输入一个KeyEvent事件就可以触发ANR了。

同样在pixel上也能复现:

在这里插入图片描述

4 原因分析

从“NOT_VISIBLE”入手,去分析为什么“recents_animation_input_consumer”失去了焦点。

4.1 InputConsumerImpl.show

一开始最先想到的,就是没有为它的SurfaceControl在InputMonitor.UpdateInputForAllWindowsConsumer.updateInputWindows方法中调用show:

在这里插入图片描述

“recents_animation_input_consumer”会在resetInputConsumers方法中hide,然后如果下面的条件满足,就会调用show去显示。

但是分析log可以知道,此时的recents animation还没有结束,因此这里的activeRecents是不会为空的,因此应该是调用了show方法的,并且后续我们复现问题后看log也的确如此。

接下来还是只能靠多添加log去分析。

4.2 FocusResolver.isTokenFocusable

首先是FocusResolver.isTokenFocusable中:

在这里插入图片描述

能够返回Focusability::NOT_VISIBLE说明是WindowInfoHandle的WindowInfo包含了NOT_VISIBLE标志位,而WindowInfo是在SurfaceFlinger.buildWindowInfos处构建的:

在这里插入图片描述

并且能够看到,SurfaceFlinger.buildWindowInfos中调用了Layer.fillInputInfo来填充WindowInfo的信息,和本题相关的就是NOT_VISBLE这个标志的设置,是根据Layer.isVisibleForInput的结果决定是否设置该标志位的。

继续打印log,发现果然是“recents_animation_input_consumer”对应的Layer的isVisibleForInput返回了false,导致这里为其Layer添加了NOT_VISBLE标志位。

4.3 Layer.isVisibleForInput

Layer.isVisibleForInput函数的定义为:

在这里插入图片描述

Layer.hasInputInfo函数的内容为:

在这里插入图片描述

判断Layer是否设置过WindowInfo。

而再次回顾InputConsumerImpl创建并且显示的逻辑:

在这里插入图片描述

可知两点:

1)、在构造方法中创建了InputWindowHandle对象,并且在SurfaceControl显示的时候进行了InputWindowHandle的设置。

2)、在SurfaceControl显示的时候一同设置的,还有相对Layer,并且该对象没有父Layer,只有相对Layer。

因此从上面的信息我们知道,“recents_animation_input_consumer”对应的Layer的函数hasInputInfo是会返回true的,接下来需要继续分析Layer.canReceiveInput为何返回了false。

4.4 Layer.canReceiveInput和Layer.isHiddenByPolicy

在这里插入图片描述

Layer.canReceiveInput首先是判断了Layer.isHiddenByPolicy,并且从log中看到,“recents_animation_input_consumer”的Layer的isHiddenByPolicy返回了false。

看Layer.isHiddenByPolicy的内容能很明显的看到,当前Layer是否可见,实现是看其父Layer和相对Layer是否可见的,如果父Layer或者相对Layer是不可见的,那么该Layer就被直接认为是不可见的,不需要再继续看该Layer自身的设置了。

从上面的分析中我们得知,在本题中,“recents_animation_input_consumer”的Layer是没有父Layer的,但是是有相对Layer的,即在recents动画中被设置为InputMonitor.mActiveRecentsLayerRef的那个WindowContainer的Layer(目前aosp14的dev分支还是ActivityRecord,我们的代码这里是Task,我这里继续分析我们的代码),并且复现问题的时候,该Task的isHiddenByPolicy返回了true:

在这里插入图片描述

这一般就是上层WMS处为Task调用了Transaction.hide。

4.5 Task.prepareSurfaces

最后最终到是在Task.prepareSurfaces中,认定该Task不在可见,所以调用Transaction.setVisibility -> Transaction.hide来为该Task的Layer设置了hidden的标志位:

在这里插入图片描述

1)、Task是否可见,看的是其isVisible方法,由于Task没有重写isVisible方法,因此这里调用的是WindowContainer.isVisible方法。

2)、WindowContainer.isVisible的逻辑是,如果子WindowContainer中有一个是可见的,那么当前WindowContainer也会被认为是可见的。

3)、Task的子容器都是ActivityRecord,所以最终是看该Task中的ActivityRecord中有没有一个ActivityRecord的成员变量mVisible为true。

很明显,在我们的复现问题的过程中,该Task中的所有Activity都被finish了,所以没有一个ActivityRecord是可见的,因此这个Task也被认为是不可见的,那么在Task.prepareSurfaces中,就会为该Task调用Transaction.hide设置hidden标志位,这导致在SurfaceFlinger处,该Task对应的Layer调用isHiddenByPolicy将返回false,那么以该Task为相对Layer的“recents_animation_input_consumer”调用isHiddenByPolicy也只会返回false,最终为“recents_animation_input_consumer”对应的WindowInfo设置了NOT_VISIBLE标志位,在InputDispatcher中被认为是不可见,不再满足作为焦点窗口的条件。

5 一点题外话

5.1 根本原因分析

这一题和我们之前解决的问题很像,同样是在InputDispatcher处,“recents_animation_input_consumer”丢失焦点后,没有再次获得焦点,导致InputDispatcher这一侧的焦点窗口一直为null,从而出现ANR:

1)、之前的那个问题是“recents_animation_input_consumer”的相对Layer的那个Task,整个被移除掉了,所以在SurfaceFlinger处,遍历不到“recents_animation_input_consumer”,因此那个问题,焦点从“recents_animation_input_consumer”离开的时候,原因是”NO_WINDOW“。

2)、本题中,“recents_animation_input_consumer”的相对Layer的那个Task,虽然还存在,但是其中的所有ActivityRecord,都调用了finish,因此所有的ActivityRecord都是不可见的,因此这个Task也不可见,因此“recents_animation_input_consumer”也被认为是不可见,所以焦点从“recents_animation_input_consumer”离开的时候,原因是”NOT_VISIBLE“。

所以根本原因都是一致的,即:

1)、在WMS的InputMonitor处,它为“recents_animation_input_consumer”请求焦点的时候,是不在乎其相对Layer是什么状态的,只要满足InputMonitor要求的的条件,InputMonitor就为“recents_animation_input_consumer”请求焦点。

2)、在SurfaceFlinger和InputDispatcher处,它们是会关心“recents_animation_input_consumer”的相对Layer是什么状态的,如果其相对Layer不可见,甚至不再存在了,那么就不会为“recents_animation_input_consumer”请求焦点。

正是这两侧为“recents_animation_input_consumer”请求焦点的相关逻辑的区别,导致了这类ANR问题的出现。

5.2 Task没有被移除

另外还有一点疑问就是,“recents_animation_input_consumer”的相对Layer的那个Task,虽然它里面的所有Activity都调用了finish了,但是还剩一个“com.example.demoapp/.MainActivity”没有被移除,还在Task里面:

在这里插入图片描述

看到复现问题时候的log:

在这里插入图片描述

该Activity调用了finish后实际上并没有移除,而是一直在Task中,状态则是STOPPING。

直到recents动画结束后,该Activity才被移除,然后是Task也被移除:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1026958.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高防DNS和高防IP一样吗?

高防DNS和高防IP在功能和目标上有所不同,因此它们并不完全相同。 高防DNS是一种针对DNS服务的防护措施,旨在保护域名解析免受DDoS攻击等网络威胁的影响。它利用高防服务器和高防机房的资源,对无效流量进行清洗,保障DNS服务器的安…

零基础学习挖掘PHP网站漏洞

教程介绍 本套课程,分为三个阶段:第一阶段:基础篇 学习PHP开发的基础知识,对PHP常见的漏洞进行分析,第二阶段:进阶篇 实战PHP漏洞靶场,了解市面上的PHP主流网站开发技术,并对市面上…

图解MySQL目录

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 一 .图解MySQL介绍 重点突击 MySQL 索引、事务、锁、日志等面试常问知识。 二 . 基础篇 执行一条 select 语句,期间发生了什么? : 执行一条 select 语句,期间发生了什…

SQL的事务及其ACID属性

目录 SQL中的事务简介事务和ACID属性SQL事务中的关键命令示例SQL事务的隔离层级1. 未提交读取2. 提交后读取3. 可重复读取4. 可序列化脏读、不可重复读或虚读脏读取不可重复读取(未提交读取)虚读取推荐超级课程: Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速…

使用express Vue+Node搭建的网上购物商城

前言 项目采用的技术栈: VueNodeMySQL 前端:用Vue-cli搭建,使用Vue全家桶element-ui 后端:express框架 数据库:MySQL 一、功能 普通用户 注册、登录(图形验证码)定位 (腾讯地图定位功能&#…

查找中常见的树数据结构

查找中常见的树数据结构 一、排序二叉树二、平衡二叉树三、红黑树(自平衡二叉树)四、B树五、B树 在动态查找中常见的树相关的数据结构包括: 排序二叉树(Binary Search Trees)平衡二叉树(AVL Trees&#xff…

ssh 公私钥

一、生成ssh公私钥 生成自定义名称的SSH公钥和私钥对,需要使用ssh-keygen命令,这是大多数Linux和Unix系统自带的标准工具。下面,简单展示如何使用ssh-keygen命令来生成具有自定义名称的SSH密钥对。 步骤 1: 打开终端 首先,打开我…

鸿蒙HarmonyOS应用开发之在NDK工程中使用预构建库

在NDK工程中,可以通过CMake语法规则引入并使用预构建库。在引用预构建库时,模块libs目录中的预构建库,以及在CMakeList.txt编译脚本中声明的预构建库都会被打包。 例如在项目中需要使用预构建库libavcodec_ffmpeg.so,其开发态存放…

【数据库管理操作】Mysql 创建学生数据库及对数据表进行修改

MySQL 创建学生成绩数据库 1.创建数据库 create database studentscore;创建完成之后,如果需要使用该数据,使用use命令 use studentscore;创建表前查看当前数据库中包含的表 show tables; 2.创建bclass表 create table bclass( class_id char(8) …

蓝桥杯刷题day09——霓虹【算法赛】

一、问题描述 晚上,小蓝正无聊的走在大路上,小蓝所在的街区是—个带有赛博朋克风格的街区。 他抬头—看,看到了很多霓虹灯牌。在其中的某一个店铺前,挂着一排的数字灯牌,每一个数字的显示都依靠7段LED管,亮着的灯管组成数字,具体来说如下图所示: 小蓝刚学过数字电路,他…

数据库系统概论-第3章 关系数据库标准语言SQL

3.1 SQL概述 3.2 学生-课程数据库 3.3 数据定义 3.4 数据查询 3.5 数据更新 3.6 空值的处理 3.7 视图 3.8 小结

Java_19 罗马数字转整数

罗马数字转整数 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1…

vue3+threejs新手从零开发卡牌游戏(十三):上场手牌添加攻击力文字

在utils/common.ts下新建渲染场上手牌文字方法: // 渲染场上手牌文字 const renderSiteCardText (mesh: any, font: any) > {return new Promise((resolve, reject) > {let pos mesh.positionconst geometry new TextGeometry( ATK ${mesh.userData._ATK}…

蓝桥杯2023省赛:矩阵总面积|模拟、数学(几何)

题目链接: 0矩形总面积 - 蓝桥云课 (lanqiao.cn) 说明: 参考文章:矩形总面积计算器:计算两个矩形的总面积,包括重叠区域_矩形r1的左下角坐标为x1, yl 、宽度为w1、高度为h1, 矩形r2的左下角坐标为x2,y2、宽-CSDN博客…

面试算法-93-交错字符串

题目 给定三个字符串 s1、s2、s3&#xff0c;请你帮忙验证 s3 是否是由 s1 和 s2 交错 组成的。 两个字符串 s 和 t 交错 的定义与过程如下&#xff0c;其中每个字符串都会被分割成若干 非空 子字符串 &#xff1a; s s1 s2 … sn t t1 t2 … tm |n - m| < 1 交错…

如何用联合(共用体)union验证系统大小端

一&#xff1a;思路 由联合体的特点&#xff0c;可知上图&#xff0c;char c 和 int i 共用四个字节&#xff0c;假设是小端&#xff0c;则由左到右是低地址到高地址&#xff0c;四个字节的内容如图所示01 00 00 00 代码展示&#xff1a; 如果第一个字节是1&#xff0c;则证明…

爱上数据结构:顺序表和链表

一、线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条…

NAT---网络地址转换技术

Network Address Translation 1、起源&#xff1a;ip地址不够用 2、作用&#xff1a;让私网地址映射成公网地址&#xff0c;进而访问网络。 3、私网Ip地址的范围&#xff1a; A类&#xff1a;10.0.0.0-10.255.255.255 B类&#xff1a;172.16.0.0-172.31.255.255 C类&…

Vue3更新Package.json版本号

由于我之前已经更新过了&#xff0c;下面的方法提示我已经是最新的了&#xff0c;记录一下&#xff0c;过段时间在测试一下 npm install -g vue/clivue upgrade

数据库被.[Goodmorningfriends@onionmail.org].faust勒索病毒加密,能恢复吗?

.faust勒索病毒有什么特点及危害&#xff1f; .faust勒索病毒是一种恶意软件&#xff0c;以其复杂的加密技术和勒索行为而闻名。这种病毒的主要目标是通过加密受害者的数据文件&#xff0c;然后勒索赎金以解密这些文件。它通常通过恶意附件、恶意链接或潜在的不安全下载源传播&…