Skip to content

Add didANRKillOnPreviousExecution() to FirebaseCrashlytics#8178

Open
jrodiz wants to merge 4 commits into
mainfrom
feature/jrc--Issue4201-2.API.to.check.previous.ANR
Open

Add didANRKillOnPreviousExecution() to FirebaseCrashlytics#8178
jrodiz wants to merge 4 commits into
mainfrom
feature/jrc--Issue4201-2.API.to.check.previous.ANR

Conversation

@jrodiz
Copy link
Copy Markdown
Contributor

@jrodiz jrodiz commented May 18, 2026

Add didANRKillOnPreviousExecution() to FirebaseCrashlytics

Implements #4201.

Crashlytics already detects ANRs and files reports for them, and developers have
long been able to call didCrashOnPreviousExecution() to gate logic that should
only run after a fatal crash. However, there was no equivalent API for ANRs:
developers who want to adapt their app's behavior after an ANR (e.g., disable a
heavy feature on the next cold start, log a custom key, or show a recovery
dialog) had no supported way to do so.

didCrashOnPreviousExecution() works by reading a marker file written at crash
time. ANRs cannot use that mechanism because the main thread is frozen and the
crash handler never runs. Instead, the new method uses ApplicationExitInfo
(API 30+), which records process-exit reasons at the OS level — the same source
already queried internally by SessionReportingCoordinator when filing ANR
reports. The new API delegates to that existing infrastructure rather than
introducing a separate detection path.

Usage

The API mirrors didCrashOnPreviousExecution() — synchronous, returns a
boolean. Call it once on a cold start (typically from Application.onCreate
or your DI startup) and react to the result. The first call may briefly
block, so prefer a background thread.

Kotlin

class MyApplication : Application() {
  override fun onCreate() {
    super.onCreate()

    // Run on a background dispatcher — the first call queries the OS for
    // the previous run's exit reason and may block for up to a few seconds.
    CoroutineScope(Dispatchers.IO).launch {
      val crashlytics = Firebase.crashlytics
      if (crashlytics.didANRKillOnPreviousExecution()) {
        crashlytics.setCustomKey("recovered_from_anr", true)
        // e.g., disable the heavy feature that caused the ANR last run,
        // surface an in-app recovery prompt, etc.
      }
    }
  }
}

What you can do with it

  • Set setCustomKey("recovered_from_anr", true) to tag any follow-up
    report this run might file, for correlation in the console.
  • Skip a suspected-bad feature for one run, re-enable on the next clean start.
  • Show an in-app recovery UI (clear cache, sign out, reset a preference).
  • Bump your own analytics counter for ANR-induced restarts.

What to avoid

  • Calling it on the main thread — first call can block up to ~3 s
    (Binder IPC + filesystem I/O); subsequent calls are cached and free.
  • Calling it before FirebaseCrashlytics.getInstance() is available.
  • Treating true as "the user just crashed" — ANRs and fatal crashes are
    independent; both predicates can fire on the same cold start.
  • Relying on the result on API < 30 — safe to call, but always false.

Test plan

  • ./gradlew :firebase-crashlytics:testDebugUnitTest passes locally.
  • SessionReportingCoordinatorRobolectricTest covers the four
    didRelevantAnrOccur branches.
  • CrashlyticsCoreInitializationTest.testOnPreExecute_didNotANROnPreviousExecution
    verifies the happy (no-ANR) path end-to-end through CrashlyticsCore,
    including the lazy first-call path.

jrodiz added 4 commits May 18, 2026 13:10
Implements firebase-android-sdk#4201 by exposing a new public API method
that detects whether the app was killed by an ANR in the previous run,
mirroring the existing didCrashOnPreviousExecution() pattern.

On Android API 30+ the method queries ApplicationExitInfo (already used
internally for ANR session reporting) against the previous session's start
timestamp. On older API levels it always returns false.
… check failures

- Add null guard on ActivityManager from getSystemService() before
  calling getHistoricalProcessExitReasons() to avoid potential NPE.
- Log exceptions in checkForPreviousAnr() at verbose level instead of
  silently ignoring them, to aid diagnosability.
The previous implementation called future.get(3, SECONDS) from onPreExecute
on the main thread during eager Crashlytics initialization, just to populate
a boolean that almost no app would read. Defer the work to the first call
of didANRKillOnPreviousExecution() and cache the result. Apps that never
call the API pay nothing at cold start; apps that do call it pay on their
own thread, once.

To keep the result correct after finalizeSessions removes the previous
session, capture the previous-execution session ID on the common worker
during onPreExecute (cheap, no blocking get) and read it from the cached
field instead of getCurrentSessionId() when the ANR check finally runs.
@jrodiz jrodiz requested a review from mrober May 18, 2026 19:13
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

@github-actions
Copy link
Copy Markdown
Contributor

📝 PRs merging into main branch

Our main branch should always be in a releasable state. If you are working on a larger change, or if you don't want this change to see the light of the day just yet, consider using a feature branch first, and only merge into the main branch when the code complete and ready to be released.

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.

1 participant