From 2472f8b7e52959a26dfd3aee3650b6237299603c Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Mon, 3 Oct 2022 07:19:13 +0800 Subject: [PATCH] Fix some custom system can't read application crash info on-time will get wrong errors data problem --- .../bean/AppErrorsDisplayBean.kt | 2 + .../bean/AppErrorsInfoBean.kt | 37 +++++++++++++------ .../hook/entity/FrameworkHooker.kt | 22 ++++++++--- .../errors/AppErrorsDisplayActivity.kt | 12 +++--- .../utils/tool/FrameworkTool.kt | 23 ++++++++++++ 5 files changed, 71 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsDisplayBean.kt b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsDisplayBean.kt index fe82394..dc1158c 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsDisplayBean.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsDisplayBean.kt @@ -26,6 +26,7 @@ import java.io.Serializable /** * 应用异常信息显示 bean + * @param pid APP 进程 ID * @param userId APP 用户 ID * @param packageName APP 包名 * @param processName APP 进程名 @@ -36,6 +37,7 @@ import java.io.Serializable * @param isShowReopenButton 是否显示重新打开按钮 */ data class AppErrorsDisplayBean( + @Keep var pid: Int, @Keep var userId: Int, @Keep var packageName: String, @Keep var processName: String, diff --git a/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt index c7e489f..08bc4f7 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt @@ -33,6 +33,7 @@ import java.util.* /** * 应用异常信息 bean + * @param pid 进程 ID * @param userId 用户 ID * @param packageName 包名 * @param isNativeCrash 是否为原生层异常 @@ -46,31 +47,40 @@ import java.util.* * @param timestamp 记录时间戳 */ data class AppErrorsInfoBean( - @Keep var userId: Int, - @Keep var packageName: String, - @Keep var isNativeCrash: Boolean, - @Keep var exceptionClassName: String, - @Keep var exceptionMessage: String, - @Keep var throwFileName: String, - @Keep var throwClassName: String, - @Keep var throwMethodName: String, - @Keep var throwLineNumber: Int, - @Keep var stackTrace: String, - @Keep var timestamp: Long, + @Keep var pid: Int = -1, + @Keep var userId: Int = -1, + @Keep var packageName: String = "", + @Keep var isNativeCrash: Boolean = false, + @Keep var exceptionClassName: String = "", + @Keep var exceptionMessage: String = "", + @Keep var throwFileName: String = "", + @Keep var throwClassName: String = "", + @Keep var throwMethodName: String = "", + @Keep var throwLineNumber: Int = -1, + @Keep var stackTrace: String = "", + @Keep var timestamp: Long = -1L ) : Serializable { companion object { + /** + * 创建一个空的 [AppErrorsInfoBean] + * @return [AppErrorsInfoBean] + */ + fun createEmpty() = AppErrorsInfoBean().apply { isEmpty = true } + /** * 从 [ApplicationErrorReport.CrashInfo] 克隆 + * @param pid APP 进程 ID * @param packageName APP 包名 * @param userId APP 用户 ID * @param crashInfo [ApplicationErrorReport.CrashInfo] * @return [AppErrorsInfoBean] */ - fun clone(packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) = + fun clone(pid: Int, packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) = (crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash -> AppErrorsInfoBean( + pid = pid, userId = userId ?: 0, packageName = packageName ?: "unknown", isNativeCrash = isNativeCrash, @@ -91,6 +101,9 @@ data class AppErrorsInfoBean( } } + /** 标识当前内容是否为空 */ + var isEmpty = false + /** * 获取异常本地化 UTC 时间 * @return [String] diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index 6d462dc..78cf15d 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -76,7 +76,7 @@ object FrameworkHooker : YukiBaseHooker() { ) /** 已记录的 APP 用户 ID */ - private var appUserIdRecords = HashMap() + private var appUserIdRecords = HashMap() /** 已忽略错误的 APP 数组 - 直到重新解锁 */ private var mutedErrorsIfUnlockApps = HashSet() @@ -99,6 +99,7 @@ object FrameworkHooker : YukiBaseHooker() { } FrameworkTool.Host.with(instance = this) { onOpenAppUsedFramework { appContext?.openApp(it.first, it.second) } + onPushAppErrorInfoData { appErrorsRecords.firstOrNull { e -> e.pid == it } ?: AppErrorsInfoBean.createEmpty() } onPushAppErrorsInfoData { appErrorsRecords } onRemoveAppErrorsInfoData { appErrorsRecords.remove(it) @@ -257,7 +258,7 @@ object FrameworkHooker : YukiBaseHooker() { /** 崩溃标题 */ val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName) /** 写入到用户 ID 记录 */ - appUserIdRecords[proc.toString()] = userId + appUserIdRecords[pid] = userId /** 打印错误日志 */ if (isApp) loggerE( msg = "App \"$packageName\"${if (packageName != processName) " --process \"$processName\"" else ""}" + @@ -298,6 +299,7 @@ object FrameworkHooker : YukiBaseHooker() { /** 启动错误对话框显示窗口 */ AppErrorsDisplayActivity.start( context, AppErrorsDisplayBean( + pid = pid, userId = userId, packageName = packageName, processName = processName, @@ -320,12 +322,20 @@ object FrameworkHooker : YukiBaseHooker() { /** 当前进程信息 */ val proc = args().first().any() + /** 当前 pid 信息 */ + val pid = ProcessRecordClass.toClass().field { name { it == "mPid" || it == "pid" } }.get(proc).int() + /** 当前 APP 信息 */ val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast() - /** 添加当前异常信息到第一位 */ - appErrorsRecords.add( - 0, AppErrorsInfoBean.clone(appInfo?.packageName, appUserIdRecords[proc.toString()], args().last().cast()) - ) + /** 启动新线程延迟防止方法执行顺序在前导致无法正确获取数据 */ + newThread { + /** 延迟 50ms */ + Thread.sleep(50) + /** 添加当前异常信息到第一位 */ + appErrorsRecords.add( + 0, AppErrorsInfoBean.clone(pid, appInfo?.packageName, appUserIdRecords[pid], args().last().cast()) + ) + } /** 保存异常记录到本地 */ saveAllAppErrorsRecords() } diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt index 0e70190..e26a9fd 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/errors/AppErrorsDisplayActivity.kt @@ -84,13 +84,11 @@ class AppErrorsDisplayActivity : BaseActivity() cancel() } binding.errorDetailItem.setOnClickListener { - FrameworkTool.fetchAppErrorsInfoData(context) { appErrorsInfos -> - appErrorsInfos.takeIf { it.isNotEmpty() } - ?.filter { it.packageName == appErrorsDisplay.packageName } - ?.takeIf { it.isNotEmpty() }?.get(0)?.let { - AppErrorsDetailActivity.start(context, it) - cancel() - } ?: toast(msg = "No errors founded") + FrameworkTool.fetchAppErrorInfoData(context, appErrorsDisplay.pid) { appErrorsInfo -> + appErrorsInfo.takeIf { it.isEmpty.not() }?.also { + AppErrorsDetailActivity.start(context, it) + cancel() + } ?: toast(LocaleString.unableGetAppErrorsRecordTip) } } binding.mutedIfUnlockItem.setOnClickListener { diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt index afd5ee3..b2fdff5 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/tool/FrameworkTool.kt @@ -56,10 +56,12 @@ object FrameworkTool { private const val CALL_UNMUTE_ERRORS_APP_DATA_RESULT = "call_unmute_errors_app_data_result" private const val CALL_UNMUTE_ALL_ERRORS_APPS_DATA_RESULT = "call_unmute_all_errors_apps_data_result" + private val CALL_APP_ERROR_DATA_GET = ChannelData("call_app_error_data_get") private val CALL_OPEN_SPECIFY_APP = ChannelData>("call_open_specify_app") private val CALL_APP_LIST_DATA_GET = ChannelData("call_app_info_list_data_get") private val CALL_APP_ERRORS_DATA_REMOVE = ChannelData("call_app_errors_data_remove") private val CALL_APP_LIST_DATA_GET_RESULT = ChannelData>("call_app_info_list_data_get_result") + private val CALL_APP_ERROR_DATA_GET_RESULT = ChannelData("call_app_error_data_get_result") private val CALL_APP_ERRORS_DATA_GET_RESULT = ChannelData>("call_app_errors_data_get_result") private val CALL_MUTED_ERRORS_APP_DATA_GET_RESULT = ChannelData>("call_muted_app_errors_data_get_result") private val CALL_UNMUTE_ERRORS_APP_DATA = ChannelData("call_unmute_errors_app_data") @@ -88,6 +90,14 @@ object FrameworkTool { */ fun onOpenAppUsedFramework(result: (Pair) -> Unit) = instance?.dataChannel?.wait(CALL_OPEN_SPECIFY_APP) { result(it) } + /** + * 监听发送指定 APP 异常信息 + * @param result 回调数据 + */ + fun onPushAppErrorInfoData(result: (Int) -> AppErrorsInfoBean) { + instance?.dataChannel?.with { wait(CALL_APP_ERROR_DATA_GET) { put(CALL_APP_ERROR_DATA_GET_RESULT, result(it)) } } + } + /** * 监听发送 APP 异常信息数组 * @param result 回调数据 @@ -235,6 +245,19 @@ object FrameworkTool { fun openAppUsedFramework(context: Context, packageName: String, userId: Int) = context.dataChannel(SYSTEM_FRAMEWORK_NAME).put(CALL_OPEN_SPECIFY_APP, Pair(packageName, userId)) + /** + * 获取指定 APP 异常信息 + * @param context 实例 + * @param pid 当前进程 ID + * @param result 回调数据 + */ + fun fetchAppErrorInfoData(context: Context, pid: Int, result: (AppErrorsInfoBean) -> Unit) { + context.dataChannel(SYSTEM_FRAMEWORK_NAME).with { + wait(CALL_APP_ERROR_DATA_GET_RESULT) { result(it) } + put(CALL_APP_ERROR_DATA_GET, pid) + } + } + /** * 获取 APP 异常信息数组 * @param context 实例