Como estou tentando criar uma tela personalizada para chamadas recebidas, estou tentando atender programaticamente uma chamada recebida. Estou usando o código a seguir, mas não está funcionando no 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");
android
android-5.0-lollipop
phone-call
incoming-call
maveróide
fonte
fonte
Respostas:
Atualização com Android 8.0 Oreo
Embora a pergunta tenha sido originalmente feita para o suporte do Android L, as pessoas ainda parecem estar acertando essa pergunta e resposta, então vale a pena descrever as melhorias introduzidas no Android 8.0 Oreo. Os métodos compatíveis com versões anteriores ainda são descritos abaixo.
O que mudou?
A partir do Android 8.0 Oreo , o grupo de permissão PHONE também contém a permissão ANSWER_PHONE_CALLS . Como o nome da permissão sugere, segurá-la permite que seu aplicativo aceite programaticamente chamadas de entrada por meio de uma chamada de API adequada sem qualquer invasão do sistema usando reflexão ou simulação do usuário.
Como utilizamos essa mudança?
Você deve verificar a versão do sistema no tempo de execução se estiver oferecendo suporte a versões mais antigas do Android, para que possa encapsular essa nova chamada de API enquanto mantém o suporte para essas versões mais antigas do Android. Você deve solicitar permissões em tempo de execução para obter essa nova permissão durante o tempo de execução, como é padrão nas versões mais recentes do Android.
Depois de obter a permissão, seu aplicativo apenas precisa chamar o método acceptRingingCall do TelecomManager . Uma invocação básica tem a seguinte aparência:
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 quando você tiver controle ilimitado sobre o dispositivo.
O que é isso?
Existe TelephonyManager.answerRingingCall () que é um método interno oculto. Ele funciona como uma ponte para ITelephony.answerRingingCall () que foi discutido nas interwebs e parece promissor no início. É não disponível em 4.4.2_r1 como foi introduzida apenas em cometer 83da75d para Android 4.4 KitKat ( linha 1537 em 4.4.3_r1 ) e mais tarde "reintroduzido" em cometer f1e1e77 para Lollipop ( linha 3138 em 5.0.0_r1 ) devido à forma como o A árvore Git foi estruturada. Isso significa que, a menos que você ofereça suporte apenas a dispositivos com Lollipop, o que provavelmente é uma decisão ruim com base na pequena participação de mercado dele no momento, você ainda precisará fornecer métodos de fallback se for seguir esse caminho.
Como usaríamos isso?
Como o método em questão está oculto do uso de aplicativos SDK, você precisa usar reflexão para examinar e usar o método dinamicamente durante o tempo de execução. Se você não está familiarizado com a reflexão, pode ler rapidamente O que é reflexão e por que ela é útil? . Você também pode se aprofundar nos detalhes em Trail: The Reflection API se estiver interessado em fazê-lo.
E como isso fica no 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); }
Isso é bom demais para ser verdade!
Na verdade, há um pequeno problema. Este método deve ser totalmente funcional, mas o gerenciador de segurança deseja que os chamadores mantenham android.permission.MODIFY_PHONE_STATE . Essa permissão está no reino de recursos apenas parcialmente documentados do sistema, já que terceiros não devem tocá-la (como você pode ver na documentação). Você pode tentar adicionar um
<uses-permission>
para ele, mas não adiantará porque o nível de proteção para essa permissão é assinatura | sistema ( consulte a linha 1201 do core / AndroidManifest em 5.0.0_r1 ).Você pode ler o Problema 34785: Atualizar a documentação do android: protectionLevel, criada em 2012, para ver que faltam detalhes sobre a "sintaxe de tubo" específica, mas, a partir de experimentos, parece que deve funcionar como um 'AND' significando todos sinalizadores especificados devem ser cumpridos para que a permissão seja concedida. Trabalhando sob essa suposição, isso significaria que você deve ter seu aplicativo:
Instalado como um aplicativo do sistema.
Isso deve ser adequado e pode ser realizado solicitando aos usuários que instalem usando um ZIP na recuperação, como ao fazer o root ou instalar aplicativos do Google em ROMs personalizados que ainda não os têm embalados.
Assinado com a mesma assinatura que frameworks / base, também conhecido como sistema, também conhecido como ROM.
É aqui que surgem os problemas. Para fazer isso, você precisa ter em mãos as chaves usadas para assinar frameworks / base. Você não só teria que obter acesso às chaves do Google para imagens de fábrica do Nexus, mas também teria acesso às chaves de todos os outros OEMs e desenvolvedores de ROM. Isso não parece plausível, então você pode ter seu aplicativo assinado com as chaves do sistema criando uma ROM personalizada e pedindo que seus usuários mudem para ela (o que pode ser difícil) ou encontrando um exploit com o qual o nível de proteção de permissão possa ser contornado (o que também pode ser difícil).
Além disso, esse comportamento parece estar relacionado ao problema 34792: Android Jelly Bean / 4.1: android.permission.READ_LOGS não funciona mais, o que também utiliza o mesmo nível de proteção junto com um sinalizador de desenvolvimento não documentado.
Trabalhar com o TelephonyManager parece bom, mas não funcionará a menos que você obtenha a permissão apropriada, o que não é tão fácil de fazer na prática.
Que tal usar o TelephonyManager de outras maneiras?
Infelizmente, parece que você deve manter o android.permission.MODIFY_PHONE_STATE para usar as ferramentas legais, o que significa que você terá dificuldade em obter acesso a esses métodos.
Método 2: chamada de serviço CÓDIGO DE SERVIÇO
Para quando você puder testar se a compilação em execução no dispositivo funcionará com o código especificado.
Sem poder interagir com o TelephonyManager, também existe a possibilidade de interagir com o serviço através do
service
executável.Como é que isso funciona?
É bastante simples, mas há ainda menos documentação sobre essa rota do que outras. Sabemos com certeza que o executável recebe dois argumentos - o nome do serviço e o código.
O nome do serviço que queremos usar é telefone .
Isso pode ser visto executando
service list
.O código que queremos usar parece ter sido 6, mas agora parece ser 5 .
Parece que foi baseado em IBinder.FIRST_CALL_TRANSACTION + 5 para muitas versões agora (de 1.5_r4 a 4.4.4_r1 ), mas durante o teste local, o código 5 funcionou para atender uma chamada recebida. Como o Lollipo é uma atualização massiva em todos os sentidos, é compreensível que as partes internas tenham mudado aqui também.
Isso resulta com um comando de
service call phone 5
.Como usamos isso programaticamente?
Java
O código a seguir é uma implementação aproximada feita para funcionar como uma prova de conceito. Se você realmente deseja seguir em frente e usar este método, provavelmente deseja verificar as diretrizes para o uso do su sem problemas e, possivelmente, alternar para o libsuperuser mais desenvolvido da 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(); }
Manifesto
<!-- Inform the user we want them root accesses. --> <uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>
Isso realmente requer acesso root?
Infelizmente, parece que sim. Você pode tentar usar Runtime.exec nele, mas não consegui obter nenhuma sorte com essa rota.
Quão estável é isso?
Estou feliz que você perguntou. Por não estar documentado, isso pode falhar em várias versões, conforme ilustrado pela aparente diferença de código acima. O nome do serviço provavelmente deve permanecer phone em vários builds, mas pelo que sabemos, o valor do código pode mudar em vários builds da mesma versão (modificações internas por, digamos, o skin do OEM), por sua vez, quebrando o método usado. Portanto, vale a pena mencionar que o teste ocorreu em um Nexus 4 (mako / occam). Eu pessoalmente aconselharia você a não usar esse método, mas como não consigo encontrar um método mais estável, acredito que essa seja a melhor chance.
Método original: intenções do código-chave do fone de ouvido
Por momentos em que você tem que se acomodar.
A secção seguinte foi fortemente influenciado por esta resposta por Riley C .
O método de intenção de fone de ouvido simulado, conforme postado na pergunta original, parece ser transmitido exatamente como seria de se esperar, mas não parece cumprir o objetivo de atender a chamada. Embora pareça haver um código que deve lidar com essas intents, elas simplesmente não estão sendo consideradas, o que significa que deve haver algum tipo de contra-medidas contra esse método. O log também não mostra nada de interesse e eu pessoalmente não acredito que vá valer a pena vasculhar a fonte do Android para isso apenas devido à possibilidade de o Google introduzir uma pequena mudança que facilmente quebra o método usado de qualquer maneira.
Existe algo que possamos fazer agora?
O comportamento pode ser reproduzido de forma consistente usando o executável de entrada. Ele recebe um argumento de código-chave, para o qual simplesmente passamos KeyEvent.KEYCODE_HEADSETHOOK . O método nem mesmo requer acesso root, tornando-o adequado para casos de uso comuns no público em geral, mas há uma pequena desvantagem no método - o evento de pressionamento de botão do fone de ouvido não pode ser especificado para exigir uma permissão, o que significa que funciona como um verdadeiro pressione o botão e borbulha por toda a cadeia, o que significa que você deve ser cauteloso ao simular o pressionamento do botão, pois isso poderia, por exemplo, acionar o reprodutor de música para iniciar a reprodução se ninguém mais com maior prioridade estiver pronto para lidar o 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
Há uma boa API pública para Android 8.0 Oreo e posterior.
Não há API pública anterior ao Android 8.0 Oreo. As APIs internas estão fora dos limites ou simplesmente sem documentação. Você deve proceder com cautela.
fonte
A solução totalmente funcional é baseada no código @Valter Strods.
Para fazê-lo funcionar, você deve exibir uma atividade (invisível) na tela de bloqueio onde o código é executado.
AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <activity android:name="com.mysms.android.lib.activity.AcceptCallActivity" android:launchMode="singleTop" android:excludeFromRecents="true" android:taskAffinity="" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@style/Mysms.Invisible"> </activity>
Atividade de aceitação de chamada
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
<style name="Mysms.Invisible"> <item name="android:windowFrame">@null</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@null</item> </style>
Finalmente chame a 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);
fonte
A seguir está uma abordagem alternativa que funcionou para mim. Ele envia o evento-chave para o servidor de telecomunicações diretamente usando a API MediaController. Isso requer que o aplicativo tenha permissão BIND_NOTIFICATION_LISTENER_SERVICE e receba a concessão explícita de acesso à Notificação do usuário:
@TargetApi(Build.VERSION_CODES.LOLLIPOP) void sendHeadsetHookLollipop() { MediaSessionManager mediaSessionManager = (MediaSessionManager) getApplicationContext().getSystemService(Context.MEDIA_SESSION_SERVICE); try { List<MediaController> 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
no código acima pode ser apenas uma classe vazia.import android.service.notification.NotificationListenerService; public class NotificationReceiverService extends NotificationListenerService{ public NotificationReceiverService() { } }
Com a seção correspondente no manifesto:
<service android:name=".NotificationReceiverService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.service.notification.NotificationListenerService" /> </intent-filter>
Como o alvo do evento é explícito, isso provavelmente deve evitar qualquer efeito colateral de acionar o reprodutor de mídia.
Nota: o servidor de telecomunicações pode não estar imediatamente ativo após o evento de toque. Para que isso funcione de forma confiável, pode ser útil para o aplicativo implementar MediaSessionManager.OnActiveSessionsChangedListener para monitorar quando o servidor de telecomunicações se torna ativo, antes de enviar o evento.
Atualizar:
No Android O , é necessário simular
ACTION_DOWN
antesACTION_UP
, caso contrário, o acima não terá efeito. ou seja, o seguinte é necessário:m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK)); m.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
Mas, uma vez que uma chamada oficial para atender chamadas está disponível desde o Android O (veja a resposta superior), pode não haver mais necessidade desse hack, a menos que alguém esteja preso a um nível de API de compilação antigo antes do Android O.
fonte
Para elaborar um pouco sobre a resposta de @Muzikant, e modificá-la um pouco para funcionar um pouco mais limpa no meu dispositivo, tente
input keyevent 79
a constante para KeyEvent.KEYCODE_HEADSETHOOK . Muito 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();
Perdoe as convenções de codificação bastante ruins, não sou muito versado em chamadas Runtime.exec (). Observe que meu dispositivo não tem acesso root, nem estou solicitando privilégios de root.
O problema com essa abordagem é que ela só funciona sob certas condições (para mim). Ou seja, se eu executar o tópico acima a partir de uma opção de menu que o usuário seleciona enquanto uma chamada está tocando, a chamada atende perfeitamente. Se eu executá-lo de um receptor que monitora o status das chamadas recebidas, ele é totalmente ignorado.
Portanto, no meu Nexus 5, ele funciona bem para atender o usuário e deve se adequar ao propósito de uma tela de chamada personalizada. Ele simplesmente não funcionará para nenhum tipo de aplicativo automatizado de controle de chamadas.
Também digno de nota são todas as advertências possíveis, incluindo que isso também irá provavelmente parar de funcionar em uma atualização ou duas.
fonte
input keyevent 79
funciona bem no Sony Xperia 5.0. Funciona ao chamar de uma atividade ou de um receptor de transmissão.por meio dos comandos adb Como atender uma chamada por adb
Lembre-se de que o Android é Linux com uma grande JVM no front end. Você pode baixar um aplicativo de linha de comando e enraizar o telefone e agora você tem um computador Linux normal e linha de comando que faz todas as coisas normais. Execute scripts, você pode até usar ssh para ele (truque do OpenVPN)
fonte
Obrigado @notz, a resposta de está funcionando para mim no Lolillop. Para manter esse código funcionando com o SDK do Android antigo, você pode fazer 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(); } } }
fonte
Como ligar o viva-voz após atender chamadas automaticamente.
Resolvi meu problema acima com setSpeakerphoneOn. Acho que vale a pena postar aqui, já que o caso de uso para atendimento automático de uma chamada telefônica muitas vezes também requer o viva-voz para ser útil. Obrigado novamente a todos neste tópico, que trabalho incrível.
Isso funciona para mim no Android 5.1.1 no meu Nexus 4 sem ROOT. ;)
Permissão necessária:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Código 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 }
fonte
Execute o seguinte comando como root:
input keyevent 5
Mais detalhes sobre a simulação de eventos-chave aqui .
Você pode usar esta classe base que criei para executar comandos como root do seu aplicativo.
fonte
teste isto: primeiro adicione as permissões e use killCall () para desligar use answerCall () para atender a chamada
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission> 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(); } }
fonte
Para sua informação, se você estiver interessado em como ENCERRAR uma chamada em andamento no Android O, Valter
Method 1: TelephonyManager.answerRingingCall()
funciona se você alterar o método que você invocar para serendCall
.Requer apenas a
android.permission.CALL_PHONE
permissão.Aqui está o 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); } }
fonte