Skip to content

Commit

Permalink
feat(metadata):tests
Browse files Browse the repository at this point in the history
  • Loading branch information
YYChen01988 committed Jan 30, 2024
1 parent 9f44dd5 commit a7ca352
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 35 deletions.
2 changes: 1 addition & 1 deletion bugsnag-plugin-android-exitinfo/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$override fun onSend(event: Event): Boolean</ID>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo)</ID>
<ID>LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit )</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$16</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$3</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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<ApplicationExitInfo>,
sessionIdBytes: ByteArray
Expand Down
1 change: 1 addition & 0 deletions features/fixtures/mazerunner/bugsnag-dependency.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand Down
15 changes: 15 additions & 0 deletions features/full_tests/exit_information.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Feature: Reporting with other exception handlers installed

Background:
Given I clear all persistent data

@skip_below_android_11
Scenario: Signal raised with overwritten config
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"

0 comments on commit a7ca352

Please sign in to comment.