说在前面的话
如果想以此功能置于app内部窃取用户的通话,基本是不可行的。作为内部app(如风控电话、客户回访等)倒是可行。至于为什么后续会讲到。
1 为什么做不到获取用户通话
- 用户打电话可以通过app拨打也可以通过系统拨号键盘打,对于app进程永生不死在安卓上就是一个伪命题
- 就算做到进程永生不死,对于录音也是很难的。各个论坛里面搜索通话录音出现的最多的代码是电话接通之后利用以下代码块进行录音
recorder=new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);//读麦克风的声音
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//设置输出格式
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 编码方式
File file=new File(Environment.getDownloadCacheDirectory().getAbsolutePath(),"recorder");
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);此句代码是设置声音来源,MIC为麦克风。所以最终的结果是只能录自己说话的声音。根本就没有任何意义。
有人会说可以设置其他声音来源,好像是还真有合适的来源,具体如下:
MediaRecorder.AudioSource.MIC参数说明
其中第一个参数就是选择录音源的,其可选参数如下:
MediaRecorder.AudioSource.CAMCORDER
设定录音来源于同方向的相机麦克风相同,若相机无内置相机或无法识别,则使用预设的麦克风
MediaRecorder.AudioSource.DEFAULT 默认音频源
MediaRecorder.AudioSource.MIC
设定录音来源为主麦克风。
MediaRecorder.AudioSource.VOICE_CALL
设定录音来源为语音拨出的语音与对方说话的声音
MediaRecorder.AudioSource.VOICE_COMMUNICATION
摄像头旁边的麦克风
MediaRecorder.AudioSource.VOICE_DOWNLINK
下行声音
MediaRecorder.AudioSource.VOICE_RECOGNITION
语音识别
MediaRecorder.AudioSource.VOICE_UPLINK
上行声音
亲测只有MediaRecorder.AudioSource.VOICE_CALL是可以实现双向录音的,但是只在5.1及以下的手机。后面查看源码才发现
/** Voice call uplink + downlink audio source
* <p>
* Capturing from <code>VOICE_CALL</code> source requires the
* {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
* This permission is reserved for use by system components and is not available to
* third-party applications.
* </p>
*/
public static final int VOICE_CALL = 4;
使用VOICE_CALL需要的是系统app,要想把第三方应用设置为系统app简直不要太难。(拿到 签名文件签名 在AndroidMainFest.xml 文件里面加上 android.sharedUserId:="android.uid.system"、或者hook com.android.server.pm.PackageManagerService compareSignatures方法 返回0
然后重启手机即可)
2 最后的损招
由于app是内部使用,让使用人员设置手机为通话自动录音然后监听通话状态。最后遍历文件(通话录音文件一般包含拨打的电话号码)找到对应的录音文件上传即可。
2.1获取TelephonyManager
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(myPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
2.2 设置监听
class MyPhoneListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String phoneNumber) {
Log.e(TAG, "onCallStateChanged ====phoneNumber" + phoneNumber);
if (state == TelephonyManager.CALL_STATE_IDLE) {
Log.e(TAG, "onCallStateChanged ====CALL_STATE_IDLE");
if (isNeedUpload) {
// 清除
FileUtil.clearSearchList();
// 查找
FileUtil.searchRecorderFile(new File(Environment.getExternalStorageDirectory().getAbsolutePath()), currentNum);
Log.e(TAG, "查找==" + FileUtil.searchList.size());
// 筛选 如果查找出多个 针对小米筛选出时间最近的
FileUtil.siftAudioFile();
Log.e(TAG, "筛选==" + FileUtil.searchList.size());
for (int i = 0; i < FileUtil.searchList.size(); i++) {
File file = FileUtil.searchList.get(i);
Log.e(TAG, "呼叫:挂断电话==" + file.getName() + "======" + file.getAbsolutePath());
Toast.makeText(getApplicationContext(), "正在上传" + file.getName(), Toast.LENGTH_LONG).show();
// 如果有多个 其他的按照原来的规则上传Upload.getInstance(getApplicationContext()).doUpload(getApplicationContext(), FileUtil.getOssObjectName(currentNum, file.getName()), file.getAbsolutePath(), new UploadCompleteCallback() {
@Override
public void onSuccess(PutObjectRequest request, PutObjectResult result) {
}
@Override
public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) {
Log.e(TAG, "上传失败");
}
}
});
}
isNeedUpload = false;
currentNum = "";
// Toast.makeText(getApplicationContext(), "正在上传", Toast.LENGTH_LONG).show();
} else {
if (!TextUtils.isEmpty(currentNum)) {
Log.e(TAG, "呼叫:没有接通");
}
}
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
Log.e(TAG, "摘机");
Log.e(TAG, "onCallStateChanged ====CALL_STATE_OFFHOOK");
Toast.makeText(getApplicationContext(), "摘机", Toast.LENGTH_LONG).show();
if (!TextUtils.isEmpty(currentNum)) {
isNeedUpload = true;
}
}
super.onCallStateChanged(state, phoneNumber);
}
2.3利用广播监听拨打电话
private void regOutCallReceiver() {
phoneReceiver = new PhoneReceiver();
IntentFilter intentFilter = new IntentFilter();
//设置拨号广播过滤
intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
intentFilter.addAction("android.intent.action.PHONE_STATE");
registerReceiver(phoneReceiver, intentFilter);
}
class PhoneReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 如果是去电
if (intent.getAction() == Intent.ACTION_NEW_OUTGOING_CALL) {
currentNum = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(getApplicationContext(), "呼叫:" + currentNum, Toast.LENGTH_LONG).show();
} else {
if (tm == null) {
tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
}
if (myPhoneListener == null) {
myPhoneListener = new MyPhoneListener();
}
// 监听电话状态
tm.listen(myPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}
PhoneReceiver通过系统打电话广播获取到要拨打的号码用来判断是否需要上传,然后通过监听通话状态完成上传操作。
2.4递归查找疑似录音文件
/**
* 查找疑似文件
*
* @param parentFile new File(Environment.getExternalStorageDirectory().getAbsolutePath())
* @param suspectedName 拨打出去的号码
*/
public static void searchRecorderFile(File parentFile, String suspectedName) {
File[] childlist = parentFile.listFiles();
for (File file : childlist) {
if (file.isDirectory()) {
searchRecorderFile(file, suspectedName);
} else {
String fileAbsolutePath = file.getName();
// 包含电话号码 且是音频
if (fileAbsolutePath.indexOf(suspectedName) > -1 && isAudio(fileAbsolutePath)) {
searchList.add(file);
}
}
}
}