Android PhoneStateListener Example - Truiton
Skip to content

Android PhoneStateListener Example

Android PhoneStateListener

While searching through some documentation I found an interesting class of android through which we can listen to some basic network related events, called Android PhoneStateListener which lead to this tutorial Android PhoneStateListener Example. Another class which needs an introduction is TelephonyManager, as this is the class which uses the Android PhoneStateListener to listen telephony service updates. This tutorial is the first one in the series of exploring Android PhoneStateListener and TelephonyManager classes at Truiton.com. Links to complete series are:

1) Android PhoneStateListener Example
2) Android onDataConnectionStateChanged – Detect Network Type
3) Android onSignalStrengthsChanged – LTE Strength Measurement

To start off let me give a brief intro about what all things I’ll be covering in this tutorial. Here I would be instantiating an object of Android TelephonyManager and listening to the updates of Context.TELEPHONY_SERVICE. To monitor the changes of specific telephony states like PhoneStateListener.LISTEN_CELL_INFO, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE and many more, I will be declaring a class CustomPhoneStateListener which will extend Android PhoneStateListener and override all of its methods.

Android TelephonyManager

This class of android is the main class through which one can access the telephony services of a device. All the information in this class is accessed through a system-level service called Context.TELEPHONY_SERVICE. Since this tutorial is focused on Android PhoneStateListener lets not get in depth of this class. The only relevant method for us in this class is TelephonyManager.listen(PhoneStateListener listener, int events). And for this method I will be declaring a custom PhoneStateListener.

Android PhoneStateListener

This is the actual class which gets callbacks, whenever some change is observed in TELEPHONY_SERVICE. Android PhoneStateListener has many callback methods which are called if we register for their specific LISTEN_ flag in the TelephonyManager’s listen method. Have a look at the listing below to correlate LISTEN_ flags with Android PhoneStateListener’s callback methods:

ANDROID PHONESTATELISTENER CALLBACK METHODS

LISTEN_ FlagCallback Method
PhoneStateListener.LISTEN_CALL_STATEonCallStateChanged(int state, String incomingNumber)
PhoneStateListener.LISTEN_CELL_INFOonCellInfoChanged(List<CellInfo> cellInfo)
PhoneStateListener.LISTEN_CELL_LOCATIONonCellLocationChanged(CellLocation location)
PhoneStateListener.LISTEN_DATA_ACTIVITYonDataActivity(int direction)
PhoneStateListener.LISTEN_DATA_CONNECTION_STATEonDataConnectionStateChanged(int state, int networkType)
PhoneStateListener.LISTEN_SERVICE_STATEonServiceStateChanged(ServiceState serviceState)
PhoneStateListener.LISTEN_SIGNAL_STRENGTHSonSignalStrengthsChanged(SignalStrength signalStrength)
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATORonCallForwardingIndicatorChanged(boolean cfi)
PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATORonMessageWaitingIndicatorChanged(boolean mwi)

Now since we are dealing here with phone states and locations we would require to declare some permissions in manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.truiton.phonestatelistener"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".PhoneStateListenerActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Next lets define our PhoneStateListenerActivity

package com.truiton.phonestatelistener;

import android.support.v7.app.ActionBarActivity;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class PhoneStateListenerActivity extends ActionBarActivity {
 TelephonyManager tManager;

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
 // Added because of PhoneStateListener.LISTEN_CELL_INFO
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_phone_state_listener);

 tManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
 tManager.listen(new CustomPhoneStateListener(this),
 PhoneStateListener.LISTEN_CALL_STATE
 | PhoneStateListener.LISTEN_CELL_INFO // Requires API 17
 | PhoneStateListener.LISTEN_CELL_LOCATION
 | PhoneStateListener.LISTEN_DATA_ACTIVITY
 | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
 | PhoneStateListener.LISTEN_SERVICE_STATE
 | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
 | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
 | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR);
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 getMenuInflater().inflate(R.menu.phone_state_listener, menu);
 return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 int id = item.getItemId();
 if (id == R.id.action_settings) {
 return true;
 }
 return super.onOptionsItemSelected(item);
 }
}

