IllegalStateException on MediaRecorder.start() - android

Morning!
I have a weird problem with a production app. Some users get an IllegalStateException on a MediaRecorder start. I cannot reproduce the problem on the few devices I own or even on an emulator. This error occurs on different versions and devices, (Android 5 to 10 and every kind of devices), and has many occurrences.
Here is the code
if (mRecorder == null) {
mRecorder = new MediaRecorder();
} else {
try {
mRecorder.stop();
} catch (Exception e) {}
}
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setAudioSamplingRate(44100);
mRecorder.setAudioEncodingBitRate(128000);
mRecorder.setOutputFile(mSoundFile.getAbsolutePath());
mRecorder.setAudioChannels(1);
mRecorder.setMaxDuration(mMaxDurationInMs);
mRecorder.prepare();
mRecorder.start();
And the stacktrace
java.lang.IllegalStateException: null
at android.media.MediaRecorder.native_start(MediaRecorder.java)
at android.media.MediaRecorder.start(MediaRecorder.java:1603)
at de.boxine.someapp.util.media.VoiceRecorder.start(VoiceRecorder.java:115)
at de.boxine.someapp.recording.audio.RecordAudioPresenter.startRecording(RecordAudioPresenter.java:186)
at de.boxine.someapp.recording.audio.RecordAudioPresenter.startRecordingByAvailableSource(RecordAudioPresenter.java:204)
at de.boxine.someapp.recording.audio.RecordAudioPresenter.access$500(RecordAudioPresenter.java:27)
at de.boxine.someapp.recording.audio.RecordAudioPresenter$2.onPermissionGranted(RecordAudioPresenter.java:116)
at de.boxine.someapp.mvp.AbstractPresenter.checkPermissionBeforeRun(AbstractPresenter.java:67)
at de.boxine.someapp.recording.audio.RecordAudioPresenter.onRecordButtonClicked(RecordAudioPresenter.java:108)
at de.boxine.someapp.recording.audio.RecordAudioFragment.lambda$onCreateView$0$RecordAudioFragment(RecordAudioFragment.java:112)
at de.boxine.someapp.recording.audio.-$$Lambda$RecordAudioFragment$9H7EZrHdPKO0BFbM_UTVYf_SFBU.onClick
at android.view.View.performClick(View.java:6659)
at android.view.View.performClickInternal(View.java:6631)
at android.view.View.access$3100(View.java:790)
at android.view.View$PerformClick.run(View.java:26187)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
I also have these permissions in my manifest (I ask them realtime on the recent Android versions)
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Thanks for the help.

Thanks guys for the help. I think the problem in my case was that the phone was in a call (or other communication app).
To prevent this I added this check before
public static boolean isMicrophoneAvailable() {
AudioManager audioManager = (AudioManager) MyApp.getAppContext().getSystemService(Context.AUDIO_SERVICE);
return audioManager.getMode() == MODE_NORMAL;
}

The use case I generally use is, I create the instance of the media recorder and start when the user presses the button. once the recording is finished and the user presses the stop button then I release the media recorder.
Here is my piece of code:
private fun startRecording() {
mRecorder = MediaRecorder()
try {
mRecorder.prepare()
mRecorder.start()
} catch (e: IOException) {
e.printStackTrace()
}
}
When the user presses the stop button then invoke the stopRecording method.
private fun stopRecording() {
mRecorder.stop()
mRecorder.release()
}
The same this you can apply in your application.
Note: The code is in Kotlin.

Related

Camera Error "Can't Connect to the Camera" or in some phones the error appear "Camera is using by another app"

I have implemented to record audio in background using Android MediaRecorder if the audio recording is in progress and user open's the native camera to record video, it gives
Camera Error "Can't Connect to the Camera"
or on some phones, the error appears as
Your camera is in use by another application
If I stop mediarecorder then the native camera video recording works fine, I searched for events to know when Camera is going to start video so then in my app I stop the mediarecorder, I found the BroadcastReceiver with filters
<receiver android:name=".receiver.CameraReceiver">
<intent-filter android:priority="10000">
<action android:name="android.Medintent.action.CAMERA_BUTTON" />
<action android:name="android.hardware.action.NEW_PICTURE" />
<action android:name="android.hardware.action.NEW_VIDEO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
</receiver>
NEW_VIDEO and NEW_PICTURE events fired when picture or video is captured and saved in the directory. Anyone knows how can this issue be solved? I want to know in my app the event when Native/Camera Apps going to record video. Thanks in advance
Even i had the same issue.
Once camera resource is being used by an application,untill it is released, you can use them in some other app or even a service.
If any service is using the camera resource, untill it releases the same we cannot use camera hardware.
You can doublecheck if camera hardware is being used using this code :-
private boolean isCameraInUse() {
Log.v(TAG, "isCameraInUse()");
boolean isCameraInUse = false;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)//Use Camera Api for Version Code < 23 and mCamera manager above it.
{
String cameraId = null;
CameraManager camManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
// Usually front mCamera is at 0 position.
try {
cameraId = camManager.getCameraIdList()[0];
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
isCameraInUse = true;
}
try {
camManager.setTorchMode(cameraId, true);
camManager.setTorchMode(cameraId, false);
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
isCameraInUse = true;
}
} else {
Camera c = null;
try {
c = Camera.open();
} catch (RuntimeException e) {
Log.e(TAG, Log.getStackTraceString(e));
turnFlashOff(mContext);
isCameraInUse = true;
} finally {
if (c != null) c.release();
}
}
return isCameraInUse;
}
CameraManager.AvailabilityCallback provides onCameraAvailable(String cameraId) method to detect whether a camera is available or not.
https://developer.android.com/reference/android/hardware/camera2/CameraManager.AvailabilityCallback.html
Getting all camera IDs is the same as the above as shown by #GAGAN.
CameraManager camManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
// Usually front mCamera is at position 0.
try {
cameraId = camManager.getCameraIdList()[0];
} catch (CameraAccessException e) {
Log.e(TAG, Log.getStackTraceString(e));
Log.cat("Error: ", e.getReason() + "");
if (e.CAMERA_DISABLED) { /* ENABLE CAMERA */ }
else if ( /* ... you can go to the link below to do various logic */ ) {
//...
}
else { /* do nothing */ }
}
https://developer.android.com/reference/android/hardware/camera2/CameraAccessException.html#CAMERA_IN_USE
Note: Until other applications use your camera hardware, they are using purposefully used by them. So until these apps don't free your hardware, you can't use that, it's clear. You can't know whether these apps actually need the camera. We believe process reserves hardware when it needs.
But, we can setup the listener on when camera becomes available (free) so that you can use it immediately.
CameraManager.AvailabilityCallback availabilityCallback = new CameraManager.AvailabilityCallback() {
#Override
public void onCameraAvailable(String cameraId) {
// your further stuffs. You must put all of your camera related logic here.
}
public void onCameraUnavailable(String cameraId) {
//you can logcat camera not available
}
};
The use of CameraManager.AvailabilityCallback abstract class is when you instantiate object, you do anonymous instantiation implementing callbacks like onCameraAvailable(), which is actually a listener that you are getting from the Camera Observer.
Answer: If you would have put your camera processing commands inside onCameraAvailable() callback, I guarantee, you wouldn't have got the error that you showed.
If the camera is used by other apps in the background, it means, those other apps are kind of buggy, because the camera needs foreground preview; they are not releasing the camera even after they are done with it. Nobody uses camera in background. Even in this case also, you are not supposed to kill camera process forcefully, because memory leaks can happen. Because you don't know how other processes are using the camera.

Method called after release

