¿Cómo se pueden responder las llamadas entrantes mediante progtwigción en Android 5.0 (Lollipop)?

Como estoy tratando de crear una pantalla personalizada para las llamadas entrantes, estoy tratando de responder programáticamente a una llamada entrante. Estoy usando el siguiente código pero no funciona en Android 5.0.

// Simulate a press of the headset button to pick up the call 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"); 

Actualización con Android 8.0 Oreo

Aunque originalmente se solicitó la compatibilidad de Android L con la pregunta, la gente todavía parece estar respondiendo esta pregunta y respuesta, por lo que vale la pena describir las mejoras introducidas en Android 8.0 Oreo. Los métodos compatibles con versiones anteriores se describen a continuación.

¿Qué cambió?

A partir de Android 8.0 Oreo , el grupo de permisos TELÉFONO también contiene el permiso ANSWER_PHONE_CALLS . Tal como lo indica el nombre del permiso, al mantenerlo, permite que su aplicación acepte las llamadas entrantes a través de una API correcta sin ningún tipo de intrusión alrededor del sistema mediante la reflexión o la simulación del usuario.

¿Cómo utilizamos este cambio?

Debería verificar la versión del sistema en tiempo de ejecución si está soportando versiones anteriores de Android para que pueda encapsular esta nueva llamada API mientras mantiene el soporte para esas versiones anteriores de Android. Debe seguir solicitando permisos en tiempo de ejecución para obtener ese nuevo permiso durante el tiempo de ejecución, como es estándar en las versiones más nuevas de Android.

