From f064c161a1f3edb3186336ec09ad196d15410998 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Tue, 10 Sep 2024 15:57:07 -0700 Subject: [PATCH] Record internal error when startup trace fails to be recorded --- .../internal/logging/InternalErrorType.kt | 6 +----- .../capture/startup/AppStartupTraceEmitter.kt | 14 ++++++++++---- .../internal/capture/startup/StartupTracker.kt | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt index 11c6472dd9..535d45a925 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/logging/InternalErrorType.kt @@ -6,7 +6,6 @@ package io.embrace.android.embracesdk.internal.logging enum class InternalErrorType { UNCAUGHT_EXC_HANDLER, ANR_DATA_FETCH, - UNBALANCED_CALL, DISABLE_DATA_CAPTURE, ENABLE_DATA_CAPTURE, NETWORK_STATUS_CAPTURE_FAIL, @@ -17,7 +16,6 @@ enum class InternalErrorType { FG_SESSION_CACHE_FAIL, ACTIVITY_LISTENER_FAIL, PROCESS_STATE_CALLBACK_FAIL, - TIME_TRAVEL, ANR_HEARTBEAT_CHECK_FAIL, CFG_CHANGE_DATA_CAPTURE_FAIL, SESSION_CHANGE_DATA_CAPTURE_FAIL, @@ -31,12 +29,10 @@ enum class InternalErrorType { NATIVE_CRASH_LOAD_FAIL, INVALID_NATIVE_SYMBOLS, NATIVE_HANDLER_INSTALL_FAIL, - INVALID_JS_EXCEPTION, SAFE_DATA_CAPTURE_FAIL, PROCESS_STATE_SUMMARY_FAIL, - UNBALANCED_FG_CALL, - UNBALANCED_BG_CALL, ANR_HEARTBEAT_STOP_FAIL, SDK_START_FAIL, CONFIG_DESERIALIZATION_FAIL, + APP_STARTUP_TRACE_NOT_RECORDED, } diff --git a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/AppStartupTraceEmitter.kt b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/AppStartupTraceEmitter.kt index ab6064b874..51ba0751eb 100644 --- a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/AppStartupTraceEmitter.kt +++ b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/AppStartupTraceEmitter.kt @@ -4,6 +4,7 @@ import android.os.Build.VERSION_CODES import android.os.Process import io.embrace.android.embracesdk.internal.clock.nanosToMillis import io.embrace.android.embracesdk.internal.logging.EmbLogger +import io.embrace.android.embracesdk.internal.logging.InternalErrorType import io.embrace.android.embracesdk.internal.spans.PersistableEmbraceSpan import io.embrace.android.embracesdk.internal.spans.SpanService import io.embrace.android.embracesdk.internal.utils.Provider @@ -97,6 +98,7 @@ internal class AppStartupTraceEmitter( private var sdkInitEndedInForeground: Boolean? = null private val startupRecorded = AtomicBoolean(false) + private val dataCollectionComplete = AtomicBoolean(false) private val endWithFrameDraw: Boolean = versionChecker.isAtLeast(VERSION_CODES.Q) override fun applicationInitStart(timestampMs: Long?) { @@ -161,16 +163,20 @@ internal class AppStartupTraceEmitter( * Called when app startup is considered complete, i.e. the data can be used and any additional updates can be ignored */ private fun dataCollectionComplete(callback: () -> Unit) { - if (!startupRecorded.get()) { - synchronized(startupRecorded) { - if (!startupRecorded.get()) { + if (!dataCollectionComplete.get()) { + synchronized(dataCollectionComplete) { + if (!dataCollectionComplete.get()) { backgroundWorker.submit { recordStartup() if (!startupRecorded.get()) { - logger.logWarning("App startup trace recording attempted but did not succeed") + logger.trackInternalError( + type = InternalErrorType.APP_STARTUP_TRACE_NOT_RECORDED, + throwable = IllegalStateException("App startup trace not recorded after terminal app startup event") + ) } } callback() + dataCollectionComplete.set(true) } } } diff --git a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/StartupTracker.kt b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/StartupTracker.kt index 37d0d9446f..be52e37449 100644 --- a/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/StartupTracker.kt +++ b/embrace-android-features/src/main/kotlin/io/embrace/android/embracesdk/internal/capture/startup/StartupTracker.kt @@ -43,6 +43,7 @@ class StartupTracker( private var isFirstDraw = false private var nullWindowCallbackErrorLogged = false private var startupActivityId: Int? = null + private var startupDataCollectionComplete = false override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) { if (activity.useAsStartupActivity()) { @@ -53,6 +54,7 @@ class StartupTracker( override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { if (activity.useAsStartupActivity()) { val activityName = activity.localClassName + val application = activity.application appStartupDataCollector.startupActivityInitStart() if (versionChecker.isAtLeast(Build.VERSION_CODES.Q)) { if (!isFirstDraw) { @@ -66,7 +68,7 @@ class StartupTracker( val callback = { appStartupDataCollector.firstFrameRendered( activityName = activityName, - collectionCompleteCallback = {} + collectionCompleteCallback = { startupComplete(application) } ) } decorView.viewTreeObserver.registerFrameCommitCallback(callback) @@ -96,9 +98,10 @@ class StartupTracker( override fun onActivityResumed(activity: Activity) { if (activity.observeForStartup()) { + val application = activity.application appStartupDataCollector.startupActivityResumed( activityName = activity.localClassName, - collectionCompleteCallback = { } + collectionCompleteCallback = { startupComplete(application) } ) } } @@ -111,6 +114,13 @@ class StartupTracker( override fun onActivityDestroyed(activity: Activity) {} + private fun startupComplete(application: Application) { + if (!startupDataCollectionComplete) { + application.unregisterActivityLifecycleCallbacks(this) + startupDataCollectionComplete = true + } + } + /** * Returns true if the Activity instance is being used as the startup Activity. It will return false if [useAsStartupActivity] has * not been called previously to setup the Activity instance to be used as the startup Activity.