To detect unauthorised microphone access in your app, define when capture is allowed, then prove that every recording started from that trigger and went only where it should. On Android, data access auditing reports each microphone operation with a stack trace, and AudioRecordingConfiguration tells you when your capture is silenced. On iOS, AVAudioApplication reports the record permission and the App Privacy Report shows when the app used the microphone. Both systems also show the user an indicator during capture. This guide covers your own app’s capture path, with separate steps for Android and iOS.
Define authorised capture
Draw the real feature first: voice message, support call, voice note or dictation. Mark the start trigger, the selected input, the capture component, any temporary file and the backend request. List every library inside your app that can reach the microphone, including call, analytics and advertising SDKs.
“Unauthorised” then has a precise meaning in your app:
- capture that starts without the expected user action;
- capture that continues after the feature ends or the app leaves the foreground;
- capture by a component that has no purpose on the journey;
- audio sent to an endpoint the feature does not use.
Each case needs its own test and evidence. A permission grant covers every component in your process, so attribution matters more than the permission state.
What the user and the platform already show
Both platforms make capture visible to the person holding the phone. Your tests should check that what they see matches what the feature intends.
| Mechanism | Android | iOS |
|---|---|---|
| Runtime permission | RECORD_AUDIO | NSMicrophoneUsageDescription plus the record permission prompt |
| Capture indicator | Status bar icon when an app uses the microphone (Android 12 onwards) | Orange indicator while the microphone is in use (iOS 14 onwards) |
| Global switch | Microphone access toggle in Quick Settings and the Privacy screen on supported devices (Android 12 onwards) | Per-app microphone switch in Settings |
| Access history | Privacy dashboard covering location, camera and microphone (Android 12 onwards) | App Privacy Report, past seven days (iOS 15.2 onwards) |
An indicator appearing outside the feature is the simplest finding a tester can make. Record it with the screen, the time and the build.
Android: limits and audit
Platform limits that shape your test
- Background capture. Since Android 9, an app without a visible activity or a foreground service receives silence when it captures audio. A background read that returns real audio means a foreground service is running.
- Foreground service type. Capturing audio from a foreground service requires the
microphoneforeground service type. Apps targeting Android 14 must also declare theFOREGROUND_SERVICE_MICROPHONEpermission and passFOREGROUND_SERVICE_TYPE_MICROPHONEtostartForeground(). BecauseRECORD_AUDIOis a while-in-use permission, a microphone foreground service cannot be started while the app is in the background. Review every service that declares this type; each one should map to a feature in your capture diagram. - Sensor toggle. When the user turns microphone access off, apps receive silent audio.
SensorPrivacyManager.supportsSensorToggle()tells you whether the device offers the toggle, so you can explain silence instead of reporting an error. - Shared input. Since Android 10, the system decides which app receives audio when several capture at once. Privileged apps such as the assistant or an accessibility service can share input with your app, and your app can be silenced by a call. Register
AudioManager.registerAudioRecordingCallback()and readAudioRecordingConfiguration.isClientSilenced()to record when your capture was silenced.
Attribute each capture
Android 11 (API level 30) added data access auditing. Register an AppOpsManager.OnOpNotedCallback with setOnOpNotedCallback() and log the stack trace in onNoted(). For audio, the operation is OPSTR_RECORD_AUDIO. A capture whose stack trace leads to an SDK rather than your feature code is a finding. Declare an <attribution> tag per feature and use Context.createAttributionContext(), so the record names the part of the app that captured.
iOS: permission, session and report
Every capture needs an NSMicrophoneUsageDescription that states the purpose. From iOS 17, use AVAudioApplication.requestRecordPermission(completionHandler:) to ask and AVAudioApplication.shared.recordPermission to read the state; this replaces the older AVAudioSession record-permission API. Check the state before showing a record button and handle denial with a non-audio alternative.
Treat the audio session as part of the evidence. Observe AVAudioSession.interruptionNotification so a phone call, alarm or Siri request stops and resumes capture in line with the feature’s consent model. After an interruption, require a fresh user action if resuming would surprise the person.
iOS has no in-app audit callback like Android’s, so attribution comes from three sources:
- App Privacy Report. On a test device with the report turned on, run one journey and confirm that microphone access appears only while the feature ran. The report also lists the domains the app contacted.
- SDK privacy manifests. Compare what each third-party SDK declares in its
PrivacyInfo.xcprivacyfile with your capture diagram. - Code review of capture call sites. Search the app and its dependencies for
AVAudioEngine,AVAudioRecorderand audio unit set-up, and confirm each sits behind the intended trigger.
Run the test
Use dedicated physical devices, a non-production build, a test account and synthetic audio. Simulators are useful for development checks but do not exercise real microphone hardware or indicators.
- Record the OS version, device, app build, framework and audio libraries.
- Run the journey with permission granted. Confirm the indicator appears only during capture, and collect the Android audit record or the iOS report entry.
- Deny the permission, then grant it again in Settings. The app explains the unavailable feature and does not ask repeatedly.
- On Android, turn the microphone toggle off and confirm the app reports silence, not a security event.
- Send the app to the background during capture and confirm capture stops, unless a declared foreground service is meant to continue.
- Repeat with headsets, an external microphone and an incoming call.
- Search your controlled backend for uploads outside the feature.
A capture started by injected code looks different from one started by your own SDK: the trigger or stack trace includes code that is not in your build. Otukan detects hooking and tampering in the app process and reports it to your backend, so you can line that evidence up with your own capture records. For audio content checks, see voice and audio injection detection.
Respond in proportion
| Scenario | Expected behaviour |
|---|---|
| Permission denied or sensor toggle off | Explain the unavailable function and keep non-audio alternatives |
| Capture silenced by a call or assistant | Pause or end the recording; no security finding |
| Accessory connected or removed | Re-establish the intended input |
| SDK captures without a purpose | Block the release; remove or configure the SDK |
| Hooking evidence during capture | Apply the runtime policy for that flow and keep support access |
Release checklist
- Each capture feature has a recorded trigger, input, destination and stop condition.
- Every Android foreground service with the
microphonetype maps to a feature. - Android test builds log
OPSTR_RECORD_AUDIOwith stack traces and attribution tags. - iOS builds are checked against the App Privacy Report and SDK privacy manifests.
- Silence, interruptions and sensor toggles produce clear messages, not alerts.
- Diagnostics never include audio content.
The same method applies to other protected data; see how to detect unauthorised contacts access. The app and backend split is covered on the integration overview.
Further reading
- Sharing audio input — Android Developers
- Foreground service types — Android Developers
- Audit access to data — Android Developers
- Behaviour changes: all apps (Android 12) — Android Developers
- AVAudioApplication — Apple Developer Documentation
- About App Privacy Report — Apple Support