Skip to main content

priv/native/jni/mob_bluetooth_jni.c

// mob_bluetooth_jni.c — JNI delivery thunks for the mob_bluetooth plugin.
//
// Copied VERBATIM from mob-core's beam_jni.c Bluetooth section, with ONLY the
// JNI symbol prefix renamed:
//   Java_com_example_mob_1plugin_1demo_MobBridge_nativeDeliverBt*
//     →  Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBt*
//
// Compiled as a plain C object via the plugin `jni_source` pipeline
// (build.zig `-Dplugin_jni_sources`) — NOT a NIF init (no
// STATIC_ERLANG_NIF_LIBNAME). Each thunk unmarshals the Java args into C
// primitives, calls the matching `mob_deliver_bt_*` export (defined in the
// sibling zig NIF `mob_bluetooth_nif.zig`, linked into the same .so), then
// releases.
//
// mob-core's beam_jni.c gets these `mob_deliver_bt_*` prototypes from
// `mob_beam.h`; this plugin source isn't built against that header, so the
// prototypes are declared inline below (copied byte-for-byte from the
// `mob_deliver_bt_*` block of mob_beam.h, which mirrors the zig export
// signatures).

#include <jni.h>
#include <stddef.h>

// ── mob_deliver_bt_* prototypes (from the zig NIF, replicating mob_beam.h) ──

// Discovery (no-payload 2-tuples)
void mob_deliver_bt_discovery_started(jlong pid);
void mob_deliver_bt_discovery_finished(jlong pid);
void mob_deliver_bt_discovery_cancelled(jlong pid);

// Discovery / pairing events (3-tuples, no session)
void mob_deliver_bt_discovered(jlong pid, const char *address, const char *name,
                               int bonded);
void mob_deliver_bt_paired(jlong pid, const char *address, const char *name,
                           int bonded);
void mob_deliver_bt_pair_failed(jlong pid, const char *address,
                                const char *reason);
void mob_deliver_bt_unpaired(jlong pid, const char *address);
void mob_deliver_bt_error(jlong pid, const char *reason);

// Paired-list streaming (begin / entry / finish)
void mob_deliver_bt_paired_list_begin(jlong pid);
void mob_deliver_bt_paired_list_entry(jlong pid, const char *address,
                                      const char *name, int bonded);
void mob_deliver_bt_paired_list_finish(jlong pid);

// HFP profile
void mob_deliver_bt_hfp_connecting(jlong pid, int session, const char *address);
void mob_deliver_bt_hfp_connected(jlong pid, int session, const char *address,
                                  const char *name);
void mob_deliver_bt_hfp_connect_failed(jlong pid, const char *address,
                                       const char *reason);
void mob_deliver_bt_hfp_disconnected(jlong pid, int session,
                                     const char *reason_atom);
void mob_deliver_bt_hfp_vendor_subscribed(jlong pid, int session);
void mob_deliver_bt_hfp_vendor_at(jlong pid, int session, const char *cmd,
                                  int cmd_type, const char *args,
                                  const char *address);
void mob_deliver_bt_hfp_sco_started(jlong pid, int session,
                                    const char *address);
void mob_deliver_bt_hfp_sco_stopped(jlong pid, int session);
void mob_deliver_bt_hfp_error(jlong pid, int session, const char *reason);

// SPP profile
void mob_deliver_bt_spp_connected(jlong pid, int session, const char *address,
                                  const char *name);
void mob_deliver_bt_spp_connect_failed(jlong pid, const char *address,
                                       const char *reason);
void mob_deliver_bt_spp_disconnected(jlong pid, int session,
                                     const char *reason_atom);
void mob_deliver_bt_spp_data(jlong pid, int session, const char *bytes,
                             size_t len);
void mob_deliver_bt_spp_written(jlong pid, int session, int size);
void mob_deliver_bt_spp_error(jlong pid, int session, const char *reason);

// ── Bluetooth Classic (Mob.Bt suite) — JNI thunks ───────────────────────
// Each thunk unmarshals Java args into C primitives, calls the matching
// mob_deliver_bt_* helper, then releases.

