web-dev-qa-db-fra.com

Comment répondre/mettre fin à un appel par programme dans Android 4.1?

J'écris une application Android dans laquelle j'ai besoin de répondre à un appel entrant, de travailler et de mettre fin à l'appel. Après tout googler, je pouvais trouver deux manières différentes d’atteindre cet objectif, qui ne fonctionnaient pas avec les versions récentes d’Android, en particulier après la version 4.1, Jelly Bean. 

I.) Accédez à "com.Android.internal.telephony.ITelephony" à l'aide de Java Reflection dans le récepteur de diffusion pour "Android.intent.action.PHONE_STATE". Vous trouverez ci-dessous un exemple de code dans des centaines de publications connexes:

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";
 private ITelephony telephonyService;

@Override
 public void onReceive(Context context, Intent intent) {
  if (!intent.getAction().equals("Android.intent.action.PHONE_STATE")) 
    return;

  Log.v(TAG, "Receving....");
  TelephonyManager telephony = (TelephonyManager) 
  context.getSystemService(Context.TELEPHONY_SERVICE);  
  try {
      Log.v(TAG, "Get getTeleService...");
      Class c = Class.forName(telephony.getClass().getName());
      Method m = c.getDeclaredMethod("getITelephony");
      m.setAccessible(true);
      telephonyService = (ITelephony) m.invoke(telephony);
      telephonyService.silenceRinger();
      Log.v(TAG, "Answering Call now...");
      telephonyService.answerRingingCall();
      Log.v(TAG, "Call answered...");
      //telephonyService.endCall();
  } catch (Exception e) {
   e.printStackTrace();
   Log.e(TAG,
           "FATAL ERROR: could not connect to telephony subsystem");
   Log.e(TAG, "Exception object: " + e);
  }
 }
}

Le problème avec ce code est que 

<uses-permission Android:name="Android.permission.MODIFY_PHONE_STATE" /> 

est requise pour que cette méthode fonctionne, et cette autorisation a été définie comme "pour les applications système uniquement" à partir d'Android v 2.3. En bref, les applications utilisateur normales ne peuvent plus définir cette autorisation dans le fichier manifeste. 

II.) Une autre méthode consiste à simuler une poussée du crochet du casque qui permet à Android de répondre à l'appel. Cela se fait en diffusant "Intent.ACTION_MEDIA_BUTTON" comme indiqué dans le code ci-dessous.

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";

 @Override
 public void onReceive(Context context, Intent intent) {
     if (!intent.getAction().equals("Android.intent.action.PHONE_STATE")) 
         return;

     String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
     if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
         String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

         Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON);
         answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
         context.sendOrderedBroadcast(answer, null);
         Log.d(TAG, "Answered incoming call from: " + number);
     }   
     return;
 } 
}

Cette méthode fonctionne jusqu'à Android 4.1, après quoi Android a interdit aux applications utilisateur de diffuser "Intent.ACTION_MEDIA_BUTTON". 

Ma conclusion est donc qu’il n’ya actuellement aucun moyen d’atteindre cet objectif avec Android 4.1 ou ultérieur. 

Quelqu'un d'autre a-t-il trouvé une autre solution ou solution de contournement à ce problème?

53
Piyush

Cela fonctionne d'Android 2.2 à 4.0 et maintenant, après avoir ajouté le crochet d'essai à la dernière ligne, cela fonctionne pour 4.1.2 et 4.2 Franchement, je ne sais pas comment ça marche, mais ça marche pour moi.

Log.d(tag, "InSecond Method Ans Call");
// 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));
sendOrderedBroadcast(buttonUp, "Android.permission.CALL_PRIVILEGED");

Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
    sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) { 
    e.printStackTrace();
}

Cela fonctionne pour moi dans Android 4.1.2 ainsi que j'ai testé sur 4.2 Cela donne toujours une exception qui est traitée.

Modifier pour mettre fin à l'appel

J'espère que cela aidera toutes les personnes à la recherche d'une solution totale pour répondre à l'appel.

/**
 * Reject button click listener will reject the incoming call.
 */
private class RejectCallOnClickListener implements OnClickListener {
    @Override
    public void onClick(View v) {
        Log.d(tag, "OnRejectButton: " + "Reject OnClick");
        ignoreCall();
        exitCleanly();
    }
}

/**
 * ignore incoming calls
 */
