Click here to Skip to main content
15,880,392 members
Articles / Mobile Apps / Android

Detecting Incoming and Outgoing Phone Calls on Android

Rate me:
Please Sign up or sign in to vote.
4.73/5 (19 votes)
13 Mar 2013CPOL3 min read 255.3K   9.8K   26   31
In this article, I'll show you how to detect incoming and outgoing phone calls on the Android platform.

Introduction

In this article, I'll show you how to detect incoming and outgoing phone calls on the Android platform. It can be useful if you need to perform some action when a call is made. For example, to block it, to log it, or send call information to a server.

This article gives step-by-step instructions on how to create a simple demo app that will detect incoming and outgoing phone calls and show a "toast" message with phone number. You can extend and use this code for your own needs.

Incoming Calls

For incoming calls, we need to use the TelephonyManager class and its method listen to register a listener that will receive call state, data connection, network, SIM, and other events related to telephony. We are interested only in the call state notifications. This requires android.permission.READ_PHONE_STATE permission.

So, create our listener class derived from PhoneStateListener, and override the onCallStateChanged method, as follows:

Java
/**
* Listener to detect incoming calls. 
*/
private class CallStateListener extends PhoneStateListener {
  @Override
  public void onCallStateChanged(int state, String incomingNumber) {
      switch (state) {
          case TelephonyManager.CALL_STATE_RINGING:
          // called when someone is ringing to this phone
    
          Toast.makeText(ctx, 
                  "Incoming: "+incomingNumber, 
                  Toast.LENGTH_LONG).show();
          break;
      }
  }
}

Now I'll explain the onCallStateChanged method.

First argument - state is the call state, it can be CALL_STATE_RINGING, CALL_STATE_OFFHOOK, or CALL_STATE_IDLE. Ringing is the state when someone is calling us, offhook is when there is active or on hold call, and idle is when nobody is calling us and there is no active call. We are interested in the ringing state.

Second argument - incomingNumber, it's the number who is calling us.

As shown in the code above, the listener shows the "toast" message with the phone number when an incoming call is ringing.

Next, get an instance of TelephonyManager and register the listener:

Objective-C
tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);

When the app is no longer needed to receive notifications, it must unregister a listener by call:

Objective-C
tm.listen(callStateListener, PhoneStateListener.LISTEN_NONE);

Outgoing Calls

For outgoing calls, the system sends a broadcast action android.intent.action.NEW_OUTGOING_CALL. We need to make a broadcast receiver, that will receive the intent with this action.

To receive this broadcast, the android.permission.PROCESS_OUTGOING_CALLS permission is required.

Create the broadcast receiver class:

Objective-C
/**
* Broadcast receiver to detect the outgoing calls.
*/
public class OutgoingReceiver extends BroadcastReceiver {
     public OutgoingReceiver() {
     }

     @Override
     public void onReceive(Context context, Intent intent) {
         String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
         
         Toast.makeText(ctx, 
           "Outgoing: "+number, 
           Toast.LENGTH_LONG).show();
     }
}

As with incoming calls, this code will show a "toast" message with phone number when there is an outgoing call.

Register the broadcast receiver:

Java
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
ctx.registerReceiver(outgoingReceiver, intentFilter);

We finished with the calls detection code. And now, we need to create an activity that will enable/disable calls detection. It will be an activity with a simple UI, with a textview showing detection status, button to enable/disable detection, and exit button.

But here is another issue - when our activity loses focus, calls detection is disabled. To prevent this, we have to make a service that will run and that will enable detection on start, and disable on stop.

Create the service:

Objective-C
/**
 * Call detect service. 
 * This service is needed, because MainActivity can lost it's focus,
 * and calls will not be detected.
 * 
 * @author Moskvichev Andrey V.
 *
 */
public class CallDetectService extends Service {
    private CallHelper callHelper;
 
    public CallDetectService() {
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        callHelper = new CallHelper(this);
  
        int res = super.onStartCommand(intent, flags, startId);
        callHelper.start();
        return res;
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
  
        callHelper.stop();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // not supporting binding
        return null;
   }
}

Create the activity.

Get the UI elements and set the onclick button handlers:

Java
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    textViewDetectState = (TextView) findViewById(R.id.textViewDetectState);
    
    buttonToggleDetect = (Button) findViewById(R.id.buttonDetectToggle);
    buttonToggleDetect.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
             setDetectEnabled(!detectEnabled);
         }
    });
    
    buttonExit = (Button) findViewById(R.id.buttonExit);
    buttonExit.setOnClickListener(new OnClickListener() {
         @Override
         public void onClick(View v) {
             setDetectEnabled(false);
             MainActivity.this.finish();
         }
    });
}

