Sessions
How an Extentos session works — the glasses connection-state machine, what persists across backgrounding and reconnects, and the three browser-simulator roles.
"Session" means two related things in Extentos, depending on the transport. On real glasses it's the connection between your app and the hardware, modeled by a six-state machine you observe through glasses.connection.state. In the browser simulator it's additionally a backend session that multiple parties join with distinct roles. This page covers both, plus what survives backgrounding and reconnects.
The connection-state machine
glasses.connection.state is a StateFlow<GlassesState> (Android) / ObservableState<GlassesState> (iOS). GlassesState is a sealed type with six buckets, advancing roughly in order from "not set up" to "connected", with Disconnected reachable from anywhere:
NotRegistered ─▶ Registered ─▶ DeviceDiscovered ─▶ Connecting ─▶ Active
▲ │
└──────────────────── Disconnected(cause) ◀────────────────────┘
(reachable from any state)| State | Payload | Meaning |
|---|---|---|
NotRegistered | — | The install hasn't registered/authenticated. Show a pairing prompt. |
Registered | — | Registered; scanning for a known device. |
DeviceDiscovered | deviceId: String | A device was found; connection is about to begin. |
Connecting | deviceId: String | Handshake in progress. |
Active | state: ActiveState | Connected and usable — capability calls (camera, audio, …) work here. |
Disconnected | cause: DisconnectCause | The connection ended. cause carries why (user intent, thermal, hinges, transport failure, or a simulator-session event). |
Active is the "transport is up" state — necessary for using the glasses, but not sufficient, and not a signal that glasses are present. On the vendorless audio baseline it is reported whenever audio is routable — which the phone's own mic and speaker always satisfy — so it does not by itself tell you a camera exists. Gate on state == Active and the capability: glasses.capabilities.camera. See choose your path. The transitions are driven by the transport — your code observes; it doesn't drive the machine by hand (you nudge it only via connect() / disconnect() / reconnect()).
Disconnected.cause is the actionable part — it distinguishes "the user took the glasses off" from "Bluetooth dropped" from "the simulator session expired," so you can choose the right recovery. The full branch-on-cause pattern, the retry policy, and the reconnect call live in Handle disconnects; the complete DisconnectCause and ConnectError payload shapes are in the error reference.
You rarely render this machine yourself.
ExtentosConnectionPagerenders the canonical pairing → connecting → connected → disconnected flow from this exact state. Observeglasses.connection.statedirectly only for custom in-app affordances (e.g. disabling a button while notActive).
What persists
The ExtentosGlasses instance is the durable object — construct one at app init (in your Application subclass / App) and hold it for the app's lifetime. It owns the transport, the auto-bind probe, the connection state, registration, and telemetry hooks. Constructing more than one is a bug: there's no caching, and each fires its own auto-bind probe and telemetry registration.
- Across reconnects:
reconnect()re-runs registration and re-fetches device metadata (name, firmware, link state), then the state flips back toActive. Toggle state (glasses.toggles) and your registered voice phrases / assistant configuration are held by the library across the drop — you don't re-register them. - Across app backgrounding: whether the connection survives backgrounding depends on platform plumbing — an Android foreground service or iOS background mode. That setup is its own topic: see Background sessions. Without it, the OS may tear the connection down when your app leaves the foreground, surfacing as a
Disconnected. - What does NOT persist on the device: the event log is a 512-entry ring buffer — it's mirrored to the backend during simulator mode but is not durable storage on the glasses. Assistant conversation history is an in-memory replay buffer (replayed to the provider on reconnect), not disk-persisted by the SDK; if you need it across app restarts, you persist it yourself.
Simulator sessions and roles
The browser simulator adds a second sense of "session": a backend session, identified by a sessionId, that more than one party joins. Where a real-glasses session is a 1:1 link between your app and hardware, a simulator session is a small hub with three roles (transport vs app simulation):
| Role | Who | What it does |
|---|---|---|
app | Your running app (via BrowserSimTransport) | The code under test — exactly what runs in production, only the transport swapped. |
browser | The hardware surrogate at extentos.com/s/{sessionId} | Drives real webcam frames, microphone audio, speech-to-text, and TTS playback; injects hardware alerts. Stands in for the glasses. |
observer | Read-only spectators | Watch the session both directions without participating. Capped at 8 per session. |
The app and browser roles are singletons per session (one each); observers are the only multi-party role.
hardware_ready gating
Because the surrogate (the browser role) is the simulated hardware, a simulator session isn't usable until that surrogate has joined and acknowledged. The backend tracks a hardware_ready flag and only flips it true once the browser surrogate is present and ack'd; it then fans hardware_ready out to the app and any observers. Until then, the app side behaves as "hardware not present" — the analogue of Connecting on real glasses. This is what makes the simulator faithful: there's no app-layer behavior available before the (simulated) hardware is ready, exactly as on glasses.
Simulator sessions also carry per-device facts the app reacts to — for example display_capable is true only on display-capable device models (Ray-Ban Display), so a display feature lights up in the simulator only against the right device. When a simulator session ends (idle timeout, lifetime cap, user-ended, backend restart), the app sees Disconnected(SimulatorSessionExpired(reason, …)) — a simulator-only DisconnectCause you won't encounter on hardware.
The same code on both
The point of the connection-state machine is that it's transport-independent: your handler code observes glasses.connection.state and calls glasses.camera.capturePhoto() identically whether the transport underneath is BrowserSimTransport, LocalSimTransport, or RealMetaTransport. The simulator is designed to behave like the glasses, and that fidelity is under active validation on hardware — so a session that's green in the simulator is strong evidence — the final gate is a real-hardware run. The substrate deltas (transport, audio format, STT/TTS provider) are contained at the transport layer; the session model your app sees is the same on both. See Transport vs app simulation for exactly what differs.
Related
- Handle disconnects — branching on
Disconnected.cause, reconnect, and retry policy. - Background sessions — keeping the connection alive when backgrounded.
- Transport vs app simulation — the transports and what the simulator simulates.
- Error reference —
GlassesState,DisconnectCause, andConnectErrorpayloads.
Related
Handle disconnects
Observe glasses.connection.state, branch on the typed Disconnected.cause to tell a user-pulled-the-glasses-off from a Bluetooth drop, and reconnect cleanly. The connection-state spine for any Meta Ray-Ban app.
Background sessions
Keep a Meta Ray-Ban session alive when your app backgrounds — the Android foreground service for mic, iOS background modes, and which capabilities survive.
Transport vs app simulation
Meta's Mock Device Kit simulates the transport layer; Extentos simulates the app layer — voice, photo capture, and the wearing experience. Both matter.
Error reference
Every typed error the Extentos SDK can return — ConnectError, CaptureError, AudioError, TransportError, the ExtentosError umbrella, and the Meta-DAT DeviceSessionError — with their payload fields and meaning. Lifecycle operations return ExtentosResult<T, E> with these concrete failure variants rather than throwing; pattern-match them. Generated from the Rust core.
The display capability
Render UI on the Ray-Ban Display with the glasses.display builder DSL in Kotlin and Swift — text, images, buttons, media, and Neural Band input. Gated per device. Beta.
Permissions
Extentos derives Android permissions and iOS Info.plist keys from the capabilities your handler uses, plus the always-required Bluetooth and Meta DAT keys.