Skip to content

Mob.Bt: Bluetooth Classic peripheral (HFP/SPP/HID) - #9

Merged
GenericJam merged 1 commit into
GenericJam:masterfrom
HeroesLament:migrate/bt
May 16, 2026
Merged

Mob.Bt: Bluetooth Classic peripheral (HFP/SPP/HID)#9
GenericJam merged 1 commit into
GenericJam:masterfrom
HeroesLament:migrate/bt

Conversation

@HeroesLament

@HeroesLamentHeroesLament commented May 15, 2026

Copy link
Copy Markdown

Adds a Bluetooth Classic API for Android. iOS returns :unsupported (Classic profiles need MFi).

Companion to GenericJam/mob_new#4 (BT codegen templates). Either order works at build time — runtime short-circuits to :unsupported when the matching MobBridge methodID isn't present — but the user-visible feature only works end-to-end with both in place.

See the commit message for full details (Elixir surface, NIF inventory, atom cache, paired-list streaming accumulator, JNI signatures).

cc @GenericJam

Adds a Bluetooth Classic API for Android. iOS returns :unsupported
(Classic profiles need MFi). Companion to mob_new#3 (Kotlin / manifest
/ JNI templates).
## Elixir surface
- lib/mob/bt.ex — Mob.Bt (discovery, pairing, disconnect)
- lib/mob/bt/hfp.ex — HFP profile (connect, SCO audio, vendor AT)
- lib/mob/bt/spp.ex — SPP profile (RFCOMM client)
- lib/mob/bt/hid.ex — HID profile (raw input reports)
Vendor AT subscribe takes a caller-specified `:company_ids` keyword so
apps can target specific BT SIG company codes per call (Hytera 313,
Apple 76, Qualcomm 10, Plantronics 1117, etc.). Empty list = no events.
## NIF surface
16 NIFs in android/jni/mob_nif.zig:
- bt_list_paired/0, bt_start_discovery/0, bt_cancel_discovery/0
- bt_pair/1, bt_unpair/1, bt_disconnect/1
- bt_hfp_connect/1, bt_hfp_subscribe_vendor_at/2,
bt_hfp_send_vendor_at/3, bt_hfp_start_sco/1, bt_hfp_stop_sco/1,
bt_hfp_send_audio/2 (DIRTY_IO)
- bt_spp_connect/1, bt_spp_write/2 (DIRTY_IO)
- bt_hid_connect/1, bt_hid_subscribe_raw/1
33 mob_deliver_bt_* exports the JNI thunks (in mob_new#3's generated
beam_jni.c) invoke when Kotlin emits BT events. Each builds a 4-tuple
`{:bt | :bt_hfp | :bt_spp | :bt_hid, tag, session_or_nil, payload}`
and posts it to the originating pid.
BridgeMethods gains 16 jmethodID slots; nif_load caches them via
cacheOptional. The bt_* NIFs short-circuit with
`{:bt, :error, nil, %{reason: :unsupported}}` when the matching
methodID is null, so apps from an older mob_new template still boot.
BT atom cache (~30 atoms + map keys) is initialised once at nif_load.
The paired-list streaming accumulator is a 16-slot fixed-size table
protected by its own mutex, holding up to 128 entries per concurrent
caller so multiple processes can list paired devices simultaneously.
## Verified
- mix test: 760 passed, 0 failed
- zig ast-check android/jni/mob_nif.zig exits 0
GenericJam added a commit that referenced this pull request May 16, 2026
…eripheral
Adds a Bluetooth Classic API for Android covering HFP (audio + vendor
AT commands), SPP (RFCOMM byte streams), and HID (input reports). iOS
returns :unsupported (Classic profiles need MFi).
Companion to mob_new#4 (BT codegen templates). Either order works at
build time; runtime short-circuits to :unsupported when the matching
MobBridge methodID isn't present.
Includes follow-on review fixes: drops a Jason.encode! call that
would have failed at runtime (Jason is dev-only), dedupes the
encode_device helper across modules, and adds 14 unit tests covering
the JSON encoder surface.
@GenericJam
GenericJam merged commit 8ce0c42 into GenericJam:masterMay 16, 2026
1 check passed
@HeroesLament
HeroesLament deleted the migrate/bt branch May 16, 2026 06:20
Sign up for freeto 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.

2 participants

@HeroesLament@GenericJam