Android15的广播ANR源码流程

跟了下实际代码的流程,大概如下哈:

App.sendBroadcast()  // 应用发起广播→ AMS.broadcastIntentWithFeature()  // 通过Binder IPC进入system_server进程→ AMS.broadcastIntentLocked()  // 权限校验+广播分类(前台/后台)→ BroadcastQueueModernImpl.enqueueBroadcastLocked()  // 按进程隔离的队列管理→ BroadcastQueueModernImpl.updateRunningListLocked()  // 检查进程活跃状态→ BroadcastQueueModernImpl.scheduleReceiverWarmLocked()  // 预热进程(若未启动)→ BroadcastQueueModernImpl.dispatchReceivers()  // 关键:启动超时检测(前台10s/后台60s)   ├─ 通过Handler发送MSG_DELIVERY_TIMEOUT延迟消息   └─ 豁免条件:系统未就绪或广播标记为timeoutExempt→ IApplicationThread.scheduleRegisteredReceiver()  // Binder投递到目标进程→ ActivityThread.post(Args.run())  // 主线程消息队列调度→ Args.run()  // 封装广播处理逻辑→ BroadcastReceiver.onReceive()  // 开发者代码执行点(耗时操作直接触发ANR)→ AMS.finishReceiver()  // 正常完成通知→ BroadcastQueueModernImpl.finishReceiverLocked()  // 移除超时检测(Handler取消MSG_DELIVERY_TIMEOUT)   └─ 若超时未完成→ AMS.appNotResponding()  // 触发ANR弹窗(含Intent/接收者类名等上下文)

0

源码分析

→ App.sendBroadcast() → → AMS.broadcastIntentWithFeature() → → → AMS.broadcastIntentLocked() /broadcastIntentLockedTraced()→ → → → BroadcastQueueModernImpl.enqueueBroadcastLocked()→ → → → → BroadcastQueueModernImpl.updateRunningListLocked() → → → → → →BroadcastQueueModernImpl.scheduleReceiverWarmLocked() → → → → → → BroadcastQueueModernImpl.dispatchReceivers() → → → → → → → IApplicationThread.scheduleRegisteredReceiver()→→ → → → → → → → ActivityThread.post()→→ → → → → → → → → Args.run() → → → → → → → → → → BroadcastReceiver.onReceive()→ → → → → → → → → → → AMS.finishReceiver() → → → → → → → → → → → →BroadcastQueueModernImpl.finishReceiverLocked() → → → → → → → → → → → →→AMS.appNotResponding(queue.app, tr)

0. 广播产生并发送给AMS

应用或系统广播产生并发送给AMS

源码路径:frameworks/base/core/java/android/app/ContextImpl.java

