SDKAndroid

Android SDK lifecycle

When to create and shut down ExtentosGlasses, how the bundled foreground service keeps the glasses mic alive in the background, and how Extentos behaves across Activity lifecycle and process death.

ExtentosGlasses is a long-lived object with an explicit start and stop. You own its lifetime — it doesn't auto-register with the Android lifecycle, so where you create and release it is your decision.

Create once, shut down once

// Start: hold the handle for the life of the glasses session.
val glasses = ExtentosGlasses.create(config)

// Stop: release the transport and cancel the SDK's coroutine scope.
suspend fun tearDown() {
    glasses.shutdown()
}

shutdown() is a suspend function — call it from a coroutine. It shuts down the active transport (closing the DAT/Bluetooth or WebSocket connection) and cancels the internal SupervisorJob scope that drives every Flow the SDK emits. After shutdown(), the instance is spent; create a new one if you need to reconnect.

Where to anchor it:

  • App-scoped session (the common case — glasses connection persists across screens): create it in a holder tied to your application or a shared ViewModel, and call shutdown() when the user explicitly disconnects or the app is being torn down.

  • Screen-scoped session: create it in a screen-level ViewModel and call shutdown() from onCleared(). The rule is one live instance at a time, not one per process — this is safe only if exactly one screen owns it and the old one is shut down before the next is created. If two screens can be alive at once, use the app-scoped holder instead; two concurrent instances each run their own auto-bind probe and telemetry registration and will fight over the transport.

    class GlassesViewModel : ViewModel() {
        val glasses = ExtentosGlasses.create(config)
    
        override fun onCleared() {
            viewModelScope.launch { glasses.shutdown() }
        }
    }

Don't create a fresh ExtentosGlasses per Activity.onCreate if the same glasses session should outlive configuration changes — a ViewModel survives rotation; the Activity does not.

Activity and Fragment wiring

The SDK needs an Activity in two situations, both supplied through ExtentosConfig:

  • applicationContext — required when the transport resolves to RealMeta (Meta DAT initializes against a Context). Pass applicationContext, not an Activity, so the reference is safe to hold.
  • activityProvider: () -> Activity? — Meta registration (the pairing handoff) needs a foreground Activity. Provide a closure that returns your current top Activity (e.g. from an ActivityLifecycleCallbacks-tracked reference), so the SDK can launch registration when the user connects.
ExtentosConfig(
    applicationContext = applicationContext,
    activityProvider = { topActivityRef.get() },   // your current foreground Activity
)

Observe connection state from your UI through connection.state (a StateFlow<GlassesState>) — collect it lifecycle-aware with repeatOnLifecycle so you stop collecting when the screen is stopped. See Threading.

Background capture and the foreground service

Capability subscriptions (glasses.audio.*, the assistant) run on the SDK's coroutine scope, which is bound to your process — not to any Activity. While your app is foreground, that's enough. To keep the glasses mic alive while your app is backgrounded (a voice assistant or live transcription that must keep listening), start the library's bundled foreground service:

import com.extentos.glasses.core.service.GlassesForegroundService

// AFTER RECORD_AUDIO is granted at runtime — typically in the grant callback:
GlassesForegroundService.start(this)

// When background capture is no longer needed:
GlassesForegroundService.stop(this)

The service is declared for you in the library manifest (foregroundServiceType="microphone"), so you only start/stop it — see Manifest setup. Key rules:

  • Permission first. RECORD_AUDIO must already be granted. On Android 14+, starting the service without the grant throws SecurityException at service-create time and kills the app before the first frame draws.
  • Not from Application.onCreate. The grant can't exist that early. Start it from an Activity (or a ViewModel scoped to one) inside the permission-grant callback, or after a checkSelfPermission(...) == PERMISSION_GRANTED check.
  • Idempotent. Calling start again while running just updates the notification.
  • Camera-only or strictly foreground apps don't need it.

Process death

Extentos holds no implicit cross-process state to restore: an ExtentosGlasses instance and its transport connection do not survive the process being killed. After process death and relaunch, you re-run ExtentosGlasses.create(...) and reconnect — exactly as on first launch. The dev-loop pairing identity is persisted to filesDir, so an Auto-resolved dev session can re-bind without re-pairing, but the live glasses connection itself is re-established from scratch.

Because operations return ExtentosResult rather than throwing, a connection lost across a lifecycle gap surfaces as a typed failure on the next call — CaptureError/AudioError.NotConnected from a capability call, or a ConnectError (NoDeviceAvailable / TransportFailure) from connect()/reconnect() — pattern-match and re-establish rather than guarding with try/catch.