Comment répondre aux appels entrants de façon programmatique sur Android 5.0 (Lollipop)?

comme j'essaie de créer un écran personnalisé pour les appels entrants, j'essaie de répondre programmatiquement à un appel entrant. J'utilise le code suivant mais il ne fonctionne pas sur 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");
78
demandé sur Vadim Kotov 2014-11-14 09:59:54

10 réponses

mise à jour avec Android 8.0 Oreo

même si la question a été initialement demandé pour le soutien Android L, Les gens semblent toujours frapper cette question et la réponse, il est donc intéressant de décrire les améliorations introduites dans Android 8.0 Oreo. Les méthodes compatibles en amont sont encore décrites ci-dessous.

Qu'est-ce qui a changé?

commençant par Android 8.0 Oreo , le Téléphone permission group contient également le ANSWER_PHONE_CALLS permission . Comme le nom de permission le suggère, le fait de le conserver permet à votre application d'accepter programmatiquement les appels entrants via un appel API approprié sans aucun piratage autour du système en utilisant la réflexion ou la simulation de l'utilisateur.

comment utiliser ce changement?

Vous devriez "1519300920 le" système de contrôle version à l'exécution si vous supportez les anciennes versions Android afin que vous puissiez encapsuler ce nouvel appel API tout en maintenant le support pour ces anciennes versions Android. Vous devez suivre pour demander les permissions à l'exécution pour obtenir cette nouvelle permission pendant l'exécution, comme c'est la norme sur les nouvelles versions Android.

après avoir obtenu la permission, votre application doit simplement appeler le Telecommanager's acceptingcall method. Une invocation de base ressemble alors comme suit:

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éthode 1: TelephonyManager.answerringcall()

pour quand vous avez le contrôle illimité sur l'appareil.

Qu'est-ce que c'est?

Il ya Téléphonymanager.answererringcall () qui est une méthode interne cachée. Il fonctionne comme un pont pour ITelephony.answerringcall() qui a été discuté sur les interwebs et semble prometteur au début. Il est pas disponible sur 4.4.2_r1 comme il a été introduit seulement dans commit 83da75d pour Android 4.4 KitKat ( ligne 1537 sur 4.4.3_r1 ) et plus tard" réintroduit "dans commit f1e1e77 pour Lollipop ( ligne 3138 sur 5.0.1519170920" ) en raison de la structure de l'arbre des Gitans. Cela signifie qu'à moins que vous ne supportez que les appareils avec Lollipop, ce qui est probablement une mauvaise décision basée sur la part de marché minuscule de celui-ci dès maintenant, vous devez toujours fournir des méthodes de repli si vous allez dans cette voie.

comment l'utiliserions-nous?

comme la méthode en question est cachée de L'utilisation des applications SDK, vous devez utiliser réflexion pour examiner et utiliser de manière dynamique la méthode pendant l'exécution. Si vous n'êtes pas familier avec la réflexion, vous pouvez rapidement lire qu'est-Ce que la réflexion, et pourquoi est-il utile? . Vous pouvez également creuser plus profondément dans les détails à Trail: L'API de réflexion si vous êtes intéressé à le faire.

et à quoi ressemble le code?

// 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);
}

C'est trop beau pour être vrai!

en fait, il y a un petit problème. Ce la méthode devrait être entièrement fonctionnelle, mais le gestionnaire de la sécurité veut que les appelants tiennent android.autorisation.MODIFY_PHONE_STATE . Cette autorisation est dans le domaine des fonctionnalités partiellement documentées du système car les tiers ne sont pas censés y toucher (comme vous pouvez le voir dans la documentation). Vous pouvez essayer d'ajouter un <uses-permission> pour elle, mais cela ne fera aucun bien parce que le niveau de protection pour cette permission est signature / système ( voir la ligne 1201 du core/AndroidManifest sur 5.0.0_r1 ).

