From a679c90d66c23514a3c4a837e7af079b3cbad96b Mon Sep 17 00:00:00 2001 From: tore-statsig <74584483+tore-statsig@users.noreply.github.com> Date: Thu, 11 Jul 2024 10:42:30 -0700 Subject: [PATCH] feat: opt out of optional metadata fields (#243) * feat: opt out of optional metadata fields * fix: lints * fix: remove unused import * fix: lint --- .../com/statsig/androidsdk/StatsigClient.kt | 8 ++- .../com/statsig/androidsdk/StatsigMetadata.kt | 65 ++++++++++++++----- .../com/statsig/androidsdk/StatsigOptions.kt | 2 + .../com/statsig/androidsdk/StatsigTest.kt | 36 ++++++++-- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/statsig/androidsdk/StatsigClient.kt b/src/main/java/com/statsig/androidsdk/StatsigClient.kt index 31a5f27..2908854 100644 --- a/src/main/java/com/statsig/androidsdk/StatsigClient.kt +++ b/src/main/java/com/statsig/androidsdk/StatsigClient.kt @@ -823,7 +823,11 @@ class StatsigClient() : LifecycleEventListener { if (!this::statsigNetwork.isInitialized) { statsigNetwork = StatsigNetwork(application, sdkKey, errorBoundary, getSharedPrefs(), options) } - statsigMetadata = StatsigMetadata() + statsigMetadata = if (options.optOutNonSdkMetadata) { + createCoreStatsigMetadata() + } else { + createStatsigMetadata() + } errorBoundary.setMetadata(statsigMetadata) errorBoundary.setDiagnostics(diagnostics) @@ -1010,7 +1014,7 @@ class StatsigClient() : LifecycleEventListener { private fun populateStatsigMetadata() { statsigMetadata.overrideStableID(options.overrideStableID) try { - if (application.packageManager != null) { + if (application.packageManager != null && !options.optOutNonSdkMetadata) { val pInfo: PackageInfo = application.packageManager.getPackageInfo(application.packageName, 0) statsigMetadata.appVersion = pInfo.versionName diff --git a/src/main/java/com/statsig/androidsdk/StatsigMetadata.kt b/src/main/java/com/statsig/androidsdk/StatsigMetadata.kt index c11f83c..c7bd846 100644 --- a/src/main/java/com/statsig/androidsdk/StatsigMetadata.kt +++ b/src/main/java/com/statsig/androidsdk/StatsigMetadata.kt @@ -6,25 +6,17 @@ import java.util.* internal data class StatsigMetadata( @SerializedName("stableID") var stableID: String? = null, - @SerializedName("appIdentifier") var appIdentifier: String? = null, - @SerializedName("appVersion") var appVersion: String? = null, - @SerializedName("deviceModel") var deviceModel: String? = Build.MODEL, - @SerializedName("deviceOS") var deviceOS: String = "Android", - @SerializedName("locale") var locale: String? = Locale.getDefault().toString(), - @SerializedName("language") - var language: String? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // API 21+ - Locale.getDefault().toLanguageTag() - } else { - Locale.getDefault().let { locale -> - "${locale.language}-${locale.country}" - } - }, @SerializedName("sdkType") var sdkType: String? = "android-client", @SerializedName("sdkVersion") var sdkVersion: String? = BuildConfig.VERSION_NAME, @SerializedName("sessionID") var sessionID: String = UUID.randomUUID().toString(), - @SerializedName("systemVersion") var systemVersion: String = Build.VERSION.SDK_INT.toString(), - @SerializedName("systemName") var systemName: String = "Android", + @SerializedName("appIdentifier") var appIdentifier: String? = null, + @SerializedName("appVersion") var appVersion: String? = null, + @SerializedName("deviceModel") var deviceModel: String? = null, + @SerializedName("deviceOS") var deviceOS: String? = null, + @SerializedName("locale") var locale: String? = null, + @SerializedName("language") var language: String? = null, + @SerializedName("systemVersion") var systemVersion: String? = null, + @SerializedName("systemName") var systemName: String? = null, ) { internal fun overrideStableID(overrideStableID: String?) { if (overrideStableID != null && overrideStableID != stableID) { @@ -32,3 +24,44 @@ internal data class StatsigMetadata( } } } + +internal fun createStatsigMetadata(): StatsigMetadata { + return StatsigMetadata( + stableID = null, + sdkType = "android-client", + sdkVersion = BuildConfig.VERSION_NAME, + sessionID = UUID.randomUUID().toString(), + appIdentifier = null, + appVersion = null, + deviceModel = Build.MODEL, + deviceOS = "Android", + locale = Locale.getDefault().toString(), + language = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // API 21+ + Locale.getDefault().toLanguageTag() + } else { + Locale.getDefault().let { locale -> + "${locale.language}-${locale.country}" + } + }, + systemVersion = Build.VERSION.SDK_INT.toString(), + systemName = "Android", + ) +} + +internal fun createCoreStatsigMetadata(): StatsigMetadata { + return StatsigMetadata( + stableID = null, + sdkType = "android-client", + sdkVersion = BuildConfig.VERSION_NAME, + sessionID = UUID.randomUUID().toString(), + appIdentifier = null, + appVersion = null, + deviceModel = null, + deviceOS = null, + locale = null, + language = null, + systemVersion = null, + systemName = null, + ) +} diff --git a/src/main/java/com/statsig/androidsdk/StatsigOptions.kt b/src/main/java/com/statsig/androidsdk/StatsigOptions.kt index a452288..0b4a338 100644 --- a/src/main/java/com/statsig/androidsdk/StatsigOptions.kt +++ b/src/main/java/com/statsig/androidsdk/StatsigOptions.kt @@ -94,6 +94,8 @@ class StatsigOptions( var customCacheKey: ((String, StatsigUser) -> String) = { sdkKey, user -> "${user.getCacheKey()}:$sdkKey" }, var disableLogEventRetries: Boolean = false, + + var optOutNonSdkMetadata: Boolean = false, ) { private var environment: MutableMap? = null diff --git a/src/test/java/com/statsig/androidsdk/StatsigTest.kt b/src/test/java/com/statsig/androidsdk/StatsigTest.kt index 64252cc..6677402 100644 --- a/src/test/java/com/statsig/androidsdk/StatsigTest.kt +++ b/src/test/java/com/statsig/androidsdk/StatsigTest.kt @@ -120,10 +120,10 @@ class StatsigTest { assertEquals(15, parsedLogs.events.count()) // first 2 are exposures pre initialize() completion assertEquals("custom_stable_id", parsedLogs.statsigMetadata.stableID) - assertEquals("Android", parsedLogs.statsigMetadata.systemName) - assertEquals("Android", parsedLogs.statsigMetadata.deviceOS) - assert(parsedLogs.statsigMetadata.locale.toString().startsWith("en")) - assert(parsedLogs.statsigMetadata.language.toString().startsWith("en")) + assertEquals("Android", (parsedLogs.statsigMetadata as StatsigMetadata).systemName) + assertEquals("Android", (parsedLogs.statsigMetadata as StatsigMetadata).deviceOS) + assert((parsedLogs.statsigMetadata as StatsigMetadata).locale.toString().startsWith("en")) + assert((parsedLogs.statsigMetadata as StatsigMetadata).language.toString().startsWith("en")) // validate diagnostics assertEquals(parsedLogs.events[0].eventName, "statsig::diagnostics") @@ -229,6 +229,34 @@ class StatsigTest { assertEquals(parsedLogs.events[12].metadata!!["isManualExposure"], "true") } + @Test + fun testInitializeWithOptOutNonSdkMetadata() { + val user = StatsigUser("123") + user.customIDs = mapOf("random_id" to "abcde") + + // Initialize Statsig with optOutNonSdkMetadata set to true + TestUtil.startStatsigAndWait( + app, + user, + StatsigOptions(optOutNonSdkMetadata = true, overrideStableID = "custom_stable_id"), + network = network, + ) + client = Statsig.client + assertTrue(client.checkGate("always_on")) + + client.shutdown() + + var parsedLogs = Gson().fromJson(flushedLogs, LogEventData::class.java) + assertNull(parsedLogs.statsigMetadata.appIdentifier) + assertNull(parsedLogs.statsigMetadata.appVersion) + assertNull(parsedLogs.statsigMetadata.deviceModel) + assertNull(parsedLogs.statsigMetadata.locale) + assertNull(parsedLogs.statsigMetadata.language) + assertNull(parsedLogs.statsigMetadata.deviceOS) + assertNull(parsedLogs.statsigMetadata.systemVersion) + assertNull(parsedLogs.statsigMetadata.systemName) + } + @Test fun testReinitialize() = runBlocking { var countdown = CountDownLatch(1)