From b27d61d218059360b2d9482e87a79b6bac6aeeae Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jul 2026 15:00:13 +0200 Subject: [PATCH 1/4] fix(android): Apply user info policy to distinct ID Generate the default Android installation ID after programmatic configuration and only when the resolved user information policy allows it. Preserve custom distinct IDs and keep legacy behavior when Data Collection is absent. Refs #5666 Co-Authored-By: Claude --- .../core/AndroidOptionsInitializer.java | 8 ---- .../io/sentry/android/core/SentryAndroid.java | 8 ++++ .../core/AndroidOptionsInitializerTest.kt | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 9cc5cb3df0f..2a60e5750d7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -471,14 +471,6 @@ private static void readDefaultOptionValues( options.addInAppInclude(packageName); } } - - if (options.getDistinctId() == null) { - try { - options.setDistinctId(Installation.id(context)); - } catch (RuntimeException e) { - options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e); - } - } } /** diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index f27259fd635..150569dcecf 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -149,6 +149,14 @@ public static void init( "Error in the 'OptionsConfiguration.configure' callback.", t); } + if (options.getDistinctId() == null + && options.getDataCollectionResolver().isUserInfoWithLegacyAlways()) { + try { + options.setDistinctId(Installation.id(context)); + } catch (RuntimeException e) { + options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e); + } + } // if SentryPerformanceProvider was disabled or removed, // we set the app start / sdk init time here instead diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index f8724d286f8..068b0964bcc 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -111,6 +111,12 @@ class AndroidOptionsInitializerTest { ) sentryOptions.configureOptions() + if ( + sentryOptions.distinctId == null && + sentryOptions.dataCollectionResolver.isUserInfoWithLegacyAlways + ) { + sentryOptions.distinctId = Installation.id(if (useRealContext) context else mockContext) + } AndroidOptionsInitializer.initializeIntegrationsAndProcessors( sentryOptions, if (useRealContext) context else mockContext, @@ -349,6 +355,44 @@ class AndroidOptionsInitializerTest { installation.deleteOnExit() } + @Test + fun `init should not set generated distinct id when user info is disabled`() { + fixture.initSut(configureOptions = { dataCollection.setUserInfo(false) }) + + assertNull(fixture.sentryOptions.distinctId) + } + + @Test + fun `init should set generated distinct id when user info is enabled`() { + fixture.initSut(configureOptions = { dataCollection.setUserInfo(true) }) + + assertNotNull(fixture.sentryOptions.distinctId) + } + + @Test + fun `init should preserve explicit distinct id when user info is disabled`() { + fixture.initSut( + configureOptions = { + dataCollection.setUserInfo(false) + distinctId = "custom-id" + } + ) + + assertEquals("custom-id", fixture.sentryOptions.distinctId) + } + + @Test + fun `init should set generated distinct id when explicit value is null`() { + fixture.initSut( + configureOptions = { + dataCollection.setUserInfo(true) + distinctId = null + } + ) + + assertNotNull(fixture.sentryOptions.distinctId) + } + @Test fun `init should set proguard uuid id on start`() { fixture.initSut( From 1786e17a864c2415bb4b5c3c0b25330b7d273af9 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 24 Jul 2026 15:33:18 +0200 Subject: [PATCH 2/4] fix(spring): Bind Data Collection key-value policies Make KeyValueCollectionBehavior JavaBean-bindable so Spring Boot properties correctly configure cookie, header, and query policies. Cover binding across all supported Spring Boot generations. Co-Authored-By: Claude --- .../boot4/SentryAutoConfigurationTest.kt | 30 +++++++++++++++++++ .../jakarta/SentryAutoConfigurationTest.kt | 30 +++++++++++++++++++ .../boot/SentryAutoConfigurationTest.kt | 30 +++++++++++++++++++ sentry/api/sentry.api | 3 ++ .../io/sentry/KeyValueCollectionBehavior.java | 19 +++++++++--- .../sentry/KeyValueCollectionBehaviorTest.kt | 21 +++++++++++++ 6 files changed, 129 insertions(+), 4 deletions(-) diff --git a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt index ef1f12aeecf..8e4cc6119d2 100644 --- a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt @@ -12,6 +12,7 @@ import io.sentry.IProfileConverter import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration +import io.sentry.KeyValueCollectionBehavior import io.sentry.NoOpContinuousProfiler import io.sentry.NoOpProfileConverter import io.sentry.NoOpTransportFactory @@ -306,6 +307,35 @@ class SentryAutoConfigurationTest { } } + @Test + fun `data collection key value properties are applied to SentryOptions`() { + contextRunner + .withPropertyValues( + "sentry.dsn=http://key@localhost/proj", + "sentry.data-collection.cookies.mode=off", + "sentry.data-collection.query-params.mode=allow-list", + "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.http-headers.request.mode=deny-list", + "sentry.data-collection.http-headers.request.terms=forwarded,-ip", + "sentry.data-collection.http-headers.response.mode=allow-list", + "sentry.data-collection.http-headers.response.terms=content-type,x-request-id", + ) + .run { + val dataCollection = it.getBean(SentryProperties::class.java).dataCollection + assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) + assertThat(dataCollection.queryParams!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.httpHeaders.request!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) + assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") + assertThat(dataCollection.httpHeaders.response!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.httpHeaders.response!!.terms) + .containsExactly("content-type", "x-request-id") + } + } + @Test fun `when tracePropagationTargets are not set, default is returned`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj").run { diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index 91677d16b4e..308e623cba2 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -13,6 +13,7 @@ import io.sentry.IProfileConverter import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration +import io.sentry.KeyValueCollectionBehavior import io.sentry.NoOpContinuousProfiler import io.sentry.NoOpProfileConverter import io.sentry.NoOpTransportFactory @@ -314,6 +315,35 @@ class SentryAutoConfigurationTest { } } + @Test + fun `data collection key value properties are applied to SentryOptions`() { + contextRunner + .withPropertyValues( + "sentry.dsn=http://key@localhost/proj", + "sentry.data-collection.cookies.mode=off", + "sentry.data-collection.query-params.mode=allow-list", + "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.http-headers.request.mode=deny-list", + "sentry.data-collection.http-headers.request.terms=forwarded,-ip", + "sentry.data-collection.http-headers.response.mode=allow-list", + "sentry.data-collection.http-headers.response.terms=content-type,x-request-id", + ) + .run { + val dataCollection = it.getBean(SentryProperties::class.java).dataCollection + assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) + assertThat(dataCollection.queryParams!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.httpHeaders.request!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) + assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") + assertThat(dataCollection.httpHeaders.response!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.httpHeaders.response!!.terms) + .containsExactly("content-type", "x-request-id") + } + } + @Test fun `when tracePropagationTargets are not set, default is returned`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj").run { diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index d9e598d0473..5eebd1bcc6b 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -13,6 +13,7 @@ import io.sentry.IProfileConverter import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration +import io.sentry.KeyValueCollectionBehavior import io.sentry.NoOpContinuousProfiler import io.sentry.NoOpProfileConverter import io.sentry.NoOpTransportFactory @@ -312,6 +313,35 @@ class SentryAutoConfigurationTest { } } + @Test + fun `data collection key value properties are applied to SentryOptions`() { + contextRunner + .withPropertyValues( + "sentry.dsn=http://key@localhost/proj", + "sentry.data-collection.cookies.mode=off", + "sentry.data-collection.query-params.mode=allow-list", + "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.http-headers.request.mode=deny-list", + "sentry.data-collection.http-headers.request.terms=forwarded,-ip", + "sentry.data-collection.http-headers.response.mode=allow-list", + "sentry.data-collection.http-headers.response.terms=content-type,x-request-id", + ) + .run { + val dataCollection = it.getBean(SentryProperties::class.java).dataCollection + assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) + assertThat(dataCollection.queryParams!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.httpHeaders.request!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) + assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") + assertThat(dataCollection.httpHeaders.response!!.mode) + .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(dataCollection.httpHeaders.response!!.terms) + .containsExactly("content-type", "x-request-id") + } + } + @Test fun `when tracePropagationTargets are not set, default is returned`() { dsnEnabledRunner.run { diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 2a4ab4f0c1a..6e61380f81b 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1447,6 +1447,7 @@ public abstract interface class io/sentry/JsonUnknown { } public final class io/sentry/KeyValueCollectionBehavior { + public fun ()V public static fun allowList ([Ljava/lang/String;)Lio/sentry/KeyValueCollectionBehavior; public static fun denyList ([Ljava/lang/String;)Lio/sentry/KeyValueCollectionBehavior; public fun equals (Ljava/lang/Object;)Z @@ -1454,6 +1455,8 @@ public final class io/sentry/KeyValueCollectionBehavior { public fun getTerms ()Ljava/util/List; public fun hashCode ()I public static fun off ()Lio/sentry/KeyValueCollectionBehavior; + public fun setMode (Lio/sentry/KeyValueCollectionBehavior$Mode;)V + public fun setTerms (Ljava/util/List;)V } public final class io/sentry/KeyValueCollectionBehavior$Mode : java/lang/Enum { diff --git a/sentry/src/main/java/io/sentry/KeyValueCollectionBehavior.java b/sentry/src/main/java/io/sentry/KeyValueCollectionBehavior.java index d9daeb9bc02..35cc0896719 100644 --- a/sentry/src/main/java/io/sentry/KeyValueCollectionBehavior.java +++ b/sentry/src/main/java/io/sentry/KeyValueCollectionBehavior.java @@ -20,12 +20,15 @@ public enum Mode { ALLOW_LIST } - private final @NotNull Mode mode; - private final @NotNull List terms; + private @NotNull Mode mode = Mode.DENY_LIST; + private @NotNull List terms = Collections.emptyList(); + + /** Creates a behavior that collects values using the built-in sensitive deny-list. */ + public KeyValueCollectionBehavior() {} private KeyValueCollectionBehavior(final @NotNull Mode mode, final @NotNull List terms) { - this.mode = mode; - this.terms = Collections.unmodifiableList(new ArrayList<>(terms)); + setMode(mode); + setTerms(terms); } /** Disables collection of the category. */ @@ -53,10 +56,18 @@ private KeyValueCollectionBehavior(final @NotNull Mode mode, final @NotNull List return mode; } + public void setMode(final @NotNull Mode mode) { + this.mode = mode; + } + public @NotNull List getTerms() { return terms; } + public void setTerms(final @NotNull List terms) { + this.terms = Collections.unmodifiableList(new ArrayList<>(terms)); + } + @Override public boolean equals(final Object other) { if (this == other) { diff --git a/sentry/src/test/java/io/sentry/KeyValueCollectionBehaviorTest.kt b/sentry/src/test/java/io/sentry/KeyValueCollectionBehaviorTest.kt index 7e014eec504..09cd92f4fce 100644 --- a/sentry/src/test/java/io/sentry/KeyValueCollectionBehaviorTest.kt +++ b/sentry/src/test/java/io/sentry/KeyValueCollectionBehaviorTest.kt @@ -4,6 +4,27 @@ import com.google.common.truth.Truth.assertThat import kotlin.test.Test class KeyValueCollectionBehaviorTest { + @Test + fun `default constructor uses deny list with no terms`() { + val behavior = KeyValueCollectionBehavior() + + assertThat(behavior.mode).isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) + assertThat(behavior.terms).isEmpty() + } + + @Test + fun `setters update mode and defensively copy terms`() { + val terms = mutableListOf("token") + val behavior = KeyValueCollectionBehavior() + + behavior.mode = KeyValueCollectionBehavior.Mode.ALLOW_LIST + behavior.terms = terms + terms[0] = "password" + + assertThat(behavior.mode).isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) + assertThat(behavior.terms).containsExactly("token") + } + @Test fun `off has no terms`() { val behavior = KeyValueCollectionBehavior.off() From c030de20b82446cd3e2dd905128ab40ab1118ec4 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 10 Aug 2026 14:43:53 +0200 Subject: [PATCH 3/4] test(spring): Bind URL query parameter policies Use the canonical URL query parameter property name in Spring Boot 2, 3, and 4 binding coverage. Refs #5666 Co-Authored-By: Claude --- .../io/sentry/spring/boot4/SentryAutoConfigurationTest.kt | 8 ++++---- .../spring/boot/jakarta/SentryAutoConfigurationTest.kt | 8 ++++---- .../io/sentry/spring/boot/SentryAutoConfigurationTest.kt | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt index 8e4cc6119d2..e9a33755334 100644 --- a/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-4/src/test/kotlin/io/sentry/spring/boot4/SentryAutoConfigurationTest.kt @@ -313,8 +313,8 @@ class SentryAutoConfigurationTest { .withPropertyValues( "sentry.dsn=http://key@localhost/proj", "sentry.data-collection.cookies.mode=off", - "sentry.data-collection.query-params.mode=allow-list", - "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.url-query-params.mode=allow-list", + "sentry.data-collection.url-query-params.terms=page,sort", "sentry.data-collection.http-headers.request.mode=deny-list", "sentry.data-collection.http-headers.request.terms=forwarded,-ip", "sentry.data-collection.http-headers.response.mode=allow-list", @@ -323,9 +323,9 @@ class SentryAutoConfigurationTest { .run { val dataCollection = it.getBean(SentryProperties::class.java).dataCollection assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) - assertThat(dataCollection.queryParams!!.mode) + assertThat(dataCollection.urlQueryParams!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) - assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.urlQueryParams!!.terms).containsExactly("page", "sort") assertThat(dataCollection.httpHeaders.request!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index 308e623cba2..b3abc45c4b1 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -321,8 +321,8 @@ class SentryAutoConfigurationTest { .withPropertyValues( "sentry.dsn=http://key@localhost/proj", "sentry.data-collection.cookies.mode=off", - "sentry.data-collection.query-params.mode=allow-list", - "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.url-query-params.mode=allow-list", + "sentry.data-collection.url-query-params.terms=page,sort", "sentry.data-collection.http-headers.request.mode=deny-list", "sentry.data-collection.http-headers.request.terms=forwarded,-ip", "sentry.data-collection.http-headers.response.mode=allow-list", @@ -331,9 +331,9 @@ class SentryAutoConfigurationTest { .run { val dataCollection = it.getBean(SentryProperties::class.java).dataCollection assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) - assertThat(dataCollection.queryParams!!.mode) + assertThat(dataCollection.urlQueryParams!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) - assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.urlQueryParams!!.terms).containsExactly("page", "sort") assertThat(dataCollection.httpHeaders.request!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index 5eebd1bcc6b..834440faf1c 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -319,8 +319,8 @@ class SentryAutoConfigurationTest { .withPropertyValues( "sentry.dsn=http://key@localhost/proj", "sentry.data-collection.cookies.mode=off", - "sentry.data-collection.query-params.mode=allow-list", - "sentry.data-collection.query-params.terms=page,sort", + "sentry.data-collection.url-query-params.mode=allow-list", + "sentry.data-collection.url-query-params.terms=page,sort", "sentry.data-collection.http-headers.request.mode=deny-list", "sentry.data-collection.http-headers.request.terms=forwarded,-ip", "sentry.data-collection.http-headers.response.mode=allow-list", @@ -329,9 +329,9 @@ class SentryAutoConfigurationTest { .run { val dataCollection = it.getBean(SentryProperties::class.java).dataCollection assertThat(dataCollection.cookies!!.mode).isEqualTo(KeyValueCollectionBehavior.Mode.OFF) - assertThat(dataCollection.queryParams!!.mode) + assertThat(dataCollection.urlQueryParams!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.ALLOW_LIST) - assertThat(dataCollection.queryParams!!.terms).containsExactly("page", "sort") + assertThat(dataCollection.urlQueryParams!!.terms).containsExactly("page", "sort") assertThat(dataCollection.httpHeaders.request!!.mode) .isEqualTo(KeyValueCollectionBehavior.Mode.DENY_LIST) assertThat(dataCollection.httpHeaders.request!!.terms).containsExactly("forwarded", "-ip") From 2296468d890e77768ca4a4f434237f593a61d12a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 2 Sep 2026 11:32:40 +0200 Subject: [PATCH 4/4] revert: fix(android): Apply user info policy to distinct ID This reverts commit b27d61d218059360b2d9482e87a79b6bac6aeeae.\n\nKeep generated installation IDs independent of the userInfo policy. Restore\ndefault generation before programmatic configuration so applications can\ncontinue clearing the distinct ID in the configuration callback.\n\nRefs #5666\n\nCo-Authored-By: Claude --- .../core/AndroidOptionsInitializer.java | 8 ++++ .../io/sentry/android/core/SentryAndroid.java | 8 ---- .../core/AndroidOptionsInitializerTest.kt | 44 ------------------- 3 files changed, 8 insertions(+), 52 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 2a60e5750d7..9cc5cb3df0f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -471,6 +471,14 @@ private static void readDefaultOptionValues( options.addInAppInclude(packageName); } } + + if (options.getDistinctId() == null) { + try { + options.setDistinctId(Installation.id(context)); + } catch (RuntimeException e) { + options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e); + } + } } /** diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 150569dcecf..f27259fd635 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -149,14 +149,6 @@ public static void init( "Error in the 'OptionsConfiguration.configure' callback.", t); } - if (options.getDistinctId() == null - && options.getDataCollectionResolver().isUserInfoWithLegacyAlways()) { - try { - options.setDistinctId(Installation.id(context)); - } catch (RuntimeException e) { - options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e); - } - } // if SentryPerformanceProvider was disabled or removed, // we set the app start / sdk init time here instead diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index 068b0964bcc..f8724d286f8 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -111,12 +111,6 @@ class AndroidOptionsInitializerTest { ) sentryOptions.configureOptions() - if ( - sentryOptions.distinctId == null && - sentryOptions.dataCollectionResolver.isUserInfoWithLegacyAlways - ) { - sentryOptions.distinctId = Installation.id(if (useRealContext) context else mockContext) - } AndroidOptionsInitializer.initializeIntegrationsAndProcessors( sentryOptions, if (useRealContext) context else mockContext, @@ -355,44 +349,6 @@ class AndroidOptionsInitializerTest { installation.deleteOnExit() } - @Test - fun `init should not set generated distinct id when user info is disabled`() { - fixture.initSut(configureOptions = { dataCollection.setUserInfo(false) }) - - assertNull(fixture.sentryOptions.distinctId) - } - - @Test - fun `init should set generated distinct id when user info is enabled`() { - fixture.initSut(configureOptions = { dataCollection.setUserInfo(true) }) - - assertNotNull(fixture.sentryOptions.distinctId) - } - - @Test - fun `init should preserve explicit distinct id when user info is disabled`() { - fixture.initSut( - configureOptions = { - dataCollection.setUserInfo(false) - distinctId = "custom-id" - } - ) - - assertEquals("custom-id", fixture.sentryOptions.distinctId) - } - - @Test - fun `init should set generated distinct id when explicit value is null`() { - fixture.initSut( - configureOptions = { - dataCollection.setUserInfo(true) - distinctId = null - } - ) - - assertNotNull(fixture.sentryOptions.distinctId) - } - @Test fun `init should set proguard uuid id on start`() { fixture.initSut(