I want to make an app which records the incoming and outgoing calls and it run automatically when user get or make any call.
6 Answers
Ok, for this first of all you need to use Device Policy Manager, and need to make your device Admin device. After that you have to create one BroadCast receiver and one service. I am posting code here and its working fine.
MainActivity:
public class MainActivity extends Activity {
private static final int REQUEST_CODE = 0;
private DevicePolicyManager mDPM;
private ComponentName mAdminName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Initiate DevicePolicyManager.
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mAdminName = new ComponentName(this, DeviceAdminDemo.class);
if (!mDPM.isAdminActive(mAdminName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
startActivityForResult(intent, REQUEST_CODE);
} else {
// mDPM.lockNow();
// Intent intent = new Intent(MainActivity.this,
// TrackDeviceService.class);
// startService(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (REQUEST_CODE == requestCode) {
Intent intent = new Intent(MainActivity.this, TService.class);
startService(intent);
}
}
}
//DeviceAdminDemo class
public class DeviceAdminDemo extends DeviceAdminReceiver {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
public void onEnabled(Context context, Intent intent) {
};
public void onDisabled(Context context, Intent intent) {
};
}
//TService Class
public class TService extends Service {
MediaRecorder recorder;
File audiofile;
String name, phonenumber;
String audio_format;
public String Audio_Type;
int audioSource;
Context context;
private Handler handler;
Timer timer;
Boolean offHook = false, ringing = false;
Toast toast;
Boolean isOffHook = false;
private boolean recordstarted = false;
private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onDestroy() {
Log.d("service", "destroy");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// final String terminate =(String)
// intent.getExtras().get("terminate");//
// intent.getStringExtra("terminate");
// Log.d("TAG", "service started");
//
// TelephonyManager telephony = (TelephonyManager)
// getSystemService(Context.TELEPHONY_SERVICE); // TelephonyManager
// // object
// CustomPhoneStateListener customPhoneListener = new
// CustomPhoneStateListener();
// telephony.listen(customPhoneListener,
// PhoneStateListener.LISTEN_CALL_STATE);
// context = getApplicationContext();
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
// if(terminate != null) {
// stopSelf();
// }
return START_NOT_STICKY;
}
public class CallBr extends BroadcastReceiver {
Bundle bundle;
String state;
String inCall, outCall;
public boolean wasRinging = false;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_IN)) {
if ((bundle = intent.getExtras()) != null) {
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
wasRinging = true;
Toast.makeText(context, "IN : " + inCall, Toast.LENGTH_LONG).show();
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
if (wasRinging == true) {
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
String out = new SimpleDateFormat("dd-MM-yyyy hh-mm-ss").format(new Date());
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record";
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
// recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
} else if (intent.getAction().equals(ACTION_OUT)) {
if ((bundle = intent.getExtras()) != null) {
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
}
}
}
}
}
//Permission in manifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.STORAGE" />
//my_admin.xml
<device-admin xmlns:android="http://schemas.android.com/apk/res/android" >
<uses-policies>
<force-lock />
</uses-policies>
</device-admin>
//Declare following thing in manifest:
Declare DeviceAdminDemo class to manifest:
<receiver
android:name="com.example.voicerecorder1.DeviceAdminDemo"
android:description="@string/device_description"
android:label="@string/device_admin_label"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/my_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
</intent-filter>
</receiver>
<service android:name=".TService" >
</service>

- 25,177
- 13
- 126
- 165

- 7,439
- 4
- 30
- 44
-
Thanks Pratik for your quick response. But it doesn't open or run my app while i am on call. Any idea? – Ankit Sharma Sep 19 '13 at 07:01
-
4it will record the call in background....not on front, if you want to change you can do it according to your requirement, but ths code is working fine. Please accept and upvote the answer if you find it useful. Because other people can take benefit of it. – Pratik Dasa Sep 19 '13 at 07:04
-
what is the values of these tags? it gives me an error. android:description="@string/device_description" android:label="@string/device_admin_label" android:resource="@xml/my_admin" – Ankit Sharma Sep 19 '13 at 08:01
-
Hello world!
Settings Disable the Force Stop button using Device Policy Manager API Device Admin – Pratik Dasa Sep 19 '13 at 08:38 -
1No partik doesn't work at all, don't know why even it doesn't make any directory in SD card. is there any way to make device admin? – Ankit Sharma Sep 19 '13 at 09:19
-
Is it asking you for Device Admin?? – Pratik Dasa Sep 19 '13 at 09:20
-
yes, i used same name as you mentioned. when i got incoming call it doesn't run. Is there any way to run this class? – Ankit Sharma Sep 19 '13 at 09:31
-
The proper `AudioSource` to use for recording voice calls is `VOICE_CALL`. – Michael Oct 10 '13 at 08:23
-
@pratik I tried your code too however I am getting OKB AMR file when I talk about 25 seconds. First of all it crashed in Samsung GTS7562 so I changed to VOICE_CALL after that. this is the result. Could you find out why this happened? – Saty Apr 29 '14 at 13:12
-
what is the values of these tags? it gives me an error. android:description="@string/device_description" android:label="@string/device_admin_label" android:resource="@xml/my_admin" kindly tell me what values should be enter – Awais Usmani Sep 20 '14 at 08:44
-
@pratik what is @xml/my_admin?? – Avijit Sep 25 '14 at 06:06
-
@pratik I am very sorry to say its force closing... Whenever I am reciving the phone (else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) its giving a force close... Can you please say me the reason.. its almost 6 hours I cant solve it... \ – Avijit Sep 25 '14 at 11:19
-
@Avijit many people have working and I personally tell that its working fine, but in your code there may be some glitches that's why its force closing, just debug your app and check, you have to do little work to make it running. – Pratik Dasa Sep 25 '14 at 11:41
-
So far I can say its the code for only incoming number. Its only showing toast for outgoing call.. But if I am checking state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK) for outgoing action its force closing it.. Its may be not only my issue... @pratik – Avijit Sep 25 '14 at 12:01
-
@Avjit ya it may be true, because my requirements was not for outgoing call, incoming working fine. But many people has been make this working with outgoing as well using this answer. Let me search on this and get back to you – Pratik Dasa Sep 25 '14 at 12:04
-
Hello @Pratt, you gave great example. Really its very helpful. But please tell me where should I put my_admin.xml file means in which folder? Thank you. – Ankit Oct 28 '14 at 11:26
-
@Ankit create one folder named "xml" under "res" directory and put "my_admin.xml" inside it. – Pratik Dasa Oct 28 '14 at 11:36
-
okk thank you very much @pratt. You saved my time. One more thing, why process is also killed if I kill app? I mean to say service is running but after some time call cant be recorded automatically because system kills app. – Ankit Oct 28 '14 at 12:31
-
@Ankit hat I dont know dear, you can research and check. Upvote if you found its useful. – Pratik Dasa Oct 28 '14 at 12:33
-
i want to record from one person's mic and the other's speaker how can i do that? i tried uplink and downlink options but it did not work – muratgurbuzdal Feb 26 '15 at 13:19
-
@Pratt, why did you include Device Admin? Does it have some effect? – nurgasemetey Jun 19 '15 at 09:12
-
@nurgasemetey yes, if you want to record a call, it is not allowed because people can misuse of it. So you must need to ask user to make their device admin device before record call. Without it you can not record a call. – Pratik Dasa Jun 19 '15 at 09:26
-
This approach works in some phones. Totally depends on manufacturers. Some more parameters to try: MediaRecorder.AudioSource.VOICE_CALL, VOICE_DOWNLINK, VOICE_UPLINK. If you want finer control (e.g, better quality: output PCM format), use AudioRecorder. – Helin Wang Jan 06 '16 at 01:39
-
5Is there maybe a Github repository sample for this? – android developer Apr 05 '16 at 13:56
-
3its working good... but after i restart my phone.. recording stop.. no toast, nothing working., its become useless.. how to set device_admin as boot_completed. – Sagar Chavada Jul 05 '16 at 06:22
-
Can anyone publish the additional modifications for using this solution on outgoing call too? Thanks. – eyal Oct 27 '16 at 14:58
-
5i am not able to find my records after calls – Aditya Vyas-Lakhan Mar 07 '17 at 03:50
-
Its not working mahn..phone got hanged while receiving the calll..Os is kitkat only...... – Sachin Varma Apr 10 '17 at 07:26
-
Why we need to be admin. Is it best practice or a mandatory step? Why you lock the device? – Eslam Sameh Ahmed Sep 27 '17 at 22:32
-
It is not working for OnePlus 2, Android version 7.1.1. Did it work for anybody else? – Dinesh Singh Nov 29 '17 at 04:34
-
It may possible, that in newer versions of android this may not work due to security features. – Pratik Dasa Nov 29 '17 at 09:43
-
`VOICE_COMMUNICATION` it's MIC, quality is bad! – user924 Apr 04 '18 at 21:17
-
3What is use of this `DevicePolicyManager` here? – amrezzd Aug 14 '18 at 07:33
-
@PratikDasa Will this code work to record Whatsapp calls also? – suv Jan 04 '19 at 13:32
-
@AdityaVyas-Lakhan Just add `Toast.makeText(context, "File Path" + audiofile.getAbsolutePath(), Toast.LENGTH_LONG).show();` to your TService.java file after `audiofile = File.createTempFile(file_name, ".amr", sampleDir);` – Anchit Feb 27 '19 at 19:29
-
When the call is answered, I get the following error: android java.lang.illegalstateexception: PhoneIsInUse Any idea what is going wrong? – Hashim Akhtar Feb 20 '20 at 15:49
-
Any GitHub repository for this code sample please?? Or similar. Thanks – Brendan Nov 29 '20 at 23:03
-
@Brendan Why need for a GitHub (or any other) repository? – Edw590 Dec 25 '20 at 01:53
-
1Because the example above doesn't look so organized to me. I don't really understand what's going on there. – Brendan Dec 25 '20 at 18:44
-
My app goes into the else statement of the MainActivity which does nothing because of this "(!mDPM.isAdminActive(mAdminName)", It works when I uncomment the else clause (and change TrackDeviceService to TService), can I do that? also, is it necessary to lock phone screen or can we bypass this step? – user3560827 Feb 23 '21 at 11:39
-
I can't find the location of the recorded audio file. Can someone let me know how I can navigate to it on my device? – Big Tree Oct 05 '21 at 08:52
Below code is working for me to record a outgoing phone call
//Call Recording varibales
private static final String AUDIO_RECORDER_FILE_EXT_3GP = ".3gp";
private static final String AUDIO_RECORDER_FILE_EXT_MP4 = ".mp4";
private static final String AUDIO_RECORDER_FOLDER = "AudioRecorder";
private MediaRecorder recorder = null;
private int currentFormat = 0;
private int output_formats[] = { MediaRecorder.OutputFormat.MPEG_4,
MediaRecorder.OutputFormat.THREE_GPP };
private String file_exts[] = { AUDIO_RECORDER_FILE_EXT_MP4,
AUDIO_RECORDER_FILE_EXT_3GP };
AudioManager audioManager;
//put this methods to outside of oncreate() method
private String getFilename() {
String filepath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filepath, AUDIO_RECORDER_FOLDER);
if (!file.exists()) {
file.mkdirs();
}
return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + file_exts[currentFormat]);
}
private MediaRecorder.OnErrorListener errorListener = new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder mr, int what, int extra) {
Toast.makeText(CallActivity.this,
"Error: " + what + ", " + extra, Toast.LENGTH_SHORT).show();
}
};
private MediaRecorder.OnInfoListener infoListener = new MediaRecorder.OnInfoListener() {
@Override
public void onInfo(MediaRecorder mr, int what, int extra) {
Toast.makeText(CallActivity.this,
"Warning: " + what + ", " + extra, Toast.LENGTH_SHORT)
.show();
}
};
//below part of code to make your device on speaker
audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);
//below part of code to start recording
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(output_formats[currentFormat]);
//recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(getFilename());
recorder.setOnErrorListener(errorListener);
recorder.setOnInfoListener(infoListener);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
Log.e("REDORDING :: ",e.getMessage());
e.printStackTrace();
} catch (IOException e) {
Log.e("REDORDING :: ",e.getMessage());
e.printStackTrace();
}
//For stop recording and keep in mind to set speaker off while call end or stop
audioManager.setSpeakerphoneOn(false);
try{
if (null != recorder) {
recorder.stop();
recorder.reset();
recorder.release();
recorder = null;
}
}catch(RuntimeException stopException){
}
And give permission to manifest file,
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

- 941
- 1
- 15
- 25
-
-
Hi Muhammad, Hopefully it will work because it's uses system call feature of devices so, you can try it. – Mukesh Parmar Nov 23 '15 at 05:15
-
This approach works in some phones. Totally depends on manufacturers. Some more parameters to try: MediaRecorder.AudioSource.VOICE_CALL, VOICE_DOWNLINK, VOICE_UPLINK. If you want finer control (e.g, better quality: output PCM format), use AudioRecorder. – Helin Wang Jan 06 '16 at 01:42
-
Can you please show how to do it for incoming calls too, and also put it on github? – android developer Apr 05 '16 at 14:09
-
@AbhijitChakra : Not tested on marshmallow because of implemented long back but I think it will work ,just you've to add allow permission for marshmallow. – Mukesh Parmar Sep 13 '16 at 12:12
-
@MukeshParmar I think it wont work https://code.google.com/p/android/issues/detail?id=197934 – Abhijit Chakra Sep 13 '16 at 13:33
-
@AbhijitChakra : I will update if get in touch with required. or you can update answered after short out.Thanks – Mukesh Parmar Sep 14 '16 at 05:14
-
I dont think its possible with M at atleast on my samsung firmware the the sdk is not available for audio stream.We only record the audio from the mic but not from the audio out we may test on your device might get work good luck!!! – Abhijit Chakra Sep 14 '16 at 07:31
So, basically, I want to combine 2 answers, one from this post and one from another post that I read, don't know the author of it so please sorry for using your methods.
So, here are my classes for achieving desired result:
public class StartActivity extends Activity {
public static final int REQUEST_CODE = 5912;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, StartActivity.class); // activity which is first time open in manifiest file which is declare as <category android:name="android.intent.category.LAUNCHER" />
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
startService(new Intent(this, StartService.class));
startService(new Intent(this, SmsOutgoingService.class));
try {
// Initiate DevicePolicyManager.
DevicePolicyManager mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName mAdminName = new ComponentName(this, DeviceAdminReciever.class);
if (!mDPM.isAdminActive(mAdminName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
startActivityForResult(intent, REQUEST_CODE);
} else {
mDPM.lockNow();
finish();
// Intent intent = new Intent(MainActivity.this,
// TrackDeviceService.class);
// startService(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_CODE == requestCode) {
startService(new Intent(StartActivity.this, TService.class));
finish();
}
super.onActivityResult(requestCode, resultCode, data);
}
}
And my TService
class:
public class TService extends Service {
private MediaRecorder recorder;
private File audiofile;
private boolean recordstarted = false;
private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onDestroy() {
Log.d("service", "destroy");
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("StartService", "TService");
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.registerReceiver(new CallReceiver(), filter);
return super.onStartCommand(intent, flags, startId);
}
private void startRecording() {
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record";
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
// recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
private void stopRecording() {
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
public abstract class PhonecallReceiver extends BroadcastReceiver {
//The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
private int lastState = TelephonyManager.CALL_STATE_IDLE;
private Date callStartTime;
private boolean isIncoming;
private String savedNumber; //because the passed incoming is only valid in ringing
@Override
public void onReceive(Context context, Intent intent) {
// startRecording();
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
} else {
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
state = TelephonyManager.CALL_STATE_IDLE;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
state = TelephonyManager.CALL_STATE_OFFHOOK;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state = TelephonyManager.CALL_STATE_RINGING;
}
onCallStateChanged(context, state, number);
}
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallReceived(Context ctx, String number, Date start);
protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);
protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onMissedCall(Context ctx, String number, Date start);
//Deals with actual events
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
public void onCallStateChanged(Context context, int state, String number) {
if (lastState == state) {
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = number;
onIncomingCallReceived(context, number, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
if (lastState != TelephonyManager.CALL_STATE_RINGING) {
isIncoming = false;
callStartTime = new Date();
startRecording();
onOutgoingCallStarted(context, savedNumber, callStartTime);
} else {
isIncoming = true;
callStartTime = new Date();
startRecording();
onIncomingCallAnswered(context, savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
//Went to idle- this is the end of a call. What type depends on previous state(s)
if (lastState == TelephonyManager.CALL_STATE_RINGING) {
//Ring but no pickup- a miss
onMissedCall(context, savedNumber, callStartTime);
} else if (isIncoming) {
stopRecording();
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
} else {
stopRecording();
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
public class CallReceiver extends PhonecallReceiver {
@Override
protected void onIncomingCallReceived(Context ctx, String number, Date start) {
Log.d("onIncomingCallReceived", number + " " + start.toString());
}
@Override
protected void onIncomingCallAnswered(Context ctx, String number, Date start) {
Log.d("onIncomingCallAnswered", number + " " + start.toString());
}
@Override
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end) {
Log.d("onIncomingCallEnded", number + " " + start.toString() + "\t" + end.toString());
}
@Override
protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
Log.d("onOutgoingCallStarted", number + " " + start.toString());
}
@Override
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
Log.d("onOutgoingCallEnded", number + " " + start.toString() + "\t" + end.toString());
}
@Override
protected void onMissedCall(Context ctx, String number, Date start) {
Log.d("onMissedCall", number + " " + start.toString());
// PostCallHandler postCallHandler = new PostCallHandler(number, "janskd" , "")
}
}
}
inside TService
class you will find CallReceiever
class that is going to handle everything you need from the call. You can add parameters as per your will, but, the main point is important.
From your MainActvitiy call Service that will start your Receiever. If you want to record media from receiever directly, you will get errors, so, you need to register Receiever from service. After that, you can call start recording and end recording wherever you like.
Calling return super.onStartCommand(intent, flags, startId);
will have that Service lasting for more than one call, so keep that in mind.
Finally, AndroidManifest.xml
file:
<manifest
package="your.package.name"
xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.STORAGE" />
<application
android:name=".AppController"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ui.StartActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver
android:name=".io.boot.DeviceAdminReciever"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/my_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
</intent-filter>
</receiver>
<service android:name=".io.calls.TService" >
</service>
</application>
</manifest>
So, this is it, it's working perfectly with my Samsung Galaxy s6 Edge+, Ive tested it on Galaxy Note 4 and on Samsung J5, a big thank you to the authors of this post and the post about receiving phone calls.

- 18,333
- 31
- 67
- 74

- 1,540
- 2
- 21
- 30
-
5what `startService(new Intent(this, StartService.class)); startService(new Intent(this, SmsOutgoingService.class));` are used for? – Abdelhakim AKODADI Dec 26 '16 at 03:01
-
1When the call is answered, I get the following error: android java.lang.illegalstateexception: PhoneIsInUse Any idea what is going wrong? – Hashim Akhtar Feb 20 '20 at 15:51
The accepted answer is perfect, except it does not record outgoing calls. Note that for outgoing calls it is not possible (as near as I can tell from scouring many posts) to detect when the call is actually answered (if anybody can find a way other than scouring notifications or logs please let me know). The easiest solution is to just start recording straight away when the outgoing call is placed and stop recording when IDLE is detected. Just adding the same class as above with outgoing recording in this manner for completeness:
private void startRecord(String seed) {
String out = new SimpleDateFormat("dd-MM-yyyy hh-mm-ss").format(new Date());
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record" + seed;
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_IN)) {
if ((bundle = intent.getExtras()) != null) {
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
wasRinging = true;
Toast.makeText(context, "IN : " + inCall, Toast.LENGTH_LONG).show();
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
if (wasRinging == true) {
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
startRecord("incoming");
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
} else if (intent.getAction().equals(ACTION_OUT)) {
if ((bundle = intent.getExtras()) != null) {
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
startRecord("outgoing");
if ((bundle = intent.getExtras()) != null) {
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state != null) {
if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
}
}
}
}

- 2,203
- 20
- 23
-
2Would love to know who downvoted this... I'm using this in production code and it works fine – D2TheC May 02 '18 at 09:38
-
The answer of pratt is bit uncomplete, because when you restart your device your app will working stop, recording stop, its become useless.
i m adding some line that copy in your project for complete working of Pratt answer.
<receiver
android:name=".DeviceAdminDemo"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
put this code in onReceive of DeviceAdminDemo
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
context.stopService(new Intent(context, TService.class));
Intent myIntent = new Intent(context, TService.class);
context.startService(myIntent);
}

- 5,169
- 7
- 40
- 67
-
-
@SagarChavada in my SD card i am not able to find recording..can you help? – Aditya Vyas-Lakhan Mar 07 '17 at 03:43
-
What is use of this DeviceAdminDemo? Can I start service from main activity and from boot? – Nitul Sep 12 '17 at 12:05
-
There is a simple solution to this problem using this library. I store an instance of the CallRecord class in MyService.class. When the service is first initialized, the following code is executed:
public class MyService extends Service {
public static CallRecord callRecord;
@Override
public void onCreate() {
super.onCreate();
callRecord = new CallRecord.Builder(this)
.setRecordFileName("test")
.setRecordDirName("Download")
.setRecordDirPath(Environment.getExternalStorageDirectory().getPath()) // optional & default value
.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) // optional & default value
.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB) // optional & default value
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) // optional & default value
.setShowSeed(false) // optional, default=true ->Ex: RecordFileName_incoming.amr || RecordFileName_outgoing.amr
.build();
callRecord.enableSaveFile();
callRecord.startCallReceiver();
}
@Override
public void onDestroy() {
super.onDestroy();
callRecord.stopCallReceiver();
}
}
Next, do not forget to specify permissions in the manifest. (I may have some extras here, but keep in mind that some of them are necessary only for newer versions of Android)
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_INCOMING_CALLS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Also it is crucial to request some permissions at the first start of the application. A guide is provided here.
If my code doesn't work, alternative code can be found here. I hope I helped you.

- 330
- 1
- 12