Reputation: 918
I don't mind rooting a device as the application will only be used privately, i'm working on a project that requires to monitor the call state of a device, have read the documentation on it
https://developer.android.com/reference/android/telecom/Call.html
and i have been using it but i'm having issue knowing when a call is picked, have checked the documentation and stackoverflow, have realized is a known issue from google itself.
Detecting outgoing call answered on Android
In rooted device detect if an outgoing call has been answered
and many others that i have tried. i understand there is no documented method to achieve this, i'm sure this will be possible because android itself calculate the time spent on a call and also some applications too like TRUE CALLER and some other private app monitor the time spent which is based on when a call is picked and when they hang-up from the call. Have tried a lot myself before deciding to post this, any suggestion on how to achieve this on a ROOTED device.
Upvotes: 3
Views: 1120
Reputation: 843
After going through system logs during call and looking at source code this and this in, I found that PRECISE CALL STATE is something which can be used to listen precise changes during call.
But as you can see most of the things are hidden from documentation using @hide
annotation.
When applied to a package, class, method or field, @hide removes that node and all of its children from the documentation.
Though methods and classes are hidden but they can be accessed using Java Reflection API, so I thought giving it a try. But the developer community is so large that most of the things come to your mind can already be found on Google.
So after some Google searches, I found this blog, which explains how to listen to precise call state using Java Reflection API. So I have took this code in its original form its source.
Add this in AndroidManifest.xml
file to declare Broadcast Receiver.
<receiver
android:name=".OutCallLogger"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.PRECISE_CALL_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
Permissions required:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
Also add this line to manifest for using feature android.hardware.telephony
<uses-feature android:name="android.hardware.telephony"></uses-feature>
And this is your broadcast receiver class which will be used for getting precise call state for outgoing calls.
public class OutCallLogger extends BroadcastReceiver {
public OutCallLogger() {
}
TelephonyManager Tm;
ITelephony telephonyService;
Class c = null;
Method methodGetInstance = null;
Method methodGetActiveFgCallState=null;
String TAG="Tag";
Object objectCallManager=null;
Context context1;
Class<?> classCallManager;
Class telephonyClass;
Class telephonyStubClass;
Class serviceManagerClass;
Class serviceManagerStubClass;
Class serviceManagerNativeClass;
Class serviceManagerNativeStubClass;
Method telephonyCall;
Method telephonyEndCall;
Method telephonyAnswerCall;
Method getDefault;
Method[] temps;
Constructor[] serviceManagerConstructor;
// Method getService;
Object telephonyObject;
Object serviceManagerObject;
private Timer timer= null;
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
this.context1= context;
Tm=(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
final ClassLoader classLoader = this.getClass().getClassLoader();
try {
classCallManager = classLoader.loadClass("com.android.internal.telephony.CallManager");
Log.e(TAG, "CallManager: Class loaded " + classCallManager.toString());
methodGetInstance = classCallManager.getDeclaredMethod("getInstance");
methodGetInstance.setAccessible(true);
Log.e(TAG, "CallManager: Method loaded " + methodGetInstance.getName());
objectCallManager = methodGetInstance.invoke(null);
Log.e(TAG, "CallManager: Object loaded " + objectCallManager.getClass().getName());
Method[] aClassMethods = classCallManager.getDeclaredMethods();
for(Method m : aClassMethods)
{
Log.e("MEthods", m.getName());
}
methodGetActiveFgCallState = classCallManager.getDeclaredMethod("getActiveFgCallState");
Log.e(TAG, "CallManager: Method loaded " + methodGetActiveFgCallState.getName());
Log.e(TAG, "CallManager: What is the Call state = " + methodGetActiveFgCallState.invoke(objectCallManager));
}
catch (ClassNotFoundException e) {
Log.e(TAG, e.getClass().getName() + e.toString());
}
catch (NoSuchMethodException e) {
Log.e(TAG, e.getClass().getName() + e.toString());
}
catch (InvocationTargetException e) {
Log.e(TAG, e.getClass().getName() + e.toString());
}
catch (IllegalAccessException e) {
Log.e(TAG, e.getClass().getName() + e.toString());
}
Tm.listen(new PhoneStateListener(){
public void onCallStateChanged(int state,String number) {
super.onCallStateChanged(state, number);
try {
if (methodGetActiveFgCallState.invoke(objectCallManager).toString().toLowerCase() .equals("idle"))
{
//Toast.makeText(context1, "I am in idle state", Toast.LENGTH_LONG).show(); }
if (methodGetActiveFgCallState.invoke(objectCallManager).toString().toLowerCase() .equals("active"))
{
//Toast.makeText(context1, "I am in active state", Toast.LENGTH_LONG).show(); }
Toast.makeText(context1, " "+methodGetActiveFgCallState.invoke(objectCallManager).toString(), Toast.LENGTH_LONG).show();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
A Toast will appear which will tell you about call state.
Since, you have pointed out you don't mind rooting your device, you have to install the generated apk as system app. Simply copy the generated apk to /system/app directory and restart device.
Disclaimer: I have not tested above code, as I don't have a rooted device at the moment.
Upvotes: 1
Reputation: 2665
This is an example of Telephony broadcast receiver which listens to voice calls.
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.nitesh.brill.saleslines.Common_Files.SaveData;
import com.nitesh.brill.saleslines.R;
public class MyPhoneReceiver extends BroadcastReceiver {
private String phoneNumber;
Context context;
@Override
public void onReceive(final Context context, Intent intent) {
this.context = context;
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
String extraState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
try {
if (extraState != null) {
if (extraState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
Log.e("State","Offhook");
} else if (extraState
.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
Log.e("State","Idle");
} else if (extraState
.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
if (phoneNumber == null)
phoneNumber = intent
.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.e("State","Ringing");
}
} else if (phoneNumber != null) {
Log.e("Outgoing call",""+phoneNumber);
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception");
e.printStackTrace();
}
}
}
Add this code to your manifest file
<receiver android:name=".MyPhoneReceiver">
<intent-filter>
<!-- Intent filters for broadcast receiver -->
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
Upvotes: 1