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) {
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?
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);
}
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) {
}
} catch (IOException e) {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Manifesto
<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) {
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.