How Apple, Android, Meta and Snap each expect a head-worn app to listen — the interaction timelines, the platform constraints, and the certification clauses that decide whether it ships.
Head-mounted speech guidance is thinner than mobile speech guidance on every one of these four platforms. Where a vendor publishes no HMD-specific rule, this report falls back to the comparable phone OS and says so in the cell. Claims carry one of two markers: doc means a named vendor page states it, est. means it is inferred from the mobile equivalent, from a deprecation notice, or from developer reports rather than from a published requirement.
Refresh this report whenever any of these land: a visionOS or iOS major release, an Android XR platform release or Google Play policy change, a Meta Connect announcement or a Wearables Device Access Toolkit move out of beta, a Snap OS release or the 2026 Specs consumer launch, or any revision of the App Review Guidelines, Play Developer Program Policy, Horizon VRC list or Snap submission guidelines. Every est. cell is a candidate to be replaced with a citation, or corrected. The basis tag on each column says which of those triggers is nearest.
Part 1
Two sequences per platform: a discrete command the user fires once, and a continuous listening state the user switches on. Each diagram splits the User, the Hardware/OS, and the App/Engine so you can see exactly which layer owns the microphone indicator and which layer ends the session.
Each tab has one horizontal scrollbar shared by both of its diagrams, so they move together — they are drawn at full width rather than squeezed to fit. Solid arrows into the “what the user sees” lane are changes to the display; dashed arrows back to the User are what they hear. Both lanes are written as the user would describe them rather than as widgets to build. Read the Note bands as constraints rather than steps — they mark the point where the platform, not the app, decides what happens. The recurring pattern across all four: the app owns activation and feedback, the OS owns the indicator, and nobody lets a third-party app own the wake word.
Every state below changes something the user can see and something the user can hear. Both columns are written as the user would describe them, not as components to build — a meter that tracks amplitude is one way to do the listening row, but what the row actually asks for is that the person can tell the app is live rather than frozen. No platform requires both channels, and no two platforms name the same one, so designing to the mandate alone produces an app that feels different on each device. Design the pair, then drop whichever channel a given device cannot carry.
| State | What the user sees | What the user hears |
|---|---|---|
| idle | Nothing new. The app looks the way it did before they thought about speaking. | Nothing. |
| armed | Whatever they just acted on changes, and the app names what it is listening for. | A short tone as the microphone opens. |
| listening | Something responds to the room, so they can tell the app is live rather than frozen or crashed. | Nothing. The quiet is what tells them it is their turn. |
| hearing | Their own words appear as they say them, and look unfinished, so they know the app may still change its mind. | Nothing. The app does not speak over the person speaking to it. |
| interpreting | The words stop changing and settle. The app shows that it is working on them. | A short tone as the microphone closes, so they know they can stop talking. |
| confirmed | The thing the words acted on is pointed out before it changes, so they can see what moved and what did not. | The app says back what it did, in one short sentence. |
| muted | The app says it is not listening, and says who stopped it — them, or the system. | The same closing tone as before. Never the sound that means it worked. |
| failed | The app says what went wrong in plain words, with the way to try again next to it. | A sound clearly unlike the successful one, and the reason spoken aloud if the user has no display to read. |
Two of these are easy to skip and cost the most when they are missing. listening has to look different from idle even though nothing has been said yet — otherwise the user cannot tell a live app from a hung one, and starts repeating themselves. muted has to be unmistakable from confirmed even though both end with the microphone closed — otherwise they walk away believing something happened.
| Platform | The channel the vendor names | What that leaves you |
|---|---|---|
| Apple | Either. 2.5.14 asks for “a clear visual and/or audible indication when recording”.doc | Both are individually optional, so review pressure alone will not make you build the pair. The OS indicator satisfies nothing on its own — it reports capture, not app state. |
| Android | Visual, and the OS draws it for you: the mic indicator plus the foreground-service notification.doc | No audible requirement at all. Every earcon is yours, which means nothing stops the app shipping silent — and a glanceable device is exactly where silence hurts. |
| Meta | Audible. “Ensure transparent activation” through earcons or conversational cues.doc | The visual state is entirely your design. On Ray-Ban Meta there is no display, so the audible channel is the only one there is. |
| Snap | Visual. Published lenses must “display visuals indicating required actions upon activation”.doc | Audio playback during mic capture is prohibited, so the audible channel is structurally unavailable until the mic closes. Every state change up to interpreting has to be carried visually.doc |
visionOS 26 inherits the iOS Speech framework wholesale. SpeechAnalyzer and SpeechTranscriber ship on iOS, iPadOS, macOS, visionOS and tvOS 26 and run on-device; SFSpeechRecognizer remains the legacy path. There is no visionOS-only speech API, so the mobile rules are the rules.
A · Discrete speech command — pinch-and-hold to talk
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant A as App · SwiftUI/RealityKit
participant OS as visionOS · Speech + AVFAudio
participant I as App Intents
U->>A: Pinch and hold the Ask control
A->>OS: AVAudioSession playAndRecord with duckOthers, then requestRecordPermission
A->>UI: idle to armed, the control changes and names what it is listening for
A-->>U: A short tone as the mic opens
OS-->>U: System mic indicator lights, app cannot suppress it
A->>OS: SpeechTranscriber attached to a SpeechAnalyzer input sequence
A->>UI: armed to listening, something moves with the room so it reads as live
OS-->>A: Volatile results, on-device, no network egress
A->>UI: listening to hearing, their words appear as they say them, still unsettled
U->>A: Release the pinch
A->>OS: analyzer.finalizeAndFinish
A->>UI: hearing to interpreting, the words stop changing and settle
A-->>U: A short tone as the mic closes, so they know to stop talking
A->>I: Resolve to an AppIntent and perform it
I-->>A: Intent result
A->>UI: interpreting to confirmed, the thing that changed is pointed out first
A-->>U: The app says back what it did, in one short sentence
A->>OS: setActive false, notifyOthersOnDeactivation
OS-->>U: Indicator clears, ducked audio restores
A->>UI: confirmed to idle, the control returns to how it started
Note over A,OS: 2.5.14 accepts a visual and or audible indication,
so either channel alone passes review. Pairing them
is a design decision, not a compliance one
B · User-enabled continuous speech — live dictation toggle
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant A as App
participant OS as visionOS · Speech + AVFAudio
U->>A: Turn Live dictation on
A-->>U: Consent sheet naming what is captured and where it goes
A->>OS: AVAudioSession playAndRecord plus duckOthers
A->>UI: idle to armed, a place for the words opens and stays open
A-->>U: A short tone as the mic opens, once for the session, not once a sentence
OS-->>U: Mic indicator stays lit until they turn it off
OS-->>A: Other media ducks
A->>UI: armed to listening, something moves with the room so it reads as live
loop While it is on and the app is in front of them
OS-->>A: Volatile results, low latency, may still change
A->>UI: listening to hearing, their words appear as they say them
A-->>U: Nothing. The app does not speak over them
OS-->>A: Finalized result at the sentence boundary
A->>UI: hearing to listening, the sentence settles and stops changing
A-->>U: A quiet tick as the sentence settles, switchable off
end
U->>A: Turn it off, reachable in one action at any time
A->>OS: finalizeAndFinish then setActive false
A->>UI: listening to idle, the words area closes
A-->>U: A short tone as the mic closes
OS-->>U: Indicator clears
Android XR publishes a real glasses ASR guide — SpeechRecognizer.createOnDeviceSpeechRecognizer() inside the projected activity — but it is silent on timeouts, indicators and ducking, so those rows fall back to Android mobile, where they are strictly specified.
A · Discrete speech command — touchpad hold or system wake word
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant HW as Glasses · touchpad and mics
participant A as App activity
participant OS as Android XR
participant G as Gemini
U->>HW: Touch and hold the touchpad, or speak the system wake word
HW->>OS: Assistant activation
OS-->>U: System mic privacy indicator in the status area
alt App-scoped recognition
A->>OS: checkSelfPermission RECORD_AUDIO
A->>OS: createOnDeviceSpeechRecognizer in onCreate, then startListening
A->>UI: idle to armed, a small area opens and names what it wants to hear
A-->>U: A short tone as the mic opens
OS-->>A: onReadyForSpeech then onBeginningOfSpeech
OS-->>A: onPartialResults
A->>UI: armed to hearing, their words appear as they say them
OS-->>A: onResults with RESULTS_RECOGNITION and CONFIDENCE_SCORES
A->>UI: hearing to interpreting, the words settle and the app shows it is working
OS-->>A: onEndOfSpeech, the recognizer stops itself
else System-routed to the assistant
OS->>G: Utterance
G->>A: Function call into your declared app actions
A->>UI: idle to interpreting, the app shows what it was asked to do
end
A->>UI: interpreting to confirmed, the thing that changed is pointed out first
A-->>U: The app says back what it did, in one short sentence
A->>UI: confirmed to idle, the area closes
Note over OS,A: One shot only. A single startListening ends at
onEndOfSpeech. Continuity is the app restarting it
Note over U,UI: Android mandates the visual channel at OS level and
mandates no audible one at all, so everything the user
hears here is yours to design and to keep consistent
B · User-enabled continuous speech — live captions or dictation
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant A as App
participant OS as Android XR
U->>A: Turn on continuous dictation
A->>OS: Request RECORD_AUDIO if not already held
OS-->>U: System mic indicator appears and stays up
A->>UI: idle to listening, a place for the words opens and stays open
A-->>U: A short tone as the mic opens, once for the session
loop While it is on and the app is in front of them
OS-->>A: onPartialResults
A->>UI: listening to hearing, their words appear as they say them
OS-->>A: onEndOfSpeech after silence
A->>UI: hearing to listening, the line settles and stops changing
A->>OS: startListening again to keep the session alive
Note over A,UI: The restart has to be invisible. The user never sees
a gap between one sentence and the next
end
U->>OS: May mute from the system mic control at any time
OS-->>A: onError, capture stops
A->>UI: any state to muted, the app says it stopped and says who stopped it
A-->>U: A tone that is clearly not the one that means success
U->>A: Turn it off
A->>OS: stopListening then destroy
A->>UI: muted or listening to idle, the words area closes
A-->>U: A short tone as the mic closes
Note over A,OS: Foreground only. Carrying this session on with the app
behind something else needs a microphone foreground
service and its own recovery path, not drawn here
Meta is the only vendor that publishes hard numeric listening limits: Voice SDK stops after two seconds of silence or twenty seconds total. Ray-Ban Meta is a different world again — the Wearables Device Access Toolkit gives you HFP or beta in-stream PCM, and nothing else.
A · Discrete speech command — app-supplied activation into Wit.ai
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant A as App · Meta XR Voice SDK
participant OS as Meta Horizon OS
participant W as Wit.ai NLU
Note over U,A: No third-party wake word exists. The app must
design its own activation event as the wakeup call
U->>A: Press the control, or make the hand gesture the app defines
A->>OS: RECORD_AUDIO consent, prompted at app launch
OS-->>U: System microphone state reflects capture
A->>A: AppVoiceExperience.Activate
A->>UI: idle to armed, the control changes and names what it wants to hear
A-->>U: A short tone as the mic opens, the cue Meta names explicitly
Note over A: Listening begins once audio passes a volume threshold
A->>UI: armed to listening, something moves with the room so it reads as live
A->>W: Stream captured audio for transcription and NLU
W-->>A: Partial transcript
A->>UI: listening to hearing, their words appear as they say them
W-->>A: Final transcript, intent and entities
A->>A: Conduit binds the intent to a method
A->>UI: hearing to interpreting, the words settle beside the option they matched
A-->>U: A short tone as the mic closes, then the app says back what it understood
U->>A: Confirm, or correct it before anything happens
A->>UI: interpreting to confirmed, the thing that changed is pointed out first
A-->>U: The app says what it did, in one short sentence
A->>UI: confirmed to idle, the control returns to how it started
Note over A: Recording auto-stops after 2 s of silence or 20 s of
total capture, whichever comes first
Note over U,UI: Meta names the audible channel and leaves everything the
user sees to the app. Snap requires visuals on activation and
bars audio during capture, so one shared design must carry
both channels and let each platform drop the one it cannot
B · User-enabled continuous speech — held-open mic, with the Ray-Ban fallback
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant A as App · Voice SDK
participant OS as Horizon OS
participant W as Wit.ai
participant RB as Ray-Ban Meta path
U->>A: Turn on continuous listening in app settings
A-->>U: Explain the capture, offer a mute that is always one action away
A->>OS: Hold the mic open while the app is in front of them
A->>UI: idle to listening, a standing sign that the app is listening
A-->>U: A short tone as the mic opens, once for the session
loop While it is on and the app is in front of them
A->>W: Stream audio
W-->>A: Partial then final transcripts
A->>UI: listening to hearing, their words appear as they say them
A->>A: Re-activate before the 20 s cap expires
A->>UI: hearing to listening, the sentence settles
A-->>U: Nothing at the seam. No tone, no gap
end
Note over A,OS: The 20 s cap is per activation, so a continuous session is
the app stitching activations together. The user must not
be able to see or hear the seam
U->>A: Mute
A->>A: Deactivate, release the mic
A->>UI: listening to muted, the sign says it is not listening and who stopped it
A-->>U: A short tone as the mic closes
Note over RB: Ray-Ban Meta has no display, so there is nothing for the
user to see and everything has to be heard. The toolkit
gives HFP with beamformed voice or beta in-stream PCM at
16, 44.1 or 48 kHz. No raw mic streams, no custom wake
words, no access to the Hey Meta hot-word detector
Snap split its stack: VoiceMLModule still serves Snapchat lenses, but it is no longer supported on Spectacles, which use AsrModule instead. The two have different language coverage, different callbacks and different failure modes, so a lens that targets both needs two code paths.
A · Discrete speech command — AsrModule on Spectacles
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant L as Lens · Lens Studio
participant OS as Snap OS
participant C as Snap speech service
Note over L,OS: Mic permission is declared automatically by using
AsrModule, VoiceMLModule or DialogModule
U->>L: Look at the thing and speak, or press to talk
L->>OS: require LensStudio AsrModule
OS-->>U: Snap OS reflects that the mic is capturing
L->>UI: idle to armed, the lens names what it is listening for
L->>OS: startTranscribing with mode HighSpeed and silenceUntilTerminationMs
L->>UI: armed to listening, something moves so it reads as live, not frozen
OS->>C: Audio
C-->>L: onTranscriptionUpdateEvent, isFinal false
L->>UI: listening to hearing, their words appear as they say them
C-->>L: onTranscriptionUpdateEvent, isFinal true after the silence window
L->>UI: hearing to interpreting, the words settle and stop changing
L->>L: Match the transcript to a lens action
L->>OS: stopTranscribing
L->>UI: interpreting to confirmed, the thing they spoke to reacts visibly
L-->>U: A short tone, the first sound the lens is allowed to make
L->>UI: confirmed to idle, the prompt clears
Note over C: Error codes include NoInternet and Unauthenticated, so
being offline has to be something the user can see
Note over U,UI: Snap requires visuals naming the required actions on
activation and forbids audio playback during capture, so
nothing can be heard until the mic closes
B · User-enabled continuous speech — rolling transcription
sequenceDiagram
autonumber
actor U as User
participant UI as What the user sees
participant L as Lens
participant OS as Snap OS
participant C as Snap speech service
U->>L: Turn on continuous transcription in the lens
L->>UI: idle to listening, a standing sign that the lens is listening
L->>OS: startTranscribing with mode HighAccuracy
loop While it is on and the lens is running
C-->>L: Rolling partial transcript
L->>UI: listening to hearing, their words appear as they say them
Note over C,L: silenceUntilTerminationMs marks a fragment final
L->>UI: hearing to listening, the fragment settles and stops changing
end
Note over L,UI: Audio playback during mic capture is prohibited, so for the
whole session there is nothing to hear. Every state change
has to be carried by what the user can see
U->>L: Turn it off
L->>OS: stopTranscribing
L->>UI: listening to idle, the words area clears
L-->>U: A short tone as the mic closes, now that it is allowed
Note over L,OS: A published lens may not combine sensitive user data with
external connectivity. Remote Service Gateway is the
documented exception
Part 2 · Table A
What you build with, and what the platform expects the user to see while you are listening.
| Concept | Apple · visionOSdocumented · stableReview rules are long settled. The speech API is one release old, and visionOS adds none of its own. | Android XRdocumented · thinPlatform is shipping, but the XR speech pages say nothing about indicators, timeouts or ducking — those cells rest on the mobile fallback. | Meta · Horizon OSpart betaQuest Voice SDK is mature. The Ray-Ban Wearables toolkit is beta, and its in-stream audio capture is development and beta channels only. | Snap · Snap OShigh churnVoiceML is already retired on Spectacles in favour of AsrModule, and consumer Specs arrive on Snap OS 2.0. |
|---|---|---|---|---|
| Primary native APIs | SpeechAnalyzer + SpeechTranscriber in the Speech framework, on iOS, iPadOS, macOS, visionOS and tvOS 26, running on-device. SFSpeechRecognizer is the legacy path. Intent routing through App Intents.doc |
SpeechRecognizer.createOnDeviceSpeechRecognizer() with RecognitionListener and RecognizerIntent, instantiated in the projected activity's onCreate(). Gemini Live API with function calling for app-wide voice navigation. ML Kit GenAI speech recognition where a Gemini Nano class model is present.doc |
Meta XR Voice SDK, backed by the Wit.ai NLU service, for Unity and Unreal. AppVoiceExperience.Activate() to start capture; Conduit binds a resolved intent to a method. Ray-Ban Meta uses the separate Wearables Device Access Toolkit.doc |
AsrModule on Spectacles — require('LensStudio:AsrModule'), startTranscribing() / stopTranscribing(), 40+ languages, Lens Studio 5.9.0+ and Snap OS 5.61+. VoiceMLModule for Snapchat lenses only; its capabilities are no longer supported on Spectacles.doc |
| Wake word architecture | System-level only. “Siri” is the wake word and it belongs to the OS; a third-party app cannot register its own. Apps reach the assistant by adopting App Intents, and 2.5.11 restricts you to intents your app can actually fulfil.doc A custom in-app hot word would need your own always-on recognizer, which the recording rules make hostile.est. | System-level. The touchpad hold and the system wake word both route to Gemini. No documented API for a third-party wake word, so a custom hot word means running your own detector inside a microphone foreground service with its persistent notification.est. | Explicitly unavailable. Meta's own Voice SDK tutorial tells you to design an activation event as the app's wakeup call.doc That “Hey Meta” is an always-on hardware detector Meta has not opened to third-party SDKs, and that the hardware takes no custom wake word, comes from a developer thread on Meta's toolkit repo rather than from a published rule.est. | No third-party wake word. Snap ships a fixed voice-command vocabulary for capture — “Take a Snap”, “Start Recording”, “Stop Recording” — and lens-level navigation intents through the VOICE_ENABLED_UI model. A lens starts listening when the lens decides to.doc |
| Activation methods | Pinch or tap on a visible affordance; gaze-plus-pinch on a control; Siri; Voice Control for gesture-equivalent commands; dictation from any text field, with dictation, spelling and command modes.doc | Touch-and-hold on the glasses touchpad launches Gemini; system wake word; in-app button or gesture; physical buttons for camera and display. Hands, eyes, voice, keyboard, mouse, trackpad and 6DoF controllers are all first-class inputs.doc | App-supplied only: a UI button, a controller press, a hand gesture, or any event the app defines. Voice SDK begins recording once captured audio passes a volume threshold after activation.doc | Lens-initiated startTranscribing() or startListening(); hand tracking and voice are the two Snap OS input modes. Press-to-talk is an app-level pattern, not something the platform provides.est. |
| App speech customization | contextualStrings on SFSpeechRecognitionRequest — a phrase list the recognizer is biased toward. No grammar, no custom model training.doc In the iOS 26 Speech framework the lever moved and did not fully survive: DictationTranscriber takes AnalysisContext.contextualStrings, but the long-form SpeechTranscriber does not, so on the new API you can have biasing or long-form transcription, not both.est. |
EXTRA_BIASING_STRINGS — “Optional list of strings, towards which the recognizer should bias the recognition results.” Alongside it, EXTRA_ENABLE_BIASING_DEVICE_CONTEXT biases toward what is already on the device, and is documented to “have no effect” depending on the recognizer implementation.doc No grammar. Whether the hint list is honoured is the recognizer’s choice too, so the same strings can work on one device and do nothing on the next.est. |
Keywords, synonyms and trained intents. Dynamic entities ship keywords and synonyms with each individual request and “bias the model towards values that the system knows are acceptable values”; Meta names an end user’s contact list as the example. Underneath that you train intents and entities in the Wit.ai app and can hand-add synonyms.doc | Snapchat lenses and Spectacles behave differently. Snapchat lenses bias the transcription — options.addSpeechContext(phrases, boost), boost 1–10, documented for “words which are rarer and aren’t picked up well enough”. Keyword groups (addKeywordGroup) only match against the transcript afterwards, so they never improve it. Spectacles get nothing: AsrTranscriptionOptions carries four properties — mode, silence timeout and two callbacks — and no vocabulary, hint or grammar of any kind.doc |
| Uncommon people names | Load the names before you ask. contextualStrings takes the roster you expect, and SiriKit adds INVocabulary for user-specific terms including contact names, registered as an ordered set with the most-used first.doc A name you did not supply comes back as the nearest ordinary word, and nothing in the result says a substitution happened.est. |
Supply the names as hint strings, and switch on device-context biasing, which is the nearest thing Android offers to “use the contacts on this device”.doc Because a recognizer may honour either, both or neither, the same name can resolve on one headset and fail on the next with nothing in the result to distinguish the two cases. CONFIDENCE_SCORES is the only signal you can gate on.est. |
Dynamic entities are supplied per request rather than fixed at build time, so push the roster that applies at that moment — the people in this call, this match, this room — and the model biases to it for that utterance.doc A name outside the pushed set falls back to the general model and is transcribed as something ordinary.est. | Accented, apostrophed and hyphenated names cannot be boosted at all: Snap requires that boosted phrases “should be made of lowercase a-z letters”, so the names most likely to be mis-transcribed are the ones the API will not accept. O’Brien, Nguyễn and Müller are unreachable.doc On Spectacles there is no lever at all, so an uncommon name is whatever the 40+ language model decides it heard.doc |
| Required app UI feedback | Guideline 2.5.14 requires “a clear visual and/or audible indication when recording” — this is a review requirement, not a suggestion, and it sits on top of the OS indicator. Practically: a lit listening state, streaming volatile results, and a distinct finalized state.doc | Design guidance for glasses asks for glanceable, contextual, minimally intrusive surfaces and subtitles for people who are deaf or hard of hearing. Live partial transcription is the natural fit; there is no specific waveform mandate.doc Read the mandate as the mobile prominent-disclosure rule plus the foreground-service notification.est. | “Ensure transparent activation” — give clear mic activation awareness through earcons or conversational cues, and confirm the user's input and the system's understanding of the selected option before acting.doc | Lenses published to Lens Explorer must “display visuals indicating required actions upon activation” and run at 60 fps — so a voice lens has to show that it is listening and what it expects you to say.doc |
| Multimodal fallback | 5.1.1 (iv): respect the permission setting and “where possible, provide alternative solutions for users who don't grant consent.” A voice-only flow with no keyboard or pinch equivalent fails this directly.doc | “Give users control” — let users tailor the experience and remap controller buttons and gestures to fit their physical needs. Voice is one modality among many rather than the entry point.doc | Multimodal interaction is a stated design principle: let users switch modality mid-interaction, for example from voice to text, and provide alternative input methods when an interaction errors.doc | Not stated as a requirement. Snap OS itself is hand-tracking plus voice, so hand input is the de facto fallback and a voice-only lens is a review risk rather than a documented rejection.est. |
Part 2 · Table B
What the platform does to you whether you want it or not, and the clauses that get an app sent back.
| Constraint | Apple · visionOSdocumented · stableReview rules are long settled. The speech API is one release old, and visionOS adds none of its own. | Android XRdocumented · thinPlatform is shipping, but the XR speech pages say nothing about indicators, timeouts or ducking — those cells rest on the mobile fallback. | Meta · Horizon OSpart betaQuest Voice SDK is mature. The Ray-Ban Wearables toolkit is beta, and its in-stream audio capture is development and beta channels only. | Snap · Snap OShigh churnVoiceML is already retired on Spectacles in favour of AsrModule, and consumer Specs arrive on Snap OS 2.0. |
|---|---|---|---|---|
| Mandatory privacy indicators | The OS draws a microphone indicator whenever an app uses the mic — visible in the app, in another app, or at the Home Screen — and the app cannot suppress it.doc visionOS inherits this behaviour; Apple documents the indicator for iOS and iPadOS rather than for visionOS specifically.est. | The system microphone indicator and the global mic toggle are platform behaviour on Android 12 and later, and a microphone foreground service additionally shows a persistent notification.doc Android XR docs do not restate the indicator for glasses.est. | The Horizon OS permission dialog appears at app launch and microphone appears in the app's listed permissions.doc Meta's voice design guidance asks for app-drawn awareness through earcons rather than promising an OS chrome indicator, so budget for your own.est. | Snap documents that the microphone captures voice data for recorded videos and voice commands and that camera and mic access are essential to the device functioning, but does not specify a capture indicator for lens-initiated ASR.est. |
| Continuous listening limits | No published time cap on the API. The practical limit is foreground lifetime: without an audio background mode, capture stops when the app is no longer foreground, and background capture reads as surreptitious recording under 2.5.14.est. | A single startListening() is one utterance and ends at onEndOfSpeech; continuity is your loop.doc Background capture requires a microphone foreground service, which cannot be started from the background or from a BOOT_COMPLETED receiver, and RECORD_AUDIO is while-in-use restricted.doc |
Voice SDK publishes explicit numeric limits: recording stops after 2 seconds of silence or 20 seconds of total recording, whichever comes first, so a continuous session is stitched activations.doc | silenceUntilTerminationMs finalizes a fragment after a configured silence window rather than ending the session; you choose the value.doc VoiceML notes that muting the mic for more than two minutes requires a preview reset.doc No documented session cap on Spectacles.est. |
| Audio session conflicts | AVAudioSession arbitrates: playAndRecord to capture while playing, with duckOthers or mixWithOthers to decide what happens to other media, and notifyOthersOnDeactivation to hand playback back cleanly.doc |
Standard Android audio focus and stream routing; on Ray-Ban-style audio glasses the system routes to the wearable's mics. No XR-specific ducking contract is published.est. | On Quest the app manages its own mic and playback. On Ray-Ban Meta, HFP mode beamforms the wearer's voice and narrows the return audio path, while beta in-stream capture delivers uncompressed PCM at 16, 44.1 or 48 kHz alongside video without dropping A2DP output quality.doc | Hard constraint: audio playback during microphone capture is prohibited. Duck or suspend the lens's own audio rather than mixing it under capture.doc |
| Permissions and strings | NSMicrophoneUsageDescription plus NSSpeechRecognitionUsageDescription — recognition is a second prompt on top of the mic prompt. 5.1.1 (ii) requires purpose strings that “clearly and completely describe your use of the data” and an accessible way to withdraw consent.doc |
android.permission.RECORD_AUDIO as a runtime grant, required before instantiating SpeechRecognizer. Continuous capture adds FOREGROUND_SERVICE_MICROPHONE and a manifest foregroundServiceType="microphone". Play requires a completed Data safety form and prominent disclosure before the prompt.doc |
android.permission.RECORD_AUDIO is a review-required permission: “Permitted uses include VOIP or other features that need microphone access,” and you must justify the use case to app review. If the user declines, the app cannot record at all and must recover.doc |
No manifest to write — permissions are “declared automatically when you use components like VoiceMLModule or DialogModule.” Mic falls under Sensitive User Data, which is what triggers the connectivity restriction below.doc |
| Primary rejection risks | 2.5.14 recording without explicit consent or a clear indication. 5.1.1 (iii) asking for the mic beyond core functionality. 5.1.1 (iv) no alternative for users who decline. 5.1.2 (i) sending speech to a third-party AI service without disclosure and explicit permission. 2.5.11 registering intents you cannot fulfil, or inserting anything between request and fulfilment.doc | Missing or wrong foreground-service type on Android 14+, which is a hard build-and-policy failure. Missing prominent disclosure or an inaccurate Data safety declaration for audio. Starting mic capture from the background.doc | VRC.Quest.Security.2 — request the minimum permissions required and no unsupported ones. VRC.Quest.Privacy.1–5 — the privacy policy must state what is collected, how it is used and how a user requests deletion, and the team must clear data-protection checks. An unjustified RECORD_AUDIO is the specific speech trap.doc |
Combining sensitive user data with external connectivity in a published lens, other than through Remote Service Gateway — this blocks the obvious “send the transcript to my own LLM” design. Plus the general bar: a defined objective, visuals on activation, 60 fps, no crashes, under 25 MB for Spectacles, reviewed in 2–3 business days.doc |
Part 2 · Reading
Four things separate these platforms, and none of them is the recognition quality.
SpeechAnalyzer is on-device. Android's on-device recognizer is on-device, and Gemini is not. Meta's Voice SDK routes to Wit.ai. Snap's AsrModule returns a NoInternet error, which tells you where it runs. Egress is the difference between one consent line and a privacy-policy clause.Part 3 · Sources
Every link below was fetched while compiling this report. Where a page turned out not to cover speech on the HMD, that absence is what produced an est. cell above.
RECORD_AUDIO and what counts as a permitted use.Reproduce
Copy the prompt, run it against current documentation, and compare the result against this page. Anything that has moved will show up as a changed cell.