private void ignoreCall() {
    if (USE_ITELEPHONY)
        ignoreCallAidl();
    else
        ignoreCallPackageRestart();
}
/**
 * AIDL/ITelephony technique for ignoring calls
 */
private void ignoreCallAidl() {
    try {
        // telephonyService.silenceRinger();

        telephonyService.endCall();
    } catch (RemoteException e) {
        e.printStackTrace();
        Log.d(tag, "ignoreCall: " + "Error: " + e.getMessage());

    } catch (Exception e) {
        e.printStackTrace();
        Log.d(tag, "ignoreCall" + "Error: " + e.getMessage());

    }
}
/**
 * package restart technique for ignoring calls
 */
private void ignoreCallPackageRestart() {
    ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    am.restartPackage("com.Android.providers.telephony");
    am.restartPackage("com.Android.phone");
}
/**
 * cleanup and exit routine
 */
private void exitCleanly() {
    unHookReceiver();
    this.finish();

}
16
PravinDodia

Mon application utilise le code suivant pour répondre au téléphone depuis environ 6 mois:

Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
            KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(i, null);

J'ai testé cela sur les versions Android 2.2 à 4.2.2. Je n’ai pas vu d’émission SecurityException "Intent.ACTION_MEDIA_BUTTON" dans mon appareil de test 4.2.2, ni vu les rapports de plantage du Play Store indiquant que de telles exceptions se produisaient.

Je dirai que cela ne fonctionne pas toujours. Cela ne fonctionne pas sur les appareils HTC, car ceux-ci ont un casque HeadsetObeserver qui écoute le branchement d'un casque filaire. Sans cet événement, qui est actuellement une exception SecurityException à diffuser par une application tierce, HeadsetHook KeyEvent est ignoré.

Les réponses précédentes sont trompeuses. Le bloc de code suivant ne fait rien:

Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
headSetUnPluggedintent.putExtra("state", 0);
headSetUnPluggedintent.putExtra("name", "Headset");
try {
    sendOrderedBroadcast(headSetUnPluggedintent, null);
} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

sauf générer une SecurityException et l'attraper.

Dans les autres réponses où le code fonctionne, c'est en raison de la diffusion de KeyEvent.KEYCODE_HEADSETHOOK. 

8
maddesa

En conclusion de ce fil, voici le code qui fonctionne pour moi pour Android 4.2.2. 

-> La réponse à l'appel est simulée en simulant le raccordement du micro-casque et en maintenant la diffusion en mode try-catch, comme mentionné par @PravinDodia dans le fil précédent. (Observez qu'une exception est levée et gérée dans la capture et que l'appel est répondu de toute façon. Je suppose donc que nous pouvons simplement ignorer cette exception et continuer à vivre la vie comme si rien ne s'était passé!)

-> L'appel est déconnecté via ITelephony. 

public class PhoneCallReceiver extends BroadcastReceiver {
 Context context = null;
 private static final String TAG = "Phone call";