Create the setDetectEnabled method that will toggle calls detection:

Objective-C
private void setDetectEnabled(boolean enable) {
    detectEnabled = enable;
 
    Intent intent = new Intent(this, CallDetectService.class);
    if (enable) {
          // start detect service 
          startService(intent);
        
          buttonToggleDetect.setText("Turn off");
          textViewDetectState.setText("Detecting");
    }
    else {
          // stop detect service
          stopService(intent);
  
          buttonToggleDetect.setText("Turn on");
          textViewDetectState.setText("Not detecting");
    }
}

AndroidManifest.xml

XML
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bitgriff.androidcalls"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    
   <!--
         Permissions required for calls detection.
        -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
    
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".CallDetectService"
            android:enabled="true"
            android:exported="false" >
        </service>
    </application>

</manifest>

Summary

I showed you how to detect incoming and outgoing phone calls on Android. It is all quite simple. Please send me your comments and suggestions.

In the next articles, I'll show you other aspects of Android platform programming (networking, threads, multimedia processing, integration with websites and webservices, etc). You can download the latest version of the source code for this article here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO BitGriff LLC
Russian Federation Russian Federation
My name is Andrey Moskvichev.

I'm a software developer with more than 14 years of programming experience.

I specialize in networking, Unix systems (Linux, FreeBSD), mobile programming, computer graphics, software architecture, reverse engineering, data processing systems, AI, computer vision.

I'm interested in all new cutting edge technologies.

Comments and Discussions

 
QuestionIncoming Call not detected for device API>=26 whenever app is closed and not running even in foreground Pin
Member 1386893813-Jun-18 1:19
Member 1386893813-Jun-18 1:19 
QuestionGreat Pin
Harvey Green12-Apr-17 21:31
Harvey Green12-Apr-17 21:31 
QuestionAfter removing app from recent apps means clearing memory i am not able to detect call Pin
Kishan Gohel2-Jan-17 1:33
professionalKishan Gohel2-Jan-17 1:33 
Questionthanks you sir you save my lot of time. Pin
Member 1277795514-Nov-16 23:17
Member 1277795514-Nov-16 23:17 
Suggestiondetecting incoming and outgoing calls Pin
AustinHoffman5-Sep-16 2:56
AustinHoffman5-Sep-16 2:56 
QuestionSIP Calls Pin
Member 124341333-Apr-16 13:11
Member 124341333-Apr-16 13:11 
Questionautomatic conversion from slient mode to ringing mode Pin
Member 1234989224-Feb-16 14:04
Member 1234989224-Feb-16 14:04 
QuestionHow do you detect the SIM details Pin
Member 123029532-Feb-16 4:33
Member 123029532-Feb-16 4:33 
Questioncan we send the digits to the incoming number Pin
prakashrao25-Dec-15 23:03
prakashrao25-Dec-15 23:03 
QuestionHow to detect the end of an outgoing call? Pin
Dismi12-Jan-15 0:16
Dismi12-Jan-15 0:16 
QuestionGet a Web page title when incoming or outgoming calls Pin
Panos Marmaras3-Jan-15 7:22
Panos Marmaras3-Jan-15 7:22 
QuestionMy vote 5 Pin
WuRunZhe2-Sep-14 16:16
WuRunZhe2-Sep-14 16:16 
QuestionMy application crashing when using your code with a small addition of "writing to file" Pin
Member 1095618718-Jul-14 7:16
Member 1095618718-Jul-14 7:16 
Hi,
First of all, thanks for your code. I am a beginner to coding, and pretty new to android.
I have used your code and was able to detect the incoming/outgoing calls. Later, I added a small piece of code to write these incoming and outgoing numbers along with date and time into an external storage file.
I am not sure where did I go wrong, but my application is crashing while using it. Please correct me.
I introduced a new class called - "logHelper". The code in the logHelper.java file is as below:

Java
package com.my.callslog;
import java.io.File;  
import java.io.FileNotFoundException;  
import java.io.FileWriter;
import java.io.IOException;  
import android.content.Context; 

import android.os.Environment;  
import android.widget.Toast;

public class logHelper {

