Monday, June 2, 2014

How to create PhoneStateReceiver get CLI and call events

During first 5 years of my career I luckily worked at software houses that created computer telephony related software, I had used Microsoft TAPI and Dialogic APIs to create IVR and Operator Console and Call Recorder software. When I first started Android programming in 2010, I was very curious about phone states and CLI. 





I later figured it out and came to know that a BroadcastReceiver will be the right thing to use in
this case. BroadcastReceiver is a specially designed component offered by Android OS, and
it is used to handle various broadcasts sent by apps or OS components.
The broadcasts related to telephone call states are sent by TelephonyManager which is a
component of Android OS.
The phone state receiver given below can be used in scenarios like call blocking, call tracking,
and sending information to http or cloud based servers.
This salient features of my phone state receiver are given below
  1. Get the CLI from an incoming call
  2. Get the dialled number from an outgoing call
  3. When an inbound call is connected, get informed
  4. When a call is disconnected, get notified









public class PhoneStateReceiver extends BroadcastReceiver {
public enum CallType {INCOMING, OUTGOING, NA}
private static String lastNumber = "";
private static int lastCallState = 0;
/**
* This code is provided by Naeem Akram, 
* without any warranty guarantee or liability
* This code is meant for the good of developer community
* Please don't delete this comment when reusing 
* this code, and if possible give proper
* credit to the developer.
* In case you want to hire Naeem, go to following URL:
* https://www.odesk.com/users/~012d73aa92fad47188
*/
@Override
public void onReceive(final Context context, Intent intent) {

// Check phone state
String strPhoneState = "", strNumber = "";
CallType ctVal = CallType.NA;
int callState = 0;  
TelephonyManager tm = null;

try{
tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
strPhoneState = intent
.getStringExtra(TelephonyManager.EXTRA_STATE);
callState = tm.getCallState();

Log.d(Keys.LOGTAG, "CS: " + callState + 
" - Last CS: " + lastCallState +" - Phone State: " + strPhoneState);

if(strPhoneState != null)
{
if(!strPhoneState.equals(TelephonyManager.EXTRA_STATE_IDLE))
// don't post here if phone state is idle, it will get posted later.
{
postPhoneStateEvent(context, strPhoneState);
}
}else{
strPhoneState = "";
}

Log.d(Keys.LOGTAG, "INF: Broadcast received; " + strPhoneState);

if(callState == TelephonyManager.CALL_STATE_IDLE){
// the call just ended, it might be incoming or outgoing
Log.d(Keys.LOGTAG, "CALL STATE: " + callState);    
if(intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER))
{// this is an incoming call
ctVal = CallType.INCOMING;
strNumber = intent.getStringExtra(
 TelephonyManager.EXTRA_INCOMING_NUMBER).trim();
}
else if(intent.hasExtra(Intent.EXTRA_PHONE_NUMBER))
{// this is an outgoing call
ctVal = CallType.OUTGOING;
strNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
else if(lastNumber.length() > 0)
{
ctVal = CallType.INCOMING;
strNumber = lastNumber;
lastNumber = "";
}

if(strPhoneState != null){
// maybe save phone state to DB, or send it to a server 
postPhoneStateEvent(context, strPhoneState);
}

if(ctVal == CallType.OUTGOING){
// maybe save the number of called party, 
// or send the info to a server over http
postCLIEvent(context, ctVal, strNumber);
lastNumber = "";
strNumber = "";
}

if(lastCallState == CALL_STATE_CONNECTED || 
 lastCallState == TelephonyManager.CALL_STATE_OFFHOOK){
 postPhoneStateEvent(context, "DISCONNECTED");
}
if(lastCallState == TelephonyManager.CALL_STATE_RINGING){
postPhoneStateEvent(context, "MISSED");
}
}// if callState == CALL_STATE_IDLE
else if(
callState == TelephonyManager.CALL_STATE_OFFHOOK)
{// until so far we can only figure out 
// when an incoming call gets connected
if(lastCallState == TelephonyManager.CALL_STATE_RINGING){
// offhook right after ringing means we 
// attended the call
Log.d(Keys.LOGTAG, "INCOMING CALL CONNECTED");
postPhoneStateEvent(context, "CONNECTED " + lastNumber);
// tell a server that a call is
// connected over http or socket
}
}
else{
// callstate != 0 zero means call state is idle
if(intent.hasExtra
(TelephonyManager.EXTRA_INCOMING_NUMBER))
{// this is an incoming call     
lastNumber = intent.getStringExtra
 (TelephonyManager.EXTRA_INCOMING_NUMBER).trim();
Log.d(Keys.LOGTAG, "Ringing INCOMING event.");
postPhoneStateEvent(context, CallType.INCOMING + " : " + lastNumber);
}
else if(intent.hasExtra
(Intent.EXTRA_PHONE_NUMBER)){
// this is an outgoing call
lastNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
postCLIEvent(context, CallType.OUTGOING, lastNumber);
}    
Log.d(Keys.LOGTAG, "Last Number: " + lastNumber);
}
}catch(Exception excp){
excp.printStackTrace();
} 
lastCallState = callState;
// local callState is OFFHOOK our custom 
CONNECTED val gets messed
}
private void postPhoneStateEvent(final Context context, 
 String strPhoneState) {
if(strPhoneState != null){
// POST THE PHONE STATE SOMEWHERE SOMEHOW
}
}
private void postCLIEvent(Context _context, 
 CallType _callType, String _cli){
// POST THE CLI SOMEWHERE SOMEHOW
}
}

You will need following permissions in order to sue the phone state receiver
:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

We must add every app component to our Manifest.xml file, and our phone state broadcast receiver will be added like this:

<receiver
 android:name="com.usra.ca.PhoneStateReceiver"
    android:enabled="true" >
    <intent-filter android:priority="1" >
    <action android:name="android.intent.action.PHONE_STATE"
    <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
    <action android:name="android.intent.action.ANSWER"/>
    </intent-filter>
</receiver>

Android has got only three phone states
  • IDLE
  • RINGING
  • OFFHOOK

Please feel free to comment on the post, and share ideas in general.

I'm working as a full time freelance programmer through ODesk.com, I have a strong profile over there. Feel free to ping me if you need an Android app. Elance widget is given on top right of the page, for ODesk follow the link given below:


https://www.odesk.com/users/~012d73aa92fad47188

51 comments:

  1. Thanks for this code , it works fine.. But How can i detect radio link failure during comm unication , i mean how to detect call drop event in my app?

    ReplyDelete
  2. You are welcome, I am not sure how that will happen. If the call is fully disconnected/dropped then an IDLE event will be shown in phone state receiver. If you will be keeping track of previous phone states, you can check whether previous phone state was OFFHOOK or RINGING in which cases you will know that the call was dropped after either an outgoing call or on incoming call.
    It gets complicated I know, but that's the way it is... Google does not want people to mess with its operating system :)

    ReplyDelete
  3. Hey Naeem, the statement "Log.d(Keys.LOGTAG, "Ringing INCOMING event.");" shows an error: "cannot resolve symbol Keys". Is it a local variable you created?

    ReplyDelete
  4. "LOGTAG" is a public constant defined in class "Keys". I try to use a separate class for frequently used strings and variables,

    ReplyDelete
    Replies
    1. I'm still learning and get "Multiple markers at this line
      - Log cannot be resolved
      - Keys cannot be resolved to a
      variable"
      How do I define Log, Keys and LOGTAG in this program?

      Delete
    2. I also get "Executors cannot be resolved" and "Executor cannot be resolved
      to a type" - how do I get this working?

      Delete
    3. I found "import android.util.Log;" solved the Log error

      Delete
  5. Keys is a separate class it contains a static final string named LOGTAG, you can set value to anything you like.
    You will need to import package "java.util.concurrent.Executors" in order to resolve the executors error.

    ReplyDelete
  6. NAZA We are the service provider Online casinos With every bet A complete answer

    ReplyDelete
  7. สล็อต 55 casino website that have all the betting in it to answer all the needed for the gambler

    ReplyDelete
  8. เว็บหวยออนไลน์ The most popular Online betting sites 24 hours a day, no holidays.

    ReplyDelete
  9. MariaOne way to help you make money right now

    ReplyDelete
  10. เว็บ 123 online gambling service provider that meets international standards and meets all gambler needs.

    ReplyDelete
  11. เว็บ88 Web betting on high returns are the most popular now.

    ReplyDelete
  12. เล่นยี่กี casino website that have all the betting in it to answer all the needed for the gambler

    ReplyDelete
  13. เว็บพนันออนไลน์May 12, 2022 at 2:13 PM

    เว็บพนันออนไลน์ The most trending online gambling website on the internet. Access many services easily via smartphone. Our website is 100% safe.

    ReplyDelete
  14. วิธีเล่นหวย naza55 offers a wide variety of promotions with the benefit of participating in bets.

    ReplyDelete
  15. There are many games to play. The website is easy to use, good promotion, fast deposit, withdraw within 5 seconds, admin responds quickly.เว็บคาสิโนออนไลน์ 123dd

    ReplyDelete
  16. คาสิโนออนไลน์ Enjoy and Play games that will earn your Money here Let get it

    ReplyDelete
  17. แฮนดิแคป Online gambling websites, we have a system to recommend friends that can easily make money. For you, all customers, easy to use and easy to make a casino.
    5 is famous for most people because can play when you free time. can play or home, office or drive you can play all day all night.

    ReplyDelete
  18. ราคาแฮนดิแคป I find luck quite predictable. If you want more luck, more opportunities, more enthusiasm.

    ReplyDelete
  19. Want to have money to spend? Come here. แทงบอล 123 123dd

    ReplyDelete
  20. สล็อต123 The number 1 international standard casino that responds perfectly to gamblers.

    ReplyDelete
  21. เว็บ 123 You can earn money from this way 24 hours a day.

    ReplyDelete
  22. A complete online betting site is available online on mobile, here you will learn everything. naza8k

    ReplyDelete
  23. zoom The most popular quality online gambling site at the moment

    ReplyDelete

Feel free to talk back...