Added multi-user display app's user id feature

This commit is contained in:
2022-10-03 05:43:49 +08:00
parent a6bf4e8a80
commit 2481263c00
7 changed files with 78 additions and 11 deletions

View File

@@ -32,6 +32,7 @@ import java.util.*
/**
* 应用异常信息 bean
* @param userId 用户 ID
* @param packageName 包名
* @param isNativeCrash 是否为原生层异常
* @param exceptionClassName 异常类名
@@ -44,6 +45,7 @@ import java.util.*
* @param timestamp 记录时间戳
*/
data class AppErrorsInfoBean(
@Keep var userId: Int,
@Keep var packageName: String,
@Keep var isNativeCrash: Boolean,
@Keep var exceptionClassName: String,
@@ -61,12 +63,14 @@ data class AppErrorsInfoBean(
/**
* 从 [ApplicationErrorReport.CrashInfo] 克隆
* @param packageName APP 包名
* @param userId APP 用户 ID
* @param crashInfo [ApplicationErrorReport.CrashInfo]
* @return [AppErrorsInfoBean]
*/
fun clone(packageName: String?, crashInfo: ApplicationErrorReport.CrashInfo?) =
fun clone(packageName: String?, userId: Int?, crashInfo: ApplicationErrorReport.CrashInfo?) =
(crashInfo?.exceptionClassName?.lowercase() == "native crash").let { isNativeCrash ->
AppErrorsInfoBean(
userId = userId ?: 0,
packageName = packageName ?: "unknown",
isNativeCrash = isNativeCrash,
exceptionClassName = crashInfo?.exceptionClassName ?: "unknown",
@@ -122,6 +126,7 @@ data class AppErrorsInfoBean(
"[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]: $dateTime\n" +
"[Stack Trace]:\n" + stackTrace
@@ -142,6 +147,7 @@ data class AppErrorsInfoBean(
"[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]: $dateTime\n" +
"[Stack Trace]:\n" + stackTrace

View File

@@ -75,6 +75,9 @@ object FrameworkHooker : YukiBaseHooker() {
"com.android.server.am.ErrorDialogController"
)
/** 已记录的 APP 用户 ID */
private var appUserIdRecords = HashMap<String, Int>()
/** 已忽略错误的 APP 数组 - 直到重新解锁 */
private var mutedErrorsIfUnlockApps = HashSet<String>()
@@ -250,10 +253,12 @@ object FrameworkHooker : YukiBaseHooker() {
/** 崩溃标题 */
val errorTitle = if (isRepeating) LocaleString.aerrRepeatedTitle(appName) else LocaleString.aerrTitle(appName)
/** 写入到用户 ID 记录 */
appUserIdRecords[proc.toString()] = userId
/** 打印错误日志 */
if (isApp) loggerE(
msg = "App \"$packageName\"${if (packageName != processName) " --process \"$processName\"" else ""}" +
" has crashed${if (isRepeating) " again" else ""}"
"${if (userId != 0) " --user $userId" else ""} has crashed${if (isRepeating) " again" else ""}"
) else loggerE(msg = "Process \"$processName\" has crashed${if (isRepeating) " again" else ""}")
/** 判断是否为模块自身崩溃 */
if (packageName == BuildConfig.APPLICATION_ID) {
@@ -308,10 +313,15 @@ object FrameworkHooker : YukiBaseHooker() {
paramCount = 2
}
afterHook {
/** 当前进程信息 */
val proc = args().first().any()
/** 当前 APP 信息 */
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(args().first().any()).cast<ApplicationInfo>()
val appInfo = ProcessRecordClass.toClass().field { name = "info" }.get(proc).cast<ApplicationInfo>()
/** 添加当前异常信息到第一位 */
appErrorsRecords.add(0, AppErrorsInfoBean.clone(appInfo?.packageName, args().last().cast()))
appErrorsRecords.add(
0, AppErrorsInfoBean.clone(appInfo?.packageName, appUserIdRecords[proc.toString()], args().last().cast())
)
/** 保存异常记录到本地 */
saveAllAppErrorsRecords()
}

View File

@@ -27,6 +27,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.fankes.apperrorstracking.R
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
@@ -86,6 +87,8 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
binding.appNameText.text = appNameOf(appErrorsInfo.packageName)
binding.appVersionText.text = appVersionBrandOf(appErrorsInfo.packageName)
binding.appUserIdText.isVisible = appErrorsInfo.userId > 0
binding.appUserIdText.text = LocaleString.userId(appErrorsInfo.userId)
binding.appAbiText.text = appCpuAbiOf(appErrorsInfo.packageName).ifBlank { LocaleString.noCpuAbi }
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)

View File

@@ -134,6 +134,8 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
listData[position].also { bean ->
binding.appIcon.setImageDrawable(appIconOf(bean.packageName))
binding.appNameText.text = appNameOf(bean.packageName)
binding.appUserIdText.isVisible = bean.userId > 0
binding.appUserIdText.text = LocaleString.userId(bean.userId)
binding.errorsTimeText.text = bean.crossTime
binding.errorTypeIcon.setImageResource(if (bean.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
binding.errorTypeText.text = if (bean.isNativeCrash) "Native crash" else bean.exceptionClassName.simpleThwName()

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2196F3" />
<corners android:radius="5dp" />
</shape>

View File

@@ -151,6 +151,23 @@
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/app_user_id_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/bg_blue_round"
android:ellipsize="end"
android:paddingLeft="3dp"
android:paddingTop="0.5dp"
android:paddingRight="3dp"
android:paddingBottom="0.5dp"
android:singleLine="true"
android:text="@string/user_id"
android:textColor="@color/white"
android:textSize="9sp"
android:visibility="gone" />
<ImageView
android:layout_width="13dp"
android:layout_height="13dp"

View File

@@ -39,16 +39,39 @@
android:gravity="center|start"
android:orientation="horizontal">
<TextView
android:id="@+id/app_name_text"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="0.75"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
android:gravity="center|start">
<TextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/colorTextGray"
android:textSize="15sp" />
<TextView
android:id="@+id/app_user_id_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="@drawable/bg_blue_round"
android:ellipsize="end"
android:paddingLeft="3dp"
android:paddingTop="0.5dp"
android:paddingRight="3dp"
android:paddingBottom="0.5dp"
android:singleLine="true"
android:text="@string/user_id"
android:textColor="@color/white"
android:textSize="9sp"
android:visibility="gone" />
</LinearLayout>
<TextView
android:id="@+id/errors_time_text"