In this class one can see that in the tManager.listen method a new object for CustomPhoneStateListener is defined and in the events parameter a bitwise-OR combination of Android PhoneStateListener LISTEN_ flags is passed. Now lets have a look at some of the main methods of CustomPhoneStateListener.

package com.truiton.phonestatelistener;

import java.util.List;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;

public class CustomPhoneStateListener extends PhoneStateListener {
 Context mContext;
 public static String LOG_TAG = "CustomPhoneStateListener";

 public CustomPhoneStateListener(Context context) {
 mContext = context;
 }

 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
 @Override
 public void onCellInfoChanged(List<CellInfo> cellInfo) {
 super.onCellInfoChanged(cellInfo);
 Log.i(LOG_TAG, "onCellInfoChanged: " + cellInfo);
 }

 @Override
 public void onDataActivity(int direction) {
 super.onDataActivity(direction);
 switch (direction) {
 case TelephonyManager.DATA_ACTIVITY_NONE:
 Log.i(LOG_TAG, "onDataActivity: DATA_ACTIVITY_NONE");
 break;
 case TelephonyManager.DATA_ACTIVITY_IN:
 Log.i(LOG_TAG, "onDataActivity: DATA_ACTIVITY_IN");
 break;
 case TelephonyManager.DATA_ACTIVITY_OUT:
 Log.i(LOG_TAG, "onDataActivity: DATA_ACTIVITY_OUT");
 break;
 case TelephonyManager.DATA_ACTIVITY_INOUT:
 Log.i(LOG_TAG, "onDataActivity: DATA_ACTIVITY_INOUT");
 break;
 case TelephonyManager.DATA_ACTIVITY_DORMANT:
 Log.i(LOG_TAG, "onDataActivity: DATA_ACTIVITY_DORMANT");
 break;
 default:
 Log.w(LOG_TAG, "onDataActivity: UNKNOWN " + direction);
 break;
 }
 }

 @Override
 public void onServiceStateChanged(ServiceState serviceState) {
 super.onServiceStateChanged(serviceState);
 Log.i(LOG_TAG, "onServiceStateChanged: " + serviceState.toString());
 Log.i(LOG_TAG, "onServiceStateChanged: getOperatorAlphaLong "
 + serviceState.getOperatorAlphaLong());
 Log.i(LOG_TAG, "onServiceStateChanged: getOperatorAlphaShort "
 + serviceState.getOperatorAlphaShort());
 Log.i(LOG_TAG, "onServiceStateChanged: getOperatorNumeric "
 + serviceState.getOperatorNumeric());
 Log.i(LOG_TAG, "onServiceStateChanged: getIsManualSelection "
 + serviceState.getIsManualSelection());
 Log.i(LOG_TAG,
 "onServiceStateChanged: getRoaming "
 + serviceState.getRoaming());

 switch (serviceState.getState()) {
 case ServiceState.STATE_IN_SERVICE:
 Log.i(LOG_TAG, "onServiceStateChanged: STATE_IN_SERVICE");
 break;
 case ServiceState.STATE_OUT_OF_SERVICE:
 Log.i(LOG_TAG, "onServiceStateChanged: STATE_OUT_OF_SERVICE");
 break;
 case ServiceState.STATE_EMERGENCY_ONLY:
 Log.i(LOG_TAG, "onServiceStateChanged: STATE_EMERGENCY_ONLY");
 break;
 case ServiceState.STATE_POWER_OFF:
 Log.i(LOG_TAG, "onServiceStateChanged: STATE_POWER_OFF");
 break;
 }
 }

 @Override
 public void onCallStateChanged(int state, String incomingNumber) {
 super.onCallStateChanged(state, incomingNumber);
 switch (state) {
 case TelephonyManager.CALL_STATE_IDLE:
 Log.i(LOG_TAG, "onCallStateChanged: CALL_STATE_IDLE");
 break;
 case TelephonyManager.CALL_STATE_RINGING:
 Log.i(LOG_TAG, "onCallStateChanged: CALL_STATE_RINGING");
 break;
 case TelephonyManager.CALL_STATE_OFFHOOK:
 Log.i(LOG_TAG, "onCallStateChanged: CALL_STATE_OFFHOOK");
 break;
 default:
 Log.i(LOG_TAG, "UNKNOWN_STATE: " + state);
 break;
 }
 }