vous pouvez lire numéro 34785: mise à jour android:documentation de niveau de protection qui a été créé en 2012 pour voir que nous manquons des détails sur la" syntaxe pipe " spécifique, mais d'expérimenter autour, il semble qu'il doit fonctionner comme un 'et' signifiant tous les drapeaux spécifiés doivent être remplies pour la permission d'être accordée. En partant de cette hypothèse, cela signifierait que vous devez avoir votre demande:

  1. installé comme une application système.

    cela devrait être parfait et pourrait être accompli en demandant aux utilisateurs d'installer en utilisant un ZIP dans la récupération, comme lors de l'enracinement ou l'installation d'applications Google sur des ROMs personnalisés qui ne les ont pas déjà empaquetés.

  2. Signée avec la même signature que les cadres de base/aka le système, aka la ROM.

    C'est là que les problèmes surgissent. Pour ce faire, vous devez avoir vos mains sur les clés utilisées pour la signature de frameworks/base. Vous devez non seulement obtenir L'accès aux clés de Google pour les images de Nexus factory, mais vous devez également obtenir l'accès à toutes les autres clés des développeurs OEM et ROM. Cela ne semble pas plausible de sorte que vous pouvez faire signer votre demande les clés du système soit en faisant une ROM personnalisée et en demandant à vos utilisateurs de passer à elle (qui pourrait être difficile) ou en trouvant un exploit avec lequel le niveau de protection de permission peut être contourné (qui pourrait être difficile aussi bien).

de plus, ce comportement semble être lié à numéro 34792: Android Jelly Bean / 4.1: android.autorisation.READ_LOGS ne fonctionne plus qui utilise le même niveau de protection avec un sans-papiers développement drapeau.

travailler avec le Téléphonymanager sonne bien, mais ne fonctionnera pas à moins que vous obteniez la permission appropriée qui n'est pas si facile à faire dans la pratique.

Qu'en est-il de L'utilisation Téléphonymanager d'autres façons?

malheureusement, il semble vous demander de tenir le android.autorisation.MODIFY_PHONE_STATE pour utiliser les outils frais qui à leur tour signifie que vous allez avoir du mal à obtenir l'accès à ces méthodes.


Méthode 2: code de service d'appel de service

quand vous pouvez tester que la version en cours d'exécution sur l'appareil va fonctionner avec le code indiqué.

sans pouvoir interagir avec le Gestionnaire téléphonique, il y a également la possibilité d'interagir avec le service à travers le service exécutable.

Comment cela fonctionne?

c'est assez simple, mais il y a encore moins de documentation sur cette route que d'autres. Nous savons que l'exécutable prend deux arguments: le nom du service et le code.

  • le nom de service nous voulons utiliser est téléphone .

    cela se voit en exécutant service list .

  • Le code nous voulons utiliser semble avoir été 6 , mais semble maintenant être 5 .

    on dirait Qu'il a été basé sur IBinder.FIRST_CALL_TRANSACTION + 5 for many versions now (from 1.5_r4 à 4.4.4_r1 ) mais pendant les essais locaux le code 5 a fonctionné pour répondre un appel entrant. Comme Lollipo est une mise à jour massive tout autour, il est compréhensible internes changé ici aussi.

résultat avec une commande de service call phone 5 .

comment utiliser cette programmation?

Java

le code suivant est une mise en œuvre approximative faite pour fonctionner comme une validation de principe. Si vous voulez vraiment aller de l'avant et utiliser ceci méthode, vous voulez probablement vérifier lignes directrices pour l'utilisation sans problème de su et peut-être passer à la plus développée libsuperuser par 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();
}

manifeste

<!-- Inform the user we want them root accesses. -->
<uses-permission android:name="android.permission.ACCESS_SUPERUSER"/>

est-ce que cela nécessite vraiment un accès root?

malheureusement, il semble. Vous pouvez essayer d'utiliser Runtime.exec le il, mais je n'ai pas pu obtenir de chance avec cette route.

est-ce stable?

