Tuesday, May 17, 2011

how to answer an incoming call in android 2.3

Today, I was researching on a how to answer incoming call in android 2.3 automatically. My first thought was using "ITelephony.aidl" and call the answerRingingCall(). When i looked into more details answerRingingCall() function all requires MODIFY_PHONE_STATE permission which is marked as a as "signatureOrSystem" which is mentioned here
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=commit;h=f4ece2086f3b7060edc4b93a12f04c9af648867a

and here
http://code.google.com/p/android/issues/detail?id=15031

bummer. Then thought of a another work-around and bluetooth headset popuped to my mind. In this all I had to do was to call start new intent with ACTION_UP. It worked!

here is the soulution

BroadcastReceiver PhoneState = new BroadcastReceiver() {

@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));
sendOrderedBroadcast(answer, null);

}

return;
}};

// Update on 2011-09-27

In Android 2.3.3 HTC Sensation this piece of code does not work. Reason is in 2.3.3 I found a HeadsetObserver  listening for actual bluetooth plug-in event. So you need to send a Intent pretending there is a headset connected already. To fix this problem you need to send the ACTION_HEADSET_PLUG Intent before calling the above code.


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

 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?
 sendOrderedBroadcast(headSetUnPluggedintent, null);

// Update on 2012-04-27

Lot of people are asking me how to end call, here is the code


Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);            
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
getContext().sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");

// Update on 2012 - 08 - 21
This method no longer works on Android Jelly Bean (4.1.1) and possibly later versions. Google has blocked  3rd party apps from broadcasting Intent.ACTION_MEDIA_BUTTON.

java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.intent.action.HEADSET_PLUG from pid=30943, uid=10064


Android 1 : Me 0

46 comments:

  1. Freakin' great, thanks, it worked on my emulator, I hope it will work on my boss' phone!!!

    ReplyDelete
  2. Great!

    Once the call is answered, can one play an audio file to the incoming call? Any way around?

    ReplyDelete
  3. @Sanjay

    Only way I can think of is to play the audio file in loudspeaker mode.

    ReplyDelete
  4. hey dude I tried your workout on HTC desire, but didn't work. Am I doing something wrong !!!

    ReplyDelete
  5. Hello - I am also trying on HTC with no results. Did you manage?

    ReplyDelete
    Replies
    1. i am using HTC Desire V it's not working and also i tried in 2.3.6 also not working

      Delete
  6. What is your Android version and phone model ?

    ReplyDelete
  7. HTC Sensation. 2.3.4

    I also tried on the HTC desire 2.3.3

    ReplyDelete
  8. I have not tested this code on HTC Desire 2.3.3 but I have tested this code on HTC Sensation. 2.3.4.

    Did you try the code I updated on 2011-09-27 for sensation? Easy-way to diagnose this issue is to use a headset. Call the phone and answer using headset. Look at the LogCat view. You can see the keys Android passing when answer a call. Take it and try to simulate.

    ReplyDelete
  9. Hi, thanks for you solution. It works on my HTC Desire HD. Now I'm wondering if it is possible to use the same way to end or reject a call?

    ReplyDelete
    Replies
    1. Did you succeed in rejecting call?

      Delete
    2. private void endCall2() {
      Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
      buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
      getContext().sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
      }

      This is how to End Call. - tested on LG Optimus 2.3

      Delete
    3. Thanks! I cant get the code above to answer my call - I am using an HTC Incrediable. Any idea?

      Delete
    4. I tested and the only function not working in 2.3.x Itelephony is answerRingingCall() and silenceRinger() so i would recommend you keep using the endCall() function.

      Delete
    5. This comment has been removed by the author.

      Delete
    6. Use a BroadcastReceiver to get the outgoing call state and set setResultData(null); You should be fine.

      Delete
    7. Hi i use this code for end call
      private void endCall2() {
      Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
      buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
      getContext().sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
      }
      but i am not able to end call after receving the call it will end the call when incoming call comes but not working when i accept the call......pls any solution

      Delete
  10. Hi, unfortunately this is not working on HTC DesireS, Android version-2.3.5
    Any help please!?
    Thanks, vamsi

    ReplyDelete
  11. Can you post a simple app that works ?
    Because i trying a tonn of variation on my HTC Desire HD 2.3.5 and it's not working :(

    ReplyDelete
  12. PLease up the application i have the same error

    ReplyDelete
  13. please upload source!

    ReplyDelete
  14. All of this is working perfectly fine on the Samsung Galaxy GB 2.3.6, but I am seeing that after the call is answered programmatically, there is around 2 sec delay between when the Call state is switched from ringing to active to when the actual voice is heard from calling party.

    So basically it seems that the app sends an ordered broadcast and immediately switches over from Ringing to Active mode, but the actual android InCallScreen activity doesn't take over this broadcast and hence the delay.

    Can we reduce this delay somehow, so that the transition is smooth ?

    ReplyDelete
  15. This code works great! But I would like my app to patch incoming calls through to the speaker automatically. Any suggestions??

    I have tried:

    1) Using an Audio Manager to alter the incoming audio stream

    *AudioManager.setMode(MODE_IN_CALL);

    2) Using an Audio Manager to turn on the speaker before and after answering the call

    *AudioManager.setSpeakerphoneOn(true);

    3) Unplugging the "headset" after taking the incoming call

    *Intent headSetUnPluggedintent = new Intent(Intent.ACTION_HEADSET_PLUG);
    *headSetUnPluggedintent.putExtra("state", 0); // 0 = unplugged 1 = Headset with microphone 2 = Headset without microphone
    *context.sendOrderedBroadcast(headSetUnPluggedintent, null);


    But none of this is working. Any suggestions????

    ReplyDelete
  16. Thanks for the code. It works really well. I am currently testing on android 4.0 and the activity crashes because of the ACTION_HEADSET_PLUG permission. And I cant find a permission to work with it. Any idea??

    ReplyDelete
    Replies
    1. in my test in 4.0.4 version it works without permission error. in manifest I set android.permission.READ_PHONE_STATE and android.permission.MODIFY_PHONE_STATE of course

      Delete
    2. Sorry , I don´t know what I was doing wrong but it does work fine until 4.1 that i know. Thanks

      Delete
  17. AudioManager audioManager = (AudioManager)getBaseContext().getSystemService(Context.AUDIO_SERVICE);
    audioManager.setSpeakerphoneOn(true);

    works well, but you have to make some 1000ms delay after answering. and of course: android.permission.MODIFY_AUDIO_SETTING

    ReplyDelete
  18. Hi JAN.GER,
    thanks so much for your reply. I have tried so many different things, but still no luck. I am developing on API Level 10, Android 2.3.4

    The class that I have created which answers incoming calls extends BroadcastReceiver. So when I created my AudioManager using your suggested code, I got an error that said the "getBaseContext()" method is not defined for my class. In order to made things work I had to do:

    audioManager = (AudioManager) ((ContextWrapper) context).getBaseContext().getSystemService(Context.AUDIO_SERVICE);

    I'm not sure if this is equivalent, but it does not produce any errors. Then my app MUST silence the phone ringer when receiving an incoming call so I do:

    audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);

    I'm not sure if this plays a role in my audio not patching through to the speaker or not? Then, to answer calls, I do:

    1) Intent answer = new Intent(Intent.ACTION_MEDIA_BUTTON);
    2) answer.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
    3) context.sendOrderedBroadcast(answer,
    "android.permission.CALL_PRIVILEGED");
    4) SystemClock.sleep(900);
    5) audioManager.setSpeakerphoneOn(true);

    But again, this still does not work for me. I have tried many values for the time delay....anywhere from 200ms to 2000ms. Perhaps there is a better way to create a time delay?

    And in my manifest file, I believe I have all the necessary phone/audio permissions. Also, I am registering my call receiver class like this:







    Should I be registering this differently?

    Also, another problem that I am having is that when the cell phone receives incoming calls, it tries to perform the same steps as my custom call receiver class (even though my app is not being launched/used). I do not want this to happen. The phone should answer calls normally when my app is not in use.

    Any suggestions??? And again, I really appreciate your help thus far :-)

    ReplyDelete
  19. I'm not sure what happened to the rest of my post, but it should look like this:

    And in my manifest file, I believe I have all the necessary phone/audio permissions. Also, I am registering my call receiver class like this:

    1) < receiver android:name=".helpers.CallReceiver">
    2) < intent-filter>
    3) < action
    android:name="android.intent.action.PHONE_STATE">< /action>
    4) < /intent-filter>
    5) < /receiver>

    Should I be registering this differently?

    ReplyDelete
  20. Using the following seems to work on JellyBean(4.1) despite the last broadcast being disallowed, hope this helps someone:

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

    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");
    if (Build.VERSION.SDK_INT < 16) {
    //Not allowed on JellyBean, but the rest seems to work
    context.sendOrderedBroadcast(headSetUnPluggedintent, null);
    }

    ReplyDelete
    Replies
    1. The ITelephony method of answer and endCall shown elsewhere also works on 4.1

      Delete
    2. On the HTC One X 4.1 the above broadcast method does not work (although it works on other 4.1 devices). I tried with the ITelephony method but I am receiving a security error. Do you have any ideas?

      Delete
  21. great post & workaround ... i have been testing in android 2.3.3 and it really works fine .. thanks .. keep up with great work :)

    ReplyDelete
  22. Great post...helped me a lot..am using ics 4.0.4 in my xperia neo v. Call answering works perfects but call rejecting doesnt work. any pointers?

    ReplyDelete
  23. Hello,

    I'm not a dev, I'm using tasker and unfortunately the "take call" funtion does not work on my HTC Sensation (Android 4.0.3) due to a permission error. Is it possible you could give me a Java Script I could execute on tasker to bypass this Permission error I get (I guess it is the one you mention in your post).
    Thanks

    ReplyDelete
    Replies
    1. I am having the same answer call problem with Tasker. Also the stream media to call action doesn't work on my htc desire hd. a Java script alternative would be great.

      Delete
  24. i was testing this on emulator but its not working....is it only work for device..........

    ReplyDelete
  25. Thanks Aruna, Since last 8 days I was strugling this problem. Im having Optimus One with 2.3.3 & your code work perfectly for me to reject call.

    public static void answerPhoneHeadsethook(Context context) {
    // Simulate a press of the headset button to pick up the call
    // SettingsClass.logMe(tag, "Simulating headset button");
    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");
    }

    ReplyDelete
  26. I have tried to use end call code but it gives on the getContext() method, how to resolve it?
    or where is this method id defined?

    ReplyDelete
  27. I am having the same problem with Tasker. Also the stram media to call action doesn't work on my htc desire hd. a Java script alternative would be great.

    ReplyDelete
  28. Can anybody send me full working code, please. This code don't answer on call. I tested on Android 2.3.3 and 4.1.2. And I need this so much!

    ReplyDelete
  29. Hi,
    Finally works,
    I have tested on HTC One V (Android 4.0)
    Please find my Phone state listener http://pastebin.com/wcnNEN5P

    Thank you very much for your support

    ReplyDelete
  30. I want to answer a call through my app.
    can you help me?
    plz send me code...
    Email id: R62r64@gmail.com
    thanking you...

    ReplyDelete
  31. hi..Im college student, thanks for sharing :)

    ReplyDelete