	public static void logTheRecord(String filename, String data) 
	{
		try {  
				System.out.println("********************");
				System.out.println("File name is: "+filename);
				System.out.println("data is: "+data);
				System.out.println("********************");
				
				System.out.println("*****External storage Location absolute path is:"+Environment.getExternalStorageDirectory().getAbsolutePath()+"********");
				System.out.println("*****External storage Location is:"+Environment.getExternalStorageDirectory()+"********");
				File myFile = new File (Environment.getExternalStorageDirectory(),filename);
				myFile.createNewFile();
				if(myFile.exists())
				{
					System.out.println("*****File Created. Location is:"+myFile.getAbsolutePath());
				}
				else
				{
					System.out.println("**********FILE NOT CREEATED*********");
				}
	            				System.out.println("**********File Writer Object Creation starts now*********");
				FileWriter fw = new FileWriter(myFile, true);
	         	System.out.println("**********File writer object created. File Writing starts now*********");
	         	fw.append(data);
	         	System.out.println("**********File Writing to Completed. Will start closing the file now*********");
	         	fw.close();
	         	System.out.println("**********closing the file completed*********");
	               
	         	Toast.makeText(getApplicationContext(),filename + "saved",Toast.LENGTH_LONG).show();  
	                   		
			} 
	                   
		catch (FileNotFoundException e) 
	    	{
				e.printStackTrace();
				System.out.println("**********Inside FileNotFound Exception*********");
	    	}  
	                   
		catch (IOException e) 
			{
				e.printStackTrace();
				System.out.println("**********Inside IO Exception*********");
			}

	}

	private static Context getApplicationContext() {
		// TODO Auto-generated method stub
		System.out.println("**********Inside getApplicationContext method*********");
		return null;
	}
}


The callHelper class is modified as below:
Java
import java.text.SimpleDateFormat;
import java.util.Calendar;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;

/**
 * Helper class to detect incoming and outgoing calls.
 * @author Moskvichev Andrey V.
 *
 */
public class CallHelper {

//	private LogFile logFile;
	

	/**
	 * Listener to detect incoming calls. 
	 */
	private class CallStateListener extends PhoneStateListener {
		
		@Override
		public void onCallStateChanged(int state, String incomingNumber) {
			System.out.println("**********Call State is: "+state+"*********");
			
			switch (state) {
			case TelephonyManager.CALL_STATE_RINGING:
				// called when someone is ringing to this phone
				System.out.println("**********Inside CAll_STATE_RINGING state - case*********");
				
				SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                String date = df.format(Calendar.getInstance().getTime());
                
                String tempData = date+"::"+"Incoming::"+incomingNumber+"\n";
                
                logHelper.logTheRecord("callsLog.log", tempData);
                

				Toast.makeText(ctx, 
						date + ": " + "Incoming: "+incomingNumber, 
						Toast.LENGTH_LONG).show();
				break;
			}
		}
	}
	
	/**
	 * Broadcast receiver to detect the outgoing calls.
	 */
	public class OutgoingReceiver extends BroadcastReceiver {
	    public OutgoingReceiver() {
	    }

	    @Override
	    public void onReceive(Context context, Intent intent) {
	    	System.out.println("**********Inside BroadcaseReceiver class for detecting outgoing calls*********");
	    	String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);

			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
            String date = df.format(Calendar.getInstance().getTime());
            
	        String tempData = date+"::"+"Outgoing::"+number+"\n";
	        
	        logHelper.logTheRecord("callsLog.log", tempData);
   
	    }
  
	}

	private Context ctx;
	private TelephonyManager tm;
	private CallStateListener callStateListener;
	
	private OutgoingReceiver outgoingReceiver;

	public CallHelper(Context ctx) {
		this.ctx = ctx;
		
		callStateListener = new CallStateListener();
		outgoingReceiver = new OutgoingReceiver();
	}
	
	/**
	 * Start calls detection.
	 */
	public void start() {
		System.out.println("**********Inside START DETECTING method*********");
		
		tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
		tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);
		
		IntentFilter intentFilter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
		ctx.registerReceiver(outgoingReceiver, intentFilter);
	}
	
	/**
	 * Stop calls detection.
	 */
	public void stop() {
		System.out.println("**********Inside STOP DETECTING method*********");
		
		tm.listen(callStateListener, PhoneStateListener.LISTEN_NONE);
		ctx.unregisterReceiver(outgoingReceiver);
	}

}



MainActivity.java, callDetectService.java remain same as yours.
The following is added to the AndroidManifest.xml file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">

I use Android 4.4W.

