> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Synheart Session - Kotlin SDK

> Native Kotlin SDK for Android with on-device SessionEngine, pluggable providers, and behavioral signal fusion

## Overview

The Synheart Session Kotlin SDK provides a native SessionEngine that captures heart rate data, computes session metrics on-device, and optionally fuses behavioral signals alongside HR data. The engine is timer-driven, emitting session frames at configurable intervals and delivering a session summary when complete. The SDK supports pluggable `BiosignalProvider` (for HR sources) and `BehaviorProvider` (for behavioral signals) via reflection-based wrappers.

## Installation

### Maven Central

```groovy theme={null}
dependencies {
    implementation 'ai.synheart:synheart-session:0.2.0'
}
```

### Local Module

```groovy theme={null}
// settings.gradle
include ':synheart-session'
project(':synheart-session').projectDir = new File('../synheart-session-kotlin')

// build.gradle
dependencies {
    implementation project(':synheart-session')
}
```

### Requirements

* Android API 21+
* Kotlin 1.9+

## Quick Start

### Mock Mode (Development)

```kotlin theme={null}
import ai.synheart.session.*

val config = SessionConfig(
    sessionId = UUID.randomUUID().toString(),
    mode = SessionMode.FOCUS,
    durationSec = 300,
    profile = ComputeProfile(windowSec = 60, emitIntervalSec = 5)
)

val engine = SessionEngine()
engine.start(config) { event ->
    when (event["type"] as? String) {
        "session_started" -> Log.d("Session", "Started")
        "session_frame" -> {
            val metrics = event["metrics"] as? Map<*, *>
            Log.d("Session", "HR: ${metrics?.get("hr_mean_bpm")} bpm")
        }
        "session_summary" -> Log.d("Session", "Complete")
        "session_error" -> Log.e("Session", "Error: ${event["message"]}")
    }
}

// Stop early (optional)
engine.stop(config.sessionId)
```

### Real BLE Heart Rate Data

```kotlin theme={null}
import ai.synheart.session.*

// Wrap any BLE HRM client as a BiosignalProvider (reflection-based, no compile dep needed)
val provider = WearBiosignalProvider(bleHrmClient)
val engine = SessionEngine(provider)

engine.start(config) { event ->
    // Same event handling — metrics are now computed from real HR data
}
```

### Health Connect Heart Rate Data

```kotlin theme={null}
import ai.synheart.session.*

val provider = HealthConnectBiosignalProvider(healthConnectAdapter, context)

if (provider.isAvailable) {
    val engine = SessionEngine(provider)

    engine.start(config) { event ->
        // Metrics are computed from Health Connect HR data
        // (Wear OS watches, Samsung Health, Fitbit, etc.)
    }
}
```

## Session Configuration

```kotlin theme={null}
val config = SessionConfig(
    sessionId = UUID.randomUUID().toString(),
    mode = SessionMode.FOCUS,        // or SessionMode.BREATHING
    durationSec = 60,                // session duration in seconds
    profile = ComputeProfile(
        windowSec = 10,              // sliding window size
        emitIntervalSec = 3          // session frame interval
    )
)
```

### SessionConfig

| Parameter           | Type             | Required | Default | Description                           |
| ------------------- | ---------------- | -------- | ------- | ------------------------------------- |
| `sessionId`         | `String`         | No       | UUID    | Unique session identifier             |
| `mode`              | `SessionMode`    | Yes      | -       | Session mode (`FOCUS` or `BREATHING`) |
| `durationSec`       | `Int`            | Yes      | -       | Session duration in seconds           |
| `profile`           | `ComputeProfile` | No       | 60s/5s  | Window and emit configuration         |
| `includeRawSamples` | `Boolean`        | No       | `false` | Enable raw biosignal frame streaming  |
| `windowLabel`       | `String?`        | No       | `null`  | Optional label for the session window |

### ComputeProfile

| Parameter            | Type   | Default | Description                                                  |
| -------------------- | ------ | ------- | ------------------------------------------------------------ |
| `windowSec`          | `Int`  | 60      | Sliding window size in seconds                               |
| `emitIntervalSec`    | `Int`  | 5       | Session frame emission interval in seconds                   |
| `rawEmitIntervalSec` | `Int?` | `null`  | Raw biosignal frame interval (defaults to `emitIntervalSec`) |

## Session Events

The engine callback receives `Map<String, Any>` dictionaries with a `"type"` key.

### session\_started

```kotlin theme={null}
// event["type"] == "session_started"
// event["session_id"] == "..."
// event["started_at_ms"] == 1700000000000
```

### session\_frame

Contains flat metrics computed over the sliding window:

```kotlin theme={null}
// event["type"] == "session_frame"
val metrics = event["metrics"] as? Map<String, Any>
val hr = metrics?.get("hr_mean_bpm") as? Double       // Mean HR in BPM
val sdnn = metrics?.get("hr_sdnn_ms") as? Double       // SDNN of RR intervals
val rmssd = metrics?.get("rmssd_ms") as? Double        // RMSSD approximation
val motionRms = metrics?.get("motion_rms_g") as? Double // RMS acceleration (optional)
```