je suis content que vous demandiez. Du fait qu'il n'y a pas de documentation, cela peut se casser entre différentes versions, comme l'illustre la différence de code apparente ci-dessus. Le nom du service devrait probablement rester phone à travers les différentes constructions, mais pour ce que nous savons, la valeur du code peut changer à travers plusieurs constructions de la même version (modifications internes par, disons, le La peau de L'OEM) à son tour briser la méthode utilisée. Il est donc intéressant de mentionner que les tests ont eu lieu sur un Nexus 4 (mako/occam). Je vous conseillerais personnellement de ne pas utiliser cette méthode, mais comme je ne suis pas en mesure de trouver une méthode plus stable, je crois que c'est la meilleure option.


méthode originale: sens de code de casque

Pour les moments où vous avez à régler.

la section suivante a été fortement influencée par cette réponse par Riley C .

La simulation de casque intention méthode telle que publiée dans la question d'origine semble être diffusé comme on pouvait s'y attendre, mais il ne semble pas atteindre l'objectif de répondre à l'appel. Bien qu'il semble y avoir un code en place qui devrait traiter ces intentions, ils ne sont tout simplement pas se soucier, ce qui doit cela signifie qu'il doit y avoir une sorte de nouvelles contre-mesures en place contre cette méthode. Le log ne montre rien d'intéressant non plus et je ne crois pas personnellement creuser à travers la source Android pour ce sera utile juste en raison de la possibilité de Google introduire un léger changement qui casse facilement la méthode utilisée de toute façon.

peut-on faire quelque chose maintenant?

le comportement peut être systématiquement reproduit en utilisant l'entrée exécutable. Il prend dans un argument de keycode, pour lequel nous passons simplement dans KeyEvent.KEYCODE_HEADSETHOOK . La méthode n'a même pas besoin d'un accès à la racine, ce qui la rend adaptée aux cas d'usage courant dans le grand public, mais il y a un petit inconvénient dans la méthode - l'événement de pression de bouton de casque ne peut pas être spécifié pour exiger une permission, ce qui signifie qu'il fonctionne comme une vraie pression de bouton et bulles à travers toute la chaîne, ce qui à son tour signifie que vous devez être prudent sur quand pour simuler le bouton appuyez comme il pourrait, par exemple, déclencher le lecteur de musique pour commencer la lecture si personne d'autre de priorité plus élevée est prêt à gérer l'événement.

Code

?

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

Il ya une API publique nice pour Android 8.0 Oreo et plus tard.

il n'y a pas D'API publique avant Android 8.0 Oreo. Les API internes sont hors limites ou tout simplement sans documentation. Vous devez procéder avec prudence.

124
répondu Valter Jansons 2018-05-28 20:27:37

la solution complète est basée sur le code Strods @Valter.

pour le faire fonctionner, vous devez afficher une activité (invisible) sur l'écran de verrouillage où le code est exécuté.

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>

L'Appel Accepter L'Activité

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();
         }
     }
}

Style

<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>

appelez enfin la magie!

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);
32
répondu notz 2015-04-26 18:38:20

ce qui suit est une approche alternative qui a fonctionné pour moi. Il envoie l'événement clé directement au serveur telecom en utilisant L'API MediaController. Cela exige que l'application a BIND_NOTIFICATION_LISTENER_SERVICE permission et est donné l'octroi explicite de L'accès de Notification de l'utilisateur:

@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 dans le code ci-dessus pourrait être juste une classe vide.

import android.service.notification.NotificationListenerService;

public class NotificationReceiverService extends NotificationListenerService{
     public NotificationReceiverService() {
     }
}

avec la section correspondante dans le manifeste:

    <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>

étant donné que la cible de l'événement est explicite, cela devrait probablement éviter tout effet secondaire du déclenchement du lecteur multimédia.

Note: le serveur de télécommunications peut ne pas être immédiatement actif après l'événement de sonnerie. Pour que cela fonctionne de manière fiable, il peut être utile pour l'application d'implémenter MediaSessionManager.Onactivessionssionschangedlistener pour surveiller quand le serveur de télécommunications devient actif, avant d'envoyer l'événement.

mise à jour:

