What does the microphone need from Android 17 to keep capturing? #81

Closed
opened 2026-08-01 13:01:03 +00:00 by aiko · 1 comment
Owner

Question

What foreground service, permissions and runtime dance does the microphone actually need on Android 17 / SDK 37, on the Pixel 7 Pro, for a held push-to-talk capture that survives the screen going off?

Graduated from the fog on the wayfinder map: "What foreground service the microphone actually needs, on Android 17." With the overlay pal ruled out by #47 and screen capture deferred to v2 by #45, the mic is the only foreground service v1 needs — nothing competes for the process. What is not known is what the platform now demands of it.

The phone runs SDK 37, newer than any source #44 could reach, and every release since 14 has tightened these rules: foregroundServiceType, the FOREGROUND_SERVICE_MICROPHONE permission, the while-in-use restrictions on starting one from the background, and the rules about which capture can continue once the app is not visible.

Answer at least:

  • Which android:foregroundServiceType values a mic capture needs on SDK 37, and which permissions each one now demands in the manifest.
  • Whether a foreground service is required at all for capture while the app is visible, or only to survive backgrounding.
  • What starts it legally — the restrictions on starting a foreground service from the background, and whether a hardware-key push-to-talk press counts as a qualifying trigger.
  • What happens to an in-flight AudioRecord when the screen turns off without one.
  • Whether anything here conflicts with denpa's existing manifest, which already declares a service for the disabled overlay (#23).

This is a research ticket: the answer is documentation and platform behaviour, not a change to this repo. Record it as a resolution comment with sources; the build work it implies gets its own ticket.

## Question What foreground service, permissions and runtime dance does the microphone actually need on **Android 17 / SDK 37**, on the Pixel 7 Pro, for a held push-to-talk capture that survives the screen going off? Graduated from the fog on the [wayfinder map](https://git.aiko.works/aiko/denpa/issues/41): *"What foreground service the microphone actually needs, on Android 17."* With the overlay pal ruled out by [#47](https://git.aiko.works/aiko/denpa/issues/47) and screen capture deferred to v2 by [#45](https://git.aiko.works/aiko/denpa/issues/45), the mic is the only foreground service v1 needs — nothing competes for the process. What is not known is what the platform now demands of it. The phone runs SDK 37, newer than any source [#44](https://git.aiko.works/aiko/denpa/issues/44) could reach, and every release since 14 has tightened these rules: `foregroundServiceType`, the `FOREGROUND_SERVICE_MICROPHONE` permission, the while-in-use restrictions on starting one from the background, and the rules about which capture can continue once the app is not visible. Answer at least: - Which `android:foregroundServiceType` values a mic capture needs on SDK 37, and which permissions each one now demands in the manifest. - Whether a foreground service is required at all for capture while the app **is** visible, or only to survive backgrounding. - What starts it legally — the restrictions on starting a foreground service from the background, and whether a hardware-key push-to-talk press counts as a qualifying trigger. - What happens to an in-flight `AudioRecord` when the screen turns off without one. - Whether anything here conflicts with `denpa`'s existing manifest, which already declares a service for the disabled overlay (`#23`). This is a research ticket: the answer is documentation and platform behaviour, not a change to this repo. Record it as a resolution comment with sources; the build work it implies gets its own ticket.
Author
Owner

This was generated by AI during triage.

Direct answer: a held push-to-talk capture on SDK 37 needs one service with foregroundServiceType="microphone", declaring FOREGROUND_SERVICE + FOREGROUND_SERVICE_MICROPHONE on top of the RECORD_AUDIO already there, and it must be started while the app is visible. The on-screen key satisfies that; a global hardware key from a backgrounded app does not.

No foreground service is needed to capture while the app is visible. It exists entirely to survive the screen going off — and it can never be started once the screen is already off.

1. Type and permissions

android:foregroundServiceType="microphone" is the only type capture needs (service types):

  • manifest: FOREGROUND_SERVICE_MICROPHONE, plus the base FOREGROUND_SERVICE every foreground service needs (declare)
  • startForeground() constant: FOREGROUND_SERVICE_TYPE_MICROPHONE
  • runtime: RECORD_AUDIO granted

A type passed to startForeground() that disagrees with the manifest throws MissingForegroundServiceTypeException; missing RECORD_AUDIO at service creation throws SecurityException.

No specialUse, no mediaPlayback. The microphone type carries no runtime timeout — Android 15's six-hour cap covers only dataSync and mediaProcessing (timeouts). A long hold is not the problem.

2. Required while visible?

No. RECORD_AUDIO is a while-in-use permission and a visible activity satisfies while-in-use. That matches what this repo already observes: MicRecorder.startCapture opens AudioRecord with no service at all and works, as long as the screen is on.

