Actualizar con Android 8.0 Oreo
A pesar de que la pregunta se hizo originalmente para la compatibilidad con Android L, la gente todavía parece estar respondiendo a 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 todavía se describen a continuación.
¿Qué cambió?
A partir de Android 8.0 Oreo , el grupo de permisos PHONE también contiene el permiso ANSWER_PHONE_CALLS . Como sugiere el nombre del permiso, mantenerlo permite que su aplicación acepte llamadas entrantes mediante programación a través de una llamada API adecuada sin ningún tipo de piratería en el sistema mediante la reflexión o la simulación del usuario.
¿Cómo utilizamos este cambio?
Debe verificar la versión del sistema en tiempo de ejecución si admite versiones anteriores de Android para poder encapsular esta nueva llamada a la API mientras mantiene la compatibilidad con 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 . Entonces, una invocación básica tiene el siguiente aspecto:
TelecomManager tm = (TelecomManager) mContext
.getSystemService(Context.TELECOM_SERVICE);
if (tm == null) {
throw new NullPointerException("tm == null");
}
tm.acceptRingingCall();
Método 1: TelephonyManager.answerRingingCall ()
Para cuando tienes un 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 discutido en las interwebs y parece prometedor al principio. Es no disponibles en 4.4.2_r1 ya que sólo se introdujo en comprometerse 83da75d para Android 4.4 KitKat ( línea 1537 en 4.4.3_r1 ) y más tarde "reintroducido" en comprometerse f1e1e77 de Lollipop ( línea 3138 en 5.0.0_r1 ) debido a la forma en que el El árbol de Git estaba estructurado. Esto significa que, a menos que solo admita dispositivos con Lollipop, que probablemente sea una mala decisión en función de la pequeña participación de mercado que tiene en este momento, aún debe proporcionar métodos de respaldo si sigue esta ruta.
¿Cómo usaríamos esto?
Como el método en cuestión está oculto para el uso de las aplicaciones del SDK, debe usar la reflexión para examinar y usar dinámicamente el método durante el tiempo de ejecución. Si no está familiarizado con la reflexión, puede leer rápidamente ¿Qué es la 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?
final String LOG_TAG = "TelephonyAnswer";
TelephonyManager tm = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
try {
if (tm == null) {
throw new NullPointerException("tm == null");
}
tm.getClass().getMethod("answerRingingCall").invoke(tm);
} catch (Exception e) {
Log.e(LOG_TAG, "Unable to use the Telephony Manager directly.", e);
}
¡Esto es demasiado bueno para ser verdad!
De hecho, hay un pequeño problema. Este método debería ser completamente funcional, pero el administrador de seguridad quiere que las personas que llaman tengan android.permission.MODIFY_PHONE_STATE . Este permiso está en el ámbito de las funciones del sistema que están parcialmente documentadas, ya que no se espera que terceros lo toquen (como puede ver en la documentación correspondiente). Puede intentar agregar un <uses-permission>
para él, pero eso no servirá de nada porque el nivel de protección para este permiso es firma | sistema ( consulte la línea 1201 de core / AndroidManifest en 5.0.0_r1 ).
Puede leer el Problema 34785: Actualización de android: documentación de nivel de protección que se creó en 2012 para ver que nos faltan detalles sobre la "sintaxis de tubería" específica, pero al experimentar, parece que debe funcionar como un 'Y' que significa todo el las banderas especificadas deben cumplirse para que se otorgue el permiso. Trabajando bajo esa suposición, significaría que debe tener su aplicación:
Instalado como una aplicación del sistema.
Esto debería estar bien y podría lograrse pidiendo a los usuarios que instalen usando un ZIP en la recuperación, como al rootear o instalar aplicaciones de Google en ROM personalizadas que no las tienen ya empaquetadas.
Firmado con la misma firma que frameworks / base, también conocido como 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ía que acceder a las claves de Google para las imágenes de fábrica de Nexus, sino que también tendría que acceder a las claves de todos los demás fabricantes de equipos originales y desarrolladores de ROM. Esto no parece plausible, por lo que puede firmar su aplicación con las claves del sistema, ya sea creando una ROM personalizada y pidiéndoles a sus usuarios que la cambien (lo que puede ser difícil) o encontrando un exploit con el que se pueda omitir el nivel de protección de permisos. (que también puede ser difícil).
Además, este comportamiento parece estar relacionado con el Problema 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS ya no funciona, lo que también utiliza el mismo nivel de protección junto con una marca de desarrollo no documentada.
Trabajar con TelephonyManager suena bien, pero no funcionará a menos que obtenga el permiso apropiado, lo cual no es tan fácil de hacer en la práctica.
¿Qué pasa con el uso de TelephonyManager de otras formas?
Lamentablemente, parece que es necesario que tengas el permiso android.permission.MODIFY_PHONE_STATE para usar las herramientas geniales, lo que a su vez significa que tendrás dificultades para acceder a esos métodos.
Método 2: llamada de servicio CÓDIGO DE SERVICIO
Para cuando pueda probar que la compilación que se ejecuta en el dispositivo funcionará con el código especificado.
Sin poder interactuar con el TelephonyManager, también existe la posibilidad de interactuar con el servicio a través del service
ejecutable.
¿Como funciona esto?
Es bastante simple, pero hay menos documentación sobre esta ruta que sobre 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 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 funcionó para responder una llamada entrante. Como Lollipo es una actualización masiva en todos lados, es comprensible que los componentes internos también hayan cambiado aquí.
Esto resulta con un comando 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 prueba de concepto. Si realmente desea seguir adelante y utilizar este método, probablemente desee consultar las pautas para el uso de su sin problemas 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) {
}
} catch (IOException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Manifiesto
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
¿Esto realmente requiere acceso de root?
Lamentablemente, eso parece. Puede intentar usar Runtime.exec en él, pero no pude tener suerte con esa ruta.
¿Qué tan estable es esto?
Me alegro de que lo hayas preguntado. Debido a que no está documentado, esto puede afectar a varias versiones, como lo ilustra la aparente diferencia de código anterior. El nombre del servicio probablemente debería permanecer en el teléfono en varias compilaciones, pero por lo que sabemos, el valor del código puede cambiar en varias compilaciones de la misma versión (modificaciones internas por, digamos, el skin del OEM) a su vez, rompiendo el método utilizado. Por lo tanto, vale la pena mencionar que las pruebas se realizaron en un Nexus 4 (mako / occam). Personalmente, le aconsejaría que no utilice 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: Intentos de código clave de auricular
Para momentos en los que tienes que conformarte.
En la siguiente sección se vio fuertemente influenciado por esta respuesta por Riley C .
El método de intención de auricular simulado que se publicó en la pregunta original parece transmitirse tal como se esperaría, pero no parece lograr el objetivo de responder la llamada. Si bien parece haber un código en su lugar que debería manejar esos intentos, simplemente no se les importa, lo que significa que debe haber algún tipo de contramedidas nuevas contra este método. El registro tampoco muestra nada de interés y personalmente no creo que valga la pena buscar en la fuente de Android para esto solo debido a la posibilidad de que Google introduzca un pequeño cambio que rompe fácilmente el método utilizado de todos modos.
¿Hay algo que podamos hacer ahora mismo?
El comportamiento se puede reproducir de forma coherente utilizando el ejecutable de entrada. Toma un argumento de código clave , para el cual simplemente pasamos KeyEvent.KEYCODE_HEADSETHOOK . El método ni siquiera requiere acceso de root, lo que lo hace adecuado para casos de uso comunes en el público en general, pero hay un pequeño inconveniente en el método: el evento de presión del botón del auricular no se puede especificar para requerir un permiso, lo que significa que funciona como un verdadero pulsa el botón y sube a lo largo de toda la cadena, lo que a su vez significa que debes tener cuidado sobre cuándo simular la pulsación del botón, ya que podría, por ejemplo, activar el reproductor de música para que comience la reproducción si nadie más de mayor prioridad está listo para manejar 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) {
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 versiones posteriores.
No existe una API pública anterior a Android 8.0 Oreo. Las API internas están prohibidas o simplemente sin documentación. Debe proceder con precaución.