diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/injection/SessionModule.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/injection/SessionModule.kt index 87288c99b..83f7fcf77 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/injection/SessionModule.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/injection/SessionModule.kt @@ -7,6 +7,7 @@ import io.embrace.android.embracesdk.session.caching.PeriodicSessionCacher import io.embrace.android.embracesdk.session.message.PayloadFactory import io.embrace.android.embracesdk.session.message.PayloadFactoryImpl import io.embrace.android.embracesdk.session.message.PayloadMessageCollator +import io.embrace.android.embracesdk.session.message.PayloadMessageCollatorImpl import io.embrace.android.embracesdk.session.orchestrator.OrchestratorBoundaryDelegate import io.embrace.android.embracesdk.session.orchestrator.SessionOrchestrator import io.embrace.android.embracesdk.session.orchestrator.SessionOrchestratorImpl @@ -43,7 +44,7 @@ internal class SessionModuleImpl( ) : SessionModule { override val payloadMessageCollator: PayloadMessageCollator by singleton { - PayloadMessageCollator( + PayloadMessageCollatorImpl( essentialServiceModule.configService, essentialServiceModule.metadataService, dataContainerModule.eventService, diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt index c9fd5ecfe..41608b553 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollator.kt @@ -1,193 +1,26 @@ package io.embrace.android.embracesdk.session.message -import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerService -import io.embrace.android.embracesdk.arch.schema.AppTerminationCause -import io.embrace.android.embracesdk.capture.PerformanceInfoService -import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService -import io.embrace.android.embracesdk.capture.metadata.MetadataService -import io.embrace.android.embracesdk.capture.startup.StartupService -import io.embrace.android.embracesdk.capture.thermalstate.ThermalStatusService -import io.embrace.android.embracesdk.capture.user.UserService -import io.embrace.android.embracesdk.capture.webview.WebViewService -import io.embrace.android.embracesdk.config.ConfigService -import io.embrace.android.embracesdk.event.EventService -import io.embrace.android.embracesdk.event.LogMessageService -import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan -import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData -import io.embrace.android.embracesdk.internal.spans.SpanSink -import io.embrace.android.embracesdk.internal.utils.Uuid -import io.embrace.android.embracesdk.logging.InternalErrorService -import io.embrace.android.embracesdk.payload.BetaFeatures import io.embrace.android.embracesdk.payload.Session import io.embrace.android.embracesdk.payload.SessionMessage -import io.embrace.android.embracesdk.prefs.PreferencesService -import io.embrace.android.embracesdk.session.captureDataSafely -import io.embrace.android.embracesdk.session.properties.SessionPropertiesService +import io.embrace.android.embracesdk.session.message.FinalEnvelopeParams.BackgroundActivityParams +import io.embrace.android.embracesdk.session.message.FinalEnvelopeParams.SessionParams -internal class PayloadMessageCollator( - private val configService: ConfigService, - private val metadataService: MetadataService, - private val eventService: EventService, - private val logMessageService: LogMessageService, - private val internalErrorService: InternalErrorService, - private val performanceInfoService: PerformanceInfoService, - private val webViewService: WebViewService, - private val thermalStatusService: ThermalStatusService, - private val nativeThreadSamplerService: NativeThreadSamplerService?, - private val breadcrumbService: BreadcrumbService, - private val userService: UserService, - private val preferencesService: PreferencesService, - private val spanSink: SpanSink, - private val currentSessionSpan: CurrentSessionSpan, - private val sessionPropertiesService: SessionPropertiesService, - private val startupService: StartupService -) { +internal interface PayloadMessageCollator { /** * Builds a new session object. This should not be sent to the backend but is used * to populate essential session information (such as ID), etc */ - internal fun buildInitialSession(params: InitialEnvelopeParams) = with(params) { - Session( - sessionId = Uuid.getEmbUuid(), - startTime = startTime, - isColdStart = coldStart, - messageType = Session.MESSAGE_TYPE_END, - appState = appState, - startType = startType, - number = getSessionNumber(preferencesService), - properties = getProperties(sessionPropertiesService), - ) - } + fun buildInitialSession(params: InitialEnvelopeParams): Session /** * Builds a fully populated session message. This can be sent to the backend (or stored * on disk). */ - internal fun buildFinalSessionMessage( - params: FinalEnvelopeParams.SessionParams - ): SessionMessage = with(params) { - val base = buildFinalBackgroundActivity(params) - val startupInfo = getStartupEventInfo(eventService) - - val betaFeatures = when (configService.sdkModeBehavior.isBetaFeaturesEnabled()) { - false -> null - else -> BetaFeatures( - thermalStates = captureDataSafely(thermalStatusService::getCapturedData), - ) - } - - val endSession = base.copy( - isEndedCleanly = endType.endedCleanly, - networkLogIds = captureDataSafely { - logMessageService.findNetworkLogIds( - initial.startTime, - endTime - ) - }, - properties = captureDataSafely(sessionPropertiesService::getProperties), - webViewInfo = captureDataSafely(webViewService::getCapturedData), - terminationTime = terminationTime, - isReceivedTermination = receivedTermination, - endTime = endTimeVal, - sdkStartupDuration = startupService.getSdkStartupDuration(initial.isColdStart), - startupDuration = startupInfo?.duration, - startupThreshold = startupInfo?.threshold, - betaFeatures = betaFeatures, - symbols = captureDataSafely { nativeThreadSamplerService?.getNativeSymbols() } - ) - return buildWrapperEnvelope(params, endSession, initial.startTime, endTime) - } + fun buildFinalSessionMessage(params: SessionParams): SessionMessage /** * Create the background session message with the current state of the background activity. */ - fun buildFinalBackgroundActivityMessage( - params: FinalEnvelopeParams.BackgroundActivityParams - ): SessionMessage { - val msg = buildFinalBackgroundActivity(params) - val startTime = msg.startTime - val endTime = params.endTime - return buildWrapperEnvelope(params, msg, startTime, endTime) - } - - /** - * Creates a background activity stop message. - */ - private fun buildFinalBackgroundActivity( - params: FinalEnvelopeParams - ): Session = with(params) { - val startTime = initial.startTime - return initial.copy( - endTime = endTime, - eventIds = captureDataSafely { - eventService.findEventIdsForSession() - }, - infoLogIds = captureDataSafely { logMessageService.findInfoLogIds(startTime, endTime) }, - warningLogIds = captureDataSafely { - logMessageService.findWarningLogIds( - startTime, - endTime - ) - }, - errorLogIds = captureDataSafely { - logMessageService.findErrorLogIds( - startTime, - endTime - ) - }, - infoLogsAttemptedToSend = captureDataSafely(logMessageService::getInfoLogsAttemptedToSend), - warnLogsAttemptedToSend = captureDataSafely(logMessageService::getWarnLogsAttemptedToSend), - errorLogsAttemptedToSend = captureDataSafely(logMessageService::getErrorLogsAttemptedToSend), - exceptionError = captureDataSafely(internalErrorService::currentExceptionError), - lastHeartbeatTime = endTime, - endType = lifeEventType, - unhandledExceptions = captureDataSafely(logMessageService::getUnhandledExceptionsSent), - crashReportId = crashId - ) - } - - private fun buildWrapperEnvelope( - params: FinalEnvelopeParams, - finalPayload: Session, - startTime: Long, - endTime: Long - ): SessionMessage { - val spans: List? = captureDataSafely { - when { - !params.isCacheAttempt -> { - val appTerminationCause = when { - finalPayload.crashReportId != null -> AppTerminationCause.Crash - else -> null - } - currentSessionSpan.endSession(appTerminationCause) - } - - else -> spanSink.completedSpans() - } - } - val breadcrumbs = captureDataSafely { - when { - !params.isCacheAttempt -> breadcrumbService.flushBreadcrumbs() - else -> breadcrumbService.getBreadcrumbs() - } - } - - return SessionMessage( - session = finalPayload, - userInfo = captureDataSafely(userService::getUserInfo), - appInfo = captureDataSafely(metadataService::getAppInfo), - deviceInfo = captureDataSafely(metadataService::getDeviceInfo), - performanceInfo = captureDataSafely { - performanceInfoService.getSessionPerformanceInfo( - startTime, - endTime, - finalPayload.isColdStart, - null - ) - }, - breadcrumbs = breadcrumbs, - spans = spans - ) - } + fun buildFinalBackgroundActivityMessage(params: BackgroundActivityParams): SessionMessage } diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollatorImpl.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollatorImpl.kt new file mode 100644 index 000000000..21ff2f68b --- /dev/null +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/session/message/PayloadMessageCollatorImpl.kt @@ -0,0 +1,193 @@ +package io.embrace.android.embracesdk.session.message + +import io.embrace.android.embracesdk.anr.ndk.NativeThreadSamplerService +import io.embrace.android.embracesdk.arch.schema.AppTerminationCause +import io.embrace.android.embracesdk.capture.PerformanceInfoService +import io.embrace.android.embracesdk.capture.crumbs.BreadcrumbService +import io.embrace.android.embracesdk.capture.metadata.MetadataService +import io.embrace.android.embracesdk.capture.startup.StartupService +import io.embrace.android.embracesdk.capture.thermalstate.ThermalStatusService +import io.embrace.android.embracesdk.capture.user.UserService +import io.embrace.android.embracesdk.capture.webview.WebViewService +import io.embrace.android.embracesdk.config.ConfigService +import io.embrace.android.embracesdk.event.EventService +import io.embrace.android.embracesdk.event.LogMessageService +import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan +import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData +import io.embrace.android.embracesdk.internal.spans.SpanSink +import io.embrace.android.embracesdk.internal.utils.Uuid +import io.embrace.android.embracesdk.logging.InternalErrorService +import io.embrace.android.embracesdk.payload.BetaFeatures +import io.embrace.android.embracesdk.payload.Session +import io.embrace.android.embracesdk.payload.SessionMessage +import io.embrace.android.embracesdk.prefs.PreferencesService +import io.embrace.android.embracesdk.session.captureDataSafely +import io.embrace.android.embracesdk.session.properties.SessionPropertiesService + +internal class PayloadMessageCollatorImpl( + private val configService: ConfigService, + private val metadataService: MetadataService, + private val eventService: EventService, + private val logMessageService: LogMessageService, + private val internalErrorService: InternalErrorService, + private val performanceInfoService: PerformanceInfoService, + private val webViewService: WebViewService, + private val thermalStatusService: ThermalStatusService, + private val nativeThreadSamplerService: NativeThreadSamplerService?, + private val breadcrumbService: BreadcrumbService, + private val userService: UserService, + private val preferencesService: PreferencesService, + private val spanSink: SpanSink, + private val currentSessionSpan: CurrentSessionSpan, + private val sessionPropertiesService: SessionPropertiesService, + private val startupService: StartupService +) : PayloadMessageCollator { + + /** + * Builds a new session object. This should not be sent to the backend but is used + * to populate essential session information (such as ID), etc + */ + override fun buildInitialSession(params: InitialEnvelopeParams) = with(params) { + Session( + sessionId = Uuid.getEmbUuid(), + startTime = startTime, + isColdStart = coldStart, + messageType = Session.MESSAGE_TYPE_END, + appState = appState, + startType = startType, + number = getSessionNumber(preferencesService), + properties = getProperties(sessionPropertiesService), + ) + } + + /** + * Builds a fully populated session message. This can be sent to the backend (or stored + * on disk). + */ + override fun buildFinalSessionMessage( + params: FinalEnvelopeParams.SessionParams + ): SessionMessage = with(params) { + val base = buildFinalBackgroundActivity(params) + val startupInfo = getStartupEventInfo(eventService) + + val betaFeatures = when (configService.sdkModeBehavior.isBetaFeaturesEnabled()) { + false -> null + else -> BetaFeatures( + thermalStates = captureDataSafely(thermalStatusService::getCapturedData), + ) + } + + val endSession = base.copy( + isEndedCleanly = endType.endedCleanly, + networkLogIds = captureDataSafely { + logMessageService.findNetworkLogIds( + initial.startTime, + endTime + ) + }, + properties = captureDataSafely(sessionPropertiesService::getProperties), + webViewInfo = captureDataSafely(webViewService::getCapturedData), + terminationTime = terminationTime, + isReceivedTermination = receivedTermination, + endTime = endTimeVal, + sdkStartupDuration = startupService.getSdkStartupDuration(initial.isColdStart), + startupDuration = startupInfo?.duration, + startupThreshold = startupInfo?.threshold, + betaFeatures = betaFeatures, + symbols = captureDataSafely { nativeThreadSamplerService?.getNativeSymbols() } + ) + return buildWrapperEnvelope(params, endSession, initial.startTime, endTime) + } + + /** + * Create the background session message with the current state of the background activity. + */ + override fun buildFinalBackgroundActivityMessage( + params: FinalEnvelopeParams.BackgroundActivityParams + ): SessionMessage { + val msg = buildFinalBackgroundActivity(params) + val startTime = msg.startTime + val endTime = params.endTime + return buildWrapperEnvelope(params, msg, startTime, endTime) + } + + /** + * Creates a background activity stop message. + */ + private fun buildFinalBackgroundActivity( + params: FinalEnvelopeParams + ): Session = with(params) { + val startTime = initial.startTime + return initial.copy( + endTime = endTime, + eventIds = captureDataSafely { + eventService.findEventIdsForSession() + }, + infoLogIds = captureDataSafely { logMessageService.findInfoLogIds(startTime, endTime) }, + warningLogIds = captureDataSafely { + logMessageService.findWarningLogIds( + startTime, + endTime + ) + }, + errorLogIds = captureDataSafely { + logMessageService.findErrorLogIds( + startTime, + endTime + ) + }, + infoLogsAttemptedToSend = captureDataSafely(logMessageService::getInfoLogsAttemptedToSend), + warnLogsAttemptedToSend = captureDataSafely(logMessageService::getWarnLogsAttemptedToSend), + errorLogsAttemptedToSend = captureDataSafely(logMessageService::getErrorLogsAttemptedToSend), + exceptionError = captureDataSafely(internalErrorService::currentExceptionError), + lastHeartbeatTime = endTime, + endType = lifeEventType, + unhandledExceptions = captureDataSafely(logMessageService::getUnhandledExceptionsSent), + crashReportId = crashId + ) + } + + private fun buildWrapperEnvelope( + params: FinalEnvelopeParams, + finalPayload: Session, + startTime: Long, + endTime: Long + ): SessionMessage { + val spans: List? = captureDataSafely { + when { + !params.isCacheAttempt -> { + val appTerminationCause = when { + finalPayload.crashReportId != null -> AppTerminationCause.Crash + else -> null + } + currentSessionSpan.endSession(appTerminationCause) + } + + else -> spanSink.completedSpans() + } + } + val breadcrumbs = captureDataSafely { + when { + !params.isCacheAttempt -> breadcrumbService.flushBreadcrumbs() + else -> breadcrumbService.getBreadcrumbs() + } + } + + return SessionMessage( + session = finalPayload, + userInfo = captureDataSafely(userService::getUserInfo), + appInfo = captureDataSafely(metadataService::getAppInfo), + deviceInfo = captureDataSafely(metadataService::getDeviceInfo), + performanceInfo = captureDataSafely { + performanceInfoService.getSessionPerformanceInfo( + startTime, + endTime, + finalPayload.isColdStart, + null + ) + }, + breadcrumbs = breadcrumbs, + spans = spans + ) + } +} diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadFactoryBaTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadFactoryBaTest.kt index c59d77b32..2d460fec4 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadFactoryBaTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadFactoryBaTest.kt @@ -34,7 +34,7 @@ import io.embrace.android.embracesdk.internal.spans.SpanSink import io.embrace.android.embracesdk.logging.InternalErrorService import io.embrace.android.embracesdk.session.lifecycle.ProcessState import io.embrace.android.embracesdk.session.message.PayloadFactoryImpl -import io.embrace.android.embracesdk.session.message.PayloadMessageCollator +import io.embrace.android.embracesdk.session.message.PayloadMessageCollatorImpl import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before @@ -153,7 +153,7 @@ internal class PayloadFactoryBaTest { } private fun createService(createInitialSession: Boolean = true): PayloadFactoryImpl { - val collator = PayloadMessageCollator( + val collator = PayloadMessageCollatorImpl( configService, metadataService, eventService, diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadMessageCollatorTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadMessageCollatorTest.kt index ff7c41440..c908f8a70 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadMessageCollatorTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/PayloadMessageCollatorTest.kt @@ -21,6 +21,7 @@ import io.embrace.android.embracesdk.payload.SessionMessage import io.embrace.android.embracesdk.session.message.FinalEnvelopeParams import io.embrace.android.embracesdk.session.message.InitialEnvelopeParams import io.embrace.android.embracesdk.session.message.PayloadMessageCollator +import io.embrace.android.embracesdk.session.message.PayloadMessageCollatorImpl import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -41,7 +42,7 @@ internal class PayloadMessageCollatorTest { @Before fun setUp() { initModule = FakeInitModule() - collator = PayloadMessageCollator( + collator = PayloadMessageCollatorImpl( configService = FakeConfigService(), nativeThreadSamplerService = null, thermalStatusService = FakeThermalStatusService(), diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt index 31b593484..0d2a0c541 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/session/SessionHandlerTest.kt @@ -42,7 +42,7 @@ import io.embrace.android.embracesdk.payload.SessionMessage import io.embrace.android.embracesdk.session.lifecycle.ProcessState import io.embrace.android.embracesdk.session.message.PayloadFactory import io.embrace.android.embracesdk.session.message.PayloadFactoryImpl -import io.embrace.android.embracesdk.session.message.PayloadMessageCollator +import io.embrace.android.embracesdk.session.message.PayloadMessageCollatorImpl import io.embrace.android.embracesdk.session.properties.EmbraceSessionProperties import io.embrace.android.embracesdk.worker.ScheduledWorker import io.mockk.clearAllMocks @@ -135,7 +135,7 @@ internal class SessionHandlerTest { val initModule = FakeInitModule(clock = clock) spanSink = initModule.openTelemetryModule.spanSink spanService = initModule.openTelemetryModule.spanService - val payloadMessageCollator = PayloadMessageCollator( + val payloadMessageCollator = PayloadMessageCollatorImpl( configService, metadataService, eventService,