¿Cómo programáticamente responder / finalizar una llamada en Android 4.1?

Estoy escribiendo una aplicación para Android en la que necesito responder una llamada entrante, hacer un poco de trabajo y luego finalizar la llamada. Después de buscar en Google, pude encontrar dos formas diferentes de lograr esto, las cuales no funcionan con las versiones recientes de Android, específicamente después de 4.1, Jelly Bean.

I.) Acceda a “com.android.internal.telephony.ITelephony” utilizando Java Reflection en el receptor Broadcast para “android.intent.action.PHONE_STATE”. El código de muestra siguiente se puede encontrar en cientos de publicaciones relacionadas:

public class PhoneCallReceiver extends BroadcastReceiver { Context context = null; private static final String TAG = "Phone call"; private ITelephony telephonyService; @Override public void onReceive(Context context, Intent intent) { if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) return; Log.v(TAG, "Receving...."); TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); try { Log.v(TAG, "Get getTeleService..."); Class c = Class.forName(telephony.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); telephonyService = (ITelephony) m.invoke(telephony); telephonyService.silenceRinger(); Log.v(TAG, "Answering Call now..."); telephonyService.answerRingingCall(); Log.v(TAG, "Call answered..."); //telephonyService.endCall(); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "FATAL ERROR: could not connect to telephony subsystem"); Log.e(TAG, "Exception object: " + e); } } } 

El problema con este código es que

  

es necesario para que este método funcione, y este permiso se ha definido como “solo para aplicaciones del sistema” de Android v 2.3. En resumen, las aplicaciones de usuario normales ya no pueden definir este permiso en el archivo de manifiesto.

II.) Otra forma es simular la pulsación del gancho de los auriculares que hace que Android responda la llamada. Esto se hace transmitiendo el “Intent.ACTION_MEDIA_BUTTON” como se muestra en el siguiente código.

 public class PhoneCallReceiver extends BroadcastReceiver { Context context = null; private static final String TAG = "Phone call"; @Override public void onReceive(Context context, Intent intent) { if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) return; String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON); answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(answer, null); Log.d(TAG, "Answered incoming call from: " + number); } return; } } 

Este método funciona hasta Android 4.1 después del cual Android ha restringido las aplicaciones de usuario de la transmisión “Intent.ACTION_MEDIA_BUTTON”.

Así que mi conclusión es que actualmente no hay forma de cómo podemos lograr esto en Android 4.1 o posterior.