3. What may legally start it

Two gates, and the second is the one that bites.

Background-start restrictions have a long exemption list — transitioning from user-visible, notification or widget interaction, high-priority FCM, exact alarm, SYSTEM_ALERT_WINDOW, and more (bg-start).

While-in-use restrictions override them. From the same page: an app wanting a foreground service that needs while-in-use permissions "cannot create the service while the app is in the background, even if the app falls into one of the exemptions from background start restrictions." That second list is short: a system component, app-widget interaction, notification interaction, a PendingIntent from another visible app, a device-owner DPC, a VoiceInteractionService provider, or START_ACTIVITIES_FROM_BACKGROUND.

The trap named explicitly on that page: checkSelfPermission() returns PERMISSION_GRANTED for a while-in-use permission even when backgrounded — it means "granted while in use", not "usable now". The ContextCompat.checkSelfPermission guard in MicRecorder.startCapture has exactly this shape: it will pass, and the startForeground after it will still throw.

A global hardware-key press while backgrounded is on neither list. Media buttons reach a backgrounded app only through MediaSession (media buttons), and media keys are absent from the while-in-use exemptions. See Unverified.

4. Screen off, no service, capture in flight

It does not throw, does not short-read, does not stop. It returns full buffers of zeros — digital silence indistinguishable from a quiet room. RECORD_AUDIO is a foreground app-op, so losing visibility loses the op; Android 11's page says a background-started foreground service "cannot access the microphone or camera" (foreground services privacy) without saying what arrives instead.

The zeros are this repo's own Pixel 7 Pro measurement — docs/android-build.md's app-op section, and MicRecorder's audit, which logs a one-shot verdict on the opening seconds precisely so nobody rediscovers it. adb shell cmd appops get works.aiko.ollvt.debug RECORD_AUDIO shows foreground with a fresh rejectTime when it happens.

5. Behaviour changes that break an older-targeting build

  • 34 — types mandatory, each demanding its own permission (FGS changes).
  • 35SYSTEM_ALERT_WINDOW exempts a background start only with a currently visible overlay; BOOT_COMPLETED receivers can no longer launch some foreground service types.
  • 36 — background jobs started from a foreground service obey their own quotas. Nothing microphone-specific.
  • 37 — background audio hardening, at any target. All apps need "a visible activity or ... a foreground service that is not of type SHORT_SERVICE" for background audio interaction; apps targeting 37 additionally need that service to hold while-in-use capability (bg-audio, behavior changes 17). Scope is playback, audio focus and volume — not capture. Failures are silent by design: playback silenced with no exception, requestAudioFocus() returning AUDIOFOCUS_REQUEST_FAILED. That is her output path, not the mic. Diagnostics: adb shell cmd audio set-enable-hardening throw turns the silent failures into exceptions, and adb dumpsys audio reports level: full (service present but no while-in-use) or level: partial (no service).
  • Nothing in either API 37 list touches AudioRecord, the microphone type, or RECORD_AUDIO.
  • Adjacent: Play requires target 36 from 31 Aug 2026 (target sdk). build.gradle.kts has targetSdk = 36 and is compliant; moving to 37 is what would switch on the stricter audio bar.

What this implies for Denpa

The manifest does not conflict, but it is incomplete. It declares RECORD_AUDIO, FOREGROUND_SERVICE, FOREGROUND_SERVICE_SPECIAL_USE, SYSTEM_ALERT_WINDOW, POST_NOTIFICATIONS and one <service>.overlay.OverlayService, specialUse, from #23. A second <service> with type microphone sits alongside it with no interaction. Missing: FOREGROUND_SERVICE_MICROPHONE and the service element.

Do not merge microphone onto OverlayService as specialUse|microphone. With the overlay ruled out by #47 that resurrects a component whose point is gone, and ties the mic's lifetime to overlay code paths that stop the service on permission revocation.

The start path is nearly right already. use-receiver-chrome.ts drives push-to-talk from an on-screen key in the receiver chrome, so the press happens while the activity is visible — the one condition under which a microphone service may be created, and the one that grants it while-in-use. The change is: start the service before opening AudioRecord, stop it on release. MicRecorder.startCapture/stopCapture is the seam; mic_start in src-tauri/src/audio/android.rs already routes both through with_recorder and already treats a refusal as a visible error rather than a log, which is what a SecurityException from startForeground needs.

A backgrounded global-hotkey push-to-talk is not reachable on this platform as designed. The sanctioned shape is the foreground-service notification carrying the action — notification interaction is on both exemption lists. OverlayService's own design comment already articulates that idea ("the notification as a surface rather than a tax"). Its own ticket.

#44's silent-zeros failure gains a second cause. Once a service exists, a capture reading zeros means the service failed to start or lacks while-in-use, not that the screen is off. MicRecorder.audit's warning text will want revisiting then.