Después de haber obtenido el permiso, su aplicación simplemente tiene que llamar al método acceptRingingCall de TelecomManager . Una invocación básica se ve de la siguiente manera:

 TelecomManager tm = (TelecomManager) mContext .getSystemService(Context.TELECOM_SERVICE); if (tm == null) { // whether you want to handle this is up to you really throw new NullPointerException("tm == null"); } tm.acceptRingingCall(); 

Método 1: TelephonyManager.answerRingingCall ()

Para cuando tienes control ilimitado sobre el dispositivo.

¿Que es esto?

Existe TelephonyManager.answerRingingCall () que es un método interno oculto. Funciona como un puente para ITelephony.answerRingingCall () que se ha debatido en las interwebs y parece prometedor desde el principio. No está disponible en 4.4.2_r1, ya que se introdujo solo en commit 83da75d para Android 4.4 KitKat ( línea 1537 en 4.4.3_r1 ) y luego “reintroduced” en commit f1e1e77 para Lollipop ( línea 3138 en 5.0.0_r1 ) debido a cómo el El árbol de Git fue estructurado. Esto significa que, a menos que solo admita dispositivos con Lollipop, lo que probablemente sea una mala decisión basada en su pequeña cuota de mercado hasta el momento, aún necesita proporcionar métodos alternativos si continúa por esta ruta.

¿Cómo usaríamos esto?

Como el método en cuestión está oculto para el uso de las aplicaciones SDK, debe usar la reflexión para examinar dinámicamente y usar el método durante el tiempo de ejecución. Si no está familiarizado con la reflexión, puede leer rápidamente ¿Qué es reflexión y por qué es útil? . También puede profundizar en los detalles en Trail: The Reflection API si está interesado en hacerlo.

¿Y cómo se ve eso en el código?

 // set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("answerRingingCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e); } 

¡Esto es demasiado bueno para ser verdad!

En realidad, hay un pequeño problema. Este método debe ser completamente funcional, pero el administrador de seguridad quiere que las personas que llaman mantengan android.permission.MODIFY_PHONE_STATE . Este permiso está en el ámbito de las características solo parcialmente documentadas del sistema, ya que no se espera que los terceros lo toquen (como puede ver en la documentación correspondiente). Puedes intentar agregar un pero no servirá de nada porque el nivel de protección para este permiso es signature | system ( mira la línea 1201 de core / AndroidManifest en 5.0.0_r1 ).

Puede leer el Issue 34785: Update android: protectionLevel documentation que se creó en 2012 para ver que nos faltan detalles sobre la “syntax de la tubería” específica, pero a partir de la experimentación, parece que debe funcionar como un ‘AND’, que significa todo el las banderas especificadas deben cumplirse para el permiso que se otorgará. Trabajando bajo esa suposición, significa que debe tener su aplicación:

  1. Instalado como una aplicación de sistema.

    Esto debería estar bien y podría lograrse pidiendo a los usuarios que instalen usando un ZIP en recuperación, como cuando enrutan o instalan aplicaciones de Google en ROM personalizados que no tienen ya empaquetadas.

  2. Firmado con la misma firma que frameworks / base también conocido como el sistema, también conocido como ROM.

    Aquí es donde surgen los problemas. Para hacer esto, necesita tener en sus manos las claves utilizadas para firmar frameworks / base. No solo tendrías que acceder a las claves de Google para las imágenes de fábrica de Nexus, sino que también tendrías que tener acceso a las llaves de todos los demás fabricantes de equipos originales y desarrolladores de ROM. Esto no parece plausible por lo que puede hacer que su aplicación se firme con las claves del sistema ya sea haciendo una ROM personalizada y pidiéndoles a los usuarios que se cambien a ella (lo que podría ser difícil) o encontrando un exploit con el que se puede pasar por alto el nivel de protección de permisos. (que también puede ser difícil).

Además, este comportamiento parece estar relacionado con el Issue 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS ya no funciona y utiliza el mismo nivel de protección junto con una bandera de desarrollo no documentada.

Trabajar con TelephonyManager suena bien, pero no funcionará a menos que obtenga el permiso correspondiente, lo cual no es tan fácil de hacer en la práctica.

¿Qué hay de usar TelephonyManager de otras maneras?

Lamentablemente, parece que necesitas que uses android.permission.MODIFY_PHONE_STATE para usar las herramientas geniales, lo que a su vez significa que vas a tener dificultades para acceder a esos métodos.


Método 2: llamada de servicio CÓDIGO DE SERVICIO

Para cuando puede probar que la comstackción que se ejecuta en el dispositivo funcionará con el código especificado.

Sin poder interactuar con TelephonyManager, también existe la posibilidad de interactuar con el servicio a través del ejecutable del service .

¿Como funciona esto?

Es bastante simple, pero hay incluso menos documentación sobre esta ruta que otras. Sabemos con certeza que el ejecutable tiene dos argumentos: el nombre del servicio y el código.

  • El nombre del servicio que queremos usar es teléfono .

    Esto se puede ver ejecutando la service list .

  • El código que queremos usar parece haber sido 6 pero ahora parece ser 5 .

    Parece que se ha basado en IBinder.FIRST_CALL_TRANSACTION + 5 para muchas versiones ahora (de 1.5_r4 a 4.4.4_r1 ) pero durante las pruebas locales el código 5 funcionaba para responder una llamada entrante. Como Lollipo es una actualización masiva, es comprensible que las internas también cambien aquí.

Esto resulta con un comando de service call phone 5 de service call phone 5 .

¿Cómo utilizamos esto programáticamente?

Java

El siguiente código es una implementación aproximada hecha para funcionar como una prueba de concepto. Si realmente desea seguir adelante y utilizar este método, probablemente desee consultar las pautas para el uso sin problemas de su suceso y, posiblemente, cambiar al libsuperuser más desarrollado por Chainfire .

 try { Process proc = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(proc.getOutputStream()); os.writeBytes("service call phone 5\n"); os.flush(); os.writeBytes("exit\n"); os.flush(); if (proc.waitFor() == 255) { // TODO handle being declined root access // 255 is the standard code for being declined root for SU } } catch (IOException e) { // TODO handle I/O going wrong // this probably means that the device isn't rooted } catch (InterruptedException e) { // don't swallow interruptions Thread.currentThread().interrupt(); } 

Manifiesto

   

¿Esto realmente requiere acceso a la raíz?

Lamentablemente, parece que sí. Puede intentar usar Runtime.exec en él, pero no pude obtener ninguna suerte con esa ruta.

¿Qué tan estable es esto?

Me alegra que hayas preguntado. Debido a que no está documentado, esto puede dividirse en varias versiones, como lo ilustra la aparente diferencia de código anterior. El nombre del servicio probablemente debería permanecer en varias versiones del teléfono , pero, por lo que sabemos, el valor del código puede cambiar en varias comstackciones de la misma versión (modificaciones internas, por ejemplo, del aspecto del OEM), rompiendo el método utilizado. Por lo tanto, vale la pena mencionar que las pruebas se llevaron a cabo en un Nexus 4 (mako / occam). Personalmente, le aconsejo que no use este método, pero como no puedo encontrar un método más estable, creo que esta es la mejor opción.


Método original: intenciones del código clave del auricular

Para los tiempos cuando tienes que conformarte.

La siguiente sección estuvo fuertemente influenciada por esta respuesta de Riley C.

El método de bash simulado de los auriculares tal como se publicó en la pregunta original parece transmitirse tal como cabría esperar, pero no parece lograr el objective de responder la llamada. Si bien parece haber un código en el lugar que debe manejar esos bashs, simplemente no se les está prestando atención, lo que tiene que significar que debe haber algún tipo de nuevas contramedidas en su lugar contra este método. El registro no muestra nada de interés tampoco y personalmente no creo que valga la pena explorar a través de la fuente de Android solo por la posibilidad de que Google introduzca un ligero cambio que fácilmente rompa el método utilizado de todos modos.

¿Hay algo que podamos hacer ahora mismo?

El comportamiento se puede reproducir consistentemente utilizando el ejecutable de entrada. Toma un argumento de código clave , para el cual simplemente pasamos en KeyEvent.KEYCODE_HEADSETHOOK . El método ni siquiera requiere acceso raíz, por lo que es adecuado para casos de uso común en el público en general, pero hay un pequeño inconveniente en el método: no se puede especificar que el evento del botón del auricular requiera un permiso, lo que significa que funciona como un verdadero El botón presiona y burbujea a lo largo de toda la cadena, lo que a su vez significa que debe tener cuidado al momento de simular presionar el botón, por ejemplo, puede activar el reproductor de música para iniciar la reproducción si nadie más de mayor prioridad está listo para manejarlo. el evento.

¿Código?

 new Thread(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { // Runtime.exec(String) had an I/O problem, try to fall back String enforcedPerm = "android.permission.CALL_PRIVILEGED"; Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); mContext.sendOrderedBroadcast(btnDown, enforcedPerm); mContext.sendOrderedBroadcast(btnUp, enforcedPerm); } } }).start(); 

tl; dr

Hay una buena API pública para Android 8.0 Oreo y posterior.

No hay API pública antes de Android 8.0 Oreo. Las API internas están fuera de los límites o simplemente sin documentación. Debes proceder con precaución.

La solución totalmente funcional se basa en el código de @Valter Strods.

Para que funcione, debe mostrar una actividad (invisible) en la pantalla de locking donde se ejecuta el código.

AndroidManifest.xml

     

Actividad de aceptar llamada

 package com.mysms.android.lib.activity; import android.app.Activity; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.telephony.TelephonyManager; import android.view.KeyEvent; import android.view.WindowManager; import org.apache.log4j.Logger; import java.io.IOException; public class AcceptCallActivity extends Activity { private static Logger logger = Logger.getLogger(AcceptCallActivity.class); private static final String MANUFACTURER_HTC = "HTC"; private KeyguardManager keyguardManager; private AudioManager audioManager; private CallStateReceiver callStateReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); } @Override protected void onResume() { super.onResume(); registerCallStateReceiver(); updateWindowFlags(); acceptCall(); } @Override protected void onPause() { super.onPause(); if (callStateReceiver != null) { unregisterReceiver(callStateReceiver); callStateReceiver = null; } } private void registerCallStateReceiver() { callStateReceiver = new CallStateReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); registerReceiver(callStateReceiver, intentFilter); } private void updateWindowFlags() { if (keyguardManager.inKeyguardRestrictedInputMode()) { getWindow().addFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } else { getWindow().clearFlags( WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } } private void acceptCall() { // for HTC devices we need to broadcast a connected headset boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER) && !audioManager.isWiredHeadsetOn(); if (broadcastConnected) { broadcastHeadsetConnected(false); } try { try { logger.debug("execute input keycode headset hook"); Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { // Runtime.exec(String) had an I/O problem, try to fall back logger.debug("send keycode headset hook intents"); String enforcedPerm = "android.permission.CALL_PRIVILEGED"; Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra( Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); sendOrderedBroadcast(btnDown, enforcedPerm); sendOrderedBroadcast(btnUp, enforcedPerm); } } finally { if (broadcastConnected) { broadcastHeadsetConnected(false); } } } private void broadcastHeadsetConnected(boolean connected) { Intent i = new Intent(Intent.ACTION_HEADSET_PLUG); i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); i.putExtra("state", connected ? 1 : 0); i.putExtra("name", "mysms"); try { sendOrderedBroadcast(i, null); } catch (Exception e) { } } private class CallStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { finish(); } } } 

Estilo

  

¡Finalmente llama a la magia!

 Intent intent = new Intent(context, AcceptCallActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(intent); 

El siguiente es un enfoque alternativo que funcionó para mí. Envía el evento clave al servidor de telecomunicaciones directamente utilizando la API MediaController. Esto requiere que la aplicación tenga el permiso BIND_NOTIFICATION_LISTENER_SERVICE y que el usuario le conceda acceso explícito de Notificación:

 @TargetApi(Build.VERSION_CODES.LOLLIPOP) void sendHeadsetHookLollipop() { MediaSessionManager mediaSessionManager = (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE); try { List mediaControllerList = mediaSessionManager.getActiveSessions (new ComponentName(getApplicationContext(), NotificationReceiverService.class)); for (MediaController m : mediaControllerList) { if ("com.android.server.telecom".equals(m.getPackageName())) { m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); log.info("HEADSETHOOK sent to telecom server"); break; } } } catch (SecurityException e) { log.error("Permission error. Access to notification not granted to the app."); } } 

NotificationReceiverService.class en el código anterior podría ser solo una clase vacía.

 import android.service.notification.NotificationListenerService; public class NotificationReceiverService extends NotificationListenerService{ public NotificationReceiverService() { } } 

Con la sección correspondiente en el manifiesto:

      

Dado que el objective del evento es explícito, esto probablemente debería evitar cualquier efecto secundario de activar el reproductor multimedia.

Nota: el servidor de telecomunicaciones puede no estar inmediatamente activo después del evento de llamada. Para que esto funcione de manera confiable, puede ser útil para la aplicación implementar MediaSessionManager.OnActiveSessionsChangedListener para monitorear cuándo se activa el servidor de telecomunicaciones antes de enviar el evento.

Actualizar:

En Android O , es necesario simular ACTION_DOWN antes de ACTION_UP , de lo contrario, lo anterior no tiene ningún efecto. es decir, se necesita lo siguiente:

 m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK)); 

Pero dado que está disponible una llamada oficial para responder llamada desde Android O (ver respuesta superior), es posible que ya no sea necesario este truco, a menos que uno tenga un viejo API de comstackción antes de Android O.

Para elaborar un poco sobre la respuesta de @Muzikant, y modificarlo un poco para que funcione un poco más limpio en mi dispositivo, intente input keyevent 79 , la constante para KeyEvent.KEYCODE_HEADSETHOOK . Muy aproximadamente:

  new Thread(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec( "input keyevent " + KeyEvent.KEYCODE_HEADSETHOOK ); } catch (Throwable t) { // do something proper here. } } }).start(); 

Perdonar las convenciones de encoding bastante malas, no estoy muy versado en las llamadas Runtime.exec (). Tenga en cuenta que mi dispositivo no está rooteado, ni estoy solicitando privilegios de administrador.

El problema con este enfoque es que solo funciona bajo ciertas condiciones (para mí). Es decir, si ejecuto el hilo anterior desde una opción de menú que el usuario selecciona mientras suena una llamada, la llamada responde muy bien. Si lo ejecuto desde un receptor que monitorea el estado de las llamadas entrantes, se ignora por completo.

Por lo tanto, en mi Nexus 5 funciona bien para las respuestas dirigidas por el usuario y debe adaptarse al propósito de una pantalla de llamada personalizada. Simplemente no funcionará para ningún tipo de aplicaciones automatizadas de tipo call-control.

También deben tenerse en cuenta todas las advertencias posibles, incluso que esto también dejará de funcionar en una actualización o dos.

a través de los comandos adb Cómo recoger una llamada por adb

Tenga en cuenta que Android es Linux con una JVM masiva en el front-end. Puede descargar una aplicación de línea de comando y rootear el teléfono, y ahora tiene una computadora y una línea de comandos Linux normales que hacen todas las cosas normales. Ejecutar scripts, incluso puede ssh a él (truco OpenVPN)

Gracias @notz la respuesta de está trabajando para mí en el Lolillop. Para que este código funcione con el antiguo SDK de Android, puedes hacer este código:

 if (Build.VERSION.SDK_INT >= 21) { Intent answerCalintent = new Intent(context, AcceptCallActivity.class); answerCalintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); context.startActivity(answerCalintent); } else { if (telephonyService != null) { try { telephonyService.answerRingingCall(); } catch (Exception e) { answerPhoneHeadsethook(); } } } 

Cómo activar el altavoz después de responder automáticamente las llamadas.

Resolví mi problema anterior con setSpeakerphoneOn. Creo que vale la pena publicarlo aquí, ya que el caso de uso para responder automáticamente una llamada telefónica a menudo también requiere que el altavoz sea útil. Gracias de nuevo a todos en este hilo, qué gran trabajo.

Esto funciona para mí en Android 5.1.1 en mi Nexus 4 sin ROOT. 😉

Permiso requerido:

  

Código de Java:

 // this means the phone has answered if(state==TelephonyManager.CALL_STATE_OFFHOOK) { // try and turn on speaker phone final Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { AudioManager audioManager = (AudioManager) localContext.getSystemService(Context.AUDIO_SERVICE); // this doesnt work without android.permission.MODIFY_PHONE_STATE // audioManager.setMode(AudioManager.MODE_IN_CALL); // weirdly this works audioManager.setMode(AudioManager.MODE_NORMAL); // this is important audioManager.setSpeakerphoneOn(true); // note the phone interface won't show speaker phone is enabled // but the phone speaker will be on // remember to turn it back off when your done ;) } }, 500); // half a second delay is important or it might fail } 

Ejecute el siguiente comando como root:

 input keyevent 5 

Más detalles sobre cómo simular keyevents aquí .

Puede usar esta clase base que creé para ejecutar comandos como raíz desde su aplicación.

Pruebe esto: primero agregue los permisos y luego use killCall () para colgar use answerCall () para contestar la llamada

   public void killCall() { try { TelephonyManager telephonyManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE); Class classTelephony = Class.forName(telephonyManager.getClass().getName()); Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony"); methodGetITelephony.setAccessible(true); Object telephonyInterface = methodGetITelephony.invoke(telephonyManager); Class telephonyInterfaceClass = Class.forName(telephonyInterface.getClass().getName()); Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall"); methodEndCall.invoke(telephonyInterface); } catch (Exception ex) { Log.d(TAG, "PhoneStateReceiver **" + ex.toString()); } } public void answerCall() { try { Runtime.getRuntime().exec("input keyevent " + Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK)); } catch (IOException e) { answerRingingCallWithIntent(); } } public void answerRingingCallWithIntent() { try { Intent localIntent1 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent1.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent1.putExtra("state", 1); localIntent1.putExtra("microphone", 1); localIntent1.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent1, "android.permission.CALL_PRIVILEGED"); Intent localIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent1 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK); localIntent2.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent1); getContext().sendOrderedBroadcast(localIntent2, "android.permission.CALL_PRIVILEGED"); Intent localIntent3 = new Intent(Intent.ACTION_MEDIA_BUTTON); KeyEvent localKeyEvent2 = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK); localIntent3.putExtra(Intent.EXTRA_KEY_EVENT, localKeyEvent2); getContext().sendOrderedBroadcast(localIntent3, "android.permission.CALL_PRIVILEGED"); Intent localIntent4 = new Intent(Intent.ACTION_HEADSET_PLUG); localIntent4.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); localIntent4.putExtra("state", 0); localIntent4.putExtra("microphone", 1); localIntent4.putExtra("name", "Headset"); getContext().sendOrderedBroadcast(localIntent4, "android.permission.CALL_PRIVILEGED"); } catch (Exception e2) { e2.printStackTrace(); } } 

Para su información, si le interesa cómo finalizar una llamada en curso con Android O, el Method 1: TelephonyManager.answerRingingCall() Valter Method 1: TelephonyManager.answerRingingCall() funciona si cambia el método que invoca para que sea endCall .

Solo requiere el permiso android.permission.CALL_PHONE .

Aquí está el código:

 // set the logging tag constant; you probably want to change this final String LOG_TAG = "TelephonyAnswer"; public void endCall() { TelephonyManager tm = (TelephonyManager) mContext .getSystemService(Context.TELEPHONY_SERVICE); try { if (tm == null) { // this will be easier for debugging later on throw new NullPointerException("tm == null"); } // do reflection magic tm.getClass().getMethod("endCall").invoke(tm); } catch (Exception e) { // we catch it all as the following things could happen: // NoSuchMethodException, if the answerRingingCall() is missing // SecurityException, if the security manager is not happy // IllegalAccessException, if the method is not accessible // IllegalArgumentException, if the method expected other arguments // InvocationTargetException, if the method threw itself // NullPointerException, if something was a null value along the way // ExceptionInInitializerError, if initialization failed // something more crazy, if anything else breaks // TODO decide how to handle this state // you probably want to set some failure state/go to fallback Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e); } }