| Field                 | Type     | Description                                                          |
| --------------------- | -------- | -------------------------------------------------------------------- |
| `hr_mean_bpm`         | `Double` | Mean heart rate (BPM)                                                |
| `hr_sdnn_ms`          | `Double` | SDNN of RR intervals (ms)                                            |
| `rmssd_ms`            | `Double` | RMSSD approximation (ms)                                             |
| `sample_count`        | `Int`    | Number of HR samples in window                                       |
| `start_ms`            | `Long`   | Window start timestamp (ms)                                          |
| `end_ms`              | `Long`   | Window end timestamp (ms)                                            |
| `motion_rms_g`        | `Double` | RMS acceleration in g-force (optional, when accelerometer available) |
| `motion_sample_count` | `Int`    | Number of accelerometer samples in interval (optional)               |

### biosignal\_frame

Emitted when `includeRawSamples = true`. Contains raw biosignal samples for live display:

```kotlin theme={null}
val samples = event["samples"] as? List<Map<String, Any>>
samples?.forEach { sample ->
    val bpm = sample["bpm"] as? Double
    val rr = sample["rr_intervals_ms"] as? List<Double>  // plural, array of ms
}
```

### session\_summary

```kotlin theme={null}
// event["type"] == "session_summary"
val duration = event["duration_actual_sec"] as? Int
val metrics = event["metrics"] as? Map<String, Any>
```

### session\_error

```kotlin theme={null}
// event["type"] == "session_error"
val code = event["error_code"] as? String    // e.g. "sensor_unavailable"
val message = event["message"] as? String
```

**Error codes:**

| Code                 | Description                                   |
| -------------------- | --------------------------------------------- |
| `permission_denied`  | Health data permissions not granted           |
| `sensor_unavailable` | Heart rate sensor not available               |
| `low_battery`        | Device battery too low                        |
| `os_terminated`      | Operating system terminated the session       |
| `invalid_state`      | Invalid session state (e.g., duplicate start) |

## Behavioral Signals

The `SessionEngine` accepts an optional `BehaviorProvider` that fuses behavioral data into session frames.

### Mock Behavior (Development)

```kotlin theme={null}
val engine = SessionEngine(behaviorProvider = MockBehaviorProvider())

engine.start(config) { event ->
    val behavior = event["behavior"] as? Map<*, *>
    Log.d("Session", "Stability: ${behavior?.get("stability_index")}")
}
```

### Production (via synheart-behavior)

```kotlin theme={null}
// Wrap any behavior SDK object as a BehaviorProvider (reflection-based)
val behaviorProvider = BehaviorSdkProvider(behaviorSdkInstance)
val engine = SessionEngine(behaviorProvider = behaviorProvider)
```

The `"behavior"` key is only present in frames when a `BehaviorProvider` is configured and returns data.

## Architecture

```text theme={null}
SessionEngine(provider: BiosignalProvider, behaviorProvider: BehaviorProvider?)
  |
  +-- BiosignalProvider                (interface)
  |     +-- MockBiosignalProvider       (sinusoidal mock, 1 Hz)
  |     +-- WearBiosignalProvider       (wraps BLE HRM, reflection-based)
  |     +-- HealthConnectBiosignalProvider (wraps Health Connect, reflection-based)
  |
  +-- BehaviorProvider                 (interface, pull-based)
  |     +-- MockBehaviorProvider        (stable mid-range values)
  |     +-- BehaviorSdkProvider         (wraps behavior SDK, reflection-based)
  |
  +-- SampleRingBuffer           (thread-safe sliding window)
  +-- computeMetrics()           (pure HR -> HRV metrics)
  +-- SessionError               (sealed class with 5 subclasses)
```

## Custom Provider

Any class implementing `BiosignalProvider` can drive the session engine:

```kotlin theme={null}
class MyProvider : BiosignalProvider {
    override val isAvailable = true
    override val name = "my_source"

    override fun startStreaming(onSample: (BiosignalSample) -> Unit) {
        // Push BiosignalSample values into the callback
    }

    override fun stopStreaming() { }
}

val engine = SessionEngine(MyProvider())
```

## API Reference

### SessionEngine

| Constructor                                 | Description                                  |
| ------------------------------------------- | -------------------------------------------- |
| `SessionEngine()`                           | Default mock biosignal provider, no behavior |
| `SessionEngine(provider)`                   | Custom biosignal provider                    |
| `SessionEngine(provider, behaviorProvider)` | Custom biosignal + behavioral providers      |
| `SessionEngine(behaviorProvider = bp)`      | Default mock biosignal + behavioral provider |

| Method                    | Description                                        |
| ------------------------- | -------------------------------------------------- |
| `start(config, callback)` | Start a session. Throws if already running.        |
| `stop(sessionId)`         | Stop a running session.                            |
| `getStatus()`             | Returns status map or `null` if no active session. |

## Resources

* **Repository**: [synheart-session-kotlin](https://github.com/synheart-ai/synheart-session-kotlin)
* **Issues**: [GitHub Issues](https://github.com/synheart-ai/synheart-session-kotlin/issues)