 @Override
 public void onReceive(Context context, Intent intent) {
     if (!intent.getAction().equals("Android.intent.action.PHONE_STATE")) 
         return;
     else {
         String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);

         if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
             answerPhoneHeadsethook(context, intent);
             return;
         }
         else if(state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
             Log.d(TAG, "CALL ANSWERED NOW!!");
             try {
                    synchronized(this) {
                        Log.d(TAG, "Waiting for 10 sec ");
                        this.wait(10000);
                    }
                }
                catch(Exception e) {
                    Log.d(TAG, "Exception while waiting !!");
                    e.printStackTrace();
                }
             disconnectPhoneItelephony(context);
             return;
         }
         else {
             Log.d(TAG, "ALL DONE ...... !!");
         }
     }  
 }

 public void answerPhoneHeadsethook(Context context, Intent intent) {
     String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
     if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
         String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
         Log.d(TAG, "Incoming call from: " + number);
         Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);             
         buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
         try {
             context.sendOrderedBroadcast(buttonUp, "Android.permission.CALL_PRIVILEGED");
             Log.d(TAG, "ACTION_MEDIA_BUTTON broadcasted...");
         }
         catch (Exception e) {
             Log.d(TAG, "Catch block of ACTION_MEDIA_BUTTON broadcast !");
         }

         Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
         headSetUnPluggedintent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         headSetUnPluggedintent.putExtra("state", 1); // 0 = unplugged  1 = Headset with microphone 2 = Headset without microphone
         headSetUnPluggedintent.putExtra("name", "Headset");
         // TODO: Should we require a permission?
         try {
             context.sendOrderedBroadcast(headSetUnPluggedintent, null);
             Log.d(TAG, "ACTION_HEADSET_PLUG broadcasted ...");
         }
         catch (Exception e) {
                // TODO Auto-generated catch block
                //e.printStackTrace();
                Log.d(TAG, "Catch block of ACTION_HEADSET_PLUG broadcast");
                Log.d(TAG, "Call Answered From Catch Block !!");
         }
         Log.d(TAG, "Answered incoming call from: " + number);  
    }
    Log.d(TAG, "Call Answered using headsethook");
 }

 public static void disconnectPhoneItelephony(Context context) {
     ITelephony telephonyService;
     Log.v(TAG, "Now disconnecting using ITelephony....");
      TelephonyManager telephony = (TelephonyManager) 
      context.getSystemService(Context.TELEPHONY_SERVICE);  
      try {
          Log.v(TAG, "Get getTeleService...");
          Class c = Class.forName(telephony.getClass().getName());
          Method m = c.getDeclaredMethod("getITelephony");
          m.setAccessible(true);
          telephonyService = (ITelephony) m.invoke(telephony);
          //telephonyService.silenceRinger();
          Log.v(TAG, "Disconnecting Call now...");
          //telephonyService.answerRingingCall();
          //telephonyService.endcall();
          Log.v(TAG, "Call disconnected...");
          telephonyService.endCall();
      } catch (Exception e) {
       e.printStackTrace();
       Log.e(TAG,
               "FATAL ERROR: could not connect to telephony subsystem");
       Log.e(TAG, "Exception object: " + e);
      }
 }
}

Au moins, la fonctionnalité de déconnexion fonctionne et nous savons comment cela fonctionne. Ainsi, ceux qui souhaitent développer une application Call Barring peuvent continuer. Pour ceux qui comme moi veulent répondre à un appel, je suppose que nous pouvons utiliser ceci pour le moment et espérons seulement que cela ne cessera pas de fonctionner dans la prochaine version. 

7
Piyush

Essayez ceci Répondez pour mettre fin à l’appel de façon grammatique Cela fonctionne bien pour moi.

try {

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

Essaye ça :

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

Ajouter des autorisations dans AndroidManifest.xml file as

<uses-permission Android:name="Android.permission.MODIFY_PHONE_STATE"/>
3
Raj008

La méthode ITelephony ne fonctionne pas sur la version 4.4 et je trouve que la méthode du casque/bouton multimédia autorise toujours une sonnerie assez longue avant le raccrochage. 

Cet article de blog chaps montre une nouvelle méthode que j'ai testée: 4.4.2 Galaxy s4 et HTC one mini, qui raccroche beaucoup plus rapidement et ne reçoit pas non plus un appel manqué.

http://aprogrammersday.blogspot.co.uk/2014/05/disconnect-block-drop-calls-Android-4.html

La technique utilise un exécutable d'exécution comme ci-dessous, vous devrez peut-être utiliser un numéro différent pour certains périphériques.

public class HangupPhoneCallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (TelephonyManager.EXTRA_STATE_RINGING.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {

            Executor eS = Executors.newSingleThreadExecutor();
            eS.execute(new Runnable() {
                @Override
                public void run() {
                    Runtime runtime = Runtime.getRuntime();
                    try {
                        Log.d(TAG, "service call phone 5 \n");
                        runtime.exec("service call phone 5 \n");
                    } catch (Exception exc) {
                        Log.e(TAG, exc.getMessage());
                    }
                }
            });

            return;
        }
    }
}
3
Chris.D

La déconnexion d'appels via la téléphonie informatique ne fonctionne pas sur certains appareils tels que Samsung S Duos. Mais vous pouvez toujours faire sonner la sonnerie :)

2
Akhil Dad
To end call in older version then 9.0
use this:


        TelephonyManager tm = (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);

        Method m1 = null;
        try {
            m1 = tm.getClass().getDeclaredMethod("getITelephony");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        m1.setAccessible(true);
        Object iTelephony = null;
        try {
            iTelephony = m1.invoke(tm);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        Method m3 = null;
        try {
            m3 = iTelephony.getClass().getDeclaredMethod("endCall");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            m3.invoke(iTelephony);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

& for pie

 TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
            if (telecomManager != null) {
                return telecomManager.endCall();
            }

Make sure your compile SDK version is 28
0
ritu patel