@Overridepublic void sendBroadcast(Intent intent) {    warnIfCallingFromSystemProcess(); // 防止系统进程误用普通API    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());    try {        intent.prepareToLeaveProcess(this); // 清理不可序列化对象,确保跨进程传输        // 通过Binder调用AMS的广播接口        ActivityManager.getService().broadcastIntentWithFeature(            mMainThread.getApplicationThread(), // 调用方线程标识            getAttributionTag(),                // 权限追踪标签            intent, resolvedType, null, Activity.RESULT_OK,             null, null, null, null, null, AppOpsManager.OP_NONE,             null, false, false, getUserId());    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}

1. AMS投递广播到队列并启动超时检测

源码路径:frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java

@GuardedBy("mService")private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,        @NonNull BroadcastRecord r, int index) throws BroadcastRetryException {    final ProcessRecord app = queue.app;    // 仅在满足条件时启动超时检测    if (mService.mProcessesReady && !r.timeoutExempt && !r.isAssumedDelivered(index)) {        queue.setTimeoutScheduled(true);        // 区分前台/后台广播超时阈值        final int softTimeoutMillis = (int) (r.isForeground() ? mFgConstants.TIMEOUT                 : mBgConstants.TIMEOUT);        startDeliveryTimeoutLocked(queue, softTimeoutMillis); // 启动动态超时检测    } else {        queue.setTimeoutScheduled(false); // 豁免超时检测    }    // 处理后台Activity启动权限的超时(新增特性)    if (r.mBackgroundStartPrivileges.allowsAny()) {        final long bgStartTimeout = r.isForeground() ? mFgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT                : mBgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT;        // 通过Handler延迟发送超时消息        mLocalHandler.sendMessageDelayed(            Message.obtain(mLocalHandler, MSG_BG_ACTIVITY_START_TIMEOUT, args), bgStartTimeout);    }}

ActivityManagerService定义了广播ANR的双阈值动态控制:

前台广播(mFgConstants.TIMEOUT):默认10秒,保障用户体验。

后台广播(mBgConstants.TIMEOUT):默认60秒,放宽限制以节省资源

public class ActivityManagerService extends IActivityManager.Stub        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {    static final int BROADCAST_FG_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 前台广播默认10秒    static final int BROADCAST_BG_TIMEOUT = 60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; // 后台广播默认60秒    public BroadcastQueue getBroadcastQueue(ActivityManagerService service) {        // Broadcast policy parameters        final BroadcastConstants foreConstants = new BroadcastConstants(                Settings.Global.BROADCAST_FG_CONSTANTS);        foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;        final BroadcastConstants backConstants = new BroadcastConstants(                Settings.Global.BROADCAST_BG_CONSTANTS);        backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;        return new BroadcastQueueModernImpl(service, service.mHandler,                    foreConstants, backConstants);    }}

2. 目标进程接收并处理广播

源码路径:frameworks/base/core/java/android/app/LoadedApk.ReceiverDispatcher.java

public void performReceive(Intent intent) {    // 封装为Runnable投递到主线程消息队列    Args args = new Args(intent, ...);    mActivityThread.post(args); // 使用主线程Handler}

3. 超时检测取消入口(finishReceiverLocked)

源码路径:frameworks/base/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java

​​​​​​​​​​​​​​

BroadcastQueueModernImpl.finishReceiverLocked/** * 处理广播接收完成或超时的核心逻辑,更新状态并触发后续操作 *  * @param queue 目标进程的广播队列(包含当前活跃的广播记录) * @param deliveryState 广播投递状态(如超时/DELIVERY_TIMEOUT、正常完成等) * @param reason 状态变更原因(用于日志和调试) */@GuardedBy("mService")private void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,        @DeliveryState int deliveryState, @NonNull String reason) {    // 1. 校验队列有效性:若当前无活跃广播,直接返回    if (!queue.isActive()) {        logw("Ignoring finishReceiverActiveLocked; no active broadcast for " + queue);        return;    }    // 2. 获取当前广播上下文    final int cookie = traceBegin("finishReceiver"); // 性能追踪标记    final ProcessRecord app = queue.app; // 目标进程记录    final BroadcastRecord r = queue.getActive(); // 当前处理的广播记录    final int index = queue.getActiveIndex(); // 接收者索引    final Object receiver = r.receivers.get(index); // 具体的接收者对象    // 3. 更新投递状态(如标记超时或完成)    setDeliveryState(queue, app, r, index, receiver, deliveryState, reason);    // 4. 处理超时场景    if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {        r.anrCount++; // 记录ANR次数        if (app != null && !app.isDebugging()) { // 非调试进程才触发ANR            final AutoCloseable timer = mAnrTimer.accept(queue);            final String packageName = getReceiverPackageName(receiver);            final String className = getReceiverClassName(receiver);            // 构造ANR报告记录            TimeoutRecord tr = TimeoutRecord.forBroadcastReceiver(r.intent, packageName,                    className).setExpiredTimer(timer);            mService.appNotResponding(queue.app, tr); // 触发ANR弹窗        } else {            mAnrTimer.discard(queue); // 调试进程忽略ANR        }    }     // 5. 正常完成时取消超时检测    else if (queue.timeoutScheduled()) {        cancelDeliveryTimeoutLocked(queue); // 关键:移除超时消息    }    // 6. 检查是否有等待条件被满足(如有序广播的链式触发)    checkAndRemoveWaitingFor();    traceEnd(cookie); // 结束性能追踪}

1.广播处理完成 → finishReceiverActiveLocked(DELIVERY_DONE, "正常完成")

→ cancelDeliveryTimeoutLocked()

├─ mAnrTimer.cancel()

└─ (旧版) Handler.removeMessages()

2.广播超时 → finishReceiverActiveLocked(DELIVERY_TIMEOUT, "超时")

→ mService.appNotResponding()

→ 触发ANR弹窗或日志记录

4.触发AMS的ANR弹窗

@Overridepublic void appNotResponding(@NonNull String processName, int uid,        @NonNull TimeoutRecord timeoutRecord) {    ActivityManagerService.this.appNotResponding(processName, uid, timeoutRecord);}

广播ANR中系统优化方案参考

0

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

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

相关文章

密码学中的概率论与统计学:从频率分析到现代密码攻击

在密码学的攻防博弈中,概率论与统计学始终是破解密码的“利器”。从古典密码时期通过字母频率推测凯撒密码的密钥,到现代利用线性偏差破解DES的线性密码分析,再到侧信道攻击中通过功耗数据的统计特性还原密钥,统计思维贯穿了密码分…

力扣刷题977——有序数组的平方

977. 有序数组的平方 题目: 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。示例 1: 输入:nums [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释&…

应用加速游戏盾的安全作用

在数字娱乐产业蓬勃发展的今天,游戏已从单纯的娱乐工具演变为连接全球数十亿用户的社交平台与文化载体。然而,伴随游戏市场的指数级增长,网络攻击的频率与复杂性也呈爆发式上升。从DDoS攻击导致服务器瘫痪,到外挂程序破坏公平竞技…

linux安装zsh,oh-my-zsh,配置zsh主题及插件的方法

这是一份非常详细的指南,带你一步步在 Linux 系统中安装 Zsh、配置主题和安装插件。 Zsh(Z Shell)是一个功能强大的 Shell,相比于大多数 Linux 发行版默认的 Bash,它提供了更强的自定义能力、更智能的自动补全、更漂亮…

【设计模式系列】策略模式vs模板模式

策略模式是什么?如何定义并封装一系列算法策略模式 (Strategy Pattern)模板模式 (Template Pattern)模板模式与策略模式的深度对比与区分混合使用两种模式的场景策略模式 (Strategy Pattern) 应用场景:当需要根据不同条件选择不同算法或行为时&#xff…

aigc(1.1) opensora-2.0

open sora-2.0相关链接: arxiv链接 huggingface页面 HunyuanVideo VAE open sora2.0的VAE模型复用了HunyuanVideo的3D VAE,HunyuanVideo的arxiv链接。下图来自论文,可见VAE是一个因果注意力的3D结构。在配图左侧,视频会被编码为video token序列,而在配图右侧,去噪的vide…

Linux驱动21 --- FFMPEG 音频 API

目录 一、FFMPEG 音频 API 1.1 解码步骤 创建核心上下文指针 打开输入流 获取输入流 获取解码器 初始化解码器 创建输入流指针 创建输出流指针 初始化 SDL 配置音频参数 打开音频设备 获取一帧数据 发送给解码器 从解码器获取数据 开辟数据空间 初始化内存 音频重采样…

《计算机“十万个为什么”》之 [特殊字符] 序列化与反序列化:数据打包的奇妙之旅 ✈️

《计算机“十万个为什么”》之 📦 序列化与反序列化:数据打包的奇妙之旅 ✈️欢迎来到计算机“十万个为什么”系列! 本文将以「序列化与反序列化」为主题,深入探讨计算机世界中数据的打包与解包过程。 让我们一起解开数据的神秘面…

机器学习与深度学习评价指标

机器学习与深度学习评价指标完全指南 📊 为什么需要评价指标? 想象你是一位医生,需要判断一个诊断模型的好坏。如果模型说"这个病人有癌症",你需要知道: 这个判断有多准确? 会不会漏掉真正的癌症患者? 会不会误诊健康的人? 评价指标就像是给AI模型打分的&…

Hugging Face-环境配置

打开anaconda promptconda activate pytorchpip install -i https://pypi.tuna.tsinghua.edu.cn/simple transformers datasets tokenizerspycharm找到pytorch下的python.exe#将模型下载到本地调用 from transformers import AutoModelForCausalLM,AutoTokenizer#将模型和分词工…

cnn中池化层作用

一、池化层概述 在卷积神经网络中,池化层是核心组件之一,主要作用是逐步降低特征图的空间尺寸即宽和高,从而减少计算量、控制过拟合并增强模型的鲁棒性。 核心作用 降维与减少计算量 压缩特征图的尺寸,显著减少后续层的参数数量和…

写一个音乐爬虫

今天我们写一个网易云音乐的爬虫,爬取网易云音乐热歌榜音乐链接并下载,这里用到了之前引用的BeautifulSoup和requests。 BeautifulSoup是一个Python库,用于从HTML和XML文件中提取数据。它提供了一种简单的方式来遍历文档树和搜索文档树中的元…

战斗公式和伤害走配置文件

故事背景,上次属性计算用的配置,这次伤害计算也走配置,下面是测试代码和测试数据local formulas {[100001]{id 100001,name "基础伤害",formula "function (self,tag,ishit,iscritial,counterratio)\n if ishit1 then\n …

线性代数 上

文章目录线性代数知识整理一、求行列式1、 套公式2、利用性质,化为可套公式3、抽象行列式4、抽象向量二、代数余子式的线性组合三、求AnA^nAn四、证明A可逆五、求A的逆1、定义法2、初等变换3、公式六、求秩七、线性表示的判定八、线性无关九、求极大线性无关组十、等…

红帽AI推理服务器三大特点

生成式人工智能(Gen AI)的迅猛发展,对大型语言模型(LLM)的部署提出了更高的性能、灵活性和效率要求。无论部署在何种环境中,红帽AI推理服务器都为用户提供经过强化并获得官方支持的vLLM发行版,配…

开始记录一步步学习pcl

安装参考,大神写的非常详细,一步到位 https://blog.csdn.net/qq_36812406/article/details/144307648?ops_request_misc%257B%2522request%255Fid%2522%253A%25220e215e6ac266b90ded12ed6b2eab1257%2522%252C%2522scm%2522%253A%252220140713.13010233…

Linux系统Centos7 安装mysql5.7教程 和mysql的简单指令

目录 一. 安装 MySQL 官方 Yum 仓库 二. 安装 MySQL 5.7 1.查看可用的mysql版本仓库 2.启用MySql5.7仓库 3.禁用更高版本的仓库(可选) 4.导入 MySQL GPG 公钥 5.安装MySql5.7 三. 启动 MySQL 服务 1.启动 MySQL 服务 2. 设置开机自启 3.查看服…

嵌入式——C语言:指针③

一、函数指针和指针函数(一)指针函数:是函数,函数的返回值是指针1.不能返回局部变量的值2.指针函数返回的地址可以作为下一个函数调用的参数(二)函数指针:是指针,指针指向一个函数in…

OpenCV(05)直方图均衡化,模板匹配,霍夫变换,图像亮度变换,形态学变换

【OpenCV(01)】基本图像操作、绘制,读取视频 【OpenCV(02)】图像颜色处理,灰度化,二值化,仿射变换 【OpenCV(03)】插值方法,边缘填充&#xff0…

常见的未授权访问漏洞靶场-练习教程

一.Redis未授权访问漏洞1.首先需要准备一个vps和vulhub靶场,然后进入目录。命令:进入靶场目录:cd /etc/vulhub-master/redis/4-unacc 启动靶场:docker-compose up -d2.然后启动我们kali,下载redis服务然后连接redis,并执行命令。…