these days i was try to code a custom camera, but there's many bugs i can't figure out. the most serious error log as below.
at android.hardware.Camera.setHasPreviewCallback(Native Method)
at android.hardware.Camera.setPreviewCallback(Camera.java:600)
java.lang.RuntimeException: Method called after release()
at android.hardware.Camera.setHasPreviewCallback(Native Method)
at android.hardware.Camera.setPreviewCallback(Camera.java:600)
at com.baitian.wenta.customcamera.CameraActivity.cameraRelease(SourceFile:210)
at com.baitian.wenta.customcamera.CameraActivity.runExceptionHandle(SourceFile:526)
at com.baitian.wenta.customcamera.CameraActivity.updateFlashStatus(SourceFile:859)
at com.baitian.wenta.customcamera.CameraActivity.onResume(SourceFile:152)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1199)
at com.qihoo360.mobilesafe.loader.b.callActivityOnResume(SourceFile:123)
at android.app.Activity.performResume(Activity.java:5280)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2629)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2667)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2140)
at android.app.ActivityThread.access$700(ActivityThread.java:140)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1237)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
at dalvik.system.NativeStart.main(Native Method)
it seems the Method wouldn't called after release. i make the camera null everytime when i try to release it, and there is no multithread call.
here is the camera release code:
private void cameraRelease() {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCameraExceptionLog.addLog("Camera release() After", "");
mCamera = null;
}
} catch (Throwable e) {
}
}
here is the camera init code
private void initCamera() {
try {
if (mCamera == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(0);
} else {
mCamera = Camera.open();
}
mCamera.setErrorCallback(new ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
isCanAutoFouce = false;
CameraActivity.this.setResult(ERROR_KEY);
CameraActivity.this.finish();
}
});
setFocusAreaDefault();
}
} catch (Throwable e) {
runExceptionHandle(e);
}
}
Can you help me,thank you .
You need to add mCamera.setPreviewCallback(null) in between mCamera.stopPreview() and mCamera.release(). This way you cancel any callbacks coming.
I provided a complete cameraRelease method here
I see at least three possible problems with the log and posted code:
You might want to call mCamera.setPreviewCallback(null) after mCamera.stopPreview(); and before mCamera.release().
The log seems to refer to a Camera#setPreviewCallback() being called from the cameraRelease() method, which is not being shown on the posted code. Maybe the actual running code log was of an older version of the posted code.
It seems that you reach the cameraRelease() code from some sort of error handling. It might be the case that the error that happened to trigger the cameraRelease() code could have already released the camera because of some illegal state, hence any call inside this method, such as the mCamera.stopPreview() or mCamera.setPreviewCallback(null) calls would likely fail if the camera was already released at this point.
I had the similar issue. This line is the key line.
com.qihoo360.mobilesafe.loader.b.callActivityOnResume
From the log above, the camera permission is disabled by Qihoo 360 mobilesafe. After Camera.open(), although it return normally, the camera is released immediately. So you should check and catch the error after Camera.open().
try {
Camera camera = Camera.open();
if (camera != null) {
cmaera.getParamters(); // empty operation
}
} catch (Exception e) {
e.printStackTrack()
// oops, finish activity
}
As a third-party app, how Qihoo 360 mobilesafe can do like this? How it can perform permission management? This problem confuses me. Does it utilize unknown bug in system to do this?

Switching cameras for video on the fly on android

I know it is possible as the camera app that comes with my droid phone does it, but for the life of me, I don't seem to be able to switch cameras on the fly either for video or a standard camera (which leads me to suspect I'm not doing it right!)
Currently, I have an event for the button
btnSwitchCamera.Click += new EventHandler(btnSwitchCamera_Click);
prior to that, I check for the number of cameras - if there is only one camera, the event is not enabled.
The switch code looks like this
private void btnSwitchCamera_Click(object s, EventArgs e)
{
if (isBackCamera == false)
{
try
{
RunOnUiThread(delegate
{
camera.Release();
camera = Android.Hardware.Camera.Open(1);
});
}
catch (Java.Lang.RuntimeException)
{
alertMsg(context, Application.Context.Resources.GetString(Resource.String.videoErrorTitle),
Application.Context.Resources.GetString(Resource.String.videoFailToConnect));
return;
}
isBackCamera = true;
}
else
{
try
{
RunOnUiThread(delegate
{
camera.Release();
camera = Android.Hardware.Camera.Open(0);
});
}
catch (Java.Lang.RuntimeException)
{
alertMsg(context, Application.Context.Resources.GetString(Resource.String.videoErrorTitle),
Application.Context.Resources.GetString(Resource.String.videoFailToConnect));
return;
}
isBackCamera = false;
}
}
If I click the button, the app dies claiming that I cannot connect to the service.
The video record code is nothing special - it's a bog standard set the surface, do the holder, and start/stop recording.
Am I doing this right? From the docs, I need to release the camera then open the camera with the appropriate camera number (Android.Hardware.Camera.NumberOfCameras - 1)
The manifest is correctly set.
Thanks
Paul

