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

# Android SDK

> Advanced sleep analysis capabilities for Android applications

# Sleep Cycle SDK - Android Documentation

## Overview

The Sleep Cycle SDK for Android enables developers to integrate advanced sleep analysis capabilities into their applications. The SDK provides real-time sleep tracking using audio and motion sensors, delivering detailed sleep insights and events throughout the night.

## System Requirements

Minimum Android API Level:

* Min SDK: API level 28 (Android 9.0 Pie)
* Compile SDK: API level 35

Kotlin:

* Kotlin Version: 1.9+ (JVM target 11)
* The SDK is written in Kotlin and provides a Kotlin-first API

## Installation

Find the [latest version on Maven Central](https://central.sonatype.com/artifact/com.sleepcycle.sdk/sdk-android).

### Groovy DSL

Add the Sleep Cycle SDK dependency to your `build.gradle`:

```gradle theme={null}
dependencies {
    implementation "com.sleepcycle.sdk:sdk-android:<latest-version>"
}
```

### Kotlin DSL

Add the Sleep Cycle SDK dependency to your `build.gradle.kts`:

```kotlin theme={null}
dependencies {
    implementation("com.sleepcycle.sdk:sdk-android:<latest-version>")
}
```

## Prerequisites

### Permissions

The SDK requires microphone access for audio-based sleep analysis:

```xml theme={null}
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
```

The SDK automatically includes the WAKE\_LOCK permission in its manifest to keep the device awake during analysis:

```xml theme={null}
<uses-permission android:name="android.permission.WAKE_LOCK" />
```

### Keeping the analysis active using a foreground service

To ensure continuous sleep analysis throughout the night, you must implement a foreground service. This prevents Android from terminating the analysis process during extended periods.

It is up to the host app to start the foreground service correctly. The service must declare appropriate foreground service types in its manifest to specify what system resources it needs access to. For sleep analysis, you'll typically need the `health` or `microphone` service types, which grant access to health sensors and microphone respectively.

## General

The SDK is thread safe and can be called from any thread.

## Initialize the SDK

The SDK requires authentication before use. The initialization process validates your credentials and determines available features.

```kotlin theme={null}
import com.sleepcycle.sdk.SleepCycleSdk

try {
    val features = SleepCycleSdk.initialize(
        context = applicationContext,
        apiKey = "your-api-key-here"
    )
    Log.d("SDK", "Authorized with features: $features")
} catch (e: Exception) {
    Log.e("SDK", "Authorization failed: ${e.message}")
}
```

The returned `SleepAnalysisFeatures` indicates which capabilities are available for your API key:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>sleepStaging</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Sleep staging analysis<br />
  <code>smartAlarm</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Smart alarm functionality<br />
  <code>audioEvents</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Audio event detection<br />
  <code>snoringDetection</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Snoring detection<br />
  <code>realTimeSleepStaging</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Real-time sleep staging<br />
  <code>multiChannelAnalysis</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Multi-channel analysis (stereo, two channels)<br />
  <code>extendedAudioEvents</code> <span style={{ opacity: 0.5 }}>Boolean</span> - Extended audio event types beyond the standard set
</div>

## Get the SDK state

Monitor SDK state changes using the StateFlow:

```kotlin theme={null}
import com.sleepcycle.sdk.SdkState

val sdkStateFlow: StateFlow<SdkState> = SleepCycleSdk.sdkStateFlow
```

Get the current state:

```kotlin theme={null}
val currentState: SdkState = SleepCycleSdk.getState()
```

## Start a sleep analysis session

Once initialized, you can start a sleep analysis session. The method returns a `UUID` that identifies the session.

```kotlin theme={null}
import com.sleepcycle.sdk.SleepAnalysisConfig

try {
    val sessionId: UUID = SleepCycleSdk.startAnalysis(
        config = SleepAnalysisConfig(
            useAudio = true,
            useAccelerometer = true
        )
    )
    Log.d("SDK", "Analysis started with session ID: $sessionId")
} catch (e: Exception) {
    Log.e("SDK", "Failed to start analysis: ${e.message}")
}
```

Parameters:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>config</code> <span style={{ opacity: 0.5 }}>SleepAnalysisConfig</span> - Configuration object specifying which sensors to use<br />
  <code>startMillisUtc</code> <span style={{ opacity: 0.5 }}>Long</span> - Analysis start time in UTC milliseconds (defaults to current time)<br />
  <code>dataSource</code> <span style={{ opacity: 0.5 }}>DataSource?</span> - Optional custom data source. When null, the SDK uses live device sensors<br />
  <code>audioEventListeners</code> <span style={{ opacity: 0.5 }}>List\<AudioEventListener></span> - Optional list of listeners that receive callbacks during audio analysis
</div>

## Resume a session

The SDK supports resuming a previously started analysis session. This is useful when your app restarts or the foreground service is terminated by the system.

```kotlin theme={null}
try {
    if (SleepCycleSdk.isResumePossible()) {
        SleepCycleSdk.resumeAnalysis()
    }
} catch (e: Exception) {
    Log.e("SDK", "Failed to resume analysis: ${e.message}")
}
```

## Stop a session

To stop an active analysis session and retrieve the results:

```kotlin theme={null}
try {
    val result: AnalysisResult? = SleepCycleSdk.stopAnalysis()

    result?.let { analysisResult ->
        val events = analysisResult.events
        val breathingRates = analysisResult.breathingRates
        val sleepStageIntervals = analysisResult.sleepStageIntervals

        analysisResult.statistics?.let { statistics ->
            Log.d("SDK", "Sleep duration: ${statistics.totalSleepDurationSeconds}")
        }

        events.forEach { event ->
            Log.d("SDK", "${event.type} from ${event.startTime} to ${event.endTime}")
        }

        breathingRates.forEach { breathingRate ->
            Log.d("SDK", "Breathing rate: ${breathingRate.bpm} bpm at ${breathingRate.timestampSecondsUtc}")
        }

        analysisResult.audioStatistics?.let { audioStats ->
            audioStats.healthIntervals.forEach { interval ->
                Log.d("SDK", "Audio ${interval.status}: ${interval.interval}")
            }
        }
    }
} catch (e: Exception) {
    Log.e("SDK", "Failed to stop analysis: ${e.message}")
}
```

## Analysis result

The `AnalysisResult` contains the complete output of a sleep analysis session:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>sessionId</code> <span style={{ opacity: 0.5 }}>UUID</span> - Unique session identifier<br />
  <code>startSecondsUtc</code> <span style={{ opacity: 0.5 }}>Double</span> - Session start time in UTC seconds<br />
  <code>endSecondsUtc</code> <span style={{ opacity: 0.5 }}>Double</span> - Session end time in UTC seconds<br />
  <code>timeZoneId</code> <span style={{ opacity: 0.5 }}>String</span> - IANA time zone (e.g. "Europe/Stockholm") captured at session start<br />
  <code>events</code> <span style={{ opacity: 0.5 }}>List\<Event></span> - Detected sleep events<br />
  <code>breathingRates</code> <span style={{ opacity: 0.5 }}>List\<BreathingRate></span> - Breathing rate measurements<br />
  <code>sleepStageIntervals</code> <span style={{ opacity: 0.5 }}>List\<SleepStageInterval></span> - Sleep stage data<br />
  <code>realTimeSleepStageIntervals</code> <span style={{ opacity: 0.5 }}>List\<SleepStageInterval></span> - Sleep stages emitted live during the session<br />
  <code>statistics</code> <span style={{ opacity: 0.5 }}>SleepStatistics?</span> - Aggregated sleep statistics (nullable)<br />
  <code>audioStatistics</code> <span style={{ opacity: 0.5 }}>AudioStatistics?</span> - Audio health statistics (nullable)
</div>

### SleepStatistics

When available, `statistics` contains aggregated metrics about the sleep session:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>totalSleepDurationSeconds</code> <span style={{ opacity: 0.5 }}>Double</span> - Total time spent sleeping<br />
  <code>sleepOnsetLatencySeconds</code> <span style={{ opacity: 0.5 }}>Double?</span> - Time to fall asleep<br />
  <code>sleepEfficiency</code> <span style={{ opacity: 0.5 }}>Double</span> - Ratio of sleep to time in bed (0.0 to 1.0)<br />
  <code>finalWakeTimeSecondsUtc</code> <span style={{ opacity: 0.5 }}>Double?</span> - Time of final awakening (UTC seconds)<br />
  <code>numberOfAwakenings</code> <span style={{ opacity: 0.5 }}>Int</span> - Number of awakenings during the night<br />
  <code>snoreTimeSeconds</code> <span style={{ opacity: 0.5 }}>Double</span> - Total time spent snoring<br />
  <code>snoreSessions</code> <span style={{ opacity: 0.5 }}>List\<SnoreSession></span> - Individual snoring sessions<br />
  <code>sleepStageDurationsSeconds</code> <span style={{ opacity: 0.5 }}>Map\<SleepStage, Double></span> - Duration per sleep stage
</div>

### AudioStatistics

When available, `audioStatistics` contains information about audio input health throughout the session.

## Sleep score

After a session completes, you can compute a sleep score for the night with `SleepScoring.compute()`. The score combines the night's result with a short history of previous nights.

```kotlin theme={null}
import com.sleepcycle.sdk.score.SleepScore
import com.sleepcycle.sdk.score.SleepScoring
import com.sleepcycle.sdk.score.toSleepScoreHistoryEntry

// Build a history entry from each completed result and persist it for future nights
val historyEntry = analysisResult.toSleepScoreHistoryEntry()

val score: SleepScore = SleepScoring.compute(
    result = analysisResult,
    history = recentHistoryEntries,
    dateOfBirthSecondsUtc = userDateOfBirthSecondsUtc,  // optional, age-adjusts the quality subscore
    chronoType = userChronoType                         // optional, selects the timing subscore window
)

Log.d("SDK", "Sleep score: ${score.total} (duration=${score.duration}, quality=${score.quality}, routine=${score.routine})")
```

Each `SleepScore` field is in the range 0.0–1.0, where higher is better:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>total</code> <span style={{ opacity: 0.5 }}>Float</span> - Overall sleep score for the night<br />
  <code>duration</code> <span style={{ opacity: 0.5 }}>Float</span> - How much the user slept<br />
  <code>quality</code> <span style={{ opacity: 0.5 }}>Float</span> - How well the user slept<br />
  <code>routine</code> <span style={{ opacity: 0.5 }}>Float</span> - The user's sleep schedule
</div>

`SleepScoring.identifyChronoType(history)` derives the user's `ChronoType` (`EXTREME_MORNING`, `MORNING`, `INTERMEDIATE`, `EVENING`, `EXTREME_EVENING`) from recent history, or returns `null` when there isn't enough data. The result can be passed back into `compute()` as the `chronoType` argument.

## Real-time events

The SDK provides real-time event updates during analysis through a Flow API:

```kotlin theme={null}
import com.sleepcycle.sdk.Event
import com.sleepcycle.sdk.EventType

lifecycleScope.launch {
    SleepCycleSdk.eventFlow.collect { events: List<Event> ->
        events.forEach { event ->
            when (event.type) {
                EventType.MOVEMENT -> handleMovement(event)
                EventType.SNORING -> handleSnoring(event)
                EventType.TALKING -> handleTalking(event)
                EventType.COUGHING -> handleCoughing(event)
                else -> handleOtherEvent(event)
            }
        }
    }
}
```

When the `extendedAudioEvents` feature is enabled for your API key, additional `EventType` values are reported alongside the standard ones: `BIRD`, `CAT`, `DIGESTIVE`, `DOG`, `FART`, `MUSIC`, `SNEEZE`, `THROAT_CLEARING`, `TRAFFIC`, `WATER`, and `WIND`.

Each `Event` contains:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>type</code> <span style={{ opacity: 0.5 }}>EventType</span> - The type of event<br />
  <code>startTime</code> <span style={{ opacity: 0.5 }}>Double</span> - Start timestamp in UTC seconds<br />
  <code>endTime</code> <span style={{ opacity: 0.5 }}>Double</span> - End timestamp in UTC seconds<br />
  <code>probability</code> <span style={{ opacity: 0.5 }}>Float</span> - Confidence score (0.0 to 1.0)<br />
  <code>source</code> <span style={{ opacity: 0.5 }}>EventSource</span> - Source of detection<br />
  <code>sessionId</code> <span style={{ opacity: 0.5 }}>UUID</span> - The session this event belongs to<br />
  <code>signature</code> <span style={{ opacity: 0.5 }}>FloatArray?</span> - Optional feature vector (for snoring events)
</div>

## Real-time breathing rate

The SDK provides real-time breathing rate measurements during analysis:

```kotlin theme={null}
import com.sleepcycle.sdk.BreathingRate

lifecycleScope.launch {
    SleepCycleSdk.breathingRateFlow.collect { breathingRate: BreathingRate ->
        Log.d("SDK", "Breathing rate: ${breathingRate.bpm} bpm (confidence: ${breathingRate.confidence})")
    }
}
```

Each `BreathingRate` contains:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>timestampSecondsUtc</code> <span style={{ opacity: 0.5 }}>Double</span> - Time of measurement in seconds since Unix epoch<br />
  <code>bpm</code> <span style={{ opacity: 0.5 }}>Float</span> - Breathing rate in breaths per minute<br />
  <code>confidence</code> <span style={{ opacity: 0.5 }}>Float</span> - Confidence level of the measurement (0.0 to 1.0)<br />
  <code>sessionId</code> <span style={{ opacity: 0.5 }}>UUID</span> - The session this measurement belongs to
</div>

### Real-time sleep staging (Experimental)

<Warning>
  This feature is experimental and may change in future releases. The API and behavior are subject to modification without notice.
</Warning>

The SDK can provide real-time sleep stage predictions during analysis. This feature requires the `realTimeSleepStaging` capability to be enabled for your API key.

```kotlin theme={null}
import com.sleepcycle.sdk.SleepStage
import com.sleepcycle.sdk.SleepStageInterval

lifecycleScope.launch {
    SleepCycleSdk.sleepStageFlow.collect { stageInterval: SleepStageInterval ->
        when (stageInterval.stage) {
            SleepStage.AWAKE -> Log.d("SDK", "Awake: ${stageInterval.interval}")
            SleepStage.LIGHT -> Log.d("SDK", "Light sleep: ${stageInterval.interval}")
            SleepStage.DEEP -> Log.d("SDK", "Deep sleep: ${stageInterval.interval}")
            SleepStage.REM -> Log.d("SDK", "REM sleep: ${stageInterval.interval}")
        }
    }
}
```

The flow emits `SleepStageInterval` objects approximately every 30 seconds during analysis, providing near real-time feedback on sleep state transitions.

### Real-time audio health

The SDK monitors the health of the audio input during analysis and emits status updates when the audio state changes:

```kotlin theme={null}
import com.sleepcycle.sdk.AudioHealthUpdate
import com.sleepcycle.sdk.AudioHealthStatus

lifecycleScope.launch {
    SleepCycleSdk.audioHealthFlow.collect { update: AudioHealthUpdate ->
        when (update.status) {
            AudioHealthStatus.HEALTHY -> Log.d("SDK", "Audio input healthy")
            AudioHealthStatus.FLATLINE -> Log.w("SDK", "Audio flatline detected")
            AudioHealthStatus.MISSING_INPUT -> Log.w("SDK", "Audio input missing")
        }
    }
}
```

`AudioHealthStatus` values:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>HEALTHY</code> - Audio input contains a varying signal<br />
  <code>FLATLINE</code> - Constant value detected (non-functional microphone or muted input)<br />
  <code>MISSING\_INPUT</code> - No audio input received for an extended period
</div>

## Smart alarm

The smart alarm monitors movement during a wake-up window and emits events when the user is in a light sleep phase, allowing you to wake them at an optimal moment. This requires the `smartAlarm` feature to be enabled for your API key and is only supported on the primary channel.

Configure a wake-up window with `SmartAlarmConfig` and pass it when starting analysis:

```kotlin theme={null}
import com.sleepcycle.sdk.SmartAlarmConfig

SleepCycleSdk.startAnalysis(
    config = SleepAnalysisConfig(useAudio = true, useAccelerometer = true),
    smartAlarmConfig = SmartAlarmConfig(
        wakeupWindowStartSecondsUtc = windowStartSecondsUtc,
        wakeupWindowEndSecondsUtc = windowEndSecondsUtc
    )
)
```

Observe alarm lifecycle events through `smartAlarmFlow`:

```kotlin theme={null}
import com.sleepcycle.sdk.SmartAlarmEvent

lifecycleScope.launch {
    SleepCycleSdk.smartAlarmFlow.collect { event: SmartAlarmEvent ->
        when (val type = event.type) {
            is SmartAlarmEvent.Type.Armed -> Log.d("SDK", "Smart alarm armed")
            is SmartAlarmEvent.Type.Triggered -> when (type.reason) {
                SmartAlarmEvent.Reason.OPTIMAL_WAKE_UP -> ringAlarm()
                SmartAlarmEvent.Reason.USER_INTERACTION -> ringAlarm()
                SmartAlarmEvent.Reason.WINDOW_END -> ringAlarm()
            }
        }
    }
}
```

The `smartAlarmConfig` parameter is also available on `startMultiChannelAnalysis()`.

## Event signatures

For snoring events, the `Event.signature` property contains a 16-dimensional feature vector that represents unique characteristics of the detected snore. Snore events from the same person are grouped close to each other in the signature space, allowing clustering of events by person.

## Audio event listener

The `AudioEventListener` interface allows you to receive real-time audio analysis updates during a session. Implement this interface to access raw audio samples, event detection, and volume information as analysis progresses.

```kotlin theme={null}
val audioEventListener = object : AudioEventListener {
    override fun onAudioAnalysisBatchCompleted(
        audioSamples: FloatArray,
        audioSampleRate: Int,
        audioStartTime: Double,
        audioEndTime: Double,
        audioProbability: Map<EventType, Float>,
        eventsStarted: List<EventStartedInfo>,
        eventsEnded: List<EventEndedInfo>,
        dbSpl: FloatArray,
        sessionId: UUID
    ) {
        // Process audio samples and events
    }
}

try {
    SleepCycleSdk.startAnalysis(
        config = SleepAnalysisConfig(useAudio = true),
        audioEventListeners = listOf(audioEventListener)
    )
} catch (e: Exception) {
    Log.e("SDK", "Failed to start analysis: ${e.message}")
}
```

The `audioSamples` parameter contains all processed audio data in sequence, without any gaps or overlap between batches. Each batch continues exactly where the previous batch ended, ensuring complete coverage of all analyzed audio.

The `audioProbability` parameter provides the per-batch probability for each `EventType` in the analyzed time window. The `dbSpl` parameter provides the A-weighted sound volume in dB SPL for each time frame in the batch.

## Audio clips

The SDK can capture short audio recordings when specific sleep events are detected, such as snoring, sleep talking, or coughing.

To use audio clips, create an audio clips producer and pass it to `startAnalysis`:

```kotlin theme={null}
import com.sleepcycle.sdk.*

// Configure which events trigger audio clips
val audioClipsConfig = AudioClipsConfig(
    activeTypes = hashMapOf(
        EventType.SNORING to EventTypeConfig(minDuration = 0.5),
        EventType.TALKING to EventTypeConfig(minDuration = 0.5)
    ),
    clipLength = 10.0  // Clip duration in seconds
)

// Implement receiver to handle captured clips
val audioClipsReceiver = object : AudioClipsReceiver {
    override fun onAudioClipReceived(audioClip: AudioClip) {
        // Process or store the audio clip
    }
}

try {
    // Create audio clips producer
    val audioClipsProducer = SleepCycleSdk.createAudioClipsProducer(
        config = audioClipsConfig,
        receiver = audioClipsReceiver
    )

    // Pass to startAnalysis
    SleepCycleSdk.startAnalysis(
        config = SleepAnalysisConfig(useAudio = true),
        audioEventListeners = listOf(audioClipsProducer)
    )
} catch (e: Exception) {
    Log.e("SDK", "Failed to start analysis with audio clips: ${e.message}")
}
```

Each `AudioClip` contains:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>startTime</code> <span style={{ opacity: 0.5 }}>Double</span> - Start timestamp in seconds<br />
  <code>type</code> <span style={{ opacity: 0.5 }}>EventType</span> - The event type that triggered the capture<br />
  <code>samples</code> <span style={{ opacity: 0.5 }}>FloatArray</span> - Raw audio samples<br />
  <code>sampleRate</code> <span style={{ opacity: 0.5 }}>Int</span> - Sample rate in Hz<br />
  <code>sessionId</code> <span style={{ opacity: 0.5 }}>UUID</span> - The session this clip belongs to
</div>

## Multi-channel analysis

The SDK supports analyzing two channels simultaneously using a stereo audio source. Multi-channel analysis separates the data source lifecycle from individual session lifecycles, allowing you to start and stop sessions on each channel independently.

The stereo stream is expected to come from two separate mono microphones combined into a single stereo stream, with one microphone per channel.

This requires the `multiChannelAnalysis` feature to be enabled for your API key.

### Channel separation

When using stereo input, `ChannelSeparationConfig` controls how audio events are assigned to channels. Built-in presets:

* `BED_SIDE_MICS` — bedside microphones placed apart (default)
* `CENTERED_MIC_ARRAY` — closely spaced microphone array
* `DETECTION_STRENGTH_ONLY` — no spatial filtering, uses only detection confidence

All parameters (mic distance, ambiguous zone, confidence threshold, per-event-type settings) can be tuned individually to fit your specific hardware setup and use case.

### Data source lifecycle

Start the data source with a stereo audio configuration before starting individual sessions:

```kotlin theme={null}
try {
    val dataSource = SleepCycleSdk.createLiveDataSource(
        audioFormat = DataSource.AudioFormat.STEREO
    )

    SleepCycleSdk.startDataSource(
        dataSource = dataSource,
        channelSeparationConfig = ChannelSeparationConfig.BED_SIDE_MICS
    )
} catch (e: Exception) {
    Log.e("SDK", "Failed to start data source: ${e.message}")
}
```

### Starting sessions on each channel

Once the data source is running, start a session on each channel:

```kotlin theme={null}
import com.sleepcycle.sdk.AnalysisChannel

try {
    val primarySessionId: UUID = SleepCycleSdk.startMultiChannelAnalysis(
        channel = AnalysisChannel.PRIMARY,
        config = SleepAnalysisConfig(useAudio = true, useAccelerometer = true)
    )

    val secondarySessionId: UUID = SleepCycleSdk.startMultiChannelAnalysis(
        channel = AnalysisChannel.SECONDARY,
        config = SleepAnalysisConfig(useAudio = true, useAccelerometer = false)
    )
} catch (e: Exception) {
    Log.e("SDK", "Failed to start multi-channel analysis: ${e.message}")
}
```

`AnalysisChannel` values:

<div style={{ background: 'rgba(128,128,128,0.04)', border: '1px solid rgba(128,128,128,0.15)', borderRadius: '0.5rem', padding: '0.75rem 1rem', fontSize: '0.8rem', lineHeight: '2.2' }}>
  <code>PRIMARY</code> - First audio channel (or mono)<br />
  <code>SECONDARY</code> - Second audio channel in stereo
</div>

### Stopping sessions independently

Each session can be stopped independently to retrieve its result:

```kotlin theme={null}
try {
    val primaryResult: AnalysisResult? = SleepCycleSdk.stopAnalysis(
        sessionId = primarySessionId
    )

    val secondaryResult: AnalysisResult? = SleepCycleSdk.stopAnalysis(
        sessionId = secondarySessionId
    )
} catch (e: Exception) {
    Log.e("SDK", "Failed to stop analysis: ${e.message}")
}
```

### Stopping the data source

After all sessions have been stopped, stop the data source:

```kotlin theme={null}
try {
    SleepCycleSdk.stopDataSource()
} catch (e: Exception) {
    Log.e("SDK", "Failed to stop data source: ${e.message}")
}
```

Calling `stopDataSource()` while sessions are still active will force-stop them and discard their results. To retrieve results, stop each session first.
