# `Mob.Background`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/background.ex#L1)

Background execution keep-alive via a silent audio session.

iOS suspends apps when the screen locks unless they hold an active background
execution mode. `keep_alive/0` starts a silent `AVAudioEngine` looping a
zero-filled buffer with `AVAudioSessionCategoryOptionMixWithOthers` — the OS
sees an active audio session and keeps the process running, the user hears
nothing, and any music already playing is undisturbed.

## Requirements

The app's `Info.plist` must declare the `audio` background mode:

    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>

This is included in all projects generated by `mix mob.new`. For Xcode
projects, add it under *Signing & Capabilities → Background Modes →
Audio, AirPlay, and Picture in Picture*.

## Usage

    # Keep the app alive when the screen locks (e.g. in mount/2):
    Mob.Background.keep_alive()

    # Allow suspension again when background execution is no longer needed:
    Mob.Background.stop()

`keep_alive/0` is idempotent — safe to call multiple times.

## Coexistence with Mob.Audio

**Playback** (`Mob.Audio.play/3`): both sides use `MixWithOthers`, so they
mix transparently. The silent buffer is inaudible alongside real audio.

**Recording** (`Mob.Audio.start_recording/2`): recording switches the global
`AVAudioSession` category to `PlayAndRecord`, which sends an interruption to
the keep-alive engine. The engine stops — but the recording itself holds an
active audio session, so the app stays alive for the duration of the
recording. When `stop_recording/1` is called and the session is released, iOS
fires `AVAudioSessionInterruptionTypeEnded` and the keep-alive engine restarts
automatically. No Elixir code is needed to handle this transition.

The same automatic restart applies to phone calls and any other event that
temporarily takes the audio session away from the app.

## Known limitation — observer cleanup

Internally, the NIF registers an `NSNotificationCenter` observer for
`AVAudioSessionInterruptionNotification` to handle the recording restart
described above. When `stop/0` is called, it removes observers using
`removeObserver:nil` scoped to that notification name, which removes *all*
observers for that notification in the process — not just the one registered
by this module.

In practice this is harmless because mob owns the audio session for all
`Mob.Audio` functions, and none of them rely on surviving this cleanup.
However, if you integrate a third-party audio library that registers its own
`AVAudioSessionInterruptionNotification` observer, calling `stop/0` will
silently remove that observer too. In that case, avoid calling `stop/0` while
the third-party library is active, or file an issue so the NIF can be updated
to store and remove only its own observer token.

## Apple's stance

Apple permits the `audio` background mode for apps that legitimately use
audio. Mob apps that use `Mob.Audio` recording or playback qualify. Apple
will reject apps that declare this mode without any audio feature — do not
add `UIBackgroundModes: [audio]` to an app that has no audio functionality.

## Android — Foreground Service

On Android the OS equivalent of iOS background execution is a *foreground
service*. `keep_alive/0` starts `BeamForegroundService`, which calls
`startForeground/2` with a low-priority persistent notification. The OS
will not kill a foreground service under memory pressure and will not
pause the process when the screen locks.

### Visible notification (required by Android)

Android requires every foreground service to post a visible notification.
The notification appears in the status bar and notification tray with the
app name and the text "Running in background". It has `IMPORTANCE_LOW` so
it produces no sound or vibration. There is no API to hide it — this is
an OS-level constraint designed to inform users when apps are running in
the background.

### Manifest requirements

`mix mob.new` adds the necessary declarations automatically:

    <!-- AndroidManifest.xml -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"
        android:minSdkVersion="34" />

    <service android:name=".BeamForegroundService"
        android:exported="false"
        android:foregroundServiceType="dataSync" />

For apps created before this feature was added, copy the above snippet
manually into your `AndroidManifest.xml`.

### Stop behaviour

`stop/0` sends `ACTION_STOP` to the service, which calls `stopForeground`
and `stopSelf`. The OS removes the notification immediately. If the BEAM
node goes silent (no incoming distribution traffic) the OS may still
eventually kill the process — `keep_alive/0` prevents aggressive
*background process killing* but not an eventual idle OOM kill after many
hours of complete inactivity.

# `keep_alive`

```elixir
@spec keep_alive() :: :ok
```

Starts a silent audio session to prevent iOS from suspending the app
when the screen locks. Idempotent — safe to call more than once.

# `stop`

```elixir
@spec stop() :: :ok
```

Stops the keep-alive audio session and allows iOS to suspend the app
normally when it goes to background.

---

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