Skip to content

Commit b24ec58

Browse files
romtsngetsentry-botclaude
authored andcommitted
fix(replay): Fix Compose masking on obfuscated/minified builds (#5503)
* try to run replay tests on gh emulators * Format code * feat(replay): fail fast on swallowed Compose masking errors in CI Add an internal SentryReplayDebug.failFast switch (gated on the io.sentry.replay.compose.fail-fast system property) that re-throws the exceptions ComposeViewHierarchyNode normally swallows in fromComposeNode and fromView. Enabled in the sentry-samples-android app and the on-device ReplayTest/ReplaySnapshotTest so our release/obfuscated builds running on real devices in CI crash instead of silently degrading masking. Defaults off, so customers are unaffected. Also add consumer proguard keep rules for the LayoutNode internals (getChildren/getOuterCoordinator/getCollapsedSemantics) that are looked up via reflection on Compose < 1.10, so R8 doesn't strip or rename them. Also guard the on-device tests against GitHub-hosted emulators, which can't capture screenshots reliably. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 076580b commit b24ec58

7 files changed

Lines changed: 61 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Fixes
6+
7+
- Session Replay: Fix Compose view masking not working on obfuscated/minified builds ([#5503](https://github.com/getsentry/sentry-java/pull/5503))
8+
39
## 8.43.1
410

511
### Fixes

sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/ReplayTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class ReplayTest : BaseUiTest() {
2121
// we can't run on GH actions emulator, because they don't allow capturing screenshots properly
2222
@Suppress("KotlinConstantConditions")
2323
assumeThat(BuildConfig.ENVIRONMENT != "github", `is`(true))
24+
// crash on swallowed Compose masking errors (e.g. broken obfuscated internals) so regressions
25+
// fail this on-device test instead of silently under-masking (see SentryReplayDebug)
26+
System.setProperty("io.sentry.replay.compose.fail-fast", "true")
2427
}
2528

2629
@Test

sentry-android-integration-tests/sentry-uitest-android/src/androidTestReplay/java/io/sentry/uitest/android/ReplaySnapshotTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class ReplaySnapshotTest : BaseUiTest() {
2323
// GH Actions emulators don't support capturing screenshots for replay
2424
@Suppress("KotlinConstantConditions")
2525
assumeThat(BuildConfig.ENVIRONMENT != "github", `is`(true))
26+
// crash on swallowed Compose masking errors (e.g. broken obfuscated internals) so regressions
27+
// fail this on-device test instead of silently under-masking (see SentryReplayDebug)
28+
System.setProperty("io.sentry.replay.compose.fail-fast", "true")
2629
}
2730

2831
@Test

sentry-android-replay/proguard-rules.pro

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@
2929
# Rules to detect a PreviewView view to later mask it
3030
-dontwarn androidx.camera.view.PreviewView
3131
-keepnames class androidx.camera.view.PreviewView
32+
# Rules to walk the Compose Node tree.
33+
-keep class androidx.compose.ui.node.LayoutNode {
34+
*** getChildren*(...);
35+
*** getOuterCoordinator*(...);
36+
*** getCollapsedSemantics*(...);
37+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.sentry.android.replay.util
2+
3+
/**
4+
* Internal, undocumented escape hatch used to make Session Replay fail fast instead of silently
5+
* degrading masking when an exception is swallowed (e.g. unsupported/obfuscated Compose internals).
6+
*
7+
* It is intended to be enabled only in our own sample/UI-test apps that run on real devices in CI
8+
* (which are release/obfuscated builds, so [io.sentry.android.replay.BuildConfig.DEBUG] can't be
9+
* used), so that regressions surface as crashes rather than under-masked replays. Customers should
10+
* never set this.
11+
*
12+
* Enable via:
13+
* ```
14+
* System.setProperty("io.sentry.replay.compose.fail-fast", "true")
15+
* ```
16+
*/
17+
internal object SentryReplayDebug {
18+
private const val FAIL_FAST_PROPERTY = "io.sentry.replay.compose.fail-fast"
19+
20+
/**
21+
* Read live (not cached) so it's only evaluated on the error path and unit tests can toggle it
22+
* between cases.
23+
*/
24+
val failFast: Boolean
25+
get() = "true".equals(System.getProperty(FAIL_FAST_PROPERTY), ignoreCase = true)
26+
}

sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import io.sentry.SentryLevel
2222
import io.sentry.SentryMaskingOptions
2323
import io.sentry.android.replay.SentryReplayModifiers
2424
import io.sentry.android.replay.util.ComposeTextLayout
25+
import io.sentry.android.replay.util.SentryReplayDebug
2526
import io.sentry.android.replay.util.boundsInWindow
2627
import io.sentry.android.replay.util.findPainter
2728
import io.sentry.android.replay.util.findTextColor
@@ -147,6 +148,12 @@ internal object ComposeViewHierarchyNode {
147148
)
148149
}
149150

151+
// fail fast in our own sample/UI-test apps (see SentryReplayDebug), so regressions surface
152+
// as crashes instead of silently degrading masking
153+
if (SentryReplayDebug.failFast) {
154+
throw t
155+
}
156+
150157
// If we're unable to retrieve the semantics configuration
151158
// we should play safe and mask the whole node.
152159
return GenericViewHierarchyNode(
@@ -291,6 +298,11 @@ internal object ComposeViewHierarchyNode {
291298
"""
292299
.trimIndent(),
293300
)
301+
// fail fast in our own sample/UI-test apps (see SentryReplayDebug), so regressions surface
302+
// as crashes instead of silently skipping the whole Compose subtree (i.e. not masking it)
303+
if (SentryReplayDebug.failFast) {
304+
throw e
305+
}
294306
return false
295307
}
296308

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MyApplication.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ public class MyApplication extends Application {
99

1010
@Override
1111
public void onCreate() {
12+
// Make Session Replay fail fast instead of silently degrading masking when an exception is
13+
// swallowed (e.g. unsupported/obfuscated Compose internals). This way regressions surface as
14+
// crashes in our release/obfuscated builds that run on real devices in CI. Only meant for our
15+
// own sample/UI-test apps, customers should never set this.
16+
System.setProperty("io.sentry.replay.compose.fail-fast", "true");
1217
Sentry.startProfiler();
1318
strictMode();
1419
super.onCreate();

0 commit comments

Comments
 (0)