Skip to content

Commit 0342fdd

Browse files
authored
Merge pull request #3045 from DataDog/yl/profiling/add-next-launch-api
Add Profiling.profileNextAppStartup API
2 parents 794e7e5 + 5f9b353 commit 0342fdd

File tree

7 files changed

+80
-4
lines changed

7 files changed

+80
-4
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class com.datadog.android.profiling.DdProfilingContentProvider : android.content
77
override fun update(android.net.Uri, android.content.ContentValues?, String?, Array<String>?): Int
88
object com.datadog.android.profiling.Profiling
99
fun enable(ProfilingConfiguration = ProfilingConfiguration.DEFAULT, com.datadog.android.api.SdkCore = Datadog.getInstance())
10+
fun profileNextAppStartup(com.datadog.android.api.SdkCore = Datadog.getInstance(), Boolean)
1011
data class com.datadog.android.profiling.ProfilingConfiguration
1112
class Builder
1213
fun useCustomEndpoint(String): Builder

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public final class com/datadog/android/profiling/Profiling {
1414
public static final fun enable (Lcom/datadog/android/profiling/ProfilingConfiguration;)V
1515
public static final fun enable (Lcom/datadog/android/profiling/ProfilingConfiguration;Lcom/datadog/android/api/SdkCore;)V
1616
public static synthetic fun enable$default (Lcom/datadog/android/profiling/ProfilingConfiguration;Lcom/datadog/android/api/SdkCore;ILjava/lang/Object;)V
17+
public static final fun profileNextAppStartup (Lcom/datadog/android/api/SdkCore;Z)V
18+
public static final fun profileNextAppStartup (Z)V
19+
public static synthetic fun profileNextAppStartup$default (Lcom/datadog/android/api/SdkCore;ZILjava/lang/Object;)V
1720
}
1821