 @TargetApi(Build.VERSION_CODES.GINGERBREAD)
 @Override
 public void onCellLocationChanged(CellLocation location) {
 super.onCellLocationChanged(location);
 if (location instanceof GsmCellLocation) {
 GsmCellLocation gcLoc = (GsmCellLocation) location;
 Log.i(LOG_TAG,
 "onCellLocationChanged: GsmCellLocation "
 + gcLoc.toString());
 Log.i(LOG_TAG, "onCellLocationChanged: GsmCellLocation getCid "
 + gcLoc.getCid());
 Log.i(LOG_TAG, "onCellLocationChanged: GsmCellLocation getLac "
 + gcLoc.getLac());
 Log.i(LOG_TAG, "onCellLocationChanged: GsmCellLocation getPsc"
 + gcLoc.getPsc()); // Requires min API 9
 } else if (location instanceof CdmaCellLocation) {
 CdmaCellLocation ccLoc = (CdmaCellLocation) location;
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation "
 + ccLoc.toString());
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation getBaseStationId "
 + ccLoc.getBaseStationId());
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation getBaseStationLatitude "
 + ccLoc.getBaseStationLatitude());
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation getBaseStationLongitude"
 + ccLoc.getBaseStationLongitude());
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation getNetworkId "
 + ccLoc.getNetworkId());
 Log.i(LOG_TAG,
 "onCellLocationChanged: CdmaCellLocation getSystemId "
 + ccLoc.getSystemId());
 } else {
 Log.i(LOG_TAG, "onCellLocationChanged: " + location.toString());
 }
 }

 @Override
 public void onCallForwardingIndicatorChanged(boolean cfi) {
 super.onCallForwardingIndicatorChanged(cfi);
 Log.i(LOG_TAG, "onCallForwardingIndicatorChanged: " + cfi);
 }

 @Override
 public void onMessageWaitingIndicatorChanged(boolean mwi) {
 super.onMessageWaitingIndicatorChanged(mwi);
 Log.i(LOG_TAG, "onMessageWaitingIndicatorChanged: " + mwi);
 }
}

Here in the above class I extended the Android PhoneStateListener class and I am overriding some of its key methods. Through these overridden methods we can access the telephony states, cell locations, network types, incoming call number and much more network carrier related information. In this Android PhoneStateListener Example I’ll be explaining all key methods in short except two key callback methods as they have separate tutorials. First one onDataConnectionStateChanged and second one onSignalStrengthsChanged. To keep it simple and self explanatory I am just using logs to display the all the states. Now lets start with first overridden method:

Android PhoneStateListener onDataActivity

This method is called when some data activity is observed like upload or download or both. Whenever there is a change in network data you would receive a callback here.

Android PhoneStateListener onServiceStateChanged

This method is called when something changes in network operator service. It could be a change in service when phone goes from in service to out of service or to emergency only. In this callback method, along with states some other parameters are returned like operator name from getOperatorAlphaLong method. To detect if phone is in roaming getRoaming method can be used.

Android PhoneStateListener onCallStateChanged

Well this method is one of the most used method of this class as this method listens to the call state changes. Like when phone starts ringing, call is put on hold or a call is ended.

Android PhoneStateListener onCellLocationChanged

This method is called when your device switches operator cell location. This method can provide great amounts of information regarding the location of device as its object can be cast to GsmCellLocation or CdmaCellLocation. Once we have location area code through GsmCellLocation.getLac() method and cell id through GsmCellLocation.getCid() method we can query and get actual latitude and longitude of that location by any web-service freely available on internet. Also would like to mention here that in case of CDMA device this process is not required as CdmaCellLocation class contains the latitude and longitude parameters in itself.

Concluding this tutorial I would like to mention separate tutorials are available for more methods like onSignalStrengthsChanged and onDataConnectionStateChanged. Please share this tutorial if it helped you.

1 thought on “Android PhoneStateListener Example”

Leave a Reply

Your email address will not be published. Required fields are marked *