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.
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;
}
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!
This is how is done
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
if(am.getMode()==AudioManager.MODE_IN_COMMUNICATION){
//Mic is in use
}
MODE_NORMAL -> You good to go. Mic not in use
MODE_RINGTONE -> Incoming call. The phone is ringing
MODE_IN_CALL -> A phone call is in progress
MODE_IN_COMMUNICATION -> The Mic is being used by another application
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);
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.
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()
Related
I am developing a voice call app for android using PeerJS and WebView. And I want the audio to play through the earpiece. Here is my code,
private fun initAudio(){
am = getSystemService(AUDIO_SERVICE) as AudioManager
volumeControlStream = AudioManager.STREAM_VOICE_CALL
am.mode = AudioManager.MODE_IN_COMMUNICATION
am.isSpeakerphoneOn = false//<= not working in android 12
}
private fun toggleSpeakerMode(){
am.isSpeakerphoneOn = !am.isSpeakerphoneOn // <= final value is always true in android 12
}
The above code works fine on older versions of android, but not in android 12 (emulator).
am.isSpeakerphoneOn is always true in android 12. Am I doing something wrong here? Or is it a bug in the emulator?
there is a new API call in Android 12/S/API 31, setCommunicationDevice(AudioDeviceInfo). for switching between speaker an built-in earpiece now we can use:
ArrayList<Integer> targetTypes = new ArrayList<>();
if (earpieceMode) {
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE);
} else { // play out loud
targetTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
}
// more targetTypes may be added in some cases
// set up will pick and first available, so order matters
List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
outer:
for (Integer targetType : targetTypes) {
for (AudioDeviceInfo device : devices) {
if (device.getType() == targetType) {
boolean result = audioManager.setCommunicationDevice(device);
Log.i("AUDIO_MANAGER", "setCommunicationDevice type:" + targetType + " result:" + result);
if (result) break outer;
}
}
}
mode change isn't needed (but for voip calls is strongly suggested) and my streams are AudioManager.STREAM_VOICE_CALL type (where applicable)
By default webrtc uses earpiece for voice playing.
However,alternatively You can call the setSpeakerphoneOn(false) that is defined in the AudioManager.java class.
Just pass false in this function parameter and it will disable the speaker phone in the call and earpiece will be used.
I have also tested it on the android 12 phones and it is working fine.
If issue still persist then you have some bug in your emulator.
I am using Agora, and it has some issues. One of them is the speaker's voice comes out to the media sound.
On the browser, it can't control the media volume, So, I created an app to handle this. In the app, I dispatch the volume up/down button to control media volume.
However, this method created howling issue. So, I'd like to send the sound to STREAM_VOICE_CALL and use AEC(Acoustic Echo Cancellation) API on Android so that the sound comes out to the right stream and it can handle the echo problem.
what I wrote,
private fun enableVoiceCallMode() {
with(audioManager) {
volumeControlStream = AudioManager.STREAM_VOICE_CALL
setStreamVolume(
AudioManager.STREAM_VOICE_CALL,
audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL),
0
)
}
}
But this didn't work.
And also, I tried to apply AEC like this:
private fun enableEchoCanceler() {
if (AcousticEchoCanceler.isAvailable() && aec == null) {
aec = AcousticEchoCanceler.create(audioManager.generateAudioSessionId())
aec?.enabled = true
} else {
aec!!.enabled = false
aec!!.release()
aec = null
}
}
private fun releaseEchoCanceler() {
aec!!.enabled = false
aec?.release()
aec = null
}
However, I don't know if AcousticEchoCanceler.create(audioManager.generateAudioSessionId()) is correct way or not.
please help me out.
I'm trying to develop application that using the MediaRecorder API that runs on HMT-1.
Android Studio is used for development, and the operating environment is Android 10 or higher.
While shooting a video using the MediaRecoder API, we are verifying whether the same microphone can be used in another process such as the SpeechRecognizer API.
Recording processing alone with the MediaRecoder API and voice input alone with the SpeechRecognizer API can be performed without problems.
However, if you try to record and input voice at the same time, an error will occur.
If you want to use the input voice for multiple processes, please let me know if you have any reference documents or samples.
MediaRecorder settings.
path = getExternalFilesDir(null)!!.path
mMediaRecorder = MediaRecorder()
mMediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
mMediaRecorder!!.setVideoSource(MediaRecorder.VideoSource.SURFACE)
mMediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mMediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
mMediaRecorder!!.setAudioEncodingBitRate(16)
mMediaRecorder!!.setAudioSamplingRate(44100)
mMediaRecorder!!.setVideoSize(1024, 768)
mMediaRecorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
mMediaRecorder!!.setVideoEncodingBitRate(10000000)
mMediaRecorder!!.setOutputFile(path + "/" + DateFormat.format("yyyyMMdd'-'kkmmss", Calendar.getInstance()) + ".mp4")
mMediaRecorder!!.setOnInfoListener(this)
mMediaRecorder!!.setMaxDuration(VIDEO_DURATION)
mMediaRecorder!!.setMaxFileSize(VIDEO_FILESIZE)
mMediaRecorder!!.setPreviewDisplay(mSurfaceHolder!!.surface)
val rotation = (getSystemService(WINDOW_SERVICE) as WindowManager)
if(rotation.defaultDisplay.rotation == 2){
mMediaRecorder!!.setOrientationHint(180)
}
mMediaRecorder!!.prepare()
try {
mMediaRecorder!!.start()
} catch (ex: IOException) {
ex.printStackTrace()
mMediaRecorder!!.release()
}
SpeechRecognizer settings.
mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(applicationContext)
mSpeechRecognizer?.setRecognitionListener(createRecognitionListenerStringStream { recognize_text_view.text = it })
public fun onStart(View: View){
mSpeechRecognizer?.startListening(Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH))
}
public fun onStop(View: View){
mSpeechRecognizer?.stopListening()
}
I'm working auto call recorder app, I'm able to record voice call on below android 6 using MediaRecorder.AudioSource.VOICE_CALL,
From android 6 not able to record voice call using VOICE_CALL. I managed to record using MediaRecorder.AudioSource.MIC but here incoming voice not getting recorded and I want to record voice call in normal mode not in speaker on mode. Please help me on this. (I had tried on Xiomi Redmi 4a(android 6),not working).
myRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
myRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
myRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
myRecorder.setMaxDuration(60 * 60 * 1000);
AudioManager audiomanager =
(AudioManager)getSystemService(AUDIO_SERVICE);
audiomanager.setMode(2);
Edit : There is no issue with permissions.
Update : Anyone knows how to forcing another stream to MIC audio source. This requires native android code. Please help me on this
Refer this question for more details on routing audio
You need to use ndk. Here are examples of the functions that need to be done.
Load libmedia.so and libutils.so
int load(JNIEnv *env, jobject thiz) {
void *handleLibMedia;
void *handleLibUtils;
int result = -1;
lspr func = NULL;
pthread_t newthread = (pthread_t) thiz;
handleLibMedia = dlopen("libmedia.so", RTLD_NOW | RTLD_GLOBAL);
if (handleLibMedia != NULL) {
func = dlsym(handleLibMedia, "_ZN7android11AudioSystem13setParametersEiRKNS_7String8E");
if (func != NULL) {
result = 0;
}
audioSetParameters = (lasp) func;
} else {
result = -1;
}
handleLibUtils = dlopen("libutils.so", RTLD_NOW | RTLD_GLOBAL);
if (handleLibUtils != NULL) {
fstr = dlsym(handleLibUtils, "_ZN7android7String8C2EPKc");
if (fstr == NULL) {
result = -1;
}
} else {
result = -1;
}
cmd = CM_D;
int resultTh = pthread_create(&newthread, NULL, taskAudioSetParam, NULL);
return result;}
Function setParameters
int setParam(jint i, jint as) {
pthread_mutex_lock(&mt);
audioSession = (int) (as + 1);
kvp = "input_source=4";
kvps = toString8(kvp);
cmd = (int) i;
pthread_cond_signal(&cnd);
pthread_mutex_unlock(&mt);
return 0;}
Task AudioSetParameters
void *taskAudioSetParam(void *threadid) {
while (1) {
pthread_mutex_lock(&mt);
if (cmd == CM_D) {
pthread_cond_wait(&cnd, &mt);
} else if (audioSetParameters != NULL) {
audioSetParameters(audioSession, kvps);
}
pthread_mutex_unlock(&mt);
}
}
There is a library and an example of use https://github.com/ViktorDegtyarev/CallRecLib
Xiaomi devices always have problems with permission request even run-time or install-time.
I have an Xiaomi Redmi 3 pro, and it always force to Deny some permission when I install apps, so I must manually Allow it.
If your problem is the same, I found some workaround solution and it worked for me: How to get MIUI Security app auto start permission programmatically?
First these 3 permissions are needed in Manifest as well as a runtime permission request if the device is above Marshmallow,
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
MediaRecorder.AudioSource.VOICE_CALL is not supported on all phones so you need to continue using MediaRecorder.AudioSource.MIC.
I use this and works fine on most of the devices,
recorder = new MediaRecorder();
recorder.setAudioSource(audioSource);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(your_path);
You need to set this to record your calls properly,
audioManager.setMode(AudioManager.MODE_IN_CALL);
raise volume level when you start recording
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0);
When you stop recording set the mode to normal,
audioManager.setMode(AudioManager.MODE_NORMAL); and also set the stream volume to back how it was.
This could be a Permission related issue.
With the introduction of Android 6.0 Marshmallow, the app will not be granted any permission at installation time. Instead, the application has to ask the user for a permission one-by-one at run-time.
I hope you have included the code which explicitly asks for permissions on devices with Marshmallow and above.
In automatic call recorder (callU) have a option "SoundFX" If Enable Record Calls Two Side
Link
try
MediaRecorder.AudioSource.VOICE_COMMUNICATION
and see
https://androidforums.com/threads/android-phone-with-call-recording-function.181663/
I am currently Developing a VoIP Android Application, and for VoIP support, I am using an Open source library Linphone.
Currently voice calling is happening, but video calling is not happening. After analyzing for a while, I came to know that by default when the app is loaded, the LinphoneCore library is using the H264 video codec.
But the VOIP Asterik server is configured with the VP8 video codec. I cannot change the video codec, which is configured in the server. Hence due to a codec mismatch, video data is not going.
So how can I set manually the video codec to VP8 from my app to LinphoneCore once the app is loaded?
To set videoCodec to LinphoneCore, what you can do is , once your LinphoneCore is ready, you can just Retrieve the VideoCodec Payload that it supports and then set a particular payload and disable others as shown below in the code.
private void enableVp8Codec () {
LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) {
PayloadType[] lPayLoadArr = lc.getVideoCodecs();
for (final PayloadType pt : lPayLoadArr) {
try {
if (pt.getMime().equals("VP8")) {
lc.enablePayloadType(pt, true);
} else {
lc.enablePayloadType(pt, false);
}
} catch (LinphoneCoreException e) {
Log.e("tag",e.getMessage());
}
}
}
}
This method you can probably call in onResume of your Activity