The mic stops hearing when the screen goes off, and says nothing #82
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#82
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Capture on Android works only while the app is visible. Let the screen go off mid-hold and
AudioRecordkeeps returning full buffers of zeros — a well-formed recording of nothing, indistinguishable from a quiet room.MicRecorder'sauditalready logs a verdict on the opening seconds precisely because this failure looks like success.Established by #81, which resolved the question of what the platform actually demands. Read that first — it carries the sources and the traps.
What to build
A foreground service whose only job is to hold the microphone's while-in-use state open.
AndroidManifest.xmlgainsFOREGROUND_SERVICE_MICROPHONEand a second<service>withandroid:foregroundServiceType="microphone".FOREGROUND_SERVICE,RECORD_AUDIOandPOST_NOTIFICATIONSare already declared..overlay.OverlayServiceasspecialUse|microphone. The overlay is ruled out by #47; that would resurrect a component whose point is gone and tie the mic's lifetime to code paths that stop the service on permission revocation.AudioRecordopens and stop it on release.MicRecorder.startCapture/stopCaptureis the seam;mic_startinsrc-tauri/src/audio/android.rsalready routes both throughwith_recorderand already treats a refusal as a visible error rather than a log — which is what aSecurityExceptionfromstartForegroundneeds.ContextCompat.checkSelfPermissionguard instartCapturestays, but it cannot be read as "the mic will work now": that call returnsPERMISSION_GRANTEDfor a while-in-use permission even when the app is backgrounded.#81records the citation.MicRecorder.audit's warning text wants revisiting: once a service exists, zeros mean the service failed to start or lacks while-in-use, not that the screen is off. Two causes, one symptom.What this deliberately does not do
Push-to-talk while the app is backgrounded.
#81established that a global hardware key is on neither exemption list, so a backgrounded start is not reachable as designed — the platform-sanctioned shape is the foreground service's own notification carrying the action. That is a separate decision about whether v1 wants a mic you can reach without looking at the app, and it is not this ticket.Acceptance
RECORD_AUDIO, or astartForegroundthat throws, surfaces as a visible refusal rather than a silent dead micadb shell cmd appops get works.aiko.ollvt.debug RECORD_AUDIOshowing no freshrejectTimeafter a screen-off holdThe last one is yours — the build is agent work, the verdict is a device.
Related
denpa#81(the research),denpa#44(silero and capture on Android),denpa#61(hear a real word),denpa#23(the overlay service not to reuse),denpa#47(why the overlay is out)Merged locally as
e2c069a(feature802ff29, alongside#83— see below for why they merged as one). Nothing pushed.MicService.ktis amicrophoneforeground service and nothing else.hold(context)starts it and waits on a latch foronStartCommand's verdict, returning the refusal string or null;release(context)is a plainstopService, which needs no action to route and is not subject to background-start restrictions.MicRecorderholds before theopen(rate)loop and releases on the all-rates-failed path and at the end ofstopCapture.Acceptance
OverlayService—android-mic-service.test.tsasserts the overlay staysspecialUsealone, and that the manifest's declared type agrees with thestartForegroundconstant. That pair throwsMissingForegroundServiceTypeExceptiononly on a device, which is why it is pinned here.AudioRecordopens, stopped after it closesstartForegroundbecomesMicRecorder.lastFailure, which Rust already turns into a visible error rather than a log#81established it is not reachable as designedadb shell cmd appops get works.aiko.ollvt.debug RECORD_AUDIOshows no freshrejectTimeafter a screen-off holdThe last four are the point of the ticket and none of them is done. This was built blind, deliberately and with the operator's agreement. Nobody has watched it.
What review caught
The typed
startForegroundwas gated on API 29.FOREGROUND_SERVICE_TYPE_MICROPHONEissince=30while the three-argumentstartForegroundissince=29, so atminSdk 24that would have been aNoSuchFieldErroron the first hold. Now gated on 30. Also madeholdsynchronized, thoughmic_start's lock already serialises it.Assumptions that could not be tested
microphoneservice keeps theRECORD_AUDIOapp-op granted through screen-off.#81lists this as unverified and the whole change rests on it.POST_NOTIFICATIONShides the notification rather than blocking the start. If it blocks, every capture now fails visibly where it used to fail silently. Louder is better than zeros, but it would be a surprise.MicRecorder.startCapturedoes not run on the Android main looper —hold()skips its wait if it does, and a refusal would then be reported as success.stopServicefrom a backgrounded app reliably tears down an already-foreground service.Gates
npm test941 across 73 files,npm run typecheckexits 0,cargo test28.:app:compileArm64DebugKotlinbuilt, so the manifest merges; no APK was built.