Cómo colgar la llamada saliente en Android?

Estoy desarrollando una aplicación donde una de las cosas que necesitamos es controlar la llamada saliente, al menos para poder detenerla desde nuestra aplicación.

Intenté usar Intent.ACTION_CALL de una actividad existente:

 Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); startActivity(callIntent); 

Pero detener la llamada parece no ser permitido a través de la API.

¿Puedes sugerir alguna solución?

Por ejemplo: ¿habilitar el modo avión durante la llamada? Solo un ejemplo; este truco no funcionó para mí.

Se mencionó la captura de la llamada saliente en un BroadcastReceiver y es definitivamente la mejor manera de hacerlo si desea finalizar la llamada antes de marcar.

Sin embargo, una vez que marca o in-call, esa técnica ya no funciona. La única manera de colgar que he encontrado hasta ahora es hacerlo a través de Java Reflection . Como no forma parte de la API pública, debe tener cuidado de usarla y no confiar en ella . Cualquier cambio en la composición interna de Android romperá efectivamente su aplicación.

El blog de Prasanta Paul demuestra cómo se puede lograr, que he resumido a continuación.

Obtener el objeto ITelephony :

 TelephonyManager tm = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE); try { // Java reflection to gain access to TelephonyManager's // ITelephony getter Log.v(TAG, "Get getTeleService..."); Class c = Class.forName(tm.getClass().getName()); Method m = c.getDeclaredMethod("getITelephony"); m.setAccessible(true); com.android.internal.telephony.ITelephony telephonyService = (ITelephony) m.invoke(tm); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "FATAL ERROR: could not connect to telephony subsystem"); Log.e(TAG, "Exception object: " + e); } 

Finalizando la llamada:

 telephonyService.endCall(); 

Prueba esto:

(Utilicé Reflection para acceder a funciones avanzadas de telefonía y modificar algunas)

 // required permission  try { //String serviceManagerName = "android.os.IServiceManager"; 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); } 
  1. Crea un BroadcastReceiver con una prioridad de 0.
  2. En BC intercepta el bash ACTION_NEW_OUTGOING_CALL en su método onReceive
  3. llamar a setResultData(null) en el mismo método

Esto evitará que la llamada se inicie (siempre que tu receptor sea el último en procesar la intención, creo)

Puede intentar habilitar y luego desactivar el modo avión:

 android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.AIRPLANE_MODE_ON, 1); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); intent.putExtra("state", 1); sendBroadcast(new Intent("android.intent.action.AIRPLANE_MODE")); sendBroadcast(intent); android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.AIRPLANE_MODE_ON, 0); intent.putExtra("state", 0); sendBroadcast(new Intent("android.intent.action.AIRPLANE_MODE")); sendBroadcast(intent); 

Para Ilana:

 public class ilanasReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) { if (getResultData()!=null) { String number = "123456"; setResultData(number); } } } } 

Además en Manifest poner en la sección del paquete:

  

Eso es todo.

Considerando el potencial de travesuras maravillosas, me sorprendería si esto está permitido.

Este hilo dice rotundamente que la API no puede finalizar una llamada . Otros lo han intentado .

Aquí está el código más actualizado, que también funcionará para Android P, porque tiene una API oficial para él ( aquí ):

en manifiesto, agrega esto:

  

En código, usa esto:

Java:

 @SuppressLint("PrivateApi") public static boolean endCall(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { final TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); if (telecomManager != null && ContextCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_GRANTED) { telecomManager.endCall(); return true; } return false; } //use unofficial API for older Android versions, as written here: https://stackoverflow.com/a/8380418/878126 try { final Class telephonyClass = Class.forName("com.android.internal.telephony.ITelephony"); final Class telephonyStubClass = telephonyClass.getClasses()[0]; final Class serviceManagerClass = Class.forName("android.os.ServiceManager"); final Class serviceManagerNativeClass = Class.forName("android.os.ServiceManagerNative"); final Method getService = serviceManagerClass.getMethod("getService", String.class); final Method tempInterfaceMethod = serviceManagerNativeClass.getMethod("asInterface", IBinder.class); final Binder tmpBinder = new Binder(); tmpBinder.attachInterface(null, "fake"); final Object serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder); final IBinder retbinder = (IBinder) getService.invoke(serviceManagerObject, "phone"); final Method serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder.class); final Object telephonyObject = serviceMethod.invoke(null, retbinder); final Method telephonyEndCall = telephonyClass.getMethod("endCall"); telephonyEndCall.invoke(telephonyObject); return true; } catch (Exception e) { e.printStackTrace(); LogManager.e(e); } return false; } 

o en Kotlin:

 @SuppressLint("PrivateApi") fun endCall(context: Context): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager if (ContextCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) == PackageManager.PERMISSION_GRANTED) { telecomManager.endCall() return true } return false } //use unofficial API for older Android versions, as written here: https://stackoverflow.com/a/8380418/878126 try { val telephonyClass = Class.forName("com.android.internal.telephony.ITelephony") val telephonyStubClass = telephonyClass.classes[0] val serviceManagerClass = Class.forName("android.os.ServiceManager") val serviceManagerNativeClass = Class.forName("android.os.ServiceManagerNative") val getService = serviceManagerClass.getMethod("getService", String::class.java) val tempInterfaceMethod = serviceManagerNativeClass.getMethod("asInterface", IBinder::class.java) val tmpBinder = Binder() tmpBinder.attachInterface(null, "fake") val serviceManagerObject = tempInterfaceMethod.invoke(null, tmpBinder) val retbinder = getService.invoke(serviceManagerObject, "phone") as IBinder val serviceMethod = telephonyStubClass.getMethod("asInterface", IBinder::class.java) val telephonyObject = serviceMethod.invoke(null, retbinder) val telephonyEndCall = telephonyClass.getMethod("endCall") telephonyEndCall.invoke(telephonyObject) return true } catch (e: Exception) { e.printStackTrace() return false } } 

De acuerdo con la documentación sobre ACTION_NEW_OUTGOING_CALL

La intención tendrá el siguiente valor adicional:

EXTRA_PHONE_NUMBER: el número de teléfono originalmente destinado a marcarse.

Una vez que finaliza la transmisión, resultData se usa como el número real para llamar. Si es nulo, no se realizará ninguna llamada.