Skip to content

Commit 7b11e61

Browse files
committed
RUM-9881: Push context changes from public API to the context thread
1 parent bce2bbf commit 7b11e61

File tree

5 files changed

+171
-59
lines changed

5 files changed

+171
-59
lines changed

dd-sdk-android-core/src/main/kotlin/com/datadog/android/Datadog.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ object Datadog {
9393
sdkInstanceName
9494
).apply {
9595
initialize(configuration)
96+
// not pushing to the context thread to have it set already at the
97+
// moment Datadog.initialize is completed
98+
coreFeature.trackingConsentProvider.setConsent(trackingConsent)
9699
}
97-
sdkCore.setTrackingConsent(trackingConsent)
98100
registry.register(sdkInstanceName, sdkCore)
99101

100102
return sdkCore

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

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import com.datadog.android.core.internal.system.BuildSdkVersionProvider
3636
import com.datadog.android.core.internal.time.DefaultAppStartTimeProvider
3737
import com.datadog.android.core.internal.utils.executeSafe
3838
import com.datadog.android.core.internal.utils.scheduleSafe
39+
import com.datadog.android.core.internal.utils.submitSafe
3940
import com.datadog.android.core.thread.FlushableExecutorService
4041
import com.datadog.android.error.internal.CrashReportsFeature
4142
import com.datadog.android.internal.telemetry.InternalTelemetryEvent
@@ -45,7 +46,10 @@ import com.google.gson.JsonObject
4546
import java.io.File
4647
import java.util.Locale
4748
import java.util.UUID
49+
import java.util.concurrent.Callable
50+
import java.util.concurrent.CancellationException
4851
import java.util.concurrent.ConcurrentHashMap
52+
import java.util.concurrent.ExecutionException
4953
import java.util.concurrent.ExecutorService
5054
import java.util.concurrent.ScheduledExecutorService
5155
import java.util.concurrent.TimeUnit
@@ -156,7 +160,9 @@ internal class DatadogCore(
156160
/** @inheritDoc */
157161
@AnyThread
158162
override fun setTrackingConsent(consent: TrackingConsent) {
159-
coreFeature.trackingConsentProvider.setConsent(consent)
163+
coreFeature.contextExecutorService.executeSafe("DatadogCore.setTrackingConsent", internalLogger) {
164+
coreFeature.trackingConsentProvider.setConsent(consent)
165+
}
160166
}
161167

162168
/** @inheritDoc */
@@ -167,13 +173,19 @@ internal class DatadogCore(
167173
email: String?,
168174
extraInfo: Map<String, Any?>
169175
) {
170-
coreFeature.userInfoProvider.setUserInfo(id, name, email, extraInfo)
176+
val extraInfoSnapshot = extraInfo.toMap()
177+
coreFeature.contextExecutorService.executeSafe("DatadogCore.setUserInfo", internalLogger) {
178+
coreFeature.userInfoProvider.setUserInfo(id, name, email, extraInfoSnapshot)
179+
}
171180
}
172181

173182
/** @inheritDoc */
174183
@AnyThread
175184
override fun addUserProperties(extraInfo: Map<String, Any?>) {
176-
coreFeature.userInfoProvider.addUserProperties(extraInfo)
185+
val extraInfoSnapshot = extraInfo.toMap()
186+
coreFeature.contextExecutorService.executeSafe("DatadogCore.addUserProperties", internalLogger) {
187+
coreFeature.userInfoProvider.addUserProperties(extraInfoSnapshot)
188+
}
177189
}
178190

179191
/** @inheritDoc */
@@ -272,7 +284,9 @@ internal class DatadogCore(
272284
}
273285

274286
override fun setAnonymousId(anonymousId: UUID?) {
275-
coreFeature.userInfoProvider.setAnonymousId(anonymousId?.toString())
287+
coreFeature.contextExecutorService.executeSafe("DatadogCore.setAnonymousId", internalLogger) {
288+
coreFeature.userInfoProvider.setAnonymousId(anonymousId?.toString())
289+
}
276290
}
277291

278292
override fun isCoreActive(): Boolean = isActive
@@ -285,7 +299,27 @@ internal class DatadogCore(
285299
get() = coreFeature.networkInfoProvider.getLatestNetworkInfo()
286300

287301
override val trackingConsent: TrackingConsent
288-
get() = coreFeature.trackingConsentProvider.getConsent()
302+
get() {
303+
val future = coreFeature.contextExecutorService.submitSafe(
304+
"getTrackingConsent",
305+
internalLogger,
306+
Callable<TrackingConsent> {
307+
coreFeature.trackingConsentProvider.getConsent()
308+
}
309+
)
310+
return try {
311+
future?.get() ?: TrackingConsent.NOT_GRANTED
312+
} catch (e: InterruptedException) {
313+
logTrackingConsentGetFailure(internalLogger, e)
314+
TrackingConsent.NOT_GRANTED
315+
} catch (e: CancellationException) {
316+
logTrackingConsentGetFailure(internalLogger, e)
317+
TrackingConsent.NOT_GRANTED
318+
} catch (e: ExecutionException) {
319+
logTrackingConsentGetFailure(internalLogger, e)
320+
TrackingConsent.NOT_GRANTED
321+
}
322+
}
289323

290324
override val rootStorageDir: File
291325
get() = coreFeature.storageDir
@@ -535,6 +569,15 @@ internal class DatadogCore(
535569
)
536570
}
537571

572+
private fun logTrackingConsentGetFailure(internalLogger: InternalLogger, throwable: Throwable) {
573+
internalLogger.log(
574+
level = InternalLogger.Level.ERROR,
575+
targets = listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
576+
messageBuilder = { UNABLE_TO_GET_TRACKING_CONSENT },
577+
throwable = throwable
578+
)
579+
}
580+
538581
/**
539582
* Stops all process for this instance of the Datadog SDK.
540583
*/
@@ -590,6 +633,8 @@ internal class DatadogCore(
590633
"No need to write last RUM view event: NDK" +
591634
" crash reports feature is not enabled and API is below 30."
592635

636+
internal const val UNABLE_TO_GET_TRACKING_CONSENT = "Unable to get tracking consent."
637+
593638
internal val CONFIGURATION_TELEMETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(5)
594639

595640
// fallback for APIs below Android N, see [DefaultAppStartTimeProvider].

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/ConcurrencyExt.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package com.datadog.android.core.internal.utils
88

9+
import androidx.annotation.CheckResult
910
import com.datadog.android.api.InternalLogger
1011
import com.datadog.android.lint.InternalApi
1112
import java.util.Locale
@@ -88,6 +89,7 @@ fun ScheduledExecutorService.scheduleSafe(
8889
* @param runnable Task to run.
8990
*/
9091
@InternalApi
92+
@CheckResult
9193
fun ExecutorService.submitSafe(
9294
operationName: String,
9395
internalLogger: InternalLogger,

dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.datadog.android.utils.verifyLog
3737
import com.datadog.tools.unit.annotations.TestConfigurationsProvider
3838
import com.datadog.tools.unit.extensions.TestConfigurationExtension
3939
import com.datadog.tools.unit.extensions.config.TestConfiguration
40+
import com.datadog.tools.unit.forge.aThrowable
4041
import com.google.gson.JsonObject
4142
import fr.xgouchet.elmyr.Forge
4243
import fr.xgouchet.elmyr.annotation.AdvancedForgery
@@ -66,6 +67,7 @@ import org.mockito.kotlin.any
6667
import org.mockito.kotlin.argumentCaptor
6768
import org.mockito.kotlin.doAnswer
6869
import org.mockito.kotlin.doReturn
70+
import org.mockito.kotlin.doThrow
6971
import org.mockito.kotlin.mock
7072
import org.mockito.kotlin.never
7173
import org.mockito.kotlin.reset
@@ -78,7 +80,12 @@ import org.mockito.quality.Strictness
7880
import java.util.Collections
7981
import java.util.Locale
8082
import java.util.UUID
83+
import java.util.concurrent.Callable
84+
import java.util.concurrent.CancellationException
8185
import java.util.concurrent.CountDownLatch
86+
import java.util.concurrent.ExecutionException
87+
import java.util.concurrent.Future
88+
import java.util.concurrent.LinkedBlockingQueue
8289
import java.util.concurrent.ThreadPoolExecutor
8390
import java.util.concurrent.TimeUnit
8491
import java.util.concurrent.atomic.AtomicBoolean
@@ -104,6 +111,9 @@ internal class DatadogCoreTest {
104111
@Mock
105112
lateinit var mockPersistenceExecutorService: FlushableExecutorService
106113

114+
@Mock
115+
lateinit var mockContextExecutorService: ThreadPoolExecutor
116+
107117
@Mock
108118
lateinit var mockBuildSdkVersionProvider: BuildSdkVersionProvider
109119

@@ -122,6 +132,9 @@ internal class DatadogCoreTest {
122132
whenever(mockPersistenceExecutorService.execute(any())) doAnswer {
123133
it.getArgument<Runnable>(0).run()
124134
}
135+
whenever(mockContextExecutorService.execute(any())) doAnswer {
136+
it.getArgument<Runnable>(0).run()
137+
}
125138

126139
testedCore = DatadogCore(
127140
appContext.mockInstance,
@@ -133,6 +146,7 @@ internal class DatadogCoreTest {
133146
).apply {
134147
initialize(fakeConfiguration)
135148
}
149+
testedCore.coreFeature.contextExecutorService = mockContextExecutorService
136150
}
137151

138152
@AfterEach
@@ -262,9 +276,10 @@ internal class DatadogCoreTest {
262276
) {
263277
// Given
264278
testedCore.coreFeature = mock()
265-
whenever(testedCore.coreFeature.initialized).thenReturn(AtomicBoolean())
279+
whenever(testedCore.coreFeature.initialized).thenReturn(AtomicBoolean(true))
266280
val mockUserInfoProvider = mock<MutableUserInfoProvider>()
267281
whenever(testedCore.coreFeature.userInfoProvider) doReturn mockUserInfoProvider
282+
whenever(testedCore.coreFeature.contextExecutorService) doReturn mockContextExecutorService
268283

269284
// When
270285
testedCore.setUserInfo(id, name, email)
@@ -630,10 +645,11 @@ internal class DatadogCoreTest {
630645
@Forgery fakeTrackingConsent: TrackingConsent
631646
) {
632647
// Given
633-
testedCore.coreFeature = mock()
634-
val mockConsentProvider = mock<ConsentProvider>()
635-
whenever(mockConsentProvider.getConsent()) doReturn fakeTrackingConsent
636-
whenever(testedCore.coreFeature.trackingConsentProvider) doReturn mockConsentProvider
648+
val mockFuture = mock<Future<TrackingConsent>>()
649+
whenever(mockFuture.get()) doReturn fakeTrackingConsent
650+
whenever(
651+
testedCore.coreFeature.contextExecutorService.submit(any<Callable<TrackingConsent>>())
652+
) doReturn mockFuture
637653

638654
// When
639655
val trackingConsent = testedCore.trackingConsent
@@ -642,6 +658,35 @@ internal class DatadogCoreTest {
642658
assertThat(trackingConsent).isEqualTo(fakeTrackingConsent)
643659
}
644660

661+
@Test
662+
fun `M return default tracking consent W trackingConsent() { failed to get tracking consent }`(
663+
forge: Forge
664+
) {
665+
// Given
666+
val mockFuture = mock<Future<TrackingConsent>>()
667+
val fakeThrowable = forge.anElementFrom(
668+
ExecutionException(forge.aThrowable()),
669+
CancellationException(),
670+
InterruptedException()
671+
)
672+
whenever(mockFuture.get()) doThrow fakeThrowable
673+
whenever(
674+
testedCore.coreFeature.contextExecutorService.submit(any<Callable<TrackingConsent>>())
675+
) doReturn mockFuture
676+
677+
// When
678+
val trackingConsent = testedCore.trackingConsent
679+
680+
// When + Then
681+
assertThat(trackingConsent).isEqualTo(TrackingConsent.NOT_GRANTED)
682+
mockInternalLogger.verifyLog(
683+
InternalLogger.Level.ERROR,
684+
listOf(InternalLogger.Target.USER, InternalLogger.Target.TELEMETRY),
685+
DatadogCore.UNABLE_TO_GET_TRACKING_CONSENT,
686+
fakeThrowable
687+
)
688+
}
689+
645690
@Test
646691
fun `M return root storage dir W rootStorageDir()`() {
647692
// When + Then
@@ -747,16 +792,8 @@ internal class DatadogCoreTest {
747792
)
748793
val mockCoreFeature = mock<CoreFeature>()
749794
testedCore.coreFeature = mockCoreFeature
750-
val mockPersistenceExecutor = mock<FlushableExecutorService>()
751-
val mockContextExecutor = mock<ThreadPoolExecutor>()
752-
whenever(mockCoreFeature.persistenceExecutorService) doReturn mockPersistenceExecutor
753-
whenever(mockCoreFeature.contextExecutorService) doReturn mockContextExecutor
754-
whenever(mockPersistenceExecutor.execute(any())) doAnswer {
755-
it.getArgument<Runnable>(0).run()
756-
}
757-
whenever(mockContextExecutor.execute(any())) doAnswer {
758-
it.getArgument<Runnable>(0).run()
759-
}
795+
whenever(mockCoreFeature.persistenceExecutorService) doReturn mockPersistenceExecutorService
796+
whenever(mockCoreFeature.contextExecutorService) doReturn mockContextExecutorService
760797

761798
// When
762799
testedCore.clearAllData()
@@ -782,6 +819,7 @@ internal class DatadogCoreTest {
782819
anAlphaNumericalString() to mock()
783820
}
784821
)
822+
whenever(mockContextExecutorService.queue) doReturn LinkedBlockingQueue()
785823

786824
// When
787825
testedCore.flushStoredData()

0 commit comments

Comments
 (0)