¿Alguien más ha encontrado alguna otra solución o solución alternativa a este problema?

    Esto funciona desde Android 2.2 a 4.0 y ahora después de agregar la captura de prueba a la última línea funciona para 4.1.2 y 4.2 Francamente hablando, no sé cómo funciona pero funciona para mí.

     Log.d(tag, "InSecond Method Ans Call"); // froyo and beyond trigger on buttonUp instead of buttonDown Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED"); Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG); headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); headSetUnPluggedintent.putExtra("state", 0); headSetUnPluggedintent.putExtra("name", "Headset"); try { sendOrderedBroadcast(headSetUnPluggedintent, null); } catch (Exception e) { e.printStackTrace(); } 

    Esto está funcionando para mí en Android 4.1.2, así como también lo he probado en 4.2. Esto todavía da una excepción que se maneja.

    Editar para finalizar la llamada

    Espero que esto ayude a todas las personas que buscan una solución total para responder y finalizar la llamada.

     /** * Reject button click listener will reject the incoming call. */ private class RejectCallOnClickListener implements OnClickListener { @Override public void onClick(View v) { Log.d(tag, "OnRejectButton: " + "Reject OnClick"); ignoreCall(); exitCleanly(); } } /** * ignore incoming calls */ private void ignoreCall() { if (USE_ITELEPHONY) ignoreCallAidl(); else ignoreCallPackageRestart(); } /** * AIDL/ITelephony technique for ignoring calls */ private void ignoreCallAidl() { try { // telephonyService.silenceRinger(); telephonyService.endCall(); } catch (RemoteException e) { e.printStackTrace(); Log.d(tag, "ignoreCall: " + "Error: " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); Log.d(tag, "ignoreCall" + "Error: " + e.getMessage()); } } /** * package restart technique for ignoring calls */ private void ignoreCallPackageRestart() { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); am.restartPackage("com.android.providers.telephony"); am.restartPackage("com.android.phone"); } /** * cleanup and exit routine */ private void exitCleanly() { unHookReceiver(); this.finish(); } 

    Mi aplicación ha estado utilizando el siguiente código para responder el teléfono durante aproximadamente 6 meses:

     Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON); i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(i, null); 

    He probado esto en las versiones de Android de 2.2 a 4.2.2. No he visto una SecurityException transmitiendo “Intent.ACTION_MEDIA_BUTTON” en mi dispositivo de prueba 4.2.2, ni he visto informes de fallas de Play Store indicando que tales excepciones están ocurriendo.

    Diré que esto no siempre funciona. No funciona en dispositivos HTC debido al hecho de que los dispositivos HTC tienen un HeadsetObeserver que escucha la conexión real de un auricular con cable. Sin este evento, que actualmente es una SecurityException para la transmisión de una aplicación de terceros, se ignora HeadsetHook KeyEvent.

    Las respuestas anteriores son engañosas. El siguiente bloque de código no hace nada:

     Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG); headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); headSetUnPluggedintent.putExtra("state", 0); headSetUnPluggedintent.putExtra("name", "Headset"); try { sendOrderedBroadcast(headSetUnPluggedintent, null); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 

    excepto generar una SecurityException y atraparla.

    En las otras respuestas donde el código funciona, es debido a la transmisión de KeyEvent.KEYCODE_HEADSETHOOK.

    Como conclusión de este hilo, aquí está el código que funciona para mí para Android 4.2.2.

    -> La llamada se contesta simulando la pulsación del gancho del auricular y manteniendo la transmisión en try-catch como lo menciona @PravinDodia en un hilo de texto completo. (Observe que se lanza una excepción y se maneja en catch y la llamada se responde de todos modos. ¡Así que supongo que podemos ignorar esta excepción y seguir viviendo la vida como si nada hubiera sucedido!)

    -> La llamada se desconecta con ITelephony.

     public class PhoneCallReceiver extends BroadcastReceiver { Context context = null; private static final String TAG = "Phone call"; @Override public void onReceive(Context context, Intent intent) { if (!intent.getAction().equals("android.intent.action.PHONE_STATE")) return; else { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { answerPhoneHeadsethook(context, intent); return; } else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){ Log.d(TAG, "CALL ANSWERED NOW!!"); try { synchronized(this) { Log.d(TAG, "Waiting for 10 sec "); this.wait(10000); } } catch(Exception e) { Log.d(TAG, "Exception while waiting !!"); e.printStackTrace(); } disconnectPhoneItelephony(context); return; } else { Log.d(TAG, "ALL DONE ...... !!"); } } } public void answerPhoneHeadsethook(Context context, Intent intent) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); Log.d(TAG, "Incoming call from: " + number); Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); try { context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED"); Log.d(TAG, "ACTION_MEDIA_BUTTON broadcasted..."); } catch (Exception e) { Log.d(TAG, "Catch block of ACTION_MEDIA_BUTTON broadcast !"); } Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG); headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone headSetUnPluggedintent.putExtra("name", "Headset"); // TODO: Should we require a permission? try { context.sendOrderedBroadcast(headSetUnPluggedintent, null); Log.d(TAG, "ACTION_HEADSET_PLUG broadcasted ..."); } catch (Exception e) { // TODO Auto-generated catch block //e.printStackTrace(); Log.d(TAG, "Catch block of ACTION_HEADSET_PLUG broadcast"); Log.d(TAG, "Call Answered From Catch Block !!"); } Log.d(TAG, "Answered incoming call from: " + number); } Log.d(TAG, "Call Answered using headsethook"); } public static void disconnectPhoneItelephony(Context context) { ITelephony telephonyService; Log.v(TAG, "Now disconnecting using ITelephony...."); TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); try { Log.v(TAG, "Get getTeleService..."); Class c = Class.forName(telephony.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); telephonyService = (ITelephony) m.invoke(telephony); //telephonyService.silenceRinger(); Log.v(TAG, "Disconnecting Call now..."); //telephonyService.answerRingingCall(); //telephonyService.endcall(); Log.v(TAG, "Call disconnected..."); telephonyService.endCall(); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "FATAL ERROR: could not connect to telephony subsystem"); Log.e(TAG, "Exception object: " + e); } } } 

    Al menos la funcionalidad de desconexión funciona y sabemos cómo funciona. Entonces, aquellos que quieran desarrollar una aplicación de restricción de llamadas pueden seguir adelante. Para aquellos que como yo queremos responder una llamada, creo que podemos usar esto por ahora y solo esperamos que no deje de funcionar en la próxima versión.

    Pruebe esta respuesta para finalizar la llamada usando pro gtwigticalmente. esta trabajando bien para mi.

     try { String serviceManagerName = "android.os.ServiceManager"; String serviceManagerNativeName = "android.os.ServiceManagerNative"; String telephonyName = "com.android.internal.telephony.ITelephony"; Class telephonyClass; Class telephonyStubClass; Class serviceManagerClass; Class serviceManagerStubClass; Class serviceManagerNativeClass; Class serviceManagerNativeStubClass; Method telephonyCall; Method telephonyEndCall; Method telephonyAnswerCall; Method getDefault; Method[] temps; Constructor[] serviceManagerConstructor; // Method getService; Object telephonyObject; Object serviceManagerObject; telephonyClass = Class.forName(telephonyName); telephonyStubClass = telephonyClass.getClasses()[0]; serviceManagerClass = Class.forName(serviceManagerName); serviceManagerNativeClass = Class.forName(serviceManagerNativeName); Method getService = // getDefaults[29]; serviceManagerClass.getMethod("getService", String.class); Method tempInterfaceMethod = serviceManagerNativeClass.getMethod( "asInterface", IBinder.class); Binder tmpBinder = new Binder(); tmpBinder.attachInterface(null, "fake"); serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder); IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone"); Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class); telephonyObject = serviceMethod.invoke(null, retbinder); //telephonyCall = telephonyClass.getMethod("call", String.class); telephonyEndCall = telephonyClass.getMethod("endCall"); //telephonyAnswerCall = telephonyClass.getMethod("answerRingingCall"); telephonyEndCall.invoke(telephonyObject); } catch (Exception e) { e.printStackTrace(); Log.error(DialerActivity.this, "FATAL ERROR: could not connect to telephony subsystem"); Log.error(DialerActivity.this, "Exception object: " + e); } 

    Prueba esto :

     Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED"); // froyo and beyond trigger on buttonUp instead of buttonDown Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON); buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED"); 

    Agregue permisos en el archivo AndroidManifest.xml como

      

    El método ITelephony no funciona en 4.4 y me parece que el método del botón de auriculares / medios aún permite un timbre bastante largo antes de la interrupción.

    Esta publicación de blog de chaps muestra un nuevo método que he probado como trabajar en 4.4.2 Galaxy s4 y HTC one mini que cuelga mucho más rápido y tampoco recibes una llamada perdida.

    http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-android-4.html

    La técnica utiliza un ejecutable en tiempo de ejecución como se muestra a continuación, aparentemente es posible que deba usar un número diferente para algunos dispositivos.

     public class HangupPhoneCallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) { Executor eS = Executors.newSingleThreadExecutor(); eS.execute(new Runnable() { @Override public void run() { Runtime runtime = Runtime.getRuntime(); try { Log.d(TAG, "service call phone 5 \n"); runtime.exec("service call phone 5 \n"); } catch (Exception exc) { Log.e(TAG, exc.getMessage()); } } }); return; } } } 

    La desconexión de llamadas con IT Telephony no funciona en algunos dispositivos como Samsung S Duos. Pero aún puedes silenciar el timbre 🙂