Sleep Cycle SDK

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

The SDK is published as two artifacts, built from the same source and exposing the same com.sleepcycle.sdk API. Depend on exactly one of them:

BundledSlim
Artifactcom.sleepcycle.sdk:sdk-androidcom.sleepcycle.sdk:sdk-android-slim
ML modelsEmbedded in the AARDownloaded on the first initialize()
Artifact sizeBaselineRoughly 15 MB smaller
First initialize()No downloadDownloads the models your granted features require
Network requiredOnly for authorizationFor authorization and the first download

Both artifacts are versioned and released together, and the API is identical, so switching between them is a one-line dependency change. Choose slim to keep your app download small and accept a one-time model download on first launch; choose bundled when the SDK must be usable without ever downloading anything beyond authorization.

Find the latest version on Maven Central: sdk-android and sdk-android-slim.

Groovy DSL

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

dependencies {
    // Bundled: models embedded in the AAR
    implementation "com.sleepcycle.sdk:sdk-android:<latest-version>"

    // Slim: models downloaded at runtime (use instead of the above, not alongside it)
    // implementation "com.sleepcycle.sdk:sdk-android-slim:<latest-version>"
}

Kotlin DSL

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

dependencies {
    // Bundled: models embedded in the AAR
    implementation("com.sleepcycle.sdk:sdk-android:<latest-version>")

    // Slim: models downloaded at runtime (use instead of the above, not alongside it)
    // implementation("com.sleepcycle.sdk:sdk-android-slim:<latest-version>")
}

Using the slim artifact

With the slim artifact, initialize() downloads the encrypted models required by the features your API key grants and does not return until they are all available locally. Only the required models are fetched, so the download is smaller when fewer features are enabled.

The downloaded models are stored in the app's internal files directory and reused on every later launch, so the download happens once per install (and again after an SDK upgrade that changes a model). They are removed when the app's data is cleared, when the app is uninstalled, or when you call clear().

Report progress to the user with the optional onDownloadProgress callback:

val features = SleepCycleSdk.initialize(
    context = applicationContext,
    apiKey = "your-api-key-here",
    onDownloadProgress = { progress ->
        // progress is a fraction in 0.0..1.0, reported from a background thread
        updateProgressBar(progress)
    }
)

The callback is invoked with 1.0 immediately when nothing needs to be downloaded, which is always the case for the bundled artifact and for a slim install that has already downloaded its models. The same code therefore works unchanged with both artifacts.

Provisioning failures surface as exceptions from initialize():

SdkResourceException - A required model could not be downloaded, for example when the device is offline on first launch
SdkNotEnoughDiskSpaceException - Not enough free space to store the required models

Both are recoverable: call initialize() again once the device is online or space has been freed. Downloads resume from a partial file, so a retry does not start over.

Prerequisites

Permissions

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

<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:

<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.

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}")
}

Parameters:

context Context - Android context; the application context is used internally
apiKey String - Your Sleep Cycle API key
logger Logger - Logger to use for SDK logs (defaults to logcat)
logLevel LogLevel - SDK log verbosity (defaults to INFO)
forceTokenRefresh Boolean - Force a fresh token fetch even if a cached token exists (defaults to false)
telemetry TelemetryConfig - Controls which telemetry the SDK sends (defaults to analytics off, error reporting on)
onDownloadProgress ((Float) -> Unit)? - Optional model download progress as a fraction in 0.0..1.0, reported from a background thread

With the slim artifact, initialize() also downloads the models your granted features require and does not return until they are available locally. See Using the slim artifact.

Telemetry

The telemetry parameter controls what the SDK reports back to Sleep Cycle:

analytics Boolean - Sleep session analytics events. Off by default
errorReporting Boolean - Diagnostic reports describing a technical fault. On by default

val features = SleepCycleSdk.initialize(
    context = applicationContext,
    apiKey = "your-api-key-here",
    telemetry = TelemetryConfig(analytics = true, errorReporting = true)
)

Each switch is independent. With analytics off, no analytics event is queued or sent. With errorReporting off, no diagnostic report is sent, while faults still reach the Logger you passed to initialize().

Analytics events show how the SDK performs across the devices your users run, allowing Sleep Cycle to identify an anomaly affecting a particular device model and tune the analysis for it — how a microphone variant shifts the sound level measurement, for example, or how it affects sleep staging and breathing rate detection. Leave analytics off where your privacy policy requires it; the SDK behaves identically either way.

An event carries no device or user identifier. It records the session it belongs to, when it occurred, the device model, the OS version and the SDK version, so a session cannot be tied to an installation or to any other session.

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

sleepStaging Boolean - Sleep staging analysis
smartAlarm Boolean - Smart alarm functionality
audioEvents Boolean - Audio event detection
snoringDetection Boolean - Snoring detection
realTimeSleepStaging Boolean - Real-time sleep staging
multiChannelAnalysis Boolean - Multi-channel analysis (stereo, two channels)
extendedAudioEvents Boolean - Extended audio event types beyond the standard set

Destroy and clear the SDK

destroy() releases the SDK's resources and returns it to the UNINITIALIZED state:

SleepCycleSdk.destroy()

