Skip to content

RUM-10703: Fix Session Replay NPE when getting TextView padding#2784

Merged
ambushwork merged 1 commit into
developfrom
yl/fix-session-replay-crash
Jul 9, 2025
Merged

RUM-10703: Fix Session Replay NPE when getting TextView padding#2784
ambushwork merged 1 commit into
developfrom
yl/fix-session-replay-crash

Conversation

@ambushwork

Copy link
Copy Markdown
Member

What does this PR do?

Here is the crash stack trace about this issue:

Exception java.lang.NullPointerException: Attempt to invoke virtual method 'int android.text.Layout.getHeight()' on a null object reference
  at android.widget.TextView.getBottomVerticalOffset (TextView.java:9021)
  at android.widget.TextView.getTotalPaddingBottom (TextView.java:3451)
  at com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper.resolvePadding (TextViewMapper.kt:193)
  at com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper.resolveTextPosition (TextViewMapper.kt:185)
  at com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper.createTextWireframe (TextViewMapper.kt:142)
  at com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper.map (TextViewMapper.kt:62)
  at com.datadog.android.sessionreplay.recorder.mapper.TextViewMapper.map (TextViewMapper.kt:30)

From the documentation of TextView.getLayout() we know that it's probably caused by the text update:

/**
 * Gets the {@link android.text.Layout} that is currently being used to display the text.
 * This value can be null if the text or width has recently changed.
 * @return The Layout that is currently being used to display the text.
 */

Based on that, we just need to make sure that the layout is present before reaching the getTotalPaddingBottom function.

Motivation

RUM-10703

Additional Notes

The whole function is called from MainThread, so it's unlikely that between checking textView.layout and using textView.totalPaddingBottom. the layout is set to null again due to the concurrency

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@ambushwork
ambushwork force-pushed the yl/fix-session-replay-crash branch from d539443 to 8cb6c7c Compare July 8, 2025 11:55
@codecov-commenter

codecov-commenter commented Jul 8, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.83%. Comparing base (edd95ec) to head (5984380).
⚠️ Report is 972 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #2784      +/-   ##
===========================================
- Coverage    69.97%   69.83%   -0.14%     
===========================================
  Files          824      824              
  Lines        30881    30887       +6     
  Branches      5195     5196       +1     
===========================================
- Hits         21606    21568      -38     
- Misses        7828     7848      +20     
- Partials      1447     1471      +24     
Files with missing lines Coverage Δ
...id/sessionreplay/recorder/mapper/TextViewMapper.kt 90.65% <100.00%> (+0.56%) ⬆️

... and 33 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ambushwork
ambushwork marked this pull request as ready for review July 8, 2025 12:33
@ambushwork
ambushwork requested review from a team as code owners July 8, 2025 12:33
@ambushwork
ambushwork force-pushed the yl/fix-session-replay-crash branch from 8cb6c7c to 366bbbf Compare July 8, 2025 13:44
@ambushwork
ambushwork requested a review from jonathanmos July 8, 2025 13:49
left = textView.totalPaddingStart.densityNormalized(pixelsDensity).toLong(),
right = textView.totalPaddingEnd.densityNormalized(pixelsDensity).toLong()
)
return if (textView.layout != null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we sure it is a fix that will be enough in general when fetching properties of TextView, given we cannot reproduce it?

To me the issue is that layout is null is related to the fact that we either try to create wireframe too early or too late in the TextView lifecycle and there is no guarantee that we fix this call site, but then we can have a crash somewhere later if layout is still null.

Is there a way to check that a view is in a good and reliable state in general before we start pulling the properties for the wireframe generation?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client is not able to reproduce the issue systemically neither on their side, so they can't tell which TextView exact is causing the issue. Even though they gave us the code snippet, we only know that they are using viewbinding, and programmatically assign the text content in their activity, otherwise nothing irregular.

According to the source code, null layout can also happen between TextView cleaning layout and making new layout, and in this case we are not able to prevent it by checking if the view is well loaded or not, the view can update at anytime. so in my point of view, checking layout is the best thing that we can do to make sure it.

@ambushwork
ambushwork merged commit 3ebb2b5 into develop Jul 9, 2025
25 checks passed
@ambushwork
ambushwork deleted the yl/fix-session-replay-crash branch July 9, 2025 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants