Repository navigation
ref(android): Measure ANR thresholds on the monotonic clock (JAVA-579) - #6041
Open
runningcode wants to merge 7 commits into
Open
runningcode wants to merge 7 commits into
runningcode wants to merge 7 commits into
Conversation
📲 Install BuildsAndroid
|
This was referenced Sep 2, 2026
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 2, 2026 13:35
edd22f0 to
fef63c7
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 2, 2026 13:35
cabb812 to
77eb8c9
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 2, 2026 13:37
fef63c7 to
9829cc8
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 2, 2026 13:37
77eb8c9 to
2065803
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 3, 2026 15:11
9829cc8 to
c11fec4
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 3, 2026 15:11
2065803 to
498932f
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 3, 2026 15:41
c11fec4 to
0d1d4c9
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 3, 2026 15:41
498932f to
ae9d046
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 4, 2026 08:57
0d1d4c9 to
6861150
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 4, 2026 08:58
ae9d046 to
46f3c83
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 4, 2026 15:11
6861150 to
b35959c
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 4, 2026 15:11
46f3c83 to
2c9adb8
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 8, 2026 13:27
b35959c to
1eb13ab
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 8, 2026 13:32
2c9adb8 to
d570570
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 8, 2026 15:10
1eb13ab to
5b8105e
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 8, 2026 15:10
d570570 to
51e0083
Compare
runningcode
force-pushed
the
no/java-576-checkin-stopwatch
branch
from
September 9, 2026 15:44
5b8105e to
67379d3
Compare
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 9, 2026 15:51
51e0083 to
a2620aa
Compare
runningcode
changed the base branch from
no/java-576-checkin-stopwatch
to
main
September 9, 2026 15:51
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 10, 2026 07:25
97ad063 to
76d5e07
Compare
runningcode
commented
Sep 11, 2026
| private final boolean reportInDebug; | ||
| private final ANRListener anrListener; | ||
| private final MainLooperHandler uiHandler; | ||
| private final ICurrentDateProvider timeProvider; |
Contributor
Author
There was a problem hiding this comment.
This returns EITHER a monotonic clock or a wall clock depending on the platform. This is a bad abstraction.
On android it gives you a monotonic clock which is what we want for this class, but the fact that it has Date in the name but gives you a monotonic clock is quite confusing.
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
September 11, 2026 15:19
50973fd to
fd94017
Compare
runningcode
marked this pull request as ready for review
September 11, 2026 15:21
runningcode
requested review from
0xadam-brown,
adinauer,
markushi and
romtsn
as code owners
September 11, 2026 15:21
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fd94017. Configure here.
The ANR tests advance the ticker from the test thread while the watchdog thread reads it, which without volatile is a data race that can leave the watchdog looking at a stale tick forever.
The watchdog took its readings from an ICurrentDateProvider lambda over SystemClock.uptimeMillis(). The type named no clock, so a call site could not tell what it was measuring, and the arithmetic -- now minus the last tick, compared against a threshold -- was spelled out inline. MonotonicClock and Deadline replace both: the clock is a named type, and the watchdog asks the question it actually cares about, which is whether the main thread has missed its window. The clock counts deep sleep, which uptimeMillis() did not, so a suspend between posting the ticker and checking it now looks like a missed window. It cannot fabricate an ANR: the watchdog reports only once ActivityManager confirms the process is NOT_RESPONDING, and on resume the main thread runs the ticker that is already queued.
Same reasoning as the watchdog: the suspicion and ANR thresholds are now read from a named clock rather than SystemClock, and injecting it lets the tests drive it directly instead of going through Robolectric's shadow clock. Deep sleep cannot inflate the measurement here either, because the polling thread parks itself while the app is backgrounded and resets the baseline when it wakes.
…A-579) Both ANR detectors resolved AndroidMonotonicTicker.getInstance() themselves, so each call site named the concrete Android ticker and neither could be pointed at another one. SentryAndroidOptions already knows which ticker the platform wants, but it is final, so a test cannot override the getter. Add a one-method MonotonicTickerProvider that SentryOptions implements. ANRWatchDog gains a static factory that reads its timeout, debug behavior, logger and ticker off options, replacing the telescoping convenience constructor. AnrProfilingIntegration installs the ticker in register(), which drops the two constructors it had grown and leaves the public no-arg constructor and sentry-android-core.api unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drop the JavaMonotonicTicker fallback. On Android nanoTime() stops while the device is suspended, so a default that is never meant to be used would measure main-thread stalls against the wrong clock if it ever were. Start the test ticker at a non-zero origin. A fake that starts at zero makes a reading taken against a different origin indistinguishable from a correct one, which is the mistake this whole change exists to prevent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
runningcode
force-pushed
the
no/java-579-anr-uptime-clock
branch
from
November 2, 2026 13:06
fd94017 to
88946b3
Compare
MonotonicTickerProvider only existed so a test could hand AnrProfilingIntegration a fake ticker. A package-private method taking the ticker does the same without a new core interface or a change to the SentryOptions supertype. Also rename the ANRWatchDog test that claimed to cover a device suspend: the Android ticker counts suspend, so the test only shows the threshold is measured on the injected ticker rather than wall time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
runningcode
commented
Nov 3, 2026
| * <p>Package-private so a test can install a fake ticker after {@link #register}, which {@code | ||
| * SentryAndroidOptions} cannot supply. | ||
| */ | ||
| void installTicker(final @NotNull MonotonicTicker ticker) { |
Contributor
Author
There was a problem hiding this comment.
This is probably the only weird design about this PR. Open to other ways of doing this!
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

📜 Description
Moves the ANR integrations to use the
MonotonicTickerabstraction. This also makes them more testable since we aren't hard coding a direct access toSystemClock.A note: We previously used
SystemClock.uptimeMilliswhich does not include time spent in sleep. The implementation ofMonotonicTickeron Android usesSystemClock.elapsedRealtimeNanos().ANRWatchDogpolls in the background too, so right after the device wakes up the deadline can already have passed. That doesn't produce a false ANR: the watchdog only reports when ActivityManager also lists the process as NOT_RESPONDING. The cost is one extragetProcessesInErrorState()call after a wake-up.AnrProfilingIntegrationonly polls in the foreground and resets its reading on foreground.Also
ANRWatchdogwas previously using aICurrentDateProvider. Which has two different implementations, a wall clock and a monotonic clock. It happens to work by chance on Android because on Android it should always return a monotonic clock but this is quite brittle.💡 Motivation and Context
Fixing clocks and making the code more sane.
💚 How did you test it?
./gradlew :sentry-android-core:testReleaseUnitTest :sentry:checkKotlinAbi— green.📝 Checklist
sendDefaultPIIis enabled.