Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dd-sdk-android-core/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ interface com.datadog.android.core.InternalSdkCore : com.datadog.android.api.fea
val lastViewEvent: com.google.gson.JsonObject?
val lastFatalAnrSent: Long?
val appStartTimeNs: Long
val appUptimeNs: Long
fun writeLastViewEvent(ByteArray)
fun deleteLastViewEvent()
fun writeLastFatalAnrSent(Long)
Expand Down
1 change: 1 addition & 0 deletions dd-sdk-android-core/api/dd-sdk-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ public abstract interface class com/datadog/android/core/InternalSdkCore : com/d
public abstract fun deleteLastViewEvent ()V
public abstract fun getAllFeatures ()Ljava/util/List;
public abstract fun getAppStartTimeNs ()J
public abstract fun getAppUptimeNs ()J
public abstract fun getDatadogContext (Ljava/util/Set;)Lcom/datadog/android/api/context/DatadogContext;
public abstract fun getFirstPartyHostResolver ()Lcom/datadog/android/core/internal/net/FirstPartyHostHeaderTypeResolver;
public abstract fun getLastFatalAnrSent ()Ljava/lang/Long;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ data class TimeInfo(
val serverTimeNs: Long,
val serverTimeOffsetNs: Long,
val serverTimeOffsetMs: Long
)
) {
internal companion object {
internal val EMPTY = TimeInfo(0, 0, 0, 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ interface InternalSdkCore : FeatureSdkCore {
@InternalApi
val appStartTimeNs: Long

/**
* Provide the time since the application started in nanoseconds from device boot, or our best guess
* if the actual start time is not available. Note: since the implementation may rely on [System.nanoTime],
* this property can only be used to measure elapsed time and is not related to any other notion of system
* or wall-clock time. The value is the time since VM start.
*/
@InternalApi
val appUptimeNs: Long

/**
* Writes current RUM view event to the dedicated file for the needs of NDK crash reporting.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ internal class CoreFeature(
internal val appStartTimeNs: Long
get() = appStartTimeProvider.appStartTimeNs

internal val appUptimeNs: Long
get() = appStartTimeProvider.appUptimeNs

// lazy here on purpose: we need to read it only once, even if it is used in different features
@get:WorkerThread
internal val lastViewEvent: JsonObject? by lazy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import com.datadog.android.api.context.DatadogContext
import com.datadog.android.api.context.DeviceInfo
import com.datadog.android.api.context.LocaleInfo
import com.datadog.android.api.context.ProcessInfo
import com.datadog.android.api.context.TimeInfo
import java.util.concurrent.TimeUnit
import com.datadog.android.core.internal.time.composeTimeInfo

internal class DatadogContextProvider(
private val coreFeature: CoreFeature,
Expand All @@ -30,17 +29,7 @@ internal class DatadogContextProvider(
variant = coreFeature.variant,
sdkVersion = coreFeature.sdkVersion,
source = coreFeature.sourceName,
time = with(coreFeature.timeProvider) {
val deviceTimeMs = getDeviceTimestamp()
val serverTimeMs = getServerTimestamp()
TimeInfo(
deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(deviceTimeMs),
serverTimeNs = TimeUnit.MILLISECONDS.toNanos(serverTimeMs),
serverTimeOffsetNs = TimeUnit.MILLISECONDS
.toNanos(serverTimeMs - deviceTimeMs),
serverTimeOffsetMs = serverTimeMs - deviceTimeMs
)
},
time = coreFeature.timeProvider.composeTimeInfo(),
processInfo = ProcessInfo(
isMainProcess = coreFeature.isMainProcess
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.datadog.android.core.internal.logger.SdkInternalLogger
import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver
import com.datadog.android.core.internal.system.BuildSdkVersionProvider
import com.datadog.android.core.internal.time.DefaultAppStartTimeProvider
import com.datadog.android.core.internal.time.composeTimeInfo
import com.datadog.android.core.internal.utils.executeSafe
import com.datadog.android.core.internal.utils.getSafe
import com.datadog.android.core.internal.utils.scheduleSafe
Expand Down Expand Up @@ -100,17 +101,7 @@ internal class DatadogCore(
/** @inheritDoc */
override val time: TimeInfo
get() {
return with(coreFeature.timeProvider) {
val deviceTimeMs = getDeviceTimestamp()
val serverTimeMs = getServerTimestamp()
TimeInfo(
deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(deviceTimeMs),
serverTimeNs = TimeUnit.MILLISECONDS.toNanos(serverTimeMs),
serverTimeOffsetNs = TimeUnit.MILLISECONDS
.toNanos(serverTimeMs - deviceTimeMs),
serverTimeOffsetMs = serverTimeMs - deviceTimeMs
)
}
return coreFeature.timeProvider.composeTimeInfo()
}

/** @inheritDoc */
Expand Down Expand Up @@ -387,6 +378,9 @@ internal class DatadogCore(
override val appStartTimeNs: Long
get() = coreFeature.appStartTimeNs

override val appUptimeNs: Long
get() = coreFeature.appUptimeNs

@WorkerThread
override fun writeLastViewEvent(data: ByteArray) {
// we need to write it only if we are going to read ApplicationExitInfo (available on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,7 @@ internal class NoOpContextProvider : ContextProvider {
variant = "",
source = "",
sdkVersion = "",
time = TimeInfo(
deviceTimeNs = 0L,
serverTimeNs = 0L,
serverTimeOffsetMs = 0L,
serverTimeOffsetNs = 0L
),
time = TimeInfo.EMPTY,
processInfo = ProcessInfo(isMainProcess = true),
networkInfo = NetworkInfo(
connectivity = NetworkInfo.Connectivity.NETWORK_OTHER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,15 @@ import java.util.concurrent.TimeUnit
/**
* A no-op implementation of [SdkCore].
*/
@Suppress("TooManyFunctions")
internal object NoOpInternalSdkCore : InternalSdkCore {

override val name: String = "no-op"

override val time: TimeInfo = with(System.currentTimeMillis()) {
TimeInfo(
TimeInfo.EMPTY.copy(
deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(this),
serverTimeNs = TimeUnit.MILLISECONDS.toNanos(this),
serverTimeOffsetNs = 0L,
serverTimeOffsetMs = 0L
serverTimeNs = TimeUnit.MILLISECONDS.toNanos(this)
)
}

Expand Down Expand Up @@ -78,6 +77,8 @@ internal object NoOpInternalSdkCore : InternalSdkCore {
get() = null
override val appStartTimeNs: Long
get() = 0
override val appUptimeNs: Long
get() = 0

// endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ internal interface AppStartTimeProvider {
* if the actual start time is not available.
*/
val appStartTimeNs: Long

/**
* Provide the time since the application started in nanoseconds from device boot, or our best guess
* if the actual start time is not available.
*/
val appUptimeNs: Long
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ internal class DefaultAppStartTimeProvider(
else -> DdRumContentProvider.createTimeNs
}
}

override val appUptimeNs: Long
get() = System.nanoTime() - appStartTimeNs
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ internal class KronosTimeProvider(
private val clock: Clock
) : TimeProvider {

override fun getDeviceTimestamp(): Long {
return System.currentTimeMillis()
}

override fun getServerTimestamp(): Long {
return clock.getCurrentTimeMs()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2016-Present Datadog, Inc.
*/
package com.datadog.android.core.internal.time

import com.datadog.android.api.context.TimeInfo
import com.datadog.android.internal.time.TimeProvider
import java.util.concurrent.TimeUnit

internal fun TimeProvider.composeTimeInfo(): TimeInfo {
val deviceTimeMs = getDeviceTimestamp()
val serverTimeMs = getServerTimestamp()
return TimeInfo(
deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(deviceTimeMs),
serverTimeNs = TimeUnit.MILLISECONDS.toNanos(serverTimeMs),
serverTimeOffsetNs = TimeUnit.MILLISECONDS.toNanos(serverTimeMs - deviceTimeMs),
serverTimeOffsetMs = serverTimeMs - deviceTimeMs
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,21 @@ internal class DatadogCoreTest {
assertThat(appStartTimeNs).isEqualTo(fakeAppStartTimeNs)
}

@Test
fun `M provide app uptime W appUptimeNs()`(
@LongForgery(min = 0L) fakeAppUptimeNs: Long
) {
// Given
testedCore.coreFeature = mock()
whenever(testedCore.coreFeature.appUptimeNs) doReturn fakeAppUptimeNs

// When
val appStartTimeNs = testedCore.appUptimeNs

// Then
assertThat(appStartTimeNs).isEqualTo(fakeAppUptimeNs)
}

@Test
fun `M return tracking consent W trackingConsent()`(
@Forgery fakeTrackingConsent: TrackingConsent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ class DefaultAppStartTimeProviderTest {
whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion
val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime()
val startTimeNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs)
val testedProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)

// WHEN
val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)
val providedStartTime = timeProvider.appStartTimeNs
val providedStartTime = testedProvider.appStartTimeNs

// THEN
assertThat(providedStartTime)
Expand All @@ -54,12 +54,54 @@ class DefaultAppStartTimeProviderTest {
val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock()
whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion
val startTimeNs = DdRumContentProvider.createTimeNs
val testedProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)

// WHEN
val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)
val providedStartTime = timeProvider.appStartTimeNs
val providedStartTime = testedProvider.appStartTimeNs

// THEN
assertThat(providedStartTime).isEqualTo(startTimeNs)
}

@Test
fun `M return app uptime W appUptimeNs`(
@IntForgery(min = Build.VERSION_CODES.M) apiVersion: Int
) {
// GIVEN
val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock {
on { version } doReturn apiVersion
}
val testedProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)

// WHEN
val beforeNs = System.nanoTime()
val appStartTimeNs = testedProvider.appStartTimeNs
val uptime = testedProvider.appUptimeNs
val afterNs = System.nanoTime()

// THEN
val expectedUptime = beforeNs - appStartTimeNs
assertThat(uptime)
.isGreaterThan(0)
.isCloseTo(expectedUptime, Offset.offset(TimeUnit.MILLISECONDS.toNanos(100)))
.isLessThanOrEqualTo(afterNs - appStartTimeNs)
}

@Test
fun `M return increasing uptime W appUptimeNs called multiple times`(
@IntForgery(min = Build.VERSION_CODES.M) apiVersion: Int
) {
// GIVEN
val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock()
whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion
val testedProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider)

// WHEN
val uptime1 = testedProvider.appUptimeNs
Thread.sleep(10)
val uptime2 = testedProvider.appUptimeNs

// THEN
assertThat(uptime2).isGreaterThan(uptime1)
}
}
1 change: 0 additions & 1 deletion dd-sdk-android-internal/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ class com.datadog.android.internal.thread.NamedRunnable : NamedExecutionUnit, Ru
class com.datadog.android.internal.thread.NamedCallable<V> : NamedExecutionUnit, java.util.concurrent.Callable<V>
constructor(String, java.util.concurrent.Callable<V>)
class com.datadog.android.internal.time.DefaultTimeProvider : TimeProvider
override fun getDeviceTimestamp(): Long
override fun getServerTimestamp(): Long
override fun getServerOffsetNanos(): Long
override fun getServerOffsetMillis(): Long
Expand Down
4 changes: 4 additions & 0 deletions dd-sdk-android-internal/api/dd-sdk-android-internal.api
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ public abstract interface class com/datadog/android/internal/time/TimeProvider {
public abstract fun getServerTimestamp ()J
}

public final class com/datadog/android/internal/time/TimeProvider$DefaultImpls {
public static fun getDeviceTimestamp (Lcom/datadog/android/internal/time/TimeProvider;)J
}

public final class com/datadog/android/internal/utils/ByteArrayExtKt {
public static final fun toHexString ([B)Ljava/lang/String;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ package com.datadog.android.internal.time
* The offsets are always 0.
*/
class DefaultTimeProvider : TimeProvider {
override fun getDeviceTimestamp(): Long = System.currentTimeMillis()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this change needed?


override fun getServerTimestamp(): Long = System.currentTimeMillis()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface TimeProvider {
/**
* Returns the current device timestamp in milliseconds.
*/
fun getDeviceTimestamp(): Long
fun getDeviceTimestamp(): Long = System.currentTimeMillis()

/**
* Returns the current server timestamp in milliseconds.
Expand Down
2 changes: 2 additions & 0 deletions detekt_custom_safe_calls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ datadog:
- "android.os.Looper.getMainLooper()"
- "android.os.Looper.setMessageLogging(android.util.Printer?)"
- "android.os.Process.getStartElapsedRealtime()"
- "android.os.Process.getStartUptimeMillis()"
- "android.os.Process.myPid()"
- "android.os.SystemClock.elapsedRealtime()"
- "android.os.SystemClock.elapsedRealtimeNanos()"
- "android.os.SystemClock.uptimeMillis()"
- "android.os.StrictMode.allowThreadDiskReads()"
- "android.os.StrictMode.allowThreadDiskWrites()"
- "android.os.StrictMode.setThreadPolicy(android.os.StrictMode.ThreadPolicy?)"
Expand Down
Loading
Loading