Problem: App gets deployed successfully, and starts listening. When an incoming or outgoing call comes, the app displays the number in the UI, and then crashes. While crashing, the application displays the message: "Unfortunately, CallsLog has stopped."
Upon observation, I see that the logs are written to the specified external file. Seems app is crashing immediately after writing into the file.
I use Eclipse and below is the LogCat messages:
07-18 13:05:18.393: I/System.out(1909): **********Inside START DETECTING method*********
07-18 13:05:18.573: I/System.out(1909): **********Call State is: 0*********
07-18 13:05:34.213: I/System.out(1909): **********Call State is: 1*********
07-18 13:05:34.213: I/System.out(1909): **********Inside CAll_STATE_RINGING state - case*********
07-18 13:05:34.553: I/System.out(1909): ********************
07-18 13:05:34.553: I/System.out(1909): File name is: callsLog.log
07-18 13:05:34.553: I/System.out(1909): data is: 2014-07-18T13:05:34::Incoming::123123
07-18 13:05:34.563: I/System.out(1909): ********************
07-18 13:05:34.563: I/System.out(1909): *****External storage Location absolute path is:/storage/sdcard********
07-18 13:05:34.563: I/System.out(1909): *****External storage Location is:/storage/sdcard********
07-18 13:05:34.623: I/System.out(1909): *****File Created. Location is:/storage/sdcard/callsLog.log
07-18 13:05:34.623: I/System.out(1909): **********File Writer Object Creation starts now*********
07-18 13:05:34.713: I/System.out(1909): **********File writer object created. File Writing starts now*********
07-18 13:05:34.723: I/System.out(1909): **********File Writing to Completed. Will start closing the file now*********
07-18 13:05:34.953: I/System.out(1909): **********closing the file completed*********
07-18 13:05:34.953: I/System.out(1909): **********Inside getApplicationContext method*********
07-18 13:05:35.073: D/AndroidRuntime(1909): Shutting down VM
07-18 13:05:35.073: W/dalvikvm(1909): threadid=1: thread exiting with uncaught exception (group=0xb1d7eb20)
07-18 13:05:35.523: E/AndroidRuntime(1909): FATAL EXCEPTION: main
07-18 13:05:35.523: E/AndroidRuntime(1909): Process: com.my.callslog, PID: 1909
07-18 13:05:35.523: E/AndroidRuntime(1909): java.lang.NullPointerException
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.widget.Toast.<init>(Toast.java:93)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.widget.Toast.makeText(Toast.java:241)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at com.my.callslog.logHelper.logTheRecord(logHelper.java:42)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at com.my.callslog.CallHelper$CallStateListener.onCallStateChanged(CallHelper.java:43)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.telephony.PhoneStateListener$2.handleMessage(PhoneStateListener.java:369)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.os.Handler.dispatchMessage(Handler.java:102)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.os.Looper.loop(Looper.java:136)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at android.app.ActivityThread.main(ActivityThread.java:5017)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at java.lang.reflect.Method.invokeNative(Native Method)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at java.lang.reflect.Method.invoke(Method.java:515)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
07-18 13:05:35.523: E/AndroidRuntime(1909): 	at dalvik.system.NativeStart.main(Native Method)



I used emulator 5554 (google nexus4).
Please help me with this crash.
Once it is fixed, I want to use these logs to display call patterns through some graphs.

Thanks
Ravi
AnswerRe: My application crashing when using your code with a small addition of "writing to file" Pin
Member 1095618718-Jul-14 9:00
Member 1095618718-Jul-14 9:00 
QuestionMultiple callbacks to onCallStateChanged() Pin
SLCompulsion26-Jun-14 6:44
SLCompulsion26-Jun-14 6:44 
QuestionDetect ringing state at outgoing call Pin
HichemSeeSharp25-Jun-14 10:10
HichemSeeSharp25-Jun-14 10:10 
GeneralMy vote of 5 Pin
Scyalsdk13-Jun-14 4:50
Scyalsdk13-Jun-14 4:50 
QuestionAndroid Project Control incoming messages from certain numbers Pin
makhondi28-Oct-13 5:46
makhondi28-Oct-13 5:46 
AnswerRe: Android Project Control incoming messages from certain numbers Pin
Andrey Moskvichev30-Oct-13 20:32
professionalAndrey Moskvichev30-Oct-13 20:32 
Questioncorrection of code Pin
Isha Gupta31-Jul-13 23:59
Isha Gupta31-Jul-13 23:59 
AnswerRe: correction of code Pin
Andrey Moskvichev30-Oct-13 20:31
professionalAndrey Moskvichev30-Oct-13 20:31 
QuestionSome confusing Pin
duong217914-Jun-13 17:13
duong217914-Jun-13 17:13 
AnswerRe: Some confusing Pin
Andrey Moskvichev21-Jun-13 23:08
professionalAndrey Moskvichev21-Jun-13 23:08 
Questionwhy incomming call detection dosen't use ctx.registerReceiver Pin
tieubuidoi12312-Jun-13 16:54
tieubuidoi12312-Jun-13 16:54 
Questionwhy incomming call detection dosen't use ctx.registerReceiver Pin
tieubuidoi12312-Jun-13 16:54
tieubuidoi12312-Jun-13 16:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.