// ── Adapter-level events (no session) ──────────────────────────────────

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtDiscoveryStarted(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_bt_discovery_started(pid);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtDiscoveryFinished(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_bt_discovery_finished(pid);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtDiscoveryCancelled(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_bt_discovery_cancelled(pid);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtDiscovered(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring name,
    jboolean bonded) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_name = (*env)->GetStringUTFChars(env, name, NULL);
  mob_deliver_bt_discovered(pid, c_address, c_name, bonded ? 1 : 0);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, name, c_name);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtPaired(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring name,
    jboolean bonded) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_name = (*env)->GetStringUTFChars(env, name, NULL);
  mob_deliver_bt_paired(pid, c_address, c_name, bonded ? 1 : 0);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, name, c_name);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtPairFailed(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring reason) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_pair_failed(pid, c_address, c_reason);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtUnpaired(
    JNIEnv *env, jclass cls, jlong pid, jstring address) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  mob_deliver_bt_unpaired(pid, c_address);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtError(JNIEnv *env,
                                                              jclass cls,
                                                              jlong pid,
                                                              jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_error(pid, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

// ── Paired-list streaming builder ──────────────────────────────────────

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtPairedListBegin(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_bt_paired_list_begin(pid);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtPairedListEntry(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring name,
    jboolean bonded) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_name = (*env)->GetStringUTFChars(env, name, NULL);
  mob_deliver_bt_paired_list_entry(pid, c_address, c_name, bonded ? 1 : 0);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, name, c_name);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtPairedListFinish(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_bt_paired_list_finish(pid);
}

// ── HFP profile ────────────────────────────────────────────────────────

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpConnecting(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring address) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  mob_deliver_bt_hfp_connecting(pid, (int)session, c_address);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpConnected(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring address,
    jstring name) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_name = (*env)->GetStringUTFChars(env, name, NULL);
  mob_deliver_bt_hfp_connected(pid, (int)session, c_address, c_name);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, name, c_name);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpConnectFailed(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring reason) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_hfp_connect_failed(pid, c_address, c_reason);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpDisconnected(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_hfp_disconnected(pid, (int)session, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpVendorSubscribed(
    JNIEnv *env, jclass cls, jlong pid, jint session) {
  mob_deliver_bt_hfp_vendor_subscribed(pid, (int)session);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpVendorAt(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring cmd,
    jint cmd_type, jstring args, jstring address) {
  const char *c_cmd = (*env)->GetStringUTFChars(env, cmd, NULL);
  const char *c_args = (*env)->GetStringUTFChars(env, args, NULL);
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  mob_deliver_bt_hfp_vendor_at(pid, (int)session, c_cmd, (int)cmd_type, c_args,
                               c_address);
  (*env)->ReleaseStringUTFChars(env, cmd, c_cmd);
  (*env)->ReleaseStringUTFChars(env, args, c_args);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpScoStarted(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring address) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  mob_deliver_bt_hfp_sco_started(pid, (int)session, c_address);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpScoStopped(
    JNIEnv *env, jclass cls, jlong pid, jint session) {
  mob_deliver_bt_hfp_sco_stopped(pid, (int)session);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtHfpError(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_hfp_error(pid, (int)session, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

// ── SPP profile ────────────────────────────────────────────────────────

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppConnected(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring address,
    jstring name) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_name = (*env)->GetStringUTFChars(env, name, NULL);
  mob_deliver_bt_spp_connected(pid, (int)session, c_address, c_name);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, name, c_name);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppConnectFailed(
    JNIEnv *env, jclass cls, jlong pid, jstring address, jstring reason) {
  const char *c_address = (*env)->GetStringUTFChars(env, address, NULL);
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_spp_connect_failed(pid, c_address, c_reason);
  (*env)->ReleaseStringUTFChars(env, address, c_address);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppDisconnected(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_spp_disconnected(pid, (int)session, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppData(
    JNIEnv *env, jclass cls, jlong pid, jint session, jbyteArray data) {
  jsize len = (*env)->GetArrayLength(env, data);
  jbyte *buf = (*env)->GetByteArrayElements(env, data, NULL);
  mob_deliver_bt_spp_data(pid, (int)session, (const char *)buf, (size_t)len);
  (*env)->ReleaseByteArrayElements(env, data, buf, JNI_ABORT);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppWritten(
    JNIEnv *env, jclass cls, jlong pid, jint session, jint size) {
  mob_deliver_bt_spp_written(pid, (int)session, (int)size);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBtSppError(
    JNIEnv *env, jclass cls, jlong pid, jint session, jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_bt_spp_error(pid, (int)session, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

// ── BLE (Low Energy) peripheral — mob_deliver_ble_* prototypes + thunks ──

void mob_deliver_ble_advertising_started(jlong pid);
void mob_deliver_ble_advertising_failed(jlong pid, const char *reason);
void mob_deliver_ble_central_connected(jlong pid, int central);
void mob_deliver_ble_central_disconnected(jlong pid, int central);
void mob_deliver_ble_subscribed(jlong pid, const char *characteristic);
void mob_deliver_ble_unsubscribed(jlong pid, const char *characteristic);
void mob_deliver_ble_write(jlong pid, const char *characteristic,
                           const char *bytes, size_t len);

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleAdvertisingStarted(
    JNIEnv *env, jclass cls, jlong pid) {
  mob_deliver_ble_advertising_started(pid);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleAdvertisingFailed(
    JNIEnv *env, jclass cls, jlong pid, jstring reason) {
  const char *c_reason = (*env)->GetStringUTFChars(env, reason, NULL);
  mob_deliver_ble_advertising_failed(pid, c_reason);
  (*env)->ReleaseStringUTFChars(env, reason, c_reason);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleCentralConnected(
    JNIEnv *env, jclass cls, jlong pid, jint central) {
  mob_deliver_ble_central_connected(pid, (int)central);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleCentralDisconnected(
    JNIEnv *env, jclass cls, jlong pid, jint central) {
  mob_deliver_ble_central_disconnected(pid, (int)central);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleSubscribed(
    JNIEnv *env, jclass cls, jlong pid, jstring characteristic) {
  const char *c_char = (*env)->GetStringUTFChars(env, characteristic, NULL);
  mob_deliver_ble_subscribed(pid, c_char);
  (*env)->ReleaseStringUTFChars(env, characteristic, c_char);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleUnsubscribed(
    JNIEnv *env, jclass cls, jlong pid, jstring characteristic) {
  const char *c_char = (*env)->GetStringUTFChars(env, characteristic, NULL);
  mob_deliver_ble_unsubscribed(pid, c_char);
  (*env)->ReleaseStringUTFChars(env, characteristic, c_char);
}

JNIEXPORT void JNICALL
Java_io_mob_bluetooth_MobBluetoothBridge_nativeDeliverBleWrite(
    JNIEnv *env, jclass cls, jlong pid, jstring characteristic,
    jbyteArray data) {
  const char *c_char = (*env)->GetStringUTFChars(env, characteristic, NULL);
  jsize len = (*env)->GetArrayLength(env, data);
  jbyte *buf = (*env)->GetByteArrayElements(env, data, NULL);
  mob_deliver_ble_write(pid, c_char, (const char *)buf, (size_t)len);
  (*env)->ReleaseByteArrayElements(env, data, buf, JNI_ABORT);
  (*env)->ReleaseStringUTFChars(env, characteristic, c_char);
}