Android12系统源码分析:NativeTombstoneManager

概述

android12新增的system_server进程(LocalService)本地服务,用于管理native tombstones。

该服务在开机Systemerver初始化流程启动,添加到LocalService,然后启动一个ServiceThread线程用于(mHandler.post)处理本服务的业务。

NativeTombstoneManager的功能主要是:

  • 监听/data/tombstones目录文件变动,解析为TombstoneFile对象保存,通知dropbox

  • 特定tombstones文件删除

  • 特定tombstones文件检索

值得关注的是AMS对该服务的使用,也是Android11新增API:getHistoricalProcessExitReasons()

软件架构如图:

图:NativeTombstoneManager类图

启动流程

图:NativeTombstoneManager服务启动时序图

服务比较简单,和其他SystemServer启动的服务一样,

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManagerService.java

public class NativeTombstoneManagerService extends SystemService {
private NativeTombstoneManager mManager; @Override
public void onStart() {
mManager = new NativeTombstoneManager(getContext());
//仅添加本地服务,没有binder服务
LocalServices.addService(NativeTombstoneManager.class, mManager);
} @Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mManager.onSystemReady();
}
}
}

本服务也是SystemService工具类的子类,通过重写onStart、onBootPhase获得代码流程

在onStart中初始化真正的服务实现NativeTombstoneManager,实例化后添加到LocalServices

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

