Skip to content

Commit 3eadf7d

Browse files
committed
RUM-10064: Read RUM context in Session Replay in non-blocking manner
1 parent 4e8dceb commit 3eadf7d

19 files changed

+177
-226
lines changed

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import com.datadog.android.sessionreplay.internal.recorder.mapper.WebViewWirefra
4242
import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager
4343
import com.datadog.android.sessionreplay.internal.storage.RecordWriter
4444
import com.datadog.android.sessionreplay.internal.storage.ResourcesWriter
45-
import com.datadog.android.sessionreplay.internal.time.SessionReplayTimeProvider
45+
import com.datadog.android.sessionreplay.internal.utils.RumContextProvider
4646
import com.datadog.android.sessionreplay.recorder.OptionSelectorDetector
4747
import com.datadog.android.sessionreplay.recorder.mapper.EditTextMapper
4848
import com.datadog.android.sessionreplay.recorder.mapper.ImageViewMapper
@@ -73,18 +73,19 @@ internal class DefaultRecorderProvider(
7373
resourceDataStoreManager: ResourceDataStoreManager,
7474
resourceWriter: ResourcesWriter,
7575
recordWriter: RecordWriter,
76+
rumContextProvider: RumContextProvider,
7677
application: Application
7778
): Recorder {
7879
return SessionReplayRecorder(
7980
application,
8081
resourceDataStoreManager = resourceDataStoreManager,
8182
resourcesWriter = resourceWriter,
82-
rumContextProvider = SessionReplayRumContextProvider(sdkCore),
83+
rumContextProvider = rumContextProvider,
8384
imagePrivacy = imagePrivacy,
8485
touchPrivacyManager = touchPrivacyManager,
8586
textAndInputPrivacy = textAndInputPrivacy,
8687
recordWriter = recordWriter,
87-
timeProvider = SessionReplayTimeProvider(sdkCore),
88+
timeProvider = { System.currentTimeMillis() },
8889
mappers = customMappers + builtInMappers(),
8990
customOptionSelectorDetectors = customOptionSelectorDetectors,
9091
customDrawableMappers = customDrawableMappers,

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/RecorderProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import com.datadog.android.sessionreplay.internal.recorder.Recorder
1111
import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager
1212
import com.datadog.android.sessionreplay.internal.storage.RecordWriter
1313
import com.datadog.android.sessionreplay.internal.storage.ResourcesWriter
14+
import com.datadog.android.sessionreplay.internal.utils.RumContextProvider
1415

1516
internal fun interface RecorderProvider {
1617
fun provideSessionReplayRecorder(
1718
resourceDataStoreManager: ResourceDataStoreManager,
1819
resourceWriter: ResourcesWriter,
1920
recordWriter: RecordWriter,
21+
rumContextProvider: RumContextProvider,
2022
application: Application
2123
): Recorder
2224
}

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeature.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ internal class SessionReplayFeature(
118118
internal var sessionReplayRecorder: Recorder = NoOpRecorder()
119119
internal var dataWriter: RecordWriter = NoOpRecordWriter()
120120
internal val initialized = AtomicBoolean(false)
121+
private val rumContextProvider = SessionReplayRumContextProvider()
121122

122123
// region Feature
123124

@@ -141,11 +142,13 @@ internal class SessionReplayFeature(
141142
)
142143

143144
dataWriter = createDataWriter()
145+
sdkCore.setContextUpdateReceiver(Feature.SESSION_REPLAY_FEATURE_NAME, rumContextProvider)
144146
sessionReplayRecorder =
145147
recorderProvider.provideSessionReplayRecorder(
146148
resourceDataStoreManager = resourceDataStoreManager,
147149
resourceWriter = resourcesFeature.dataWriter,
148150
recordWriter = dataWriter,
151+
rumContextProvider = rumContextProvider,
149152
application = appContext
150153
)
151154
sessionReplayRecorder.registerCallbacks()
@@ -170,6 +173,7 @@ internal class SessionReplayFeature(
170173

171174
override fun onStop() {
172175
stopRecording()
176+
sdkCore.removeContextUpdateReceiver(Feature.RUM_FEATURE_NAME, rumContextProvider)
173177
sessionReplayRecorder.unregisterCallbacks()
174178
sessionReplayRecorder.stopProcessingRecords()
175179
dataWriter = NoOpRecordWriter()

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProvider.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,32 @@
77
package com.datadog.android.sessionreplay.internal
88

99
import com.datadog.android.api.feature.Feature
10-
import com.datadog.android.api.feature.FeatureSdkCore
10+
import com.datadog.android.api.feature.FeatureContextUpdateReceiver
1111
import com.datadog.android.sessionreplay.internal.utils.RumContextProvider
1212
import com.datadog.android.sessionreplay.internal.utils.SessionReplayRumContext
1313
import java.util.UUID
1414

15-
internal class SessionReplayRumContextProvider(
16-
private val sdkCore: FeatureSdkCore
17-
) : RumContextProvider {
15+
internal class SessionReplayRumContextProvider : RumContextProvider, FeatureContextUpdateReceiver {
16+
17+
@Volatile
18+
private var rumContext = emptyMap<String, Any?>()
19+
1820
override fun getRumContext(): SessionReplayRumContext {
19-
val rumContext = sdkCore.getFeatureContext(Feature.RUM_FEATURE_NAME)
20-
return SessionReplayRumContext(
21-
applicationId = rumContext["application_id"] as? String ?: NULL_UUID,
22-
sessionId = rumContext["session_id"] as? String ?: NULL_UUID,
23-
viewId = rumContext["view_id"] as? String ?: NULL_UUID
24-
)
21+
return rumContext.let {
22+
SessionReplayRumContext(
23+
applicationId = it["application_id"] as? String ?: NULL_UUID,
24+
sessionId = it["session_id"] as? String ?: NULL_UUID,
25+
viewId = it["view_id"] as? String ?: NULL_UUID,
26+
// TODO RUM-3785 Share this property somehow, defined in RumFeature.VIEW_TIMESTAMP_OFFSET_IN_MS_KEY
27+
viewTimeOffsetMs = it["view_timestamp_offset"] as? Long ?: 0L
28+
)
29+
}
30+
}
31+
32+
override fun onContextUpdate(featureName: String, event: Map<String, Any?>) {
33+
if (featureName == Feature.RUM_FEATURE_NAME) {
34+
rumContext = event
35+
}
2536
}
2637

2738
companion object {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RumContextDataHandler.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ internal class RumContextDataHandler(
2020

2121
@MainThread
2222
internal fun createRumContextData(): RecordedQueuedItemContext? {
23-
// we will make sure we get the timestamp on the UI thread to avoid time skewing
2423
val timestamp = timeProvider.getDeviceTimestamp()
2524

26-
// TODO RUM-836 Fetch the RumContext from the core SDKContext when available
2725
val newRumContext = rumContextProvider.getRumContext()
2826

2927
if (newRumContext.isNotValid()) {
@@ -40,7 +38,7 @@ internal class RumContextDataHandler(
4038
return null
4139
}
4240

43-
return RecordedQueuedItemContext(timestamp, newRumContext.copy())
41+
return RecordedQueuedItemContext(timestamp + newRumContext.viewTimeOffsetMs, newRumContext.copy())
4442
}
4543

4644
companion object {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,13 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
5959
private val appContext: Application
6060
private val textAndInputPrivacy: TextAndInputPrivacy
6161
private val imagePrivacy: ImagePrivacy
62-
private val touchPrivacyManager: TouchPrivacyManager
63-
private val recordWriter: RecordWriter
64-
private val timeProvider: TimeProvider
65-
private val mappers: List<MapperTypeWrapper<*>>
6662
private val customOptionSelectorDetectors: List<OptionSelectorDetector>
6763
private val windowInspector: WindowInspector
6864
private val windowCallbackInterceptor: WindowCallbackInterceptor
6965
private val sessionReplayLifecycleCallback: LifecycleCallback
7066
private val recordedDataQueueHandler: RecordedDataQueueHandler
7167
private val viewOnDrawInterceptor: ViewOnDrawInterceptor
7268
private val internalLogger: InternalLogger
73-
private val resourceDataStoreManager: ResourceDataStoreManager
74-
private val internalCallback: SessionReplayInternalCallback
7569
private val uiHandler: Handler
7670
private var shouldRecord = false
7771

@@ -111,10 +105,6 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
111105
this.appContext = appContext
112106
this.textAndInputPrivacy = textAndInputPrivacy
113107
this.imagePrivacy = imagePrivacy
114-
this.touchPrivacyManager = touchPrivacyManager
115-
this.recordWriter = recordWriter
116-
this.timeProvider = timeProvider
117-
this.mappers = mappers
118108
this.customOptionSelectorDetectors = customOptionSelectorDetectors
119109
this.windowInspector = windowInspector
120110
this.recordedDataQueueHandler = RecordedDataQueueHandler(
@@ -126,8 +116,6 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
126116
),
127117
recordedDataQueue = ConcurrentLinkedQueue()
128118
)
129-
this.resourceDataStoreManager = resourceDataStoreManager
130-
this.internalCallback = internalCallback
131119

132120
val viewIdentifierResolver: ViewIdentifierResolver = DefaultViewIdentifierResolver
133121
val colorStringFormatter: ColorStringFormatter = DefaultColorStringFormatter
@@ -200,6 +188,7 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
200188
recordedDataQueueHandler,
201189
viewOnDrawInterceptor,
202190
timeProvider,
191+
rumContextProvider,
203192
internalLogger,
204193
imagePrivacy,
205194
textAndInputPrivacy,
@@ -223,28 +212,18 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
223212
appContext: Application,
224213
textAndInputPrivacy: TextAndInputPrivacy,
225214
imagePrivacy: ImagePrivacy,
226-
touchPrivacyManager: TouchPrivacyManager,
227-
recordWriter: RecordWriter,
228-
timeProvider: TimeProvider,
229-
mappers: List<MapperTypeWrapper<*>> = emptyList(),
230215
customOptionSelectorDetectors: List<OptionSelectorDetector>,
231216
windowInspector: WindowInspector = WindowInspector,
232217
windowCallbackInterceptor: WindowCallbackInterceptor,
233218
sessionReplayLifecycleCallback: LifecycleCallback,
234219
viewOnDrawInterceptor: ViewOnDrawInterceptor,
235220
recordedDataQueueHandler: RecordedDataQueueHandler,
236-
resourceDataStoreManager: ResourceDataStoreManager,
237221
uiHandler: Handler,
238-
internalLogger: InternalLogger,
239-
internalCallback: SessionReplayInternalCallback
222+
internalLogger: InternalLogger
240223
) {
241224
this.appContext = appContext
242225
this.textAndInputPrivacy = textAndInputPrivacy
243226
this.imagePrivacy = imagePrivacy
244-
this.touchPrivacyManager = touchPrivacyManager
245-
this.recordWriter = recordWriter
246-
this.timeProvider = timeProvider
247-
this.mappers = mappers
248227
this.customOptionSelectorDetectors = customOptionSelectorDetectors
249228
this.windowInspector = windowInspector
250229
this.recordedDataQueueHandler = recordedDataQueueHandler
@@ -253,8 +232,6 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder {
253232
this.sessionReplayLifecycleCallback = sessionReplayLifecycleCallback
254233
this.uiHandler = uiHandler
255234
this.internalLogger = internalLogger
256-
this.resourceDataStoreManager = resourceDataStoreManager
257-
this.internalCallback = internalCallback
258235
}
259236

260237
override fun stopProcessingRecords() {

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/WindowCallbackInterceptor.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import com.datadog.android.sessionreplay.internal.TouchPrivacyManager
1515
import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler
1616
import com.datadog.android.sessionreplay.internal.recorder.callback.NoOpWindowCallback
1717
import com.datadog.android.sessionreplay.internal.recorder.callback.RecorderWindowCallback
18+
import com.datadog.android.sessionreplay.internal.utils.RumContextProvider
1819
import com.datadog.android.sessionreplay.internal.utils.TimeProvider
1920
import java.util.WeakHashMap
2021

2122
internal class WindowCallbackInterceptor(
2223
private val recordedDataQueueHandler: RecordedDataQueueHandler,
2324
private val viewOnDrawInterceptor: ViewOnDrawInterceptor,
2425
private val timeProvider: TimeProvider,
26+
private val rumContextProvider: RumContextProvider,
2527
private val internalLogger: InternalLogger,
2628
private val imagePrivacy: ImagePrivacy,
2729
private val textAndInputPrivacy: TextAndInputPrivacy,
@@ -57,6 +59,7 @@ internal class WindowCallbackInterceptor(
5759
recordedDataQueueHandler,
5860
toWrap,
5961
timeProvider,
62+
rumContextProvider,
6063
viewOnDrawInterceptor,
6164
internalLogger,
6265
textAndInputPrivacy,

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.datadog.android.sessionreplay.internal.TouchPrivacyManager
1919
import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler
2020
import com.datadog.android.sessionreplay.internal.recorder.ViewOnDrawInterceptor
2121
import com.datadog.android.sessionreplay.internal.recorder.WindowInspector
22+
import com.datadog.android.sessionreplay.internal.utils.RumContextProvider
2223
import com.datadog.android.sessionreplay.internal.utils.TimeProvider
2324
import com.datadog.android.sessionreplay.model.MobileSegment
2425
import java.util.LinkedList
@@ -30,6 +31,7 @@ internal class RecorderWindowCallback(
3031
private val recordedDataQueueHandler: RecordedDataQueueHandler,
3132
internal val wrappedCallback: Window.Callback,
3233
private val timeProvider: TimeProvider,
34+
private val rumContextProvider: RumContextProvider,
3335
private val viewOnDrawInterceptor: ViewOnDrawInterceptor,
3436
private val internalLogger: InternalLogger,
3537
private val privacy: TextAndInputPrivacy,
@@ -131,7 +133,7 @@ internal class RecorderWindowCallback(
131133
val pointerAbsoluteY = motionEventUtils.getPointerAbsoluteY(event, pointerIndex)
132134
pointerInteractions.add(
133135
MobileSegment.MobileRecord.MobileIncrementalSnapshotRecord(
134-
timestamp = timeProvider.getDeviceTimestamp(),
136+
timestamp = timeProvider.getDeviceTimestamp() + rumContextProvider.getRumContext().viewTimeOffsetMs,
135137
data = MobileSegment.MobileIncrementalData.PointerInteractionData(
136138
pointerEventType = eventType,
137139
pointerType = MobileSegment.PointerType.TOUCH,

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/time/SessionReplayTimeProvider.kt

Lines changed: 0 additions & 33 deletions
This file was deleted.

features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/utils/SessionReplayRumContext.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@ import java.util.UUID
1313
* @param applicationId the RUM application id
1414
* @param sessionId the current RUM session id
1515
* @param viewId the current RUM view id
16+
* @param viewTimeOffsetMs the offset in ms of the current RUM view
1617
*/
1718
internal data class SessionReplayRumContext(
1819
val applicationId: String = NULL_UUID,
1920
val sessionId: String = NULL_UUID,
20-
val viewId: String = NULL_UUID
21+
val viewId: String = NULL_UUID,
22+
val viewTimeOffsetMs: Long = 0L
2123
) {
2224

23-
internal fun isNotValid(): Boolean =
24-
applicationId == NULL_UUID ||
25-
sessionId == NULL_UUID ||
26-
viewId == NULL_UUID
25+
internal fun isNotValid(): Boolean = !isValid()
2726

2827
internal fun isValid(): Boolean =
2928
applicationId != NULL_UUID &&

0 commit comments

Comments
 (0)