5

I have been searching for couple of days now and havent been able to find a suitable solution.

I am trying to check if any app in the background is using the microphone, so my app can use it, otherwise i want just to show message "Microphone in use by another app".

I tried checking all the applications in the background and their permissions but that doesnt solve my problem, since there is package wearable.app which asks for the permissions but it doesnt affect the audio, or it is not using it.

I tried the other solutions that i was able to find here or on google, but none of that seems to be the proper way.

All i want to check if the microphone is not being used, so my app can use it.

Any suggestion i will appreciate.

Borce Ivanovski
  • 561
  • 1
  • 4
  • 23

5 Answers5

17

After searching more i found the solution and i am adding it here for anyone that needs it to find it easier.

private boolean validateMicAvailability(){
    Boolean available = true;
    AudioRecord recorder =
            new AudioRecord(MediaRecorder.AudioSource.MIC, 44100,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_DEFAULT, 44100);
    try{
        if(recorder.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED ){
            available = false;

        }

        recorder.startRecording();
        if(recorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING){
            recorder.stop();
            available = false;

        }
        recorder.stop();
    } finally{
        recorder.release();
        recorder = null;
    }

    return available;
}
Borce Ivanovski
  • 561
  • 1
  • 4
  • 23
  • greate workaround, but i wonder if that can also be detected using OpenSLES. Atm my OpenSLES audio callback is just not getting triggered in case the microphone is not available. – user2479595 Sep 20 '17 at 13:18
2

You can do it the other way around.

Get the microphone in your app.

Get a list of the installed apps, who have a RECORD permission.

Then check if one of these apps is on the foreground and if there is one release the microphone so that the other app can use it (for example when a phone call occurs).

A bit dirty practice but I think it is what you are looking for.

Cheers!

  • yes but that doesnt solve my problem still. Lets say there is app that was started before mine that records audio while in the background. The user starts my app, i can not take the microphone from that app, unless they have implemented broadcast receiver to release the microphone (which i think very rarely does). So when my app tries to initiates the microphone, it will either get stuck or crash, since it can not use the microphone since the other app already uses it to record audio. – Borce Ivanovski Feb 25 '16 at 17:21
  • But there is no such app. If an app uses hardware resources such as the microphone and does not release for other apps to use it the phone would not even make a call. So unless you want to have the mic open 24/7 you can just check the time you want to capture if another app is using it. If it does you postpone your audio capture for when it is available. – Konstantinos Michael Feb 25 '16 at 17:28
  • but that is the problem, i am trying to get the microphone in my app, but it is not released from the other app, so my app is stuck or crashes. I believe it is totally different with the system processes, they can always get the resources from any app running in the background. So unless i am missing something, this doesnt work what you are saying "Get the microphone in your app". What i want to do is same as shazam, when the app is open and another app in background using the microphone and not released, it gives message "mic in use". I am trying to do the same – Borce Ivanovski Feb 25 '16 at 17:34
  • I have done [a project](https://github.com/Seichis/ColofulMusic) that does some signal processing from the sound it captures from the microphone. [This is](https://github.com/Seichis/ColofulMusic/blob/master/app/src/main/java/com/jupiter/on/tetsuo/colofulmusic/AudioClassificationService.java) the service I created to do that. I used a library called [TarsosDSP](https://github.com/JorenSix/TarsosDSP) to do that. If you are brave enough dig in. The code is messy but did not had any problems with other apps. Cheers! – Konstantinos Michael Feb 25 '16 at 17:39
  • @KonstantinosMichael just to correct one thing: phone calls can happen if microphone is being used by other apps. Also there's AudioSource.HOTWORD which is used by Assistant apps, and you can be recording the microphone in some app and the assistant app can still listen to the microphone with that audio source (aside from the calls, which use another source that bypasses the normal MIC one which is the normally used, so apps must share it one at a time) – Edw590 Mar 02 '23 at 18:40
1

AudioManager.AudioRecordingCallback()

        am.registerAudioRecordingCallback(new AudioManager.AudioRecordingCallback() {
        @Override
        public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
            super.onRecordingConfigChanged(configs);
            try {
                isMicOn = configs.get(0) != null;
            }catch (Exception e)
            {
                isMicOn = false;
            }
            if (isMicOn) {
                //microphone is on
            } else {
                // microphone is off
            }
            Toast.makeText(context, isMicOn ? "Mic on" : "Mic off", Toast.LENGTH_SHORT).show();

        }
    }, null);
Abdalla Tawfik
  • 118
  • 1
  • 5
  • Welcome to Stack Overflow! While your answer may solve this question, it would be helpful if you would include an explanation of how and why it solves it. Also, have in mind that you will help any future visitor to this question if you provide quality answer, which in turn may result with up-votes. I would encourage you to edit your answer and add explanations of how and why it solves this question. – NeNaD Jun 13 '21 at 16:47
1

Since sharing audio input behaviour varies depending on Android versions, this answer aims to provide a complete solution based on the docs.

Pre-Android 10

Before Android 10 the input audio stream could only be captured by one app at a time. If some app was already recording or listening to audio, your app could create an AudioRecord object, but an error would be returned when you called AudioRecord.startRecording() and the recording would not start.

So, you can use this function to check if the mic is used by another app for pre Android 10 versions.

private fun isAnotherAppUsingMic(): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return false

    createRecorder().apply {
        try {
            startRecording()
            if (recordingState != AudioRecord.RECORDSTATE_RECORDING) {
                return true
            }
            stop()
            return false
        } catch (e: IllegalStateException) {
            return true
        } finally {
            release()
        }
    }
}

private fun createRecorder(): AudioRecord {
    return AudioRecord(
        MediaRecorder.AudioSource.MIC,
        SAMPLE_RATE_HZ,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_16BIT,
        2 * AudioRecord.getMinBufferSize(
            SAMPLE_RATE_HZ,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT
        )
    )
}

const val SAMPLE_RATE_HZ = 44100

Android 10 and above

Android 10 imposes a priority scheme that can switch the input audio stream between apps while they are running. In most cases, if a new app acquires the audio input, the previously capturing app continues to run, but receives silence.

So, for Android versions 10 and higher, in most cases your app will take priority if there is another app like voice or screen recorder is already running and then you start using mic in your app. But you will need to check for Voice/Video call as it has higher priority and mic won't be available for your app (it will receive silence). You can use below code to check if there is an active call:

private fun isVoiceCallActive(): Boolean {
    val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
    return audioManager.mode in listOf(
        AudioManager.MODE_IN_CALL,
        AudioManager.MODE_IN_COMMUNICATION
    )
}

In summary you can merge above two function to check if mic is available before you want to use it.

fun isMicAvailable() = !isAnotherAppUsingMic() && !isVoiceCallActive()
Nijat Ahmadli
  • 711
  • 6
  • 13
0

I know this may sound a bit tedious or the long way... But have you considered recording a logcat? Record a log for both Kernel and apps. Recreate the issue, then compare both logs to see what program is occupied when the kernel utilizes the mic.

Spoot
  • 1