# `Mob.PostMortem.Android`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/post_mortem/android.ex#L1)

`ApplicationExitInfo`-backed post-mortem ingest for Android.

Pulls the OS-held history of process exits via
`ActivityManager.getHistoricalProcessExitReasons` (available on
Android 11 / API 30 and later), filters against a persistent marker
so the drain returns each exit once across boots, keeps each one in
`Mob.PostMortem.Journal` until it has been observed, and hands it to
`Mob.Defect.emit_appexit_reason/1` as a capsule on `Mob.Defect.Bus`.

## What the OS gives us

`ApplicationExitInfo` records the reason a previous process instance
of this app died — an ANR, a crash, an OOM, a user-initiated kill.
The list survives reboots and app updates, and the OS trims it
eventually (usually 16 entries per app). We do NOT run every boot
hoping to catch a live incident: we sweep whenever the caller does
and take whatever the OS still holds since the last sweep.

## Persistent marker

Without a marker, every sweep on a fresh install re-emits the entire
historical list; every subsequent boot would re-emit the same
entries. The native NIF persists the highest-seen exit timestamp to
`<filesDir>/mob_post_mortem_appexit_marker.txt` and filters the OS
list to entries strictly newer. First sweep on a fresh install still
emits the current history (that is the point of a first sweep);
every subsequent drain returns only what accumulated since.

## Until observed, not once

The marker advances when the NIF returns an exit, so the drain is
destructive: the capsule on this boot's bus is the only copy, and a
boot that dies before anyone looks — `mix mob.connect` restarts the
app, for one — would take the exit with it. So each drained exit is
written to `Mob.PostMortem.Journal` before it is emitted, and every
sweep, in this boot or a later one, emits it again until its capsule
was handed to a subscriber when emitted or returned by
`Mob.Defect.Bus.recent/1`. Within one boot a re-sweep emits nothing
it already emitted.

## Platform gating

The NIF is registered on both platforms (iOS returns `[]`
unconditionally — the iOS substrate is MetricKit, MOB-179). This
module gates on `:mob_nif.platform() == :android` so a caller on
iOS or in a host test does no work at all. On an Android device
below API 30 the native side also returns `[]` — the API was added
in Android 11.

## Redaction

The emitted capsule carries: numeric reason code, pid (an OS
identifier, not user data), timestamp, process name (normally the
app's package name; also OS-controlled), and a short
OS-generated description string like `"remote process crash"`.
NOT included this phase: the trace file contents an ANR carries —
those can hold app strings and need the same
`Mob.Agent.Receipt.summarize_error/3`-style discipline before they
can safely reach the bus.

# `sweep`

```elixir
@spec sweep() :: [Mob.Defect.Capsule.t()]
```

Sweep any ApplicationExitInfo entries the OS has recorded since the
persisted marker was last written, and every earlier one not yet
observed.

Returns the list of capsules emitted: unobserved journaled entries
first, then new ones in the order the NIF returned them (typically
OS-chronological). Empty on iOS, on Android < 11, on a host test with
the NIF not loaded, and when nothing is new or waiting to be observed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
