Skip to content

Request microphone permission before audio capture starts - #1183

Draft
hiroshihorie wants to merge 1 commit into
hiroshi/native-audio-session-presetfrom
hiroshi/mic-permission-before-capture
Draft

Request microphone permission before audio capture starts#1183
hiroshihorie wants to merge 1 commit into
hiroshi/native-audio-session-presetfrom
hiroshi/mic-permission-before-capture

Conversation

@hiroshihorie

Copy link
Copy Markdown
Member

Stacked on #1182 (base branch hiroshi/native-audio-session-preset). Will be rebased onto main once that merges. Flutter counterpart of client-sdk-swift #1085.

Why

webrtc-sdk/webrtc#265 (first shipped in m144.7559.12) removed the blocking mic permission request from the AudioEngine device. The pre-enable check is now passive: it returns kAudioEngineErrorInsufficientDevicePermission (-9000) instead of prompting, so requesting permission is the SDK's job.

flutter-webrtc still pins 144.7559.09, so this is not load-bearing yet. It is harmless there, since getUserMedia in flutter-webrtc already prompts and the status is resolved before the device's blocking path runs. Once flutter-webrtc lands .13/.14 and the pin here follows, this is what keeps the current behavior. Opening it as a draft for that reason.

What Flutter already had

flutter-webrtc's getUserMedia calls AVCaptureDevice requestAccessForMediaType: and waits for the answer, so every livekit_client mic path (publish, restartTrack on unmute, pre-connect audio) already prompted before the audio device saw the track. That part of #1085 needs no port. #1182 already maps -9000 to TrackCreateException for the direct ADM entry points (setEngineAvailability, startLocalRecording).

What this adds

The one behavior from #1085 that was missing: only prompt while the app can show the alert.

  • Native ensureMicrophoneAccess in LiveKitPlugin.swift: authorized passes, denied/restricted fail, notDetermined requests access. On iOS the request is only made while UIApplication.shared.applicationState == .active. An inactive or backgrounded app (locked screen, CallKit wake, app switcher) has the alert deferred by the system, and awaiting it would suspend getUserMedia and the _publishRunner behind it, blocking camera and screen share publishes for as long as the app stays there. Failing fast lets the next foreground attempt prompt normally. macOS can present the prompt regardless, so it always requests. No app extension concern here, the plugin is app-only.
  • LocalTrack.createStream calls it for AudioCaptureOptions on Apple platforms before getUserMedia. That is the Flutter choke point: LocalAudioTrack.create(), restartTrack() and PreConnectAudioBuffer.startRecording() all reach it. Since the prompt in Flutter happens at getUserMedia rather than at capture start, the gate sits in front of that instead of in startCapture as in Swift.
  • Failures surface as TrackCreateException through the deviceAccessDenied code introduced in Resolve a default audio session from engine state when no policy was pushed #1182.
  • Docs for withPreConnectAudio and PreConnectAudioBuffer.startRecording now say permission is requested at recording start while foregrounded, and that requesting it earlier in the app lifecycle avoids the prompt delaying the first recording. AudioManager.setEngineAvailability already documents that it does not request permission (Resolve a default audio session from engine state when no policy was pushed #1182).

Testing

  • flutter analyze, flutter test, dart format --set-exit-if-changed, import_sorter --exit-if-changed clean.
  • Unit tests cover Native.ensureMicrophoneAccess (no-op when unimplemented, propagates deviceAccessDenied). The createStream gate is behind lkPlatformIsApple() and not reachable from unit tests.
  • Flutter agent starter compiles for iOS (device SDK) and macOS with the change. On-device run against a fresh install (first-launch prompt) still to do.

Refs CLT-3243, client-sdk-swift#1085

webrtc-sdk/webrtc#265 (m144.7559.12 and later) removed the blocking mic
permission request from the AudioEngine device. It now only checks the
status and fails with kAudioEngineErrorInsufficientDevicePermission, so
requesting permission is the SDK's job.

The native plugin gains ensureMicrophoneAccess: authorized passes,
denied or restricted fails, and notDetermined requests access, on iOS
only while the app is active. An inactive or backgrounded app has the
alert deferred by the system, and waiting on it would suspend
getUserMedia and the publish queue behind it, so it fails fast instead
and the next foreground attempt prompts normally.

LocalTrack.createStream calls it for audio on Apple platforms before
getUserMedia, which covers publishing, restartTrack on unmute, and
pre-connect audio. Failures surface as TrackCreateException through the
existing audio engine error mapping.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant