Skip to content

Commit dc9cc0b

Browse files
authored
Merge pull request #2772 from DataDog/aforsythe/RUM-8875/external-refresh-rate
RUM-8875 Add `updateExternalRefreshRate` to internal RUM API
2 parents 0fc87f5 + e8b26e5 commit dc9cc0b

File tree

12 files changed

+338
-2
lines changed

12 files changed

+338
-2
lines changed

features/dd-sdk-android-rum/api/apiSurface

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ enum com.datadog.android.rum.RumSessionType
165165
class com.datadog.android.rum._RumInternalProxy
166166
fun addLongTask(Long, String)
167167
fun updatePerformanceMetric(RumPerformanceMetric, Double)
168+
fun updateExternalRefreshRate(Double)
168169
fun setInternalViewAttribute(String, Any?)
169170
fun setSyntheticsAttribute(String?, String?)
170171
fun enableJankStatsTracking(android.app.Activity)

features/dd-sdk-android-rum/api/dd-sdk-android-rum.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ public final class com/datadog/android/rum/_RumInternalProxy {
248248
public final fun enableJankStatsTracking (Landroid/app/Activity;)V
249249
public final fun setInternalViewAttribute (Ljava/lang/String;Ljava/lang/Object;)V
250250
public final fun setSyntheticsAttribute (Ljava/lang/String;Ljava/lang/String;)V
251+
public final fun updateExternalRefreshRate (D)V
251252
public final fun updatePerformanceMetric (Lcom/datadog/android/rum/RumPerformanceMetric;D)V
252253
}
253254

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/_RumInternalProxy.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class _RumInternalProxy internal constructor(private val rumMonitor: AdvancedRum
4545
rumMonitor.updatePerformanceMetric(metric, value)
4646
}
4747

