# `MobDev.OtpAssetBundle`
[🔗](https://github.com/genericjam/mob_dev/blob/main/lib/mob_dev/otp_asset_bundle.ex#L1)

Builds the `assets/otp.zip` that release-mode Android Mob apps extract on
first launch.

Mirrors the elixir-desktop example-app pattern (their `Bridge.kt:unpackZip()`)
but with Mob-specific stripping inherited from the iOS release pass — drops
unused OTP libs and standalone executables to shave bundle size and avoid
shipping anything Mob never actually executes.

Called from `mix mob.release --android` (Workstream 3). Kept as a separate
module so the strip rules + zip layout are testable without spinning up
the full release pipeline.

## Layout produced

    otp.zip
    ├── erts-VSN/...           (BEAM emulator support — minus bin/)
    ├── lib/elixir-VSN/...
    ├── lib/logger-VSN/...
    ├── lib/eex-VSN/...
    ├── lib/<other-otp-libs-the-app-uses>/...
    └── releases/...

The `MobBridge.extractOtpIfNeeded()` Kotlin function unzips this into
`<filesDir>/otp/` on first launch, keyed by `PackageInfo.lastUpdateTime`
so app updates trigger a re-extract.

## What gets stripped

Three categories, mirroring the iOS strip pass in `MobDev.Release`:

1. **Whole OTP libs the framework doesn't use** — `megaco`, `runtime_tools`,
   `wx`, `observer`, `debugger`, etc. These are dead weight in a Mob app.
2. **Standalone executables** — `priv/bin/*` and `erts-*/bin/*`. The few
   that Mob actually executes (`erl_child_setup`, `inet_gethost`, `epmd`)
   are packaged separately in `jniLibs/<abi>/` as `lib<name>.so` so they
   get the `apk_data_file` SELinux label that allows execve from
   untrusted_app. The rest aren't reachable.
3. **Static archives (`.a`)** — already linked into the app's native lib
   at build time. Shipping them inside the runtime tree is pure waste.

`.so` files inside OTP libs (e.g. `priv/lib/asn1.so`) are KEPT in the zip —
they're loaded by the BEAM at runtime via `dlopen()`, which works fine from
app data dir on Android (only `execve()` is blocked).

# `build`

```elixir
@spec build(Path.t(), Path.t(), keyword()) :: {:ok, map()} | {:error, term()}
```

Builds the OTP asset zip from `source_otp_tree` into `target_zip_path`.

Returns `{:ok, %{zipped_files: count, original_size_kb: integer,
zip_size_kb: integer}}` on success, `{:error, reason}` on failure.

## Options

  * `:strip_extra_prefixes` — additional OTP lib prefixes to drop on top of
    the default list. Atoms or strings.
  * `:keep_prefixes` — prefixes to KEEP even if in the default strip list.
    Lets a specific app opt back into a stripped lib.

# `default_stripped_prefixes`

```elixir
@spec default_stripped_prefixes() :: [String.t()]
```

Returns the list of OTP lib name prefixes that are stripped by default.
Public so tests can assert the policy without re-importing the module-private list.

---

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