You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(screenshot): Add screenshot masking using view hierarchy (#5077)
* feat(screenshot): Add screenshot masking using view hierarchy
Adds masking support to error screenshots by reusing the Session Replay
masking logic. This allows sensitive content (text, images) to be masked
before attaching screenshots to error events.
- Add SentryMaskingOptions base class for shared masking configuration
- Add SentryScreenshotOptions for screenshot-specific masking settings
- Create MaskRenderer utility for shared mask rendering (used by both
replay and screenshots)
- Add manifest metadata support for screenshot masking options
- Add snapshot tests with Dropbox Differ library for visual regression
- Update CLAUDE.md with dependency management guidelines
Masking requires the sentry-android-replay module to be present at runtime.
Without it, screenshots are captured without masking.
Refs: #3286
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Changelog
* dontwarn about classes we check via reflection at runtime
* pr id
* fix(screenshot): Only warn about missing replay module when masking is configured
The isMaskingEnabled() method was logging a warning before checking if
masking was actually configured. This caused users who never set up
screenshot masking to see spurious warnings on every event.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(screenshot): Remove sensitive view classes when setMaskAllImages(false) is called
setMaskAllImages(true) was adding WebView, VideoView, and ExoPlayer
classes to maskViewClasses, but setMaskAllImages(false) only removed
ImageView. This caused asymmetric toggle behavior where disabling
image masking didn't restore the original state.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(screenshot): Recycle bitmap copy on masking failure to prevent memory leak
When an exception occurred in applyMasking after creating a mutable
copy of the bitmap, the catch block returned the original screenshot
without recycling the copy. This caused bitmap memory to accumulate
until GC runs, potentially causing OOM issues on frequent errors.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: Resolve merge conflicts with main and integrate trackCustomMasking
Move trackCustomMasking() to SentryMaskingOptions as an abstract method
so it can be called polymorphically from replay view hierarchy code.
SentryReplayOptions provides the real implementation, while
SentryScreenshotOptions provides a no-op. Also adds
CAMERAX_PREVIEW_VIEW_CLASS_NAME to SentryMaskingOptions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Clean up slop
* fix(test): Implement abstract trackCustomMasking in test stub
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(screenshot): Use peekDecorView instead of getDecorView
peekDecorView returns null if the decor view hasn't been created yet,
avoiding forced creation. This is consistent with the rest of the
codebase (ScreenshotUtils, ViewHierarchyEventProcessor).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(screenshot): Per-call MaskRenderer, main-thread VH capture, and don't leak unmasked screenshots
- Use per-call MaskRenderer via try-with-resources instead of shared instance
- Remove Closeable from ScreenshotEventProcessor (nothing to clean up)
- Capture view hierarchy on main thread via runOnUiThread + CountDownLatch
- Apply masking on the calling thread (only VH traversal needs main thread)
- Return null on masking failure to avoid sending unmasked screenshots
- Fix setMaskViewContainerClass to not trigger trackCustomMasking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix tests and remove slop
* clean up
* fix(masking): Remove from opposite set when adding mask/unmask view class
addMaskViewClass now removes from unmaskViewClasses and vice versa,
preventing stale entries from silently blocking masking when
setMaskAllText(false)/setMaskAllImages(false) is called with defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* delegate to super in SentryReplayOptions
* Do not capture screenshot when copy bitmap fails
* fix(screenshot): Recycle bitmaps on all early-return paths to prevent memory leaks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(screenshot): Log missing replay module warning once in constructor instead of per event
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Move PR id info to AGENTS.md
* refactor: Rename getScreenshotOptions() to getScreenshot() to match getSessionReplay() pattern
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: Move screenshot masking changelog entry to Unreleased with code snippets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(screenshot): Avoid crash from uncaught exception in view hierarchy traversal and unnecessary bitmap alloc in MaskRenderer.close()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Fix MaskRendererTest after lazy bitmap init guard and simplify test setup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(screenshot): Wrap runOnUiThread in try-catch to handle destroyed activity race condition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Bail out early if replay module is not available but masking is enabled for screenshots
* fix(test): Expect no screenshot when masking configured without replay module
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CLAUDE.md
-1Lines changed: 0 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,5 +3,4 @@
3
3
## STOP — Required Reading (Do This First)
4
4
5
5
Before doing ANYTHING else (including answering questions), you MUST use the Read tool to load [AGENTS.md](AGENTS.md) and follow ALL of its instructions, including reading the required `.cursor/rules/*.mdc` files it references.
6
-
7
6
Do NOT skip this step. Do NOT proceed without reading these files first.
0 commit comments