Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ androidComponents {
// Workaround for GitHub Actions.
// Strongly transfer type to [String].
@Suppress("UNNECESSARY_SAFE_CALL")
val currentSuffix = gropify.github.ci.commit.id?.let { suffix ->
val currentSuffix = gropify.github.ci.commit.id?.let { suffix: String ->
if (suffix.isNotBlank()) "-$suffix" else ""
}
val currentVersion = "${output.versionName.get()}$currentSuffix(${output.versionCode.get()})"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import java.util.Locale
* @param targetSdk 目标 SDK 版本
* @param minSdk 最低 SDK 版本
* @param isNativeCrash 是否为原生层异常
* @param isAnr 是否为 ANR (Application Not Responding)
* @param exceptionClassName 异常类名
* @param exceptionMessage 异常信息
* @param throwClassName 抛出异常的类名
Expand Down Expand Up @@ -78,6 +79,8 @@ data class AppErrorsInfoBean(
var minSdk: Int = -1,
@SerializedName("isNativeCrash")
var isNativeCrash: Boolean = false,
@SerializedName("isAnr")
var isAnr: Boolean = false,
@SerializedName("exceptionClassName")
var exceptionClassName: String = "",
@SerializedName("exceptionMessage")
Expand Down Expand Up @@ -134,6 +137,37 @@ data class AppErrorsInfoBean(
timestamp = System.currentTimeMillis()
)
}

/**
* 从 [ApplicationErrorReport.AnrInfo] 克隆
* @param context 当前实例
* @param pid APP 进程 ID
* @param userId APP 用户 ID
* @param packageName APP 包名
* @param anrInfo [ApplicationErrorReport.AnrInfo]
* @return [AppErrorsInfoBean]
*/
fun cloneAnr(context: Context, pid: Int, userId: Int, packageName: String?, anrInfo: ApplicationErrorReport.AnrInfo?) =
AppErrorsInfoBean(
pid = pid,
userId = userId,
cpuAbi = packageName?.let { context.appCpuAbiOf(it) } ?: "",
packageName = packageName ?: "unknown",
versionName = packageName?.let { context.appVersionNameOf(it).ifBlank { "unknown" } } ?: "",
versionCode = packageName?.let { context.appVersionCodeOf(it) } ?: -1L,
targetSdk = packageName?.let { context.appTargetSdkOf(it) } ?: -1,
minSdk = packageName?.let { context.appMinSdkOf(it) } ?: -1,
isNativeCrash = false,
isAnr = true,
exceptionClassName = "ANR",
exceptionMessage = anrInfo?.cause ?: "Application Not Responding",
throwFileName = anrInfo?.activity?.toString() ?: "unknown",
throwClassName = packageName ?: "unknown",
throwMethodName = "unknown",
throwLineNumber = -1,
stackTrace = anrInfo?.info?.trim() ?: "unknown",
timestamp = System.currentTimeMillis()
)
}

/**
Expand Down Expand Up @@ -248,7 +282,11 @@ data class AppErrorsInfoBean(
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
[Target SDK]: ${targetSdk.takeIf { it != -1 } ?: "unknown"}
[Min SDK]: ${minSdk.takeIf { it != -1 } ?: "unknown"}
[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}
[Error Type]: ${when {
isAnr -> "ANR"
isNativeCrash -> "Native"
else -> "JVM"
}}
[Crash Time]: $utcTime
[Stack Trace]:
""".trimIndent() + "\n$stackTrace"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ object FrameworkHooker : YukiBaseHooker() {
* @param errors [AppErrorsClass] 实例
* @param proc [ProcessRecordClass] 实例
* @param resultData [AppErrorDialog_DataClass] 实例 - 默认空
* @param isAnr 是否为 ANR - 默认 false
*/
private class AppErrorsProcessData(errors: Any?, proc: Any?, resultData: Any? = null) {
private class AppErrorsProcessData(errors: Any?, proc: Any?, resultData: Any? = null, val isAnr: Boolean = false) {

/**
* 获取当前包列表实例
Expand Down Expand Up @@ -330,7 +331,12 @@ object FrameworkHooker : YukiBaseHooker() {
val appNameWithUserId = if (userId != 0) "$appName (${locale.userId(userId)})" else appName

/** 崩溃标题 */
val errorTitle = if (isRepeatingCrash) locale.aerrRepeatedTitle(appNameWithUserId) else locale.aerrTitle(appNameWithUserId)
val errorTitle = when {
isAnr && isRepeatingCrash -> locale.anrRepeatedTitle(appNameWithUserId)
isAnr -> locale.anrTitle(appNameWithUserId)
isRepeatingCrash -> locale.aerrRepeatedTitle(appNameWithUserId)
else -> locale.aerrTitle(appNameWithUserId)
}

/** 使用通知推送异常信息 */
fun showAppErrorsWithNotify() =
Expand Down Expand Up @@ -408,6 +414,16 @@ object FrameworkHooker : YukiBaseHooker() {
YLog.info("Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
}

/**
* 处理 APP 进程 ANR 数据
* @param context 当前实例
* @param info ANR 错误报告数据实例
*/
private fun AppErrorsProcessData.handleAppAnrInfo(context: Context, info: ApplicationErrorReport.AnrInfo?) {
AppErrorsRecordData.add(AppErrorsInfoBean.cloneAnr(context, pid, userId, appInfo?.packageName, info))
YLog.info("Received ANR application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
}

override fun onHook() {
/** 注册生命周期 */
registerLifecycle()
Expand Down Expand Up @@ -502,6 +518,31 @@ object FrameworkHooker : YukiBaseHooker() {
/** 创建 APP 进程异常数据类 */
AppErrorsProcessData(instance, proc).handleAppErrorsInfo(context, args(index = 1).cast())
}
/** Hook ANR handling methods */
firstMethodOrNull {
name = "appNotResponding"
}?.hook()?.after {
/** 当前实例 */
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after

/** 当前进程信息 - 第一个参数是 ProcessRecord */
val proc = args().first().any() ?: return@after YLog.warn("Received ANR but got null ProcessRecord")

/** 创建 APP 进程异常数据类并展示 ANR UI */
AppErrorsProcessData(instance, proc, isAnr = true).handleShowAppErrorUi(context)
}
firstMethodOrNull {
name = "handleAnrInActivityController"
returnType = Boolean::class
}?.hook()?.after {
/** 当前实例 */
val context = appContext ?: firstFieldOrNull { name = "mContext" }?.of(instance)?.get<Context>() ?: return@after

/** 当前进程信息 */
val proc = args().first().any() ?: return@after YLog.warn("Received ANR but got null ProcessRecord")
/** 创建 ANR 数据 - args(1) 应该包含 AnrInfo */
AppErrorsProcessData(instance, proc, isAnr = true).handleAppAnrInfo(context, args(index = 1).cast())
}
}
}
}
2 changes: 2 additions & 0 deletions module-app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">アプリを閉じる</string>
<string name="aerr_title">%1$s が停止しました</string>
<string name="aerr_repeated_title">%1$s が繰り返し停止しています</string>
<string name="anr_title">%1$s は応答していません</string>
<string name="anr_repeated_title">%1$s は繰り返し応答していません</string>
<string name="mute_if_unlock_tip">デバイスのロックが解除されるまで「%1$s」のエラーをミュートにします</string>
<string name="mute_if_restart_tip">デバイスが再起動されるまで「%1$s」のエラーをミュートにします</string>
<string name="back">戻る</string>
Expand Down
2 changes: 2 additions & 0 deletions module-app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">关闭应用</string>
<string name="aerr_title">%1$s 已停止运行</string>
<string name="aerr_repeated_title">%1$s 屡次停止运行</string>
<string name="anr_title">%1$s 无响应</string>
<string name="anr_repeated_title">%1$s 屡次无响应</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的错误直到设备重新解锁</string>
<string name="mute_if_restart_tip">忽略“%1$s”的错误直到设备重新启动</string>
<string name="back">返回</string>
Expand Down
2 changes: 2 additions & 0 deletions module-app/src/main/res/values-zh-rHK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">結束程式</string>
<string name="aerr_title">%1$s 已停止運作</string>
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
<string name="anr_title">%1$s 無回應</string>
<string name="anr_repeated_title">%1$s 屢次無回應</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
<string name="back">回退</string>
Expand Down
2 changes: 2 additions & 0 deletions module-app/src/main/res/values-zh-rMO/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">結束程式</string>
<string name="aerr_title">%1$s 已停止運作</string>
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
<string name="anr_title">%1$s 無回應</string>
<string name="anr_repeated_title">%1$s 屢次無回應</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
<string name="back">回退</string>
Expand Down
2 changes: 2 additions & 0 deletions module-app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">結束程式</string>
<string name="aerr_title">%1$s 已停止運作</string>
<string name="aerr_repeated_title">%1$s 屢次停止運作</string>
<string name="anr_title">%1$s 無回應</string>
<string name="anr_repeated_title">%1$s 屢次無回應</string>
<string name="mute_if_unlock_tip">忽略“%1$s”的錯誤直到裝置重新展示</string>
<string name="mute_if_restart_tip">忽略“%1$s”的錯誤直到裝置重新開機</string>
<string name="back">回退</string>
Expand Down
2 changes: 2 additions & 0 deletions module-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<string name="close_app">Close App</string>
<string name="aerr_title">%1$s has stopped</string>
<string name="aerr_repeated_title">%1$s keeps stopping</string>
<string name="anr_title">%1$s isn\'t responding</string>
<string name="anr_repeated_title">%1$s keeps not responding</string>
<string name="mute_if_unlock_tip">Muted errors for \'%1$s\' until device is re-unlocked</string>
<string name="mute_if_restart_tip">Muted errors for \'%1$s\' until device reboots</string>
<string name="back">Back</string>
Expand Down