Call Recorder Not Working in Android 10 (Q)

Call recorder not working in android 10 (Q)

It's possible using Accessibility Service.

Remote call recorder and BoldBeast both record perfectly both side voice in Android 10 (Pixel 3A) without having to root or being a system app. Both of them use Accessibility service.

Detailed info here in this link.

Call Recorder Not Working After Oreo Update

Call recording is not allowed from Android 6 Marshmallow.
There was a workaround in native code around the root requirement, which worked until Android 8.1.

This was patched in Android 9 Pie, and since this version recording calls is not possible anymore. You can find a detailed explanation from the developer of ACR (the most popular call recorder for Android) here.

For the Android 6-7-8 workaround, you can check an example implementation here (it basically uses native C++ code to bypass the check done in Java).

Recording calls in android why this not works

Finally Got the Result... Now I can record both incoming and outgoing calls(2.2)..

Changed the whole structure..

Here is my CallRecordingService.java
Toasting will make you understood whats going arround... :)

package com.exampled.beta;

import java.io.File;
import java.io.IOException;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class CallRecordingService extends Service
{
final MediaRecorder recorder = new MediaRecorder();
boolean recording = false;
int i = 0;
String fname;

BroadcastReceiver CallRecorder = new BroadcastReceiver()
{
@Override
public void onReceive(Context arg0, Intent intent)
{
// TODO Auto-generated method stub
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
i++;

if(TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state))
{
Toast.makeText(getApplicationContext(), state, Toast.LENGTH_LONG).show();

Toast.makeText(arg0, "Start CaLLED "+recording+fname, Toast.LENGTH_LONG).show();

startRecording();
}

if(TelephonyManager.EXTRA_STATE_IDLE.equals(state) && recording == true)
{
Toast.makeText(getApplicationContext(), state, Toast.LENGTH_LONG).show();

Toast.makeText(arg0, "STOP CaLLED :"+recording, Toast.LENGTH_LONG).show();
stopRecording();
}

if(TelephonyManager.EXTRA_STATE_RINGING.equals(state))
{
fname = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Toast.makeText(getApplicationContext(), state+" : "+fname, Toast.LENGTH_LONG).show();
}
}
};

BroadcastReceiver OutGoingNumDetector = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
// TODO Auto-generated method stub
fname=intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
};

@Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
Toast.makeText(getApplicationContext(), "Service Created", Toast.LENGTH_LONG).show();

IntentFilter RecFilter = new IntentFilter();
RecFilter.addAction("android.intent.action.PHONE_STATE");
registerReceiver(CallRecorder, RecFilter);
IntentFilter OutGoingNumFilter=new IntentFilter();
OutGoingNumFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
registerReceiver(OutGoingNumDetector, OutGoingNumFilter);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
// TODO Auto-generated method stub
return super.onStartCommand(intent, flags, startId);
}

@Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}

@Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(CallRecorder);
unregisterReceiver(OutGoingNumDetector);
Toast.makeText(getApplicationContext(), "Destroyed", Toast.LENGTH_SHORT).show();
}

public void startRecording()
{
if(recording==false)
{
Toast.makeText(getApplicationContext(), "Recorder_Sarted"+fname, Toast.LENGTH_LONG).show();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
String file= Environment.getExternalStorageDirectory().toString();
String filepath= file+"/11111111111111";
File dir= new File(filepath);
dir.mkdirs();

filepath+="/"+fname+".3gp";
recorder.setOutputFile(filepath);

try {
recorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
recorder.start();
recording=true;
}
}

public void stopRecording()
{
if(recording==true)
{
Toast.makeText(getApplicationContext(), "Recorder_Relesed from "+recording, Toast.LENGTH_LONG).show();

recorder.stop();
recorder.reset();
recorder.release();
recording=false;
broadcastIntent();
}
}

public void broadcastIntent()
{
Intent intent = new Intent();
intent.setAction("com.exampled.beta.CUSTOM_INTENT");
sendBroadcast(intent);
Toast.makeText(getApplicationContext(), "BroadCaste", Toast.LENGTH_LONG).show();
}
}

ServiceCaller.java

package com.exampled.beta;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class ServiceCaller extends BroadcastReceiver
{
@Override
public void onReceive(Context arg0, Intent arg1)
{
// TODO Auto-generated method stub
arg0.stopService(new Intent(arg0,CallRecordingService.class));
Intent intent=new Intent(arg0, CallRecordingService.class);
arg0.startService(intent);
Toast.makeText(arg0, "Service Explicitely", Toast.LENGTH_SHORT).show();
}
}

MainActivity.java

package com.exampled.beta;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;

public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,CallRecordingService.class);

startService(intent);
finish();
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is
// present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}

PERMISSIONS

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

Outgoing call recording audio file has no sound

The issue was the android version - from android 10 it didn't allow me to record the call but on android 9 it did.

AccessibilityService with MediaRecorder 30 sec audio in Android Q(10) while call receive (Answer) could not work

i have fix it out as below,

package nisarg.app.demo;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;

import java.io.IOException;

import androidx.core.app.NotificationCompat;

import static nisarg.app.demo.MainActivity.fileName;
import static nisarg.app.demo.MainActivity.player;
import static nisarg.app.demo.MainActivity.recorder;

public class MyService extends AccessibilityService {

public static final String LOG_TAG_S = "MyService:";

WindowManager windowManager;
// ImageView back,home,notification,minimize;
//WindowManager.LayoutParams params;
// AccessibilityService service;

@SuppressLint("RtlHardcoded")
@Override
public void onCreate() {
super.onCreate();

windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

Log.i("start Myservice","MyService");

startForegroundService();

/*if (!Settings.canDrawOverlays(getApplicationContext())) {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}*/

/* back = new ImageView(this);
home = new ImageView(this);
minimize = new ImageView(this);
notification = new ImageView(this);

back.setImageResource(R.drawable.ic_launcher_background);
home.setImageResource(R.drawable.ic_launcher_background);
minimize.setImageResource(R.drawable.ic_launcher_background);
notification.setImageResource(R.drawable.ic_launcher_background);
*/
/* params= new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

params.gravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
params.x = 10;
params.y = 50;

windowManager.addView(home, params);

params= new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

params.gravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
params.x = 10;
params.y = 100;

windowManager.addView(minimize, params);

params= new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

params.gravity = Gravity.CENTER_VERTICAL|Gravity.RIGHT;
params.x = 10;
params.y = 150;

windowManager.addView(notification, params);

back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
} catch (Exception e) {
e.printStackTrace();
}
}
});

home.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
} catch (Exception e) {
e.printStackTrace();
}
}
});

notification.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
} catch (Exception e) {
e.printStackTrace();
}
}
});

minimize.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
} catch (Exception e) {
e.printStackTrace();
}
}
});
*/

}

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

Log.e(LOG_TAG_S, "Event :"+event.getEventType());

}

@Override
public void onInterrupt() {

}

/* @Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d("TAG", "onServiceConnected");
}*/

/* @Override
public void onServiceConnected() {
// Set the type of events that this service wants to listen to. Others
// won't be passed to this service.

info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
AccessibilityEvent.TYPE_VIEW_FOCUSED;

// If you only want this service to work with specific applications, set their
// package names here. Otherwise, when the service is activated, it will listen
// to events from all applications.
info.packageNames = new String[]
{"nisarg.app.demo"};

// Set the type of feedback your service will provide.
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;

// Default services are invoked only if no package-specific ones are present
// for the type of AccessibilityEvent generated. This service *is*
// application-specific, so the flag isn't necessary. If this was a
// general-purpose service, it would be worth considering setting the
// DEFAULT flag.

// info.flags = AccessibilityServiceInfo.DEFAULT;

info.notificationTimeout = 100;

this.setServiceInfo(info);

}*/

@Override
protected void onServiceConnected() {
System.out.println("onServiceConnected");

//==============================Record Audio while Call received===============//

WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
FrameLayout layout = new FrameLayout(this);

WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_FULLSCREEN |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS|
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;

windowManager.addView(layout, params);
layout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {

//You can either get the information here or on onAccessibilityEvent

Log.e(LOG_TAG_S, "Window view touched........:");
Log.e(LOG_TAG_S, "Window view touched........:");
return true;
}
});

//==============To Record Audio wile Call received=================

AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
info.eventTypes=AccessibilityEvent.TYPES_ALL_MASK;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
info.notificationTimeout = 100;
info.packageNames = null;
setServiceInfo(info);

try {
//startRecording();
startRecordingA();
} catch (Exception e) {
e.printStackTrace();
}

new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// This method will be executed once the timer is over

//stopRecording();
stopRecordingA();
}
}, 30000);

}

private void startPlaying() {
player = new MediaPlayer();
try {
player.setDataSource(fileName);
player.prepare();
player.start();
} catch (IOException e) {
Log.e(LOG_TAG_S, "prepare() failed");
}
}

private void stopPlaying() {
player.release();
player = null;
}

private void startRecordingA() {
recorder = new MediaRecorder();
// This must be needed sourcea
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setOutputFile(fileName);
//recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
recorder.setAudioEncodingBitRate(48000);
} else {
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setAudioEncodingBitRate(64000);
}
recorder.setAudioSamplingRate(16000);

try {
recorder.prepare();
} catch (IOException e) {
Log.e(LOG_TAG_S, "prepare() failed");
}

recorder.start();
}

private void stopRecordingA() {

Log.e(LOG_TAG_S, "stop recording");
recorder.stop();
recorder.release();
recorder = null;
}

public static final String CHANNEL_ID = "MyAccessibilityService";

private void startForegroundService() {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)

.setContentTitle("recording Service")
.setContentText("Start")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);

}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Recording Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}

//=================================================Added code start==========

MediaRecorder mRecorder;
private boolean isStarted;
byte buffer[] = new byte[8916];

public void startRecording() {
try {

/* String timestamp = new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss", Locale.US).format(new Date());
String fileName =timestamp+".3gp";
mediaSaver = new MediaSaver(context).setParentDirectoryName("Accessibility").

setFileNameKeepOriginalExtension(fileName).
setExternal(MediaSaver.isExternalStorageReadable());*/
//String selectedPath = Environment.getExternalStorageDirectory() + "/Testing";
//String selectedPath = Environment.getExternalStorageDirectory().getAbsolutePath() +"/Android/data/" + packageName + "/system_sound";

mRecorder = new MediaRecorder();
// mRecorder.reset();

//android.permission.MODIFY_AUDIO_SETTINGS
AudioManager mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//turn on speaker
if (mAudioManager != null) {
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); //MODE_IN_COMMUNICATION | MODE_IN_CALL
// mAudioManager.setSpeakerphoneOn(true);
// mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0); // increase Volume
hasWiredHeadset(mAudioManager);
}

//android.permission.RECORD_AUDIO
String manufacturer = Build.MANUFACTURER;
Log.d(LOG_TAG_S, manufacturer);
/* if (manufacturer.toLowerCase().contains("samsung")) {
mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
} else {
mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
}*/
/*
VOICE_CALL is the actual call data being sent in a call, up and down (so your side and their side). VOICE_COMMUNICATION is just the microphone, but with codecs and echo cancellation turned on for good voice quality.
*/
mRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION); //MIC | VOICE_COMMUNICATION (Android 10 release) | VOICE_RECOGNITION | (VOICE_CALL = VOICE_UPLINK + VOICE_DOWNLINK)
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //THREE_GPP | MPEG_4
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); //AMR_NB | AAC
mRecorder.setOutputFile(fileName);
mRecorder.prepare();
mRecorder.start();
isStarted = true;
} catch (IOException e) {
e.printStackTrace();
}
}

public void stopRecording() {
if (isStarted && mRecorder != null) {
mRecorder.stop();
mRecorder.reset(); // You can reuse the object by going back to setAudioSource() step
mRecorder.release();
mRecorder = null;
isStarted = false;
}
}

// To detect the connected other device like headphone, wifi headphone, usb headphone etc
private boolean hasWiredHeadset(AudioManager mAudioManager) {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return mAudioManager.isWiredHeadsetOn();
} else {
final AudioDeviceInfo[] devices = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
for (AudioDeviceInfo device : devices) {
final int type = device.getType();
if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
Log.d(LOG_TAG_S, "hasWiredHeadset: found wired headset");
return true;
} else if (type == AudioDeviceInfo.TYPE_USB_DEVICE) {
Log.d(LOG_TAG_S, "hasWiredHeadset: found USB audio device");
return true;
} else if (type == AudioDeviceInfo.TYPE_TELEPHONY) {
Log.d(LOG_TAG_S, "hasWiredHeadset: found audio signals over the telephony network");
return true;
}
}
return false;
}
}

//=================================End================================

public static boolean isAccessibilitySettingsOn(Context mContext) {
int accessibilityEnabled = 0;
//your package / accesibility service path/class
//
// final String service = "com.example.sotsys_014.accessibilityexample/com.accessibilityexample.Service.MyAccessibilityService";

final String service = "nisarg.app.demo/nisarg.app.demo.MyService";

boolean accessibilityFound = false;
try {
accessibilityEnabled = Settings.Secure.getInt(
mContext.getApplicationContext().getContentResolver(),
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
Log.v(LOG_TAG_S, "accessibilityEnabled = " + accessibilityEnabled);
} catch (Settings.SettingNotFoundException e) {
Log.e(LOG_TAG_S, "Error finding setting, default accessibility to not found: "
+ e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

if (accessibilityEnabled == 1) {
Log.v(LOG_TAG_S, "***ACCESSIBILIY IS ENABLED*** -----------------");
String settingValue = Settings.Secure.getString(
mContext.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(settingValue);
while (splitter.hasNext()) {
String accessabilityService = splitter.next();

Log.v(LOG_TAG_S, "-------------- > accessabilityService :: " + accessabilityService);
if (accessabilityService.equalsIgnoreCase(service)) {
Log.v(LOG_TAG_S, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
} else {
Log.v(LOG_TAG_S, "***ACCESSIBILIY IS DISABLED***");
}

return accessibilityFound;
}

}


Related Topics



Leave a reply



Submit