From 804a37046f84c450411c45d8ea31758b7a723ac3 Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Tue, 30 Jan 2024 08:52:48 +0000 Subject: [PATCH] feat(metadata):tests --- .../detekt-baseline.xml | 2 +- .../com/bugsnag/android/ExitInfoCallback.kt | 76 ++++++++++--------- .../mazerunner/bugsnag-dependency.gradle | 1 + .../mazerunner/scenarios/ExitInfoScenario.kt | 27 +++++++ .../android/mazerunner/BugsnagConfig.kt | 2 + features/full_tests/exit_information.feature | 15 ++++ 6 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt create mode 100644 features/full_tests/exit_information.feature diff --git a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml index 29c898e414..4597fca69f 100644 --- a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml +++ b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml @@ -2,7 +2,7 @@ - CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$override fun onSend(event: Event): Boolean + CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo) LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit ) MagicNumber:TraceParser.kt$TraceParser$16 MagicNumber:TraceParser.kt$TraceParser$3 diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index d987203bab..807ffd6bfe 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.app.ApplicationExitInfo import android.content.Context import android.os.Build +import android.util.Log import androidx.annotation.RequiresApi @RequiresApi(Build.VERSION_CODES.R) @@ -17,46 +18,19 @@ internal class ExitInfoCallback( override fun onSend(event: Event): Boolean { val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val allExitInfo = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO) + Log.e("Bugsnag", "allExitInfo: $allExitInfo") val sessionIdBytes = event.session?.id?.toByteArray() ?: return true + Log.e("Bugsnag", "sessionIdBytes: $sessionIdBytes") val exitInfo = findExitInfoBySessionId(allExitInfo, sessionIdBytes) ?: findExitInfoByPid(allExitInfo) ?: return true + Log.e("Bugsnag", "exitInfo: $exitInfo") try { - val reason = when (exitInfo.reason) { - ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})" - ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" - ApplicationExitInfo.REASON_SIGNALED -> "signaled" - ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" - ApplicationExitInfo.REASON_CRASH -> "crash" - ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" - ApplicationExitInfo.REASON_ANR -> "ANR" - ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" - ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" - ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" - ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" - ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" - ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" - ApplicationExitInfo.REASON_OTHER -> "other" - ApplicationExitInfo.REASON_FREEZER -> "freezer" - ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" - ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" - else -> "unknown reason (${exitInfo.reason})" - } - event.addMetadata("App", "exit reason", reason) + val reason = exitReasonOf(exitInfo) + event.addMetadata("app", "exitReason", reason) - val importance = when (exitInfo.importance) { - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" - else -> "unknown importance ${exitInfo.importance}" - } - event.addMetadata("App", "process importance", importance) + val importance = importanceDescriptionOf(exitInfo) + event.addMetadata("app", "processImportance", importance) if (exitInfo.reason == ApplicationExitInfo.REASON_CRASH_NATIVE || exitInfo.reason == ApplicationExitInfo.REASON_SIGNALED @@ -71,6 +45,40 @@ internal class ExitInfoCallback( return true } + private fun exitReasonOf(exitInfo: ApplicationExitInfo) = when (exitInfo.reason) { + ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})" + ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" + ApplicationExitInfo.REASON_SIGNALED -> "signaled" + ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" + ApplicationExitInfo.REASON_CRASH -> "crash" + ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" + ApplicationExitInfo.REASON_ANR -> "ANR" + ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" + ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" + ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" + ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" + ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" + ApplicationExitInfo.REASON_OTHER -> "other" + ApplicationExitInfo.REASON_FREEZER -> "freezer" + ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" + ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" + else -> "unknown reason (${exitInfo.reason})" + } + + private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) = when (exitInfo.importance) { + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" + else -> "unknown importance ${exitInfo.importance}" + } + private fun findExitInfoBySessionId( allExitInfo: List, sessionIdBytes: ByteArray diff --git a/features/fixtures/mazerunner/bugsnag-dependency.gradle b/features/fixtures/mazerunner/bugsnag-dependency.gradle index 081d0fc48e..99680b4ddf 100644 --- a/features/fixtures/mazerunner/bugsnag-dependency.gradle +++ b/features/fixtures/mazerunner/bugsnag-dependency.gradle @@ -16,5 +16,6 @@ dependencies { project.logger.lifecycle("Compiling full mazerunner fixture with ANR/NDK scenarios") implementation "com.bugsnag:bugsnag-android:9.9.9" implementation "com.bugsnag:bugsnag-plugin-android-okhttp:9.9.9" + implementation "com.bugsnag:bugsnag-plugin-android-exitinfo:9.9.9" } } diff --git a/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt new file mode 100644 index 0000000000..a1563816c1 --- /dev/null +++ b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt @@ -0,0 +1,27 @@ +package com.bugsnag.android.mazerunner.scenarios + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.Log + +import com.bugsnag.android.Bugsnag +import com.bugsnag.android.Configuration + +class ExitInfoScenario( + config: com.bugsnag.android.Configuration, + context: android.content.Context, + eventMetadata: String? +) : Scenario(config, context, eventMetadata) { + external fun crash(value: Int): Int + override fun startScenario() { + super.startScenario() + Bugsnag.startSession() + val main: android.os.Handler = android.os.Handler(android.os.Looper.getMainLooper()) + main.postDelayed(object : java.lang.Runnable { + override fun run() { + crash(2726) + } + }, 500) + } +} \ No newline at end of file diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt index 63048e09cd..116d236d9c 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt @@ -1,6 +1,7 @@ package com.bugsnag.android.mazerunner import android.util.Log +import com.bugsnag.android.BugsnagExitInfoPlugin import com.bugsnag.android.Configuration import com.bugsnag.android.Delivery import com.bugsnag.android.DeliveryParams @@ -19,6 +20,7 @@ fun prepareConfig( logFilter: (msg: String) -> Boolean ): Configuration { val config = Configuration(apiKey) + config.addPlugin(BugsnagExitInfoPlugin()) if (notify.isNotEmpty() && sessions.isNotEmpty()) { config.endpoints = EndpointConfiguration(notify, sessions) diff --git a/features/full_tests/exit_information.feature b/features/full_tests/exit_information.feature new file mode 100644 index 0000000000..4eb4b5e5b4 --- /dev/null +++ b/features/full_tests/exit_information.feature @@ -0,0 +1,15 @@ +Feature: Application exitInfo is reported in crashes + + Background: + Given I clear all persistent data + + @skip_below_android_11 + Scenario: Application exitInfo is reported in crashes + When I set the screen orientation to portrait + And I run "ExitInfoScenario" and relaunch the crashed app + And I configure Bugsnag for "ExitInfoScenario" + And I wait to receive an error + Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier + And the error payload field "events.0.metaData.app.processImportance" equals "foreground" + And the error payload field "events.0.metaData.app.exitReason" equals "crash" +