Skip to content

Commit

Permalink
feat(ExitInfo)added DiscardEventCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
YYChen01988 committed Nov 19, 2024
1 parent c82916e commit 64ba4cb
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal class EventStore(
override val logger: Logger

var onEventStoreEmptyCallback: () -> Unit = {}
var onDiscardEventCallback: (EventPayload) -> Unit = {}
private var isEmptyEventCallbackCalled: Boolean = false

/**
Expand All @@ -56,6 +57,7 @@ internal class EventStore(
TaskType.ERROR_REQUEST,
Runnable {
flushLaunchCrashReport()
findStoredFiles().map { discardEvents(it) }
notifyEventQueueEmpty()
}
)
Expand Down Expand Up @@ -141,6 +143,7 @@ internal class EventStore(
logger.d("No regular events to flush to Bugsnag.")
}
flushReports(storedFiles)
storedFiles.map { discardEvents(it) }
notifyEventQueueEmpty()
}
)
Expand Down Expand Up @@ -196,11 +199,13 @@ internal class EventStore(
logger.w(
"Discarding over-sized event (${eventFile.length()}) after failed delivery"
)
discardEvents(eventFile)
deleteStoredFiles(setOf(eventFile))
} else if (isTooOld(eventFile)) {
logger.w(
"Discarding historical event (from ${getCreationDate(eventFile)}) after failed delivery"
)
discardEvents(eventFile)
deleteStoredFiles(setOf(eventFile))
} else {
cancelQueuedFiles(setOf(eventFile))
Expand Down Expand Up @@ -273,6 +278,19 @@ internal class EventStore(
}
}

private fun discardEvents(eventFile: File) {
val eventFilenameInfo = fromFile(eventFile, config)
onDiscardEventCallback(
EventPayload(
eventFilenameInfo.apiKey,
null,
eventFile,
notifier,
config
)
)
}

companion object {
private const val LAUNCH_CRASH_TIMEOUT_MS: Long = 2000
val EVENT_COMPARATOR: Comparator<in File?> = Comparator { lhs, rhs ->
Expand Down
2 changes: 2 additions & 0 deletions bugsnag-plugin-android-exitinfo/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
<ID>MagicNumber:BugsnagExitInfoPlugin.kt$BugsnagExitInfoPlugin$100</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$16</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$3</ID>
<ID>MaxLineLength:ExitInfoCallback.kt$ExitInfoCallback$val</ID>
<ID>MaxLineLength:ExitInfoCallback.kt$ExitInfoCallback$val allExitInfo: List&lt;ApplicationExitInfo> = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)</ID>
<ID>MaxLineLength:ExitInfoCallbackTest.kt$ExitInfoCallbackTest$exitInfoCallback = ExitInfoCallback(context, nativeEnhancer, anrEventEnhancer, null, ApplicationExitInfoMatcher(context, 100))</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so "</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so (Java_com_example_bugsnag_android_BaseCrashyActivity_anrFromCXX+20"</ID>
<ID>MaxLineLength:TraceParserInvalidStackframesTest.kt$TraceParserInvalidStackframesTest.Companion$"#01 pc 0000000000000c5c /data/app/~~sKQbJGqVJA5glcnvEjZCMg==/com.example.bugsnag.android-fVuoJh5GpAL7sRAeI3vjSw==/lib/arm64/libentrypoint.so (Java_com_example_bugsnag_android_BaseCrashyActivity_anrFromCXX+20) ("</ID>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.bugsnag.android

import android.app.ActivityManager
import android.app.ApplicationExitInfo
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi

@RequiresApi(Build.VERSION_CODES.R)
internal class ApplicationExitInfoMatcher(
private val context: Context,
private val pid: Int
) {
fun matchExitInfo(event: Event): ApplicationExitInfo? {
val am: ActivityManager =
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val allExitInfo: List<ApplicationExitInfo> =
am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)
val sessionIdBytes: ByteArray =
event.session?.id?.toByteArray() ?: return null
val exitInfo: ApplicationExitInfo =
findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: findExitInfoByPid(allExitInfo) ?: return null
return exitInfo
}

internal fun findExitInfoBySessionId(
allExitInfo: List<ApplicationExitInfo>,
sessionIdBytes: ByteArray
) = allExitInfo.find {
it.processStateSummary?.contentEquals(sessionIdBytes) == true
}

internal fun findExitInfoByPid(allExitInfo: List<ApplicationExitInfo>) =
allExitInfo.find { it.pid == pid }

internal companion object {
private const val MAX_EXIT_INFO = 100
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
)
}

val exitInfoCallback = createExitInfoCallback(client)
client.addOnSend(exitInfoCallback)
}

private fun createExitInfoCallback(client: Client): ExitInfoCallback {
val tombstoneEventEnhancer = TombstoneEventEnhancer(
client.logger,
configuration.listOpenFds,
Expand All @@ -47,11 +42,17 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
addAllExitInfoAtFirstRun(client, exitInfoPluginStore)
exitInfoPluginStore.currentPid = Process.myPid()

val exitInfoMatcher = ApplicationExitInfoMatcher(
context = client.appContext,
pid = exitInfoPluginStore.previousPid
)

val exitInfoCallback = createExitInfoCallback(
client,
exitInfoPluginStore.previousPid, exitInfoPluginStore,
exitInfoPluginStore,
tombstoneEventEnhancer,
traceEventEnhancer
traceEventEnhancer,
exitInfoMatcher
)
InternalHooks.setEventStoreEmptyCallback(client) {
synthesizeNewEvents(
Expand All @@ -61,7 +62,13 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(
traceEventEnhancer
)
}
return exitInfoCallback

InternalHooks.setDiscardEventCallback(client) { eventPayload ->
val exitInfo = eventPayload.event?.let { exitInfoMatcher.matchExitInfo(it) }
exitInfo?.let { exitInfoPluginStore.addExitInfoKey(ExitInfoKey(exitInfo)) }
}

client.addOnSend(exitInfoCallback)
}

private fun addAllExitInfoAtFirstRun(
Expand All @@ -85,16 +92,16 @@ class BugsnagExitInfoPlugin @JvmOverloads constructor(

private fun createExitInfoCallback(
client: Client,
oldPid: Int?,
exitInfoPluginStore: ExitInfoPluginStore,
tombstoneEventEnhancer: TombstoneEventEnhancer,
traceEventEnhancer: TraceEventEnhancer
traceEventEnhancer: TraceEventEnhancer,
applicationExitInfoMatcher: ApplicationExitInfoMatcher
): ExitInfoCallback = ExitInfoCallback(
client.appContext,
oldPid,
tombstoneEventEnhancer,
traceEventEnhancer,
exitInfoPluginStore
exitInfoPluginStore,
applicationExitInfoMatcher
)

private fun synthesizeNewEvents(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.R)
internal class ExitInfoCallback(
private val context: Context,
private val pid: Int?,
private val nativeEnhancer: (Event, ApplicationExitInfo) -> Unit,
private val anrEventEnhancer: (Event, ApplicationExitInfo) -> Unit,
private val exitInfoPluginStore: ExitInfoPluginStore?
private val exitInfoPluginStore: ExitInfoPluginStore?,
private val applicationExitInfoMatcher: ApplicationExitInfoMatcher?,
) : OnSendCallback {

override fun onSend(event: Event): Boolean {
val am: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val allExitInfo: List<ApplicationExitInfo> = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO)
val sessionIdBytes: ByteArray = event.session?.id?.toByteArray() ?: return true
val exitInfo: ApplicationExitInfo = findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: findExitInfoByPid(allExitInfo) ?: return true
val exitInfo: ApplicationExitInfo = applicationExitInfoMatcher?.findExitInfoBySessionId(allExitInfo, sessionIdBytes)
?: applicationExitInfoMatcher?.findExitInfoByPid(allExitInfo) ?: return true
exitInfoPluginStore?.addExitInfoKey(ExitInfoKey(exitInfo.pid, exitInfo.timestamp))

try {
Expand Down Expand Up @@ -102,16 +102,6 @@ internal class ExitInfoCallback(
else -> "unknown importance (${exitInfo.importance})"
}

private fun findExitInfoBySessionId(
allExitInfo: List<ApplicationExitInfo>,
sessionIdBytes: ByteArray
) = allExitInfo.find {
it.processStateSummary?.contentEquals(sessionIdBytes) == true
}

private fun findExitInfoByPid(allExitInfo: List<ApplicationExitInfo>) =
allExitInfo.find { it.pid == pid }

internal companion object {
private const val MAX_EXIT_INFO = 100
private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;

class InternalHooks {

Expand All @@ -16,6 +17,10 @@ public static void setEventStoreEmptyCallback(Client client, Function0<Unit> cal
client.eventStore.setOnEventStoreEmptyCallback(callback);
}

public static void setDiscardEventCallback(Client client, Function1<EventPayload, Unit> callback) {
client.eventStore.setOnDiscardEventCallback(callback);
}

static void deliver(@NonNull Client client, @NonNull Event event) {
client.deliveryDelegate.deliver(event);
}
Expand All @@ -37,5 +42,4 @@ static Event createEmptyCrash(long exitInfoTimeStamp) {
event.updateSeverityReason(SeverityReason.REASON_SIGNAL);
return event;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal class ExitInfoCallbackTest {

@Before
fun setUp() {
exitInfoCallback = ExitInfoCallback(context, 100, nativeEnhancer, anrEventEnhancer, null)
exitInfoCallback = ExitInfoCallback(context, nativeEnhancer, anrEventEnhancer, null, ApplicationExitInfoMatcher(context, 100))
exitInfos = listOf(exitInfo1)
`when`(context.getSystemService(any())).thenReturn(am)
`when`(am.getHistoricalProcessExitReasons(any(), anyInt(), anyInt()))
Expand Down

0 comments on commit 64ba4cb

Please sign in to comment.