48+
fun updateExternalRefreshRate(frameTimeSeconds: Double) {
49+
rumMonitor.updateExternalRefreshRate(frameTimeSeconds)
50+
}
51+
4852
fun setInternalViewAttribute(key: String, value: Any?) {
4953
rumMonitor.setInternalViewAttribute(key, value)
5054
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEvent.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ internal sealed class RumRawEvent {
217217
override val eventTime: Time = Time()
218218
) : RumRawEvent()
219219

220+
internal data class UpdateExternalRefreshRate(
221+
val frameTimeSeconds: Double,
222+
override val eventTime: Time = Time()
223+
) : RumRawEvent()
224+
220225
internal data class SetInternalViewAttribute(
221226
val key: String,
222227
val value: Any?,

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ internal class RumViewManagerScope(
380380
RumRawEvent.LongTaskSent::class.java,
381381
RumRawEvent.ResourceDropped::class.java,
382382
RumRawEvent.ResourceSent::class.java,
383-
RumRawEvent.UpdatePerformanceMetric::class.java
383+
RumRawEvent.UpdatePerformanceMetric::class.java,
384+
RumRawEvent.UpdateExternalRefreshRate::class.java
384385
)
385386

386387
internal const val RUM_BACKGROUND_VIEW_ID = "com.datadog.background.view"

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ internal open class RumViewScope(
152152

153153
private val performanceMetrics: MutableMap<RumPerformanceMetric, VitalInfo> = mutableMapOf()
154154

155+
private var externalRefreshRateInfo: VitalInfo? = null
156+
155157
// endregion
156158

157159
init {
@@ -211,6 +213,7 @@ internal open class RumViewScope(
211213
is RumRawEvent.StopSession -> onStopSession(event, writer)
212214

213215
is RumRawEvent.UpdatePerformanceMetric -> onUpdatePerformanceMetric(event)
216+
is RumRawEvent.UpdateExternalRefreshRate -> onUpdateExternalRefreshRate(event)
214217
is RumRawEvent.AddViewLoadingTime -> onAddViewLoadingTime(event, writer)
215218

216219
else -> delegateEventToChildren(event, writer)
@@ -647,6 +650,31 @@ internal open class RumViewScope(
647650
)
648651
}
649652

653+
private fun onUpdateExternalRefreshRate(
654+
event: RumRawEvent.UpdateExternalRefreshRate
655+
) {
656+
if (stopped) return
657+
658+
// Convert frame time (seconds) to refresh rate (Hz)
659+
val refreshRateHz = if (event.frameTimeSeconds > 0) {
660+
1.0 / event.frameTimeSeconds
661+
} else {
662+
return // Invalid frame time
663+
}
664+
665+
val currentInfo = externalRefreshRateInfo ?: VitalInfo.EMPTY
666+
val newSampleCount = currentInfo.sampleCount + 1
667+
668+
// Calculate incremental mean using the same algorithm as performance metrics
669+
val meanValue = (refreshRateHz + (currentInfo.sampleCount * currentInfo.meanValue)) / newSampleCount
670+
externalRefreshRateInfo = VitalInfo(
671+
newSampleCount,
672+
min(refreshRateHz, currentInfo.minValue),
673+
max(refreshRateHz, currentInfo.maxValue),
674+
meanValue
675+
)
676+
}
677+
650678
@WorkerThread
651679
private fun onSetInternalViewAttribute(event: RumRawEvent.SetInternalViewAttribute) {
652680
if (stopped) return
@@ -917,7 +945,8 @@ internal open class RumViewScope(
917945

918946
val timings = resolveCustomTimings()
919947
val memoryInfo = lastMemoryInfo
920-
val refreshRateInfo = lastFrameRateInfo
948+
// Use external refresh rate data if available, otherwise fall back to internal data
949+
val refreshRateInfo = externalRefreshRateInfo ?: lastFrameRateInfo
921950
val isSlowRendered = resolveRefreshRateInfo(refreshRateInfo) ?: false
922951
// make a copy - by the time we iterate over it on another thread, it may already be changed
923952
val eventFeatureFlags = featureFlags.toMutableMap()

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/AdvancedRumMonitor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ internal interface AdvancedRumMonitor : RumMonitor, AdvancedNetworkRumMonitor {
4949

5050
fun updatePerformanceMetric(metric: RumPerformanceMetric, value: Double)
5151

52+
fun updateExternalRefreshRate(frameTimeSeconds: Double)
53+
5254
fun setInternalViewAttribute(key: String, value: Any?)
5355

5456
fun setSyntheticsAttribute(testId: String, resultId: String)

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,10 @@ internal class DatadogRumMonitor(
634634
handleEvent(RumRawEvent.UpdatePerformanceMetric(metric, value))
635635
}
636636

637+
override fun updateExternalRefreshRate(frameTimeSeconds: Double) {
638+
handleEvent(RumRawEvent.UpdateExternalRefreshRate(frameTimeSeconds))
639+
}
640+
637641
override fun setInternalViewAttribute(key: String, value: Any?) {
638642
handleEvent(RumRawEvent.SetInternalViewAttribute(key, value))
639643
}

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumInternalProxyTest.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.app.Activity
1010
import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor
1111
import com.datadog.android.rum.utils.forge.Configurator
1212
import fr.xgouchet.elmyr.Forge
13+
import fr.xgouchet.elmyr.annotation.DoubleForgery
1314
import fr.xgouchet.elmyr.annotation.LongForgery
1415
import fr.xgouchet.elmyr.annotation.StringForgery
1516
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
@@ -64,6 +65,21 @@ internal class RumInternalProxyTest {
6465
verify(mockRumMonitor).updatePerformanceMetric(metric, value)
6566
}
6667

68+
@Test
69+
fun `M proxy updateExternalRefreshRate to RumMonitor W updateExternalRefreshRate()`(
70+
@DoubleForgery(min = 0.001, max = 1.0) frameTimeSeconds: Double
71+
) {
72+
// Given
73+
val mockRumMonitor = mock(AdvancedRumMonitor::class.java)
74+
val proxy = _RumInternalProxy(mockRumMonitor)
75+
76+
// When
77+
proxy.updateExternalRefreshRate(frameTimeSeconds)
78+
79+
// Then
80+
verify(mockRumMonitor).updateExternalRefreshRate(frameTimeSeconds)
81+
}
82+
6783
@Test
6884
fun `M proxy enableJankStatsTracking to RumMonitor W enableJankStatsTracking()`() {
6985
// Given

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEventExt.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ internal fun Forge.updatePerformanceMetricEvent(): RumRawEvent.UpdatePerformance
150150
)
151151
}
152152

153+
internal fun Forge.updateExternalRefreshRateEvent(): RumRawEvent.UpdateExternalRefreshRate {
154+
val time = Time()
155+
return RumRawEvent.UpdateExternalRefreshRate(
156+
frameTimeSeconds = aDouble(),
157+
eventTime = time
158+
)
159+
}
160+
153161
internal fun Forge.addFeatureFlagEvaluationEvent(): RumRawEvent.AddFeatureFlagEvaluation {
154162
val time = Time()
155163
return RumRawEvent.AddFeatureFlagEvaluation(
@@ -206,6 +214,7 @@ internal fun Forge.anyRumEvent(excluding: List<KClass<out RumRawEvent>> = listOf
206214
strictSameTypePair(RumRawEvent.AddFeatureFlagEvaluation::class, { addFeatureFlagEvaluationEvent() }),
207215
strictSameTypePair(RumRawEvent.AddCustomTiming::class, { addCustomTimingEvent() }),
208216
strictSameTypePair(RumRawEvent.UpdatePerformanceMetric::class, { updatePerformanceMetricEvent() }),
217+
strictSameTypePair(RumRawEvent.UpdateExternalRefreshRate::class, { updateExternalRefreshRateEvent() }),
209218
strictSameTypePair(RumRawEvent.AddViewLoadingTime::class, { addViewLoadingTimeEvent() })
210219
)
211220
return this.anElementFrom(

0 commit comments

Comments
 (0)