Skip to content

Commit 16543f7

Browse files
authored
Merge pull request #1291 from DataDog/nogorodnikov/prepare-release-1.17.1
Prepare release 1.17.1
2 parents 80e6e6a + 660f66a commit 16543f7

File tree

11 files changed

+636
-106
lines changed

11 files changed

+636
-106
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 1.17.1 / 2023-02-20
2+
3+
* [BUGFIX] RUM: Revert: Detect device's refresh rate from vital monitor. See [#1251](https://github.com/DataDog/dd-sdk-android/pull/1251)
4+
* [BUGFIX] RUM: The `RumEventMapper` checks `ViewEvent`s by reference. See [#1279](https://github.com/DataDog/dd-sdk-android/pull/1279)
5+
* [BUGFIX] Global: Remove `okhttp3.internal` package usage. See [#1288](https://github.com/DataDog/dd-sdk-android/pull/1288)
6+
17
# 1.17.0 / 2023-01-30
28

39
* [FEATURE] Tracing: Allow the usage of OTel headers in distributed tracing. See [#1229](https://github.com/DataDog/dd-sdk-android/pull/1229)

buildSrc/src/main/kotlin/com/datadog/gradle/config/AndroidConfig.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ object AndroidConfig {
1818
const val MIN_SDK_FOR_COMPOSE = 21
1919
const val BUILD_TOOLS_VERSION = "33.0.0"
2020

21-
val VERSION = Version(1, 17, 0, Version.Type.Release)
21+
val VERSION = Version(1, 17, 1, Version.Type.Release)
2222
}
2323

2424
@Suppress("UnstableApiUsage")

dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/net/RequestUniqueIdentifier.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package com.datadog.android.core.internal.net
88

99
import okhttp3.Request
10-
import okhttp3.internal.Util
10+
import java.io.IOException
1111

1212
/**
1313
* Generates an identifier to uniquely track requests.
@@ -16,11 +16,25 @@ internal fun identifyRequest(request: Request): String {
1616
val method = request.method()
1717
val url = request.url()
1818
val body = request.body()
19-
return if (body == null || body == Util.EMPTY_REQUEST) {
19+
return if (body == null) {
2020
"$method$url"
2121
} else {
22-
val contentLength = body.contentLength()
22+
val contentLength = try {
23+
body.contentLength()
24+
} catch (@Suppress("SwallowedException") ioe: IOException) {
25+
0
26+
}
2327
val contentType = body.contentType()
24-
"$method$url$contentLength$contentType"
28+
// TODO RUMM-3062 It is possible that if requests are say GZIPed (as an example), or maybe
29+
// streaming case (?), they all will have contentLength = -1, so if they target the same URL
30+
// they are going to have same identifier, messing up reporting.
31+
// -1 is valid return value for contentLength() call according to the docs and stands
32+
// for "unknown" case.
33+
// We need to have a more precise identification.
34+
if (contentType != null || contentLength != 0L) {
35+
"$method$url$contentLength$contentType"
36+
} else {
37+
"$method$url"
38+
}
2539
}
2640
}

dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,6 @@ internal class RumFeature(
9797
fun initialize(context: Context, configuration: Configuration.Feature.RUM) {
9898
dataWriter = createDataWriter(configuration)
9999

100-
appContext = context.applicationContext
101-
102100
samplingRate = configuration.samplingRate
103101
telemetrySamplingRate = configuration.telemetrySamplingRate
104102
backgroundEventTracking = configuration.backgroundEventTracking
@@ -115,6 +113,8 @@ internal class RumFeature(
115113

116114
registerTrackingStrategies(context)
117115

116+
appContext = context.applicationContext
117+
118118
sdkCore.setEventReceiver(RUM_FEATURE_NAME, this)
119119

120120
initialized.set(true)
@@ -235,9 +235,7 @@ internal class RumFeature(
235235
initializeVitalMonitor(CPUVitalReader(), cpuVitalMonitor, periodInMs)
236236
initializeVitalMonitor(MemoryVitalReader(), memoryVitalMonitor, periodInMs)
237237

238-
val vitalFrameCallback = VitalFrameCallback(appContext, frameRateVitalMonitor) {
239-
initialized.get()
240-
}
238+
val vitalFrameCallback = VitalFrameCallback(frameRateVitalMonitor) { initialized.get() }
241239
try {
242240
Choreographer.getInstance().postFrameCallback(vitalFrameCallback)
243241
} catch (e: IllegalStateException) {

dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/domain/event/RumEventMapper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ internal data class RumEventMapper(
8383
// In case the event is of type ViewEvent this cannot be null according with the interface
8484
// but it can happen that if used from Java code to have null values. In this case we will
8585
// log a warning and we will use the original event.
86-
return if (event is ViewEvent && (mappedEvent == null || mappedEvent != event)) {
86+
return if (event is ViewEvent && (mappedEvent == null || mappedEvent !== event)) {
8787
internalLogger.log(
8888
InternalLogger.Level.WARN,
8989
InternalLogger.Target.USER,

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@
66

77
package com.datadog.android.rum.internal.domain.scope
88

9+
import android.annotation.SuppressLint
10+
import android.app.Activity
11+
import android.content.Context
12+
import android.os.Build
13+
import android.view.WindowManager
914
import androidx.annotation.WorkerThread
15+
import androidx.fragment.app.Fragment
1016
import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver
17+
import com.datadog.android.core.internal.system.BuildSdkVersionProvider
18+
import com.datadog.android.core.internal.system.DefaultBuildSdkVersionProvider
1119
import com.datadog.android.core.internal.utils.internalLogger
1220
import com.datadog.android.core.internal.utils.loggableStackTrace
1321
import com.datadog.android.core.internal.utils.resolveViewUrl
@@ -51,6 +59,7 @@ internal open class RumViewScope(
5159
internal val memoryVitalMonitor: VitalMonitor,
5260
internal val frameRateVitalMonitor: VitalMonitor,
5361
private val contextProvider: ContextProvider,
62+
private val buildSdkVersionProvider: BuildSdkVersionProvider = DefaultBuildSdkVersionProvider(),
5463
private val viewUpdatePredicate: ViewUpdatePredicate = DefaultViewUpdatePredicate(),
5564
private val featuresContextResolver: FeaturesContextResolver = FeaturesContextResolver(),
5665
internal val type: RumViewType = RumViewType.FOREGROUND,
@@ -120,6 +129,7 @@ internal open class RumViewScope(
120129
}
121130
}
122131

132+
private var refreshRateScale: Double = 1.0
123133
private var lastFrameRateInfo: VitalInfo? = null
124134
private var frameRateVitalListener: VitalListener = object : VitalListener {
125135
override fun onVitalUpdate(info: VitalInfo) {
@@ -140,6 +150,8 @@ internal open class RumViewScope(
140150
cpuVitalMonitor.register(cpuVitalListener)
141151
memoryVitalMonitor.register(memoryVitalListener)
142152
frameRateVitalMonitor.register(frameRateVitalListener)
153+
154+
detectRefreshRateScale(key)
143155
}
144156

145157
// region RumScope
@@ -662,6 +674,8 @@ internal open class RumViewScope(
662674
val eventJsRefreshRate = performanceMetrics[RumPerformanceMetric.JS_FRAME_TIME]
663675
?.toInversePerformanceMetric()
664676

677+
val eventRefreshRateScale = refreshRateScale
678+
665679
val updatedDurationNs = resolveViewDuration(event)
666680
val rumContext = getRumContext()
667681

@@ -697,8 +711,8 @@ internal open class RumViewScope(
697711
cpuTicksPerSecond = eventCpuTicks?.let { (it * ONE_SECOND_NS) / updatedDurationNs },
698712
memoryAverage = memoryInfo?.meanValue,
699713
memoryMax = memoryInfo?.maxValue,
700-
refreshRateAverage = refreshRateInfo?.meanValue,
701-
refreshRateMin = refreshRateInfo?.minValue,
714+
refreshRateAverage = refreshRateInfo?.meanValue?.let { it * eventRefreshRateScale },
715+
refreshRateMin = refreshRateInfo?.minValue?.let { it * eventRefreshRateScale },
702716
isSlowRendered = isSlowRendered,
703717
frustration = ViewEvent.Frustration(eventFrustrationCount.toLong()),
704718
flutterBuildTime = eventFlutterBuildTime,
@@ -964,6 +978,29 @@ internal open class RumViewScope(
964978
return stopped && activeResourceScopes.isEmpty() && (pending <= 0L)
965979
}
966980

981+
/*
982+
* The refresh rate needs to be computed with each view because:
983+
* - it requires a context with a UI (we can't get this from the application context);
984+
* - it can change between different activities (based on window configuration)
985+
*/
986+
@SuppressLint("NewApi")
987+
@Suppress("DEPRECATION")
988+
private fun detectRefreshRateScale(key: Any) {
989+
val activity = when (key) {
990+
is Activity -> key
991+
is Fragment -> key.activity
992+
is android.app.Fragment -> key.activity
993+
else -> null
994+
} ?: return
995+
996+
val display = if (buildSdkVersionProvider.version() >= Build.VERSION_CODES.R) {
997+
activity.display
998+
} else {
999+
(activity.getSystemService(Context.WINDOW_SERVICE) as? WindowManager)?.defaultDisplay
1000+
} ?: return
1001+
refreshRateScale = STANDARD_FPS / display.refreshRate
1002+
}
1003+
9671004
enum class RumViewType {
9681005
NONE,
9691006
FOREGROUND,

dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/vitals/VitalFrameCallback.kt

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66

77
package com.datadog.android.rum.internal.vitals
88

9-
import android.content.Context
109
import android.view.Choreographer
11-
import android.view.WindowManager
1210
import com.datadog.android.core.internal.utils.internalLogger
1311
import com.datadog.android.v2.api.InternalLogger
1412
import java.util.concurrent.TimeUnit
@@ -17,7 +15,6 @@ import java.util.concurrent.TimeUnit
1715
* Reads the UI framerate based on the [Choreographer.FrameCallback] and notify a [VitalObserver].
1816
*/
1917
internal class VitalFrameCallback(
20-
private val appContext: Context,
2118
private val observer: VitalObserver,
2219
private val keepRunning: () -> Boolean
2320
) : Choreographer.FrameCallback {
@@ -30,9 +27,7 @@ internal class VitalFrameCallback(
3027
if (lastFrameTimestampNs != 0L) {
3128
val durationNs = (frameTimeNanos - lastFrameTimestampNs).toDouble()
3229
if (durationNs > 0.0) {
33-
val refreshRateScale = detectRefreshRateScale()
34-
val rawFps = ONE_SECOND_NS / durationNs
35-
val frameRate = rawFps * refreshRateScale
30+
val frameRate = ONE_SECOND_NS / durationNs
3631
if (frameRate in VALID_FPS_RANGE) {
3732
observer.onNewSample(frameRate)
3833
}
@@ -55,35 +50,13 @@ internal class VitalFrameCallback(
5550
}
5651
}
5752

58-
@Suppress("DEPRECATION")
59-
private fun detectRefreshRateScale(): Double {
60-
val windowManager = appContext.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
61-
return if (windowManager == null) {
62-
internalLogger.log(
63-
InternalLogger.Level.WARN,
64-
InternalLogger.Target.MAINTAINER,
65-
"WindowManager is null, can't detect max refresh rate!"
66-
)
67-
1.0
68-
} else if (windowManager.defaultDisplay == null) {
69-
internalLogger.log(
70-
InternalLogger.Level.WARN,
71-
InternalLogger.Target.MAINTAINER,
72-
"Display is null, can't detect max refresh rate!"
73-
)
74-
1.0
75-
} else {
76-
STANDARD_FPS / windowManager.defaultDisplay.refreshRate
77-
}
78-
}
79-
8053
// endregion
8154

8255
companion object {
8356
val ONE_SECOND_NS: Double = TimeUnit.SECONDS.toNanos(1).toDouble()
8457

8558
private const val MIN_FPS: Double = 1.0
86-
private const val STANDARD_FPS: Double = 60.0
87-
val VALID_FPS_RANGE = MIN_FPS.rangeTo(STANDARD_FPS)
59+
private const val MAX_FPS: Double = 240.0
60+
val VALID_FPS_RANGE = MIN_FPS.rangeTo(MAX_FPS)
8861
}
8962
}

dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/net/RequestUniqueIdentifierTest.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@ import fr.xgouchet.elmyr.junit5.ForgeExtension
1313
import okhttp3.MediaType
1414
import okhttp3.Request
1515
import okhttp3.RequestBody
16+
import okio.BufferedSink
1617
import org.assertj.core.api.Assertions.assertThat
1718
import org.junit.jupiter.api.Test
1819
import org.junit.jupiter.api.extension.ExtendWith
1920
import org.junit.jupiter.api.extension.Extensions
21+
import org.junit.jupiter.params.ParameterizedTest
22+
import org.junit.jupiter.params.provider.ValueSource
2023
import org.mockito.junit.jupiter.MockitoExtension
2124
import org.mockito.junit.jupiter.MockitoSettings
2225
import org.mockito.quality.Strictness
26+
import java.io.IOException
2327

2428
@Extensions(
2529
ExtendWith(MockitoExtension::class),
@@ -172,4 +176,40 @@ internal class RequestUniqueIdentifierTest {
172176
assertThat(id)
173177
.isEqualTo("DELETE•$fakeUrl$contentLength$fakeContentType; charset=utf-8")
174178
}
179+
180+
@ValueSource(strings = ["POST", "PUT", "PATCH", "DELETE"])
181+
@ParameterizedTest
182+
fun `identify request { body#contentLength throws exception }`(
183+
method: String
184+
) {
185+
val body = object : RequestBody() {
186+
override fun contentLength(): Long {
187+
throw IOException("")
188+
}
189+
190+
override fun contentType(): MediaType? {
191+
return MediaType.parse(fakeContentType)
192+
}
193+
194+
override fun writeTo(sink: BufferedSink) {
195+
// no-op
196+
}
197+
}
198+
val request = Request.Builder()
199+
.apply {
200+
when (method) {
201+
"POST" -> post(body)
202+
"PUT" -> put(body)
203+
"PATCH" -> patch(body)
204+
"DELETE" -> delete(body)
205+
}
206+
}
207+
.url(fakeUrl)
208+
.build()
209+
210+
val id = identifyRequest(request)
211+
212+
assertThat(id)
213+
.isEqualTo("$method$fakeUrl•0•$fakeContentType")
214+
}
175215
}

0 commit comments

Comments
 (0)