Android media player returns IllegalStateException

I have following code to play small audio files
private void playVoice() {
if (mPlayVoice != null) {
if (mPlayVoice.isPlaying()) {
mPlayVoice.release();
mPlayVoice = null;
}
}
mPlayVoice =
MediaPlayer.create(BirdsActivity.this, mSoundIds[getCurrentIndex()]);
mPlayVoice.start();
}
It works fine in Samsung galaxy tab but gives below error in small device(I Checked in Sony xperia mini pro my project)
08-17 12:45:45.232: ERROR/AndroidRuntime(6639): java.lang.IllegalStateException
08-17 12:45:45.232: ERROR/AndroidRuntime(6639): at android.media.MediaPlayer.isPlaying(Native Method)
08-17 12:45:45.232: ERROR/AndroidRuntime(6639): at com.android.mds.kidsapps.alphakids.BirdsActivity.playVoice(BirdsActivity.java:146)
You're doing this:
PlayVoice.release();
Do you not mean
mPlayVoice.release();
If you have other issues this is the best document to consult:
Android MediaPlayer
EDIT
Ok if you are here: isPlaying() Invalid States it show's you're trying to call isPlaying() while the player is in the error state. So you need to work out why it is already in the error state.
In general, some playback control operation may fail due to various reasons, such as unsupported audio/video format, poorly interleaved audio/video, resolution too high, streaming timeout, and the like.
Have a look at adding an error listener: setOnErrorListener()
Use the following code as i was facing the same exception.
try {
if(mPlayVoice!=null && mPlayVoice.isPlaying()) {
Log.d("TAG------->", "player is running");
mPlayVoice.stop();
Log.d("Tag------->", "player is stopped");
mPlayVoice.release();
Log.d("TAG------->", "player is released");
}
} catch(Exception e){
}
Here write whatever you want to do. Actually the condition checking like isPlaying() or checking for null generates the IllegalStateException.....
You may have to clear the audioGroup joined with audioStream. Mine worked with the following code:
public static void audioPlayCaptureStop()
{
try
{
if(audioStream.isBusy())
{
audioGroup.clear();
audioStream.release();
System.out.println("audioStream released");
}
} catch (Exception e) {
System.out.println("audioStream release exception: "+e.toString());
}
}

How to check if an Android device has voice capabilities

Does anyone know a good way of programmaticaly checking if an Android device, phone or tablet, has voice capabilities?
By voice capabilities I mean capability to make phone calls. I know there are devices, like Galaxy tab in North America, that don't have this capability.
I haven't tried this myself, but it looks like the details you need would be in the TelephonyManager:
private boolean hasPhoneAbility()
{
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if(telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE)
return false;
return true;
}
I know this question was posted a long time ago, but I still thought I would post the solution I came up with that works for me so far, just so anyone with the same problem can benefit. (Because it seems like lots of people are having trouble finding a solution).
I just checked for the device's voicemail number, and obviously if it doesn't have one, then it is not a phone. In my code, to check this, it is tm.getVoiceMailNumber();
Here's what I did:
callButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
TelephonyManager tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
String ableToMakePhoneCalls = tm.getVoiceMailNumber(); //check device for voicemail number (null means no voicemail number).
if(ableToMakePhoneCalls == null){ //If the device does not have voicemail, then it must not be a phone. So it can't call.
//I displayed an alert dialog box here
}
else{
String phoneNum = "tel:8885554444";
Intent intentPhone = new Intent(android.content.Intent.ACTION_CALL);
intentPhone.setData(Uri.parse(phoneNum));
startActivity(intentPhone);
}
}
});
In theory, you should be able to use Intent.resolveActivity to do this. There's a problem (described here) with Galaxy tabs in particular. They evidently report that they have calling capability. You can even resolve an intent successfully. Unfortunately, it resolves to a no-op activity.
I would assume that prepare() would fail if there is not mic available:
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setOutputFile(audio_file);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
mRecorder.start();
} catch (Exception e) {}

Categories

Resources