Reporting app exit information

From Android 11 (API 30), the Android OS provides information of an application process’s death in the ApplicationExitInfo API. This can be particularly useful for diagnosing native (NDK) crashes and ANR events.

Captured information

Use the bugsnag-plugin-android-exitinfo plugin to augment your native crashes and ANRs with:

Full stacktraces for native threads

These are shown on the Threads tab of the BugSnag dashboard. Without the plugin, only the thread names and states can be captured in a native crash or ANR.

Open file descriptors

The “Open FileDescriptors” tab of the BugSnag dashboard shows the files that were open at the time of the crash.

Logcat messages

The app’s Logcat messages leading up to the error are shown on the “Log Messages” tab on the BugSnag dashboard for apps running on Android 11 to 14 (the data is no longer available from Android 15).

Installation

Install bugsnag-plugin-android-exitinfo on devices running Android 11+ (API 30) by adding the dependency to your app/build.gradle:

dependencies {
  implementation("com.bugsnag:bugsnag-plugin-android-exitinfo:6.+")
}

Then add the plugin to your configuration:

BugsnagExitInfoPlugin bugsnagExitInfoPlugin = new BugsnagExitInfoPlugin();
Configuration config = Configuration.load(this);
config.addPlugin(bugsnagExitInfoPlugin);
Bugsnag.start(this, config);
Bugsnag.start(this, Configuration.load(this).apply {
  addPlugin(BugsnagExitInfoPlugin())
})

Enabling the plugin will add native stacktraces for all threads to your NDK events and the open file descriptors at the time of the crash will be displayed in a tab on the dashboard called “Open FileDescriptors”. You can also enable reporting of Logcat messages, by turning on the includeLogcat configuration option – see below.

Configuration

The plugin can be configured by passing it an ExitInfoPluginConfiguration object with one or more of the following options:

Controlling event correlation

To match a BugSnag event to a reported exit info record, we use ActivityManager‘s ProcessStateSummary to store a unique session ID. This may interfere with other tools using this API, in which case the behavior can be disabled as follows:

ExitInfoPluginConfiguration exitInfoPluginConfiguration = new ExitInfoPluginConfiguration();
exitInfoPluginConfiguration.setDisableProcessStateSummaryOverride(false);
BugsnagExitInfoPlugin bugsnagExitInfoPlugin = new BugsnagExitInfoPlugin(exitInfoPluginConfiguration);

Configuration config = Configuration.load(this);
config.addPlugin(bugsnagExitInfoPlugin);
Bugsnag.start(this, config);
Bugsnag.start(Configuration.load(this).apply {
  val exitInfoPluginConfiguration = ExitInfoPluginConfiguration().apply {
    disableProcessStateSummaryOverride = true
  }
  addPlugin(BugsnagExitInfoPlugin(exitInfoPluginConfiguration))
})

If disabled – or if session tracking is disabled – BugSnag falls back to using the process ID (PID) stored in a file.

Include Logcat

When enabled, sends Logcat messages with your event to a “Logcat” tab on the dashboard:

ExitInfoPluginConfiguration exitInfoPluginConfiguration = new ExitInfoPluginConfiguration();
exitInfoPluginConfiguration.setIncludeLogcat(true);
BugsnagExitInfoPlugin bugsnagExitInfoPlugin = new BugsnagExitInfoPlugin(exitInfoPluginConfiguration);

Configuration config = Configuration.load(this);
config.addPlugin(bugsnagExitInfoPlugin);
Bugsnag.start(this, config);
Bugsnag.start(Configuration.load(this).apply {
  val exitInfoPluginConfiguration = ExitInfoPluginConfiguration().apply {
    includeLogcat = true
  }
  addPlugin(BugsnagExitInfoPlugin(exitInfoPluginConfiguration))
})

The messages are ordered with the newest first with the oldest being truncated if the maxStringValueLength value is reached (10,000 characters by default).

This option is off by default to avoid duplicate reporting if Logcat messages are already being recorded as breadcrumbs.

List open FDs

Sends a list of open file descriptors at the time of the crash with your event to an “Open FileDescriptors” tab on the dashboard:

ExitInfoPluginConfiguration exitInfoPluginConfiguration = new ExitInfoPluginConfiguration();
exitInfoPluginConfiguration.setListOpenFds(false);
BugsnagExitInfoPlugin bugsnagExitInfoPlugin = new BugsnagExitInfoPlugin(exitInfoPluginConfiguration);

Configuration config = Configuration.load(this);
config.addPlugin(bugsnagExitInfoPlugin);
Bugsnag.start(this, config);
Bugsnag.start(Configuration.load(this).apply {
  val exitInfoPluginConfiguration = ExitInfoPluginConfiguration().apply {
    listOpenFds = false
  }
  addPlugin(BugsnagExitInfoPlugin(exitInfoPluginConfiguration))
})

This option is enabled by default.