Unverified

  • What an in-flight AudioRecord returns when visibility is lost. No primary source states it; the zeros are this repo's own measurement, corroborated only by secondary sources.
  • Whether a MediaSession media-button event can start a microphone service from the background. The bg-audio page lists media key events among things granting while-in-use to a background-started service; the bg-start page's exemption list does not include them. The two appear to disagree, and the bg-audio list is framed around playback. Untested.
  • Whether Android 17 changed anything about the microphone type or AudioRecord. Neither behaviour-change page mentions them. Absence of a documented change is not proof; AOSP release notes were not read.
  • Whether such a service survives Doze on a screen-off Pixel 7 Pro for a long hold. Not documented, not measured.
  • Whether POST_NOTIFICATIONS denial blocks the service from starting, as opposed to hiding its notification.
  • The behaviour when the screen turns off while the service is already running and holding while-in-use — expected to keep capturing, which is the premise, but no page read says it in those words.
> *This was generated by AI during triage.* **Direct answer:** a held push-to-talk capture on SDK 37 needs one service with `foregroundServiceType="microphone"`, declaring `FOREGROUND_SERVICE` + `FOREGROUND_SERVICE_MICROPHONE` on top of the `RECORD_AUDIO` already there, and it must be started **while the app is visible**. The on-screen key satisfies that; a global hardware key from a backgrounded app does not. No foreground service is needed to capture while the app is visible. It exists entirely to survive the screen going off — and it can never be started once the screen is already off. ## 1. Type and permissions `android:foregroundServiceType="microphone"` is the only type capture needs ([service types](https://developer.android.com/develop/background-work/services/fgs/service-types)): - manifest: `FOREGROUND_SERVICE_MICROPHONE`, plus the base `FOREGROUND_SERVICE` every foreground service needs ([declare](https://developer.android.com/develop/background-work/services/fgs/declare)) - `startForeground()` constant: `FOREGROUND_SERVICE_TYPE_MICROPHONE` - runtime: `RECORD_AUDIO` granted A type passed to `startForeground()` that disagrees with the manifest throws `MissingForegroundServiceTypeException`; missing `RECORD_AUDIO` at service creation throws `SecurityException`. No `specialUse`, no `mediaPlayback`. The `microphone` type carries **no runtime timeout** — Android 15's six-hour cap covers only `dataSync` and `mediaProcessing` ([timeouts](https://developer.android.com/develop/background-work/services/fgs/timeout)). A long hold is not the problem. ## 2. Required while visible? No. `RECORD_AUDIO` is a while-in-use permission and a visible activity satisfies while-in-use. That matches what this repo already observes: `MicRecorder.startCapture` opens `AudioRecord` with no service at all and works, as long as the screen is on. ## 3. What may legally start it Two gates, and the second is the one that bites. **Background-start restrictions** have a long exemption list — transitioning from user-visible, notification or widget interaction, high-priority FCM, exact alarm, `SYSTEM_ALERT_WINDOW`, and more ([bg-start](https://developer.android.com/develop/background-work/services/fgs/restrictions-bg-start)). **While-in-use restrictions override them.** From the same page: an app wanting a foreground service that needs while-in-use permissions "cannot create the service while the app is in the background, **even if the app falls into one of the exemptions from background start restrictions**." That second list is short: a system component, app-widget interaction, notification interaction, a `PendingIntent` from another *visible* app, a device-owner DPC, a `VoiceInteractionService` provider, or `START_ACTIVITIES_FROM_BACKGROUND`. The trap named explicitly on that page: `checkSelfPermission()` returns `PERMISSION_GRANTED` for a while-in-use permission **even when backgrounded** — it means "granted while in use", not "usable now". The `ContextCompat.checkSelfPermission` guard in `MicRecorder.startCapture` has exactly this shape: it will pass, and the `startForeground` after it will still throw. **A global hardware-key press while backgrounded is on neither list.** Media buttons reach a backgrounded app only through `MediaSession` ([media buttons](https://developer.android.com/media/legacy/media-buttons)), and media keys are absent from the while-in-use exemptions. See Unverified. ## 4. Screen off, no service, capture in flight It does not throw, does not short-read, does not stop. It returns full buffers of **zeros** — digital silence indistinguishable from a quiet room. `RECORD_AUDIO` is a foreground app-op, so losing visibility loses the op; Android 11's page says a background-started foreground service "cannot access the microphone or camera" ([foreground services privacy](https://developer.android.com/about/versions/11/privacy/foreground-services)) without saying what arrives instead. The zeros are this repo's own Pixel 7 Pro measurement — `docs/android-build.md`'s app-op section, and `MicRecorder`'s `audit`, which logs a one-shot verdict on the opening seconds precisely so nobody rediscovers it. `adb shell cmd appops get works.aiko.ollvt.debug RECORD_AUDIO` shows `foreground` with a fresh `rejectTime` when it happens. ## 5. Behaviour changes that break an older-targeting build - **34** — types mandatory, each demanding its own permission ([FGS changes](https://developer.android.com/develop/background-work/services/fgs/changes)). - **35** — `SYSTEM_ALERT_WINDOW` exempts a background start only with a *currently visible* overlay; `BOOT_COMPLETED` receivers can no longer launch some foreground service types. - **36** — background jobs started from a foreground service obey their own quotas. Nothing microphone-specific. - **37 — background audio hardening, at any target.** All apps need "a visible activity or ... a foreground service that is not of type `SHORT_SERVICE`" for background audio interaction; apps *targeting* 37 additionally need that service to hold while-in-use capability ([bg-audio](https://developer.android.com/about/versions/17/changes/bg-audio), [behavior changes 17](https://developer.android.com/about/versions/17/behavior-changes-17)). **Scope is playback, audio focus and volume — not capture.** Failures are silent by design: playback silenced with no exception, `requestAudioFocus()` returning `AUDIOFOCUS_REQUEST_FAILED`. That is her *output* path, not the mic. Diagnostics: `adb shell cmd audio set-enable-hardening throw` turns the silent failures into exceptions, and `adb dumpsys audio` reports `level: full` (service present but no while-in-use) or `level: partial` (no service). - Nothing in either API 37 list touches `AudioRecord`, the `microphone` type, or `RECORD_AUDIO`. - Adjacent: Play requires target 36 from 31 Aug 2026 ([target sdk](https://developer.android.com/google/play/requirements/target-sdk)). `build.gradle.kts` has `targetSdk = 36` and is compliant; moving to 37 is what would switch on the stricter audio bar. ## What this implies for Denpa **The manifest does not conflict, but it is incomplete.** It declares `RECORD_AUDIO`, `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_SPECIAL_USE`, `SYSTEM_ALERT_WINDOW`, `POST_NOTIFICATIONS` and one `<service>` — `.overlay.OverlayService`, `specialUse`, from `#23`. A second `<service>` with type `microphone` sits alongside it with no interaction. Missing: `FOREGROUND_SERVICE_MICROPHONE` and the service element. **Do not merge `microphone` onto `OverlayService`** as `specialUse|microphone`. With the overlay ruled out by [#47](https://git.aiko.works/aiko/denpa/issues/47) that resurrects a component whose point is gone, and ties the mic's lifetime to overlay code paths that stop the service on permission revocation. **The start path is nearly right already.** `use-receiver-chrome.ts` drives push-to-talk from an on-screen key in the receiver chrome, so the press happens while the activity is visible — the one condition under which a `microphone` service may be created, and the one that grants it while-in-use. The change is: start the service before opening `AudioRecord`, stop it on release. `MicRecorder.startCapture`/`stopCapture` is the seam; `mic_start` in `src-tauri/src/audio/android.rs` already routes both through `with_recorder` and already treats a refusal as a visible error rather than a log, which is what a `SecurityException` from `startForeground` needs. **A backgrounded global-hotkey push-to-talk is not reachable on this platform as designed.** The sanctioned shape is the foreground-service notification carrying the action — notification interaction is on both exemption lists. `OverlayService`'s own design comment already articulates that idea ("the notification as a surface rather than a tax"). Its own ticket. **`#44`'s silent-zeros failure gains a second cause.** Once a service exists, a capture reading zeros means the service failed to start or lacks while-in-use, not that the screen is off. `MicRecorder.audit`'s warning text will want revisiting then. ## Unverified - **What an in-flight `AudioRecord` returns** when visibility is lost. No primary source states it; the zeros are this repo's own measurement, corroborated only by secondary sources. - **Whether a `MediaSession` media-button event can start a `microphone` service from the background.** The bg-audio page lists media key events among things granting while-in-use to a background-started service; the bg-start page's exemption list does not include them. The two appear to disagree, and the bg-audio list is framed around playback. Untested. - **Whether Android 17 changed anything about the `microphone` type or `AudioRecord`.** Neither behaviour-change page mentions them. Absence of a documented change is not proof; AOSP release notes were not read. - **Whether such a service survives Doze on a screen-off Pixel 7 Pro for a long hold.** Not documented, not measured. - **Whether `POST_NOTIFICATIONS` denial blocks the service from starting**, as opposed to hiding its notification. - **The behaviour when the screen turns off while the service is already running and holding while-in-use** — expected to keep capturing, which is the premise, but no page read says it in those words.
aiko closed this issue 2026-08-01 13:07:51 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiko/denpa#81
No description provided.