Modify make crashed apps info persistence and add more info to stackOutputShareContent, stackOutputFileContent in AppErrorsInfoBean, FrameworkHooker, AppErrorsDetailActivity

This commit is contained in:
2023-01-22 14:51:15 +08:00
parent 9dce54f326
commit 4d5dadae8b
3 changed files with 47 additions and 24 deletions

View File

@@ -22,10 +22,10 @@
package com.fankes.apperrorstracking.bean
import android.app.ApplicationErrorReport
import android.content.Context
import android.os.Build
import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.utils.factory.difference
import com.fankes.apperrorstracking.utils.factory.toUtcTime
import com.fankes.apperrorstracking.utils.factory.*
import com.google.gson.annotations.SerializedName
import java.io.Serializable
import java.text.SimpleDateFormat
@@ -35,7 +35,10 @@ import java.util.*
* 应用异常信息 bean
* @param pid 进程 ID
* @param userId 用户 ID
* @param cpuAbi CPU 架构类型
* @param packageName 包名
* @param versionName 版本名称
* @param versionCode 版本号
* @param isNativeCrash 是否为原生层异常
* @param exceptionClassName 异常类名
* @param exceptionMessage 异常信息
@@ -51,8 +54,14 @@ data class AppErrorsInfoBean(
var pid: Int = -1,
@SerializedName("userId")
var userId: Int = -1,
@SerializedName("cpuAbi")
var cpuAbi: String = "",
@SerializedName("packageName")
var packageName: String = "",
@SerializedName("versionName")
var versionName: String = "",
@SerializedName("versionCode")
var versionCode: Long = -1L,
@SerializedName("isNativeCrash")
var isNativeCrash: Boolean = false,
@SerializedName("exceptionClassName")
@@ -77,18 +86,22 @@ data class AppErrorsInfoBean(
/**
* 从 [ApplicationErrorReport.CrashInfo] 克隆
* @param context 当前实例
* @param pid APP 进程 ID
* @param userId APP 用户 ID
* @param packageName APP 包名
* @param crashInfo [ApplicationErrorReport.CrashInfo]
* @return [AppErrorsInfoBean]
*/
fun clone(pid: Int, userId: Int, packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
fun clone(context: Context, pid: Int, userId: Int, packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
AppErrorsInfoBean(
pid = pid,
userId = userId,
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
packageName = packageName ?: "unknown",
versionName = packageName?.let { context.appVersionNameOf(it) } ?: "",
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
isNativeCrash = isNativeCrash,
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
exceptionMessage = if (isNativeCrash) crashInfo?.stackTrace.let {
@@ -119,6 +132,12 @@ data class AppErrorsInfoBean(
*/
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
/**
* 获取 APP 版本信息与版本号
* @return [String]
*/
val versionBrand get() = if (versionName.isBlank()) "unknown" else "$versionName($versionCode)"
/**
* 获取异常本地化 UTC 时间
* @return [String]
@@ -153,18 +172,7 @@ data class AppErrorsInfoBean(
val stackOutputShareContent
get() = "Generated by AppErrorsTracking\n" +
"Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"===============\n" +
"[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
"[System Locale]: ${Locale.getDefault()}\n" +
"[Package Name]: $packageName\n" +
(if (userId > 0) "[User Id]: $userId\n" else "") +
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
"[Crash Time]: $utcTime\n" +
"[Stack Trace]:\n" + stackTrace
"===============\n$environmentInfo"
/**
* 获取异常堆栈文件模板
@@ -175,15 +183,26 @@ data class AppErrorsInfoBean(
" Generated by AppErrorsTracking\n" +
" Project Url: https://github.com/KitsunePie/AppErrorsTracking\n" +
"================================================================\n" +
"[Device Brand]: ${Build.BRAND}\n" +
environmentInfo
/**
* 获取运行环境信息
* @return [String]
*/
private val environmentInfo
get() = "[Device Brand]: ${Build.BRAND}\n" +
"[Device Model]: ${Build.MODEL}\n" +
"[Display]: ${Build.DISPLAY}\n" +
"[Android Version]: ${Build.VERSION.RELEASE}\n" +
"[API Version]: ${Build.VERSION.SDK_INT}\n" +
"[Android API Level]: ${Build.VERSION.SDK_INT}\n" +
"[System Locale]: ${Locale.getDefault()}\n" +
"[Package Name]: $packageName\n" +
"[Process ID]: $pid\n" +
(if (userId > 0) "[User Id]: $userId\n" else "") +
"[Error Type]: ${if (isNativeCrash) "Native" else "Jvm"}\n" +
"[CPU ABI]: ${cpuAbi.ifBlank { "none" }}\n" +
"[Package Name]: $packageName\n" +
"[Version Name]: ${versionName.ifBlank { "unknown" }}\n" +
"[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}\n" +
"[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}\n" +
"[Crash Time]: $utcTime\n" +
"[Stack Trace]:\n" + stackTrace
}

View File

@@ -350,10 +350,11 @@ object FrameworkHooker : YukiBaseHooker() {
/**
* 处理 APP 进程异常数据
* @param context 当前实例
* @param info 系统错误报告数据实例
*/
private fun AppErrorsProcessData.handleAppErrorsInfo(info: ApplicationErrorReport.CrashInfo?) {
AppErrorsRecordData.add(AppErrorsInfoBean.clone(pid, userId, appInfo?.packageName, info))
private fun AppErrorsProcessData.handleAppErrorsInfo(context: Context, info: ApplicationErrorReport.CrashInfo?) {
AppErrorsRecordData.add(AppErrorsInfoBean.clone(context, pid, userId, appInfo?.packageName, info))
loggerI(msg = "Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
}
@@ -441,10 +442,13 @@ object FrameworkHooker : YukiBaseHooker() {
returnType = BooleanType
}
afterHook {
/** 当前实例 */
val context = appContext ?: field { name = "mContext" }.get(instance).cast<Context>() ?: return@afterHook
/** 当前进程信息 */
val proc = args().first().any() ?: return@afterHook loggerW(msg = "Received but got null ProcessRecord")
/** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(args(index = 1).cast())
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
}
}
}

View File

@@ -105,10 +105,10 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
}
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName)
binding.appVersionText.text = appErrorsInfo.versionBrand
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
binding.appCpuAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
binding.appCpuAbiText.text = appErrorsInfo.cpuAbi.ifBlank { LocaleString.noCpuAbi }
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
binding.errorInfoText.text = appErrorsInfo.exceptionMessage