Modify merge to new way to save and read the app errors record data

This commit is contained in:
2023-01-17 03:17:37 +08:00
parent 587c718d0a
commit 7c0c1754e9
4 changed files with 147 additions and 46 deletions

View File

@@ -104,6 +104,12 @@ data class AppErrorsInfoBean(
/** 标识当前内容是否为空 */
var isEmpty = false
/**
* 获取生成的 Json 文件名
* @return [String]
*/
val jsonFileName get() = "${packageName}_${pid}_${timestamp}.json"
/**
* 获取异常本地化 UTC 时间
* @return [String]

View File

@@ -0,0 +1,132 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
* https://github.com/KitsunePie/AppErrorsTracking
*
* This software is non-free but opensource software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and eula along with this software. If not, see
* <https://www.gnu.org/licenses/>
*
* This file is Created by fankes on 2023/1/17.
*/
@file:Suppress("StaticFieldLeak")
package com.fankes.apperrorstracking.data
import android.content.Context
import android.provider.Settings
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.utils.factory.toEntityOrNull
import com.fankes.apperrorstracking.utils.factory.toJsonOrNull
import com.highcapable.yukihookapi.hook.log.loggerE
import java.io.File
import java.util.concurrent.CopyOnWriteArrayList
/**
* [AppErrorsInfoBean] 存储控制类
*/
object AppErrorsRecordData {
/** 异常记录数据文件目录路径 */
private const val FOLDER_PATH = "/data/misc/app_errors_records/"
/** 当前实例 */
private var context: Context? = null
/**
* 获取当前异常记录数据目录
* @return [File]
*/
private val errorsInfoDataFolder by lazy { File(FOLDER_PATH) }
/**
* 获取当前全部异常记录数据文件
* @return [File]
*/
private val errorsInfoDataFiles get() = errorsInfoDataFolder.listFiles() ?: emptyArray()
/** 已记录的全部 APP 异常信息数组 */
var allData = CopyOnWriteArrayList<AppErrorsInfoBean>()
/**
* 初始化存储控制类
* @param context 实例
*/
fun init(context: Context) {
this.context = context
initializeDataDirectory()
allData = readAllDataFromFiles()
}
/** 初始化异常记录数据目录 */
private fun initializeDataDirectory() {
runCatching {
errorsInfoDataFolder.also { if (it.exists().not() || it.isFile) it.apply { delete(); mkdirs() } }
}.onFailure {
loggerE(msg = "Can't create directory \"$FOLDER_PATH\", there will be problems with the app errors records function", e = it)
}
}
/**
* 获取旧版异常记录数据并自动转换到新版
* @return [ArrayList]<[AppErrorsInfoBean]> or null
*/
private fun copyOldDataFromResolverString() = context?.let {
val keyName = "app_errors_data"
runCatching {
Settings.Secure.getString(it.contentResolver, keyName)
?.toEntityOrNull<CopyOnWriteArrayList<AppErrorsInfoBean>>()
?.onEach { e ->
e.toJsonOrNull()?.also { json -> File(errorsInfoDataFolder.absolutePath, e.jsonFileName).writeText(json) }
}.let { result ->
if (result != null) {
Settings.Secure.putString(it.contentResolver, keyName, "")
result
} else null
}
}.getOrNull()
}
/**
* 从文件获取全部异常记录数据
* @return [ArrayList]<[AppErrorsInfoBean]>
*/
private fun readAllDataFromFiles() = copyOldDataFromResolverString() ?: CopyOnWriteArrayList<AppErrorsInfoBean>().apply {
errorsInfoDataFiles.takeIf { it.isNotEmpty() }?.forEach { it.readText().toEntityOrNull<AppErrorsInfoBean>()?.let { e -> add(e) } }
}
/**
* 添加新的异常记录数据
* @param bean [AppErrorsInfoBean] 实例
*/
fun add(bean: AppErrorsInfoBean) {
allData.add(0, bean)
bean.toJsonOrNull()?.runCatching { File(errorsInfoDataFolder.absolutePath, bean.jsonFileName).writeText(this) }
}
/**
* 移除指定的异常记录数据
* @param bean [AppErrorsInfoBean] 实例
*/
fun remove(bean: AppErrorsInfoBean) {
allData.remove(bean)
runCatching { File(errorsInfoDataFolder.absolutePath, bean.jsonFileName).delete() }
}
/** 清除全部异常记录数据 */
fun clearAll() {
allData.clear()
runCatching { errorsInfoDataFolder.deleteRecursively() }
initializeDataDirectory()
}
}

View File

@@ -23,13 +23,10 @@
package com.fankes.apperrorstracking.data
import android.content.ContentResolver
import android.content.Context
import android.os.Build
import android.provider.Settings
import android.widget.CompoundButton
import com.highcapable.yukihookapi.hook.factory.modulePrefs
import com.highcapable.yukihookapi.hook.log.loggerE
import com.highcapable.yukihookapi.hook.log.loggerW
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
@@ -39,9 +36,6 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
*/
object ConfigData {
/** 存取全部应用异常数据的键值名称 */
const val APP_ERRORS_DATA = "app_errors_data"
/** 显示开发者提示 */
val SHOW_DEVELOPER_NOTICE = PrefsData("_show_developer_notice", true)
@@ -114,27 +108,6 @@ object ConfigData {
}
}
/**
* 获取 [ContentResolver] 字符串数据 (仅限 Hook 进程)
* @param key 键值名称
* @return [String]
*/
fun getResolverString(key: String) =
runCatching { (instance as? PackageParam)?.appContext?.let { Settings.Secure.getString(it.contentResolver, key) } }.getOrNull() ?: ""
/**
* 存入 [ContentResolver] 字符串数据 (仅限 Hook 进程)
* @param key 键值名称
* @param value 键值数据
*/
fun putResolverString(key: String, value: String) {
runCatching {
(instance as? PackageParam)?.appContext?.also { Settings.Secure.putString(it.contentResolver, key, value) }
}.onFailure {
loggerE(msg = "Write secure settings failed", e = it)
}
}
/**
* 是否显示开发者提示
* @return [Boolean]

View File

@@ -39,6 +39,7 @@ import com.fankes.apperrorstracking.bean.AppErrorsDisplayBean
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.bean.AppInfoBean
import com.fankes.apperrorstracking.bean.MutedErrorsAppBean
import com.fankes.apperrorstracking.data.AppErrorsRecordData
import com.fankes.apperrorstracking.data.ConfigData
import com.fankes.apperrorstracking.data.factory.isAppShowErrorsDialog
import com.fankes.apperrorstracking.data.factory.isAppShowErrorsNotify
@@ -87,9 +88,6 @@ object FrameworkHooker : YukiBaseHooker() {
/** 已忽略错误的 APP 数组 - 直到重新启动 */
private var mutedErrorsIfRestartApps = HashSet<String>()
/** 已记录的 APP 异常信息数组 */
private var appErrorsRecords = ArrayList<AppErrorsInfoBean>()
/**
* APP 进程异常数据定义类
* @param errors [AppErrorsClass] 实例
@@ -179,8 +177,8 @@ object FrameworkHooker : YukiBaseHooker() {
registerReceiver(Intent.ACTION_USER_PRESENT) { _, _ -> mutedErrorsIfUnlockApps.clear() }
/** 刷新模块 Resources 缓存 */
registerReceiver(Intent.ACTION_LOCALE_CHANGED) { _, _ -> refreshModuleAppResources() }
/** 启动时从本地获取异常记录 */
onCreate { appErrorsRecords = ConfigData.getResolverString(ConfigData.APP_ERRORS_DATA).toEntity() ?: arrayListOf() }
/** 启动时从本地获取异常记录数据 */
onCreate { AppErrorsRecordData.init(context = this) }
}
FrameworkTool.Host.with(instance = this) {
onOpenAppUsedFramework {
@@ -188,21 +186,19 @@ object FrameworkHooker : YukiBaseHooker() {
loggerI(msg = "Opened \"${it.first}\"${it.second.takeIf { e -> e > 0 }?.let { e -> " --user $e" } ?: ""}")
}
onPushAppErrorInfoData {
appErrorsRecords.firstOrNull { e -> e.pid == it } ?: run {
AppErrorsRecordData.allData.firstOrNull { e -> e.pid == it } ?: run {
loggerW(msg = "Cannot received crash application data --pid $it")
AppErrorsInfoBean.createEmpty()
}
}
onPushAppErrorsInfoData { appErrorsRecords }
onPushAppErrorsInfoData { AppErrorsRecordData.allData.toArrayList() }
onRemoveAppErrorsInfoData {
loggerI(msg = "Removed app errors info data for package \"${it.packageName}\"")
appErrorsRecords.remove(it)
saveAllAppErrorsRecords()
AppErrorsRecordData.remove(it)
}
onClearAppErrorsInfoData {
loggerI(msg = "Cleared all app errors info data, size ${appErrorsRecords.size}")
appErrorsRecords.clear()
saveAllAppErrorsRecords()
loggerI(msg = "Cleared all app errors info data, size ${AppErrorsRecordData.allData.size}")
AppErrorsRecordData.clearAll()
}
onMutedErrorsIfUnlock {
mutedErrorsIfUnlockApps.add(it)
@@ -259,9 +255,6 @@ object FrameworkHooker : YukiBaseHooker() {
}
}
/** 保存异常记录到本地 */
private fun saveAllAppErrorsRecords() = newThread { ConfigData.putResolverString(ConfigData.APP_ERRORS_DATA, appErrorsRecords.toJson()) }
/**
* 处理 APP 进程异常信息展示
* @param context 当前实例
@@ -338,11 +331,8 @@ object FrameworkHooker : YukiBaseHooker() {
* @param info 系统错误报告数据实例
*/
private fun AppErrorsData.handleAppErrorsInfo(info: ApplicationErrorReport.CrashInfo?) {
/** 添加当前异常信息到第一位 */
appErrorsRecords.add(0, AppErrorsInfoBean.clone(pid, userId, appInfo?.packageName, info))
AppErrorsRecordData.add(AppErrorsInfoBean.clone(pid, userId, appInfo?.packageName, info))
loggerI(msg = "Received crash application data${if (userId != 0) " --user $userId" else ""} --pid $pid")
/** 保存异常记录到本地 */
saveAllAppErrorsRecords()
}
override fun onHook() {