It stops the data source but does not finalize or end active sessions, so a session interrupted by an app shutdown can be picked up again with resumeAnalysis(). Call stopAnalysis() or stopDataSource() first to end them, or clearActiveSessions() afterwards.

clear() goes further: it destroys the SDK and wipes every piece of persisted state — the cached authorization token, the stored content key, and any models downloaded by the slim artifact:

SleepCycleSdk.clear(applicationContext)

The next initialize() re-authorizes and, with the slim artifact, downloads the required models again. Models embedded in the bundled artifact are unaffected.

Get the SDK state

Monitor SDK state changes using the StateFlow:

import com.sleepcycle.sdk.SdkState

val sdkStateFlow: StateFlow<SdkState> = SleepCycleSdk.sdkStateFlow

Get the current state:

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.

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:

config SleepAnalysisConfig - Configuration object specifying which sensors to use
startMillisUtc Long - Analysis start time in UTC milliseconds (defaults to current time)
dataSource DataSource? - Optional custom data source. When null, the SDK uses live device sensors
audioEventListeners List<AudioEventListener> - Optional list of listeners that receive callbacks during audio analysis

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.

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

When a resumable session is not going to be resumed, end it and discard its results with clearActiveSessions(), which makes isResumePossible() return false again:

SleepCycleSdk.clearActiveSessions()

It throws IllegalStateException if the data source is running.

Stop a session

To stop an active analysis session and retrieve the results:

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:

sessionId UUID - Unique session identifier
startSecondsUtc Double - Session start time in UTC seconds
endSecondsUtc Double - Session end time in UTC seconds
timeZoneId String - IANA time zone (e.g. "Europe/Stockholm") captured at session start
events List<Event> - Detected sleep events
breathingRates List<BreathingRate> - Breathing rate measurements
sleepStageIntervals List<SleepStageInterval> - Sleep stage data
realTimeSleepStageIntervals List<SleepStageInterval> - Sleep stages emitted live during the session
statistics SleepStatistics? - Aggregated sleep statistics (nullable)
audioStatistics AudioStatistics? - Audio health statistics (nullable)

SleepStatistics

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

totalSleepDurationSeconds Double - Total time spent sleeping
sleepOnsetLatencySeconds Double? - Time to fall asleep
sleepEfficiency Double - Ratio of sleep to time in bed (0.0 to 1.0)
finalWakeTimeSecondsUtc Double? - Time of final awakening (UTC seconds)
numberOfAwakenings Int - Number of awakenings during the night
snoreTimeSeconds Double - Total time spent snoring
snoreSessions List<SnoreSession> - Individual snoring sessions
sleepStageDurationsSeconds Map<SleepStage, Double> - Duration per sleep stage

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.

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:

total Float - Overall sleep score for the night
duration Float - How much the user slept
quality Float - How well the user slept
routine Float - The user's sleep schedule

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:

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:

type EventType - The type of event
startTime Double - Start timestamp in UTC seconds
endTime Double - End timestamp in UTC seconds
probability Float - Confidence score (0.0 to 1.0)
source EventSource - Source of detection
sessionId UUID - The session this event belongs to
signature FloatArray? - Optional feature vector (for snoring events)

Real-time breathing rate

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

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:

timestampSecondsUtc Double - Time of measurement in seconds since Unix epoch
bpm Float - Breathing rate in breaths per minute
confidence Float - Confidence level of the measurement (0.0 to 1.0)
sessionId UUID - The session this measurement belongs to

Real-time sleep staging (Experimental)

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

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

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:

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:

HEALTHY - Audio input contains a varying signal
FLATLINE - Constant value detected (non-functional microphone or muted input)
MISSING_INPUT - No audio input received for an extended period

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:

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:

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()
            }
        }
    }
}

Change the alarm while the session is running with updateSmartAlarm(). It replaces the alarm the session started with, adds one to a session started without an alarm, or cancels it when passed null:

// Move the wake-up window
SleepCycleSdk.updateSmartAlarm(
    SmartAlarmConfig(
        wakeupWindowStartSecondsUtc = newWindowStartSecondsUtc,
        wakeupWindowEndSecondsUtc = newWindowEndSecondsUtc
    )
)

// Cancel the alarm
SleepCycleSdk.updateSmartAlarm(null)

The new configuration is persisted, so a session resumed after a process death continues with it, and an alarm that has already fired is re-armed by a new configuration so it can fire again. A wake-up window that is already past fires with WINDOW_END on the next analysis tick. The call targets the primary session and throws IllegalStateException if no session is active on that channel.

The smartAlarmConfig parameter is also available on startMultiChannelAnalysis(), which rejects it for any channel other than AnalysisChannel.PRIMARY.

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.

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:

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:

startTime Double - Start timestamp in seconds
type EventType - The event type that triggered the capture
samples FloatArray - Raw audio samples
sampleRate Int - Sample rate in Hz
sessionId UUID - The session this clip belongs to

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:

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:

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:

PRIMARY - First audio channel (or mono)
SECONDARY - Second audio channel in stereo

Stopping sessions independently

Each session can be stopped independently to retrieve its result:

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:

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.

On this page