public final class NativeTombstoneManager {
NativeTombstoneManager(Context context) {
//启动handler线程,用于后续处理本服务的业务
final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
thread.start();
mHandler = thread.getThreadHandler();
//启动文件监听/data/tombstones
mWatcher = new TombstoneWatcher();
mWatcher.startWatching();
} void onSystemReady() {
registerForUserRemoval();
registerForPackageRemoval(); // 开机阶段先扫描一次/data/tombstones目录
mHandler.post(() -> {
final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
if (tombstoneFiles[i].isFile()) {
handleTombstone(tombstoneFiles[i]);

开机流程有三个动作

  • 启动handler线程,用于后续处理本服务的业务
  • TombstoneWatcher启动文件监听/data/tombstones
  • 开机阶段先扫描一次/data/tombstones目录

看一下handleTombstone

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

    private void handleTombstone(File path) {
final String filename = path.getName();
if (!filename.startsWith("tombstone_")) {
return;
} if (filename.endsWith(".pb")) {
handleProtoTombstone(path);
BootReceiver.addTombstoneToDropBox(mContext, path, true);
} else {
BootReceiver.addTombstoneToDropBox(mContext, path, false);

如果是以pb结尾的原型文件,则handleProtoTombstone方法中为该文件生成TombstoneFile对象,并添加到数据结构

private final SparseArray<TombstoneFile> mTombstones;

并且,每个新生成的tombstone文件都会同步给dropbox

新文件的监听

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

    class TombstoneWatcher extends FileObserver {
TombstoneWatcher() {
super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
} @Override
public void onEvent(int event, @Nullable String path) {
mHandler.post(() -> {
handleTombstone(new File(TOMBSTONE_DIR, path));
});

内部类TombstoneWatcher,当目录/data/tombstones有文件生成时,回调到onEvent,然后通过handleTombstone方法做处理

AciivtyManager#getHistoricalProcessExitReasons

图:getHistoricalProcessExitReasons方法时序图

需要注意返回的数据结构的处理ApplicationExitInfo。

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

    public void collectTombstones(ArrayList<ApplicationExitInfo> output, int callingUid, int pid,
int maxNum) {
CompletableFuture<Object> future = new CompletableFuture<>(); if (!UserHandle.isApp(callingUid)) {
return;
} final int userId = UserHandle.getUserId(callingUid);
final int appId = UserHandle.getAppId(callingUid); mHandler.post(() -> {
boolean appendedTombstones = false; synchronized (mLock) {
final int tombstonesSize = mTombstones.size(); tombstoneIter:
//遍历所有已知tombstoe,
//如果userid和appid和reason匹配
//则返回请求的数量的tombstone
for (int i = 0; i < tombstonesSize; ++i) {
TombstoneFile tombstone = mTombstones.valueAt(i);
if (tombstone.matches(Optional.of(userId), Optional.of(appId))) {
if (pid != 0 && tombstone.mPid != pid) {
continue;
}
//reason判断
// Try to attach to an existing REASON_CRASH_NATIVE.
final int outputSize = output.size();
for (int j = 0; j < outputSize; ++j) {
ApplicationExitInfo exitInfo = output.get(j);
if (tombstone.matches(exitInfo)) {
exitInfo.setNativeTombstoneRetriever(tombstone.getPfdRetriever());
continue tombstoneIter;
}
}
//请求数量判断
if (output.size() < maxNum) {
appendedTombstones = true;
output.add(tombstone.toAppExitInfo());
}
}
}
}
//如果超过一个则按时间戳排序
if (appendedTombstones) {
Collections.sort(output, (lhs, rhs) -> {
// Reports should be ordered with newest reports first.
long diff = rhs.getTimestamp() - lhs.getTimestamp();
if (diff < 0) {
return -1;
} else if (diff == 0) {
return 0;
} else {
return 1;
}
});
}
future.complete(null);
}); try {
future.get();

遍历所有已知tombstoe,如果userid和appid和reason匹配则返回请求的数量的tombstone

如果数量超过一个则按时间戳排序

值得注意的是CompletableFuture,函数式编程,可参考:CompletableFuture基本用法

tombstone文件的清理

目前有两种场景会清理文件

  • 主动调用接口删除,AppExitInfoTracker-->purge()
  • app被卸载时,registerForPackageRemoval-->purgePackage()-->purge()

clearHistoryProcessExitInfo

frameworks/base/services/core/java/com/android/server/am/AppExitInfoTracker.java

    void clearHistoryProcessExitInfo(String packageName, int userId) {
NativeTombstoneManager tombstoneService = LocalServices.getService(
NativeTombstoneManager.class);
Optional<Integer> appId = Optional.empty(); if (TextUtils.isEmpty(packageName)) {
synchronized (mLock) {
removeByUserIdLocked(userId);
}
} else {
final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
PackageManager.MATCH_ALL, userId);
appId = Optional.of(UserHandle.getAppId(uid));
synchronized (mLock) {
removePackageLocked(packageName, uid, true, userId);
}
} tombstoneService.purge(Optional.of(userId), appId);
schedulePersistProcessExitInfo(true);
}

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

     * Remove native tombstones matching a user and/or app.
public void purge(Optional<Integer> userId, Optional<Integer> appId) {
mHandler.post(() -> {
synchronized (mLock) {
for (int i = mTombstones.size() - 1; i >= 0; --i) {
TombstoneFile tombstone = mTombstones.valueAt(i);
if (tombstone.matches(userId, appId)) {
tombstone.purge();
mTombstones.removeAt(i);
----------------------------------------------------------------------
static class TombstoneFile {
public void purge() {
if (!mPurged) {
try {
Os.ftruncate(mPfd.getFileDescriptor(), 0);
} catch (ErrnoException ex) {
Slog.e(TAG, "Failed to truncate tombstone", ex);
}
mPurged = true;

purgePackage

frameworks/base/services/core/java/com/android/server/os/NativeTombstoneManager.java

    private void registerForPackageRemoval() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
filter.addDataScheme("package");
mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
if (uid == UserHandle.USER_NULL) return; final boolean allUsers = intent.getBooleanExtra(
Intent.EXTRA_REMOVED_FOR_ALL_USERS, false); purgePackage(uid, allUsers);
}
}, filter, null, mHandler);
}
---------------------------------------------
private void purgePackage(int uid, boolean allUsers) {
final int appId = UserHandle.getAppId(uid);
Optional<Integer> userId;
if (allUsers) {
userId = Optional.empty();
} else {
userId = Optional.of(UserHandle.getUserId(uid));
}
purge(userId, Optional.of(appId));
} private void purgeUser(int uid) {
purge(Optional.of(uid), Optional.empty());
}

f服务启动时registerForPackageRemoval调用,开启对广播的监听:ACTION_PACKAGE_FULLY_REMOVED

当app卸载时,此处也删除其对应uid的tombstone文件

同这个包删除类似,还有用户删除时也会删对应的文件:

    private void registerForUserRemoval() {
filter.addAction(Intent.ACTION_USER_REMOVED);

Android12系统源码分析:NativeTombstoneManager的更多相关文章

  1. 开源系统源码分析(filter.class.php)

    <?php class baseValidater { //最大参数个数 const MAX_ARGS=3; public static function checkBool($var) { r ...

  2. Redis学习之对象系统源码分析

    背景知识: Redis并没有直接使用sds,双端链表,字典,压缩列表,跳表等这些数据结构来直接实现键值对数据库,而是基于这些对象创建了一个对象系统,这个对象系统包含5个对象:字符串对象,列表对象,哈希 ...

  3. bootstrap_栅格系统_响应式工具_源码分析

    -----------------------------------------------------------------------------margin 为负 ​使盒子重叠 ​等高 等高 ...

  4. 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 百篇博客分析OpenHarmony源码 | v7.07

    百篇博客系列篇.本篇为: v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  5. 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 百篇博客分析OpenHarmony源码 | v5.05

    百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 任务池是如何管理的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度谁 ...

  6. 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 百篇博客分析OpenHarmony源码 | v4.05

    百篇博客系列篇.本篇为: v04.xx 鸿蒙内核源码分析(任务调度篇) | 任务是内核调度的单元 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调度 ...

  7. 告别IT,出售多年自己研发的股票分析系统源码

    不知已过而立,发狠告别IT,回头看看以前自己的多个作品,耗时最多的就是这个股票分析系统了,留在自己的电脑里也体现不出多大价值了,故打算出售源码给需要的人,联系方式QQ:874724605 注明:股票源 ...

  8. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

  9. 【精】EOS智能合约:system系统合约源码分析

    系统合约在链启动阶段就会被部署,是因为系统合约赋予了EOS链资源.命名拍卖.基础数据准备.生产者信息.投票等能力.本篇文章将会从源码角度详细研究system合约. 关键字:EOS,eosio.syst ...

随机推荐

  1. C#中调用c++的dll具体创建与调用步骤,亲测有效~ (待验证)

    使用的工具是VS2010哦~其他工具暂时还没试过 我新建的工程名是my21dll,所以会生成2个同名文件.接下来需要改动的只有画横线的部分 下面是my21dll.h里面的... 下面的1是自动生成的不 ...

  2. JDBC中级篇(MYSQL)——模拟从数据库中上传下载附件

    注意:其中的JdbcUtil是我自定义的连接工具类:代码例子链接: package b_blob_clob; import java.io.BufferedOutputStream; import j ...

  3. docker安装与配置gitlab详细过程

    docker安装与配置gitlab详细过程 1.打开网易镜像中心 https://c.163yun.com/hub#/m/home/ 2.搜索gitlab,获取下载地址.例如:docker pull  ...

  4. mysql 8.0 1405的坑

    因版本差异:password  可能对应:authentication_string字段 不行的话再尝试一下,并且修改下密码类型 ALTER USER 'root'@'%' IDENTIFIED WI ...

  5. C# - 习题03_分析代码写出结果A.X、B.Y

    时间:2017-08-23 整理:byzqy 题目:分析代码,写出程序的输出结果: 文件:Program.cs 1 using System; 2 3 namespace Interview2 4 { ...

  6. JavaScript 特殊字符

    代码输出\'单引号\"双引号\&和号\\反斜杠\n换行符\r回车符\t制表符\b退格符\f换页符

  7. Linux从头学10:三级跳过程详解-从 bootloader 到 操作系统,再到应用程序

    作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...

  8. mybaits源码分析--自定义插件(七)

    一.MyBatis插件 插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来扩展或者改变原有的功能,MyBatis中也提供的有插件,虽然叫插件,但是实际上是通过拦截器(Int ...

  9. Springboot 整合通用mapper和pagehelper展示分页数据(附github源码)

    简介 springboot 设计目的就是为了加速开发,减少xml的配置.如果你不想写配置文件只需要在配置文件添加相对应的配置就能快速的启动的程序. 通用mapp 通用mapper只支持对单表的操作,对 ...

  10. ☕【Java技术指南】「难点-核心-遗漏」Java线程状态流转及生命周期的技术指南(知识点串烧)!

    前提介绍 本章主要介绍相关线程声明周期的转换机制以及声明周期的流转关系以及相关AQS的实现和相关的基本原理,配合这相关官方文档的中英文互译的介绍. 线程状态流转及生命周期 当线程被创建并启动以后,它既 ...