1922
public final class com/datadog/android/profiling/ProfilingConfiguration {

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/Profiling.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import android.content.Context
1010
import android.os.Build
1111
import androidx.annotation.RequiresApi
1212
import com.datadog.android.Datadog
13+
import com.datadog.android.api.InternalLogger
1314
import com.datadog.android.api.SdkCore
15+
import com.datadog.android.api.feature.Feature
1416
import com.datadog.android.api.feature.FeatureSdkCore
1517
import com.datadog.android.internal.time.DefaultTimeProvider
1618
import com.datadog.android.profiling.internal.NoOpProfiler
@@ -28,8 +30,7 @@ object Profiling {
2830
private var profiler: Profiler = NoOpProfiler()
2931

3032
/**
31-
* Enables the Perfetto based profiler to start recording callstack samples during application
32-
* launch.
33+
* Enables the profiling feature.
3334
*
3435
* @param configuration Configuration to use for the feature.
3536
* @param sdkCore SDK instance to register feature in. If not provided, default SDK instance
@@ -51,6 +52,33 @@ object Profiling {
5152
featureSdkCore.registerFeature(profilingFeature)
5253
}
5354

55+
/**
56+
* Decides if the next app launch should be profiled.
57+
*
58+
* @param sdkCore SDK instance to register feature in.
59+
* @param enable enables the profiling for next app launch, otherwise disables it.
60+
*/
61+
@JvmStatic
62+
@JvmOverloads
63+
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
64+
fun profileNextAppStartup(
65+
sdkCore: SdkCore = Datadog.getInstance(),
66+
enable: Boolean
67+
) {
68+
val featureSdkCore = sdkCore as FeatureSdkCore
69+
val profilingFeature = featureSdkCore
70+
.getFeature(Feature.PROFILING_FEATURE_NAME)?.let {
71+
it.unwrap() as? ProfilingFeature
72+
}
73+
profilingFeature?.profileNextAppStartup(enable) ?: run {
74+
featureSdkCore.internalLogger.log(
75+
level = InternalLogger.Level.WARN,
76+
target = InternalLogger.Target.USER,
77+
messageBuilder = { "Profiling feature needs to be enabled before calling this method." }
78+
)
79+
}
80+
}
81+
5482
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
5583
internal fun start(context: Context, sdkInstanceNames: Set<String>) {
5684
profiler = PerfettoProfiler(

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ internal class ProfilingFeature(
3434

3535
private var perfettoResult: PerfettoResult? = null
3636

37+
internal lateinit var appContext: Context
38+
3739
override val requestFactory: RequestFactory = ProfilingRequestFactory(
3840
configuration.customEndpointUrl,
3941
sdkCore.internalLogger
@@ -46,15 +48,14 @@ internal class ProfilingFeature(
4648
get() = Feature.Companion.PROFILING_FEATURE_NAME
4749

4850
override fun onInitialize(appContext: Context) {
51+
this.appContext = appContext
4952
profiler.apply {
5053
this.internalLogger = sdkCore.internalLogger
5154
registerProfilingCallback(sdkCore.name) { result ->
5255
perfettoResult = result
5356
tryWriteProfilingEvent()
5457
}
5558
}
56-
// Set the profiling flag in SharedPreferences to profile for the next app launch
57-
ProfilingStorage.addProfilingFlag(appContext, sdkCore.name)
5859
sdkCore.setEventReceiver(name, this)
5960
sdkCore.updateFeatureContext(Feature.PROFILING_FEATURE_NAME) { context ->
6061
context.put(PROFILER_IS_RUNNING, profiler.isRunning(sdkCore.name))
@@ -89,6 +90,15 @@ internal class ProfilingFeature(
8990
)
9091
}
9192

93+
internal fun profileNextAppStartup(enable: Boolean) {
94+
// Set the profiling flag in SharedPreferences to profile for the next app launch
95+
if (enable) {
96+
ProfilingStorage.addProfilingFlag(appContext, sdkCore.name)
97+
} else {
98+
ProfilingStorage.removeProfilingFlag(appContext, setOf(sdkCore.name))
99+
}
100+
}
101+
92102
private fun tryWriteProfilingEvent() {
93103
val perfettoResult = perfettoResult ?: return
94104
val ttidEvent = ttidEvent ?: return

features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingStorage.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal object ProfilingStorage {
1515

1616
private var sharedPreferencesStorage: SharedPreferencesStorage? = null
1717

18+
@JvmStatic
1819
internal fun addProfilingFlag(appContext: Context, sdkInstanceName: String) {
1920
getStorage(appContext).apply {
2021
val oldValue = getStringSet(KEY_PROFILING_ENABLED, emptySet())
@@ -23,10 +24,12 @@ internal object ProfilingStorage {
2324
}
2425
}
2526

27+
@JvmStatic
2628
internal fun getProfilingEnabledInstanceNames(appContext: Context): Set<String> {
2729
return getStorage(appContext).getStringSet(KEY_PROFILING_ENABLED)
2830
}
2931

32+
@JvmStatic
3033
internal fun removeProfilingFlag(appContext: Context, sdkInstanceNames: Set<String>) {
3134
getStorage(appContext).apply {
3235
val value = getStringSet(KEY_PROFILING_ENABLED).toMutableSet()

features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.datadog.android.profiling.forge.Configurator
1515
import com.datadog.android.profiling.internal.Profiler
1616
import com.datadog.android.profiling.internal.ProfilingFeature
1717
import com.datadog.android.profiling.internal.ProfilingRequestFactory
18+
import com.datadog.android.profiling.internal.ProfilingStorage
1819
import com.datadog.android.rum.TTIDEvent
1920
import fr.xgouchet.elmyr.annotation.Forgery
2021
import fr.xgouchet.elmyr.annotation.StringForgery
@@ -26,6 +27,7 @@ import org.junit.jupiter.api.Test
2627
import org.junit.jupiter.api.extension.ExtendWith
2728
import org.junit.jupiter.api.extension.Extensions
2829
import org.mockito.Mock
30+
import org.mockito.Mockito
2931
import org.mockito.junit.jupiter.MockitoExtension
3032
import org.mockito.junit.jupiter.MockitoSettings
3133
import org.mockito.kotlin.any
@@ -143,4 +145,32 @@ class ProfilingFeatureTest {
143145
.isEqualTo("Profiling feature receive an event of unsupported type=${String::class.java.canonicalName}.")
144146
verify(mockProfiler, never()).stop(fakeInstanceName)
145147
}
148+
149+
@Test
150+
fun `M add profiling flag W profileNextAppStartup {enable = true}`() {
151+
Mockito.mockStatic(ProfilingStorage::class.java).use { mockedStatic ->
152+
153+
// When
154+
testedFeature.profileNextAppStartup(true)
155+
156+
// Then
157+
mockedStatic.verify {
158+
ProfilingStorage.addProfilingFlag(mockContext, fakeInstanceName)
159+
}
160+
}
161+
}
162+
163+
@Test
164+
fun `M remove profiling flag W profileNextAppStartup {enable = false}`() {
165+
Mockito.mockStatic(ProfilingStorage::class.java).use { mockedStatic ->
166+
167+
// When
168+
testedFeature.profileNextAppStartup(false)
169+
170+
// Then
171+
mockedStatic.verify {
172+
ProfilingStorage.removeProfilingFlag(mockContext, setOf(fakeInstanceName))
173+
}
174+
}
175+
}
146176
}

sample/kotlin/src/main/kotlin/com/datadog/android/sample/SampleApplication.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class SampleApplication : Application() {
168168

169169
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
170170
Profiling.enable()
171+
Profiling.profileNextAppStartup(enable = true)
171172
}
172173
}
173174

0 commit comments

Comments
 (0)