dans Android O , il faut simuler ACTION_DOWN avant ACTION_UP , sinon ce qui précède n'a pas d'effet. c'est à dire des suivants est nécessaire:

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

mais comme un appel officiel pour répondre à l'appel est disponible depuis Android O( voir réponse du haut), il ne peut pas plus longtemps être un besoin pour ce piratage, à moins que l'on est coincé avec un ancien niveau d'API de compilation avant Android O.

11
répondu headuck 2017-10-22 13:48:50

pour élaborer un tout petit peu sur la réponse de @Muzikant, et pour la modifier un peu pour travailler un peu plus propre sur mon appareil, essayez input keyevent 79 , la constante pour KeyEvent.KEYCODE_HEADSETHOOK . très grossièrement

    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();

pardonnez les mauvaises conventions de codage, Je ne suis pas très versé dans L'exécution.exec() appelle. Notez que mon appareil n'est pas enraciné, et je ne demande pas de privilèges root.

le la difficulté de cette approche est qu'il ne fonctionne que sous certaines conditions (pour moi). C'est-à-dire, si j'exécute le thread ci-dessus à partir d'une option de menu que l'utilisateur sélectionne alors qu'un appel sonne, les réponses d'appel sont très bien. Si je le lance à partir d'un récepteur qui surveille les appels entrants, il est totalement ignoré.

ainsi, sur mon Nexus 5, Il fonctionne bien pour la réponse axée sur l'utilisateur et devrait convenir à l'objet d'un écran d'appel personnalisé. Ça ne marchera pas pour une sorte de contrôle automatique des appels. application.

aussi à noter sont toutes les mises en garde possibles, y compris que cela, aussi, cessera probablement de fonctionner dans une ou deux mises à jour.

9
répondu Riley C 2014-11-25 21:52:56

via les commandes adb Comment récupérer un appel par la bad

gardez à l'esprit que Android est Linux avec une JVM massive sur le devant. Vous pouvez télécharger une application en ligne de commande et root le téléphone et maintenant vous avez un ordinateur Linux régulier et une ligne de commande qui fait toutes les choses normales. Exécutez des scripts, vous pouvez même ssh (OpenVPN trick)

0
répondu user1544207 2017-05-23 11:47:16

Merci @notz la réponse de travaille pour moi sur le Lolillop. Afin de garder ce code fonctionnel avec l'ancien SDK android, vous pouvez faire ce code:

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();  
    }  
  }  
}  
0
répondu Khac Quyet Dang 2015-05-11 15:58:26

comment allumer un haut-parleur après avoir automatiquement répondu à des appels.

j'ai résolu mon problème ci-dessus avec setSpeakerphoneOn. Je pense qu'il vaut la peine de poster ici, puisque le cas d'utilisation pour répondre automatiquement à un appel téléphonique exigerait souvent aussi haut-parleur pour être utile. Merci encore à tout le monde sur ce fil, quel travail impressionnant.

cela fonctionne pour moi sur Android 5.1.1 sur mon Nexus 4 sans ROOT. ;)

Permission requise:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

Code 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
}
0
répondu Madhava Jay 2016-03-18 12:56:15

exécutez la commande suivante en tant que root:

input keyevent 5

plus de détails sur la simulation d'événements clés ici .

vous pouvez utiliser cette classe de base que j'ai créée pour exécuter des commandes en tant que root à partir de votre application.

-1
répondu Muzikant 2014-11-23 11:17:17

tester ce: tout d'abord, ajoutez les permissions puis utilisez killCall() pour raccrocher utilisez answerCall() pour répondre à l'appel

<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();
    }
}
-2
répondu Michael 2016-04-21 07:16:51

FYI si vous êtes intéressé par la façon de mettre fin à un appel en cours sur Android O, Method 1: TelephonyManager.answerRingingCall() de Valter fonctionne si vous changez la méthode que vous invoquez pour être endCall .

il nécessite seulement la permission android.permission.CALL_PHONE .

voici le code:

// 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);
    }
}
-2
répondu Francois Dermu 2017-08-24 18:10:31