Servicio de grabación / procesamiento de llamadas! – Android

Hola,
Estoy trabajando en una solución para Android que grabe las llamadas (tanto fuera y incomming) y procesará más los datos grabados (en el punto final de mi aplicación, no se guardará ningún archivo de audio en la memoria del teléfono). He implementado BroadcastReceiver con PhoneStateListener.LISTEN_CALL_STATE que inicia un servicio de grabación si el estado es CALL_STATE_OFFHOOK. Entonces en el servicio comienzo un nuevo hilo que intenta registrar una llamada, y otro BroadcastReceiver con PhoneStateListener.LISTEN_CALL_STATE, que llama a un método para parar la grabación si el estado del teléfono se cambia a TelephonyManager.CALL_STATE_IDLE.

El archivo de audio creado está vacío. Se genera excepción cuando se llama al método recorder.stop () en mi servicio. ¿Dónde está un error? ¿Qué podría hacer mejor?

Primera (independiente) BroadcastReceiver:

package com.piotr.callerrecogniser; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; public class CallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { MyPhoneStateListener phoneListener = new MyPhoneStateListener(context); TelephonyManager telephony = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE); telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE); } class MyPhoneStateListener extends PhoneStateListener { public static final String tag = "CallerRecogniser - CallReceiver"; private Context context; MyPhoneStateListener(Context c) { super(); context = c; } public static final int NOTIFICATION_ID_RECEIVED = 0x1221; @Override public void onCallStateChanged(int state, String incomingNumber) { SharedPreferences preferences = context.getSharedPreferences("CallReceiver", Context.MODE_PRIVATE); switch (state) { case TelephonyManager.CALL_STATE_IDLE: break; //If call is answered, run recording service. Also pass "phone_number" variable with incomingNumber to shared prefs, so service will be able to access that via shared prefs. case TelephonyManager.CALL_STATE_OFFHOOK: // The call is answered String phone_number = preferences.getString("phone_number",null); Intent serv = new Intent(context, CallRecordingService.class); serv.putExtra("number", phone_number); context.startService(serv); break; //If phone is ringing, save phone_number. This is done because incomingNumber is not saved on CALL_STATE_OFFHOOK case TelephonyManager.CALL_STATE_RINGING: SharedPreferences.Editor editor = preferences.edit(); editor.putString("phone_number", incomingNumber); editor.commit(); break; } } } } 

Servicio:

 package com.piotr.callerrecogniser; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaRecorder; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; public class CallRecordingService extends Service implements Runnable { CallerRecogniserDB database = new CallerRecogniserDB(this); String phoneNumber; MediaRecorder recorder; private final Handler mHandler = new Handler(); private final BroadcastReceiver myCallStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { MyPhoneStateListener phoneListener = new MyPhoneStateListener( context); TelephonyManager telephony = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE); telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE); } class MyPhoneStateListener extends PhoneStateListener { private Context context; MyPhoneStateListener(Context c) { super(); context = c; } @Override public void onCallStateChanged(int state, String incomingNumber) { switch (state) { case TelephonyManager.CALL_STATE_IDLE: if (recording) { NotificationManager mNotificationManager = (NotificationManager) context .getSystemService(Context.NOTIFICATION_SERVICE); Notification not; not = new Notification(R.drawable.ic_launcher, "Phone call being processed", System.currentTimeMillis()); Intent notIntent = new Intent(); PendingIntent contentIntent = PendingIntent .getActivity(context, 0, notIntent, 0); not.setLatestEventInfo(context, "CallRecordingService", "Notification from idle", contentIntent); mNotificationManager.notify(NOTIFICATION_ID_RECEIVED, not); stopRecording(); } break; } } } }; private static boolean recording = false; private String INCOMING_CALL_ACTION = "android.intent.action.PHONE_STATE"; @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); IntentFilter intentToReceiveFilter = new IntentFilter(); intentToReceiveFilter.addAction(INCOMING_CALL_ACTION); this.registerReceiver(myCallStateReceiver, intentToReceiveFilter, null, mHandler); Thread aThread = new Thread(this); aThread.start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub super.onStart(intent, startId); phoneNumber = intent.getExtras().getString("number"); return START_STICKY; } @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } public static final int NOTIFICATION_ID_RECEIVED = 0x1221; @Override public void run() { Looper.myLooper(); Looper.prepare(); // TODO Auto-generated method stub database.open(); String[] numbers = database.getData(CallerRecogniserDB.KEY_NUMBER); int index = -1; outside_for: for (int i = 0; i < numbers.length; i++) { String[] splitted = numbers[i].split(","); for (String nu : splitted) { if (nu.equals(phoneNumber)) { index = i; break outside_for; } } } database.close(); if (index >= 0) { // Phone number is in a database and it's state==0 => // it has no data recorded // Notification that call is recorded recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setOutputFile(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/" + phoneNumber + ".3gp"); try { recorder.prepare(); } catch (Exception e) { e.printStackTrace(); } recording = true; recorder.start(); } } void stopRecording() { // Then clean up with when it hangs up: recorder.stop(); recorder.release(); recording=false; } } 

Permisos:

 <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

Salida de excepción:

 11-28 14:28:44.385: E/MediaRecorder(22438): stop called in an invalid state: 8 11-28 14:28:44.400: D/AndroidRuntime(22438): Shutting down VM 11-28 14:28:44.425: W/dalvikvm(22438): threadid=1: thread exiting with uncaught exception (group=0x4001e578) 11-28 14:28:44.435: E/AndroidRuntime(22438): FATAL EXCEPTION: main 11-28 14:28:44.435: E/AndroidRuntime(22438): java.lang.IllegalStateException 11-28 14:28:44.435: E/AndroidRuntime(22438): at android.media.MediaRecorder.stop(Native Method) 11-28 14:28:44.435: E/AndroidRuntime(22438): at com.piotr.callerrecogniser.CallRecordingService.stopRecording(CallRecordingService.java:159) 11-28 14:28:44.435: E/AndroidRuntime(22438): at com.piotr.callerrecogniser.CallRecordingService$1$MyPhoneStateListener.onCallStateChanged(CallRecordingService.java:65) 11-28 14:28:44.435: E/AndroidRuntime(22438): at android.telephony.PhoneStateListener$2.handleMessage(PhoneStateListener.java:369) 11-28 14:28:44.435: E/AndroidRuntime(22438): at android.os.Handler.dispatchMessage(Handler.java:99) 11-28 14:28:44.435: E/AndroidRuntime(22438): at android.os.Looper.loop(Looper.java:123) 11-28 14:28:44.435: E/AndroidRuntime(22438): at android.app.ActivityThread.main(ActivityThread.java:3691) 11-28 14:28:44.435: E/AndroidRuntime(22438): at java.lang.reflect.Method.invokeNative(Native Method) 11-28 14:28:44.435: E/AndroidRuntime(22438): at java.lang.reflect.Method.invoke(Method.java:507) 11-28 14:28:44.435: E/AndroidRuntime(22438): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847) 11-28 14:28:44.435: E/AndroidRuntime(22438): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605) 11-28 14:28:44.435: E/AndroidRuntime(22438): at dalvik.system.NativeStart.main(Native Method) 

He probado su código con pequeñas modificaciones en mi Samsung Galaxy S2. Es woks bastante bueno sin excepciones. La llamada se grabó correctamente utilizando el servicio. La única edición que encontré es que el servicio fue comenzado dos veces (el receptor de la difusión fue comenzado dos veces y TelephonyManager.PhoneStateListner llamó dos veces con estado OFFHOOK) Tengo que cambiar constante en el método setAudioSource de VOICE_CALL a VOICE_UPLINK. Parece que la definición de enum de Java y enum en el encabezado de C son diferentes. Se grabó la llamada para ambos lados (UPLINK y DOWNLINK)

La documentación de android indica que esta excepción se lanzará si stop () se llama antes de start ().

Añadiría un cheque para asegurarse de que su indicador de grabación esté configurado antes de llamar al método stop (). Esto probablemente se deshará de su error.

Sin embargo, por alguna razón start () nunca se llama, posiblemente debido a su código de base de datos, y que es su problema real.

Pejter,

Tuve un requisito similar cuando intenté portar a Android una aplicación que desarrollé para teléfonos Symbian.

Después de algunas investigaciones llegué a la triste conclusión de que esto es probablemente imposible en la mayoría (tal vez incluso en todos) los teléfonos Android disponibles en la actualidad.

A pesar de tener la API necesaria desde Android 1.6, la mayoría de los teléfonos Android no son compatibles con esto debido a la arquitectura de hardware o la implementación de firmware. Básicamente, el problema es que el procesador principal del teléfono no tiene acceso al flujo de audio de banda base. Normalmente tiene acceso al micrófono local, por lo que en algunos teléfonos es posible grabar el usuario local y posiblemente una débil señal del usuario remoto que llega al micrófono a través del acoplamiento acústico entre el auricular y el micrófono. Esto no era lo suficientemente bueno para los requisitos de mi aplicación, así que todavía no tengo una versión de Android.

Sé que no estoy resolviendo su problema (y tal vez incluso no relacionado con la excepción que recibe), pero tal vez le ahorrará de perder su tiempo.

Para obtener más información, puede echar un vistazo a lo siguiente:

  1. http://code.google.com/p/android/issues/detail?id=2117

  2. https://groups.google.com/d/topic/android-developers/AbU85mtDgQw/discussion

Intenta usar

 mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); 

en lugar de

 mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL); 
  • Diálogo de alerta del servicio de Android
  • Detectar si mi servicio de accesibilidad está habilitado
  • Cómo crear una actividad y un servicio de Android que utilicen procesos independientes
  • Notificaciones diarias a ciertas horas
  • ¿Cómo puede un servicio escuchar los gestos / eventos táctiles?
  • Servicio y un BroadCastReceiver
  • Inicio del servicio desde otro servicio
  • Cámara Android: se ejecuta en segundo plano desde el servicio
  • ¿Cuáles son las ventajas y desventajas de ejecutar un servicio en un proceso diferente?
  • Parámetro startId de servicio de Android
  • Android: Cómo enviar la solicitud http a través del servicio cada 10 segundos
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.