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:
public void onReceive(final Context context, Intent intent) {

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

tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
strPhoneState = intent
callState = tm.getCallState();

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

if(strPhoneState != null)
// don't post here if phone state is idle, it will get posted later.
postPhoneStateEvent(context, strPhoneState);
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);    
{// this is an incoming call
ctVal = CallType.INCOMING;
strNumber = intent.getStringExtra(
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
postPhoneStateEvent(context, "CONNECTED " + lastNumber);
// tell a server that a call is
// connected over http or socket
// callstate != 0 zero means call state is idle
{// this is an incoming call     
lastNumber = intent.getStringExtra
Log.d(Keys.LOGTAG, "Ringing INCOMING event.");
postPhoneStateEvent(context, CallType.INCOMING + " : " + lastNumber);
else if(intent.hasExtra
// 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){
lastCallState = callState;
// local callState is OFFHOOK our custom 
CONNECTED val gets messed
private void postPhoneStateEvent(final Context context, 
 String strPhoneState) {
if(strPhoneState != null){
private void postCLIEvent(Context _context, 
 CallType _callType, String _cli){

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:

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

Android has got only three phone states
  • IDLE

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

I'm working as a full time freelance programmer through, 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:


  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?

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

  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?

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

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

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

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

  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.


Feel free to talk back...