失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android 通话录音功能

Android 通话录音功能

时间:2020-11-20 08:43:03

相关推荐

Android 通话录音功能

通话录音功能因为涉及隐私问题,Android 6.0上就移除官方的通话录音接口,只能通过其他方式去获取调用。

录音时需要设置音频类型,系统中定义以下几种

(MediaRecorder.AudioSource)

CAMCORDER 录音来源于同方向的相机麦克风相同,若相机无内置相机或无法识别,则使用预设的麦克风

DEFAULT 默认音频源

MIC 录音来源为主麦克风

REMOTE_SUBMIX 用于远程呈现的音频流的子混音的音频源,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请

UNPROCESSED 与默认相同

VOICE_CALL 记录上行与下行音频源,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请

VOICE_COMMUNICATION 麦克风音频源针对VoIP等语音通信进行了调整,可以接收到通话的双方语音

VOICE_DOWNLINK、VOICE_UPLINK 上行下行的语音,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请

VOICE_PERFORMANCE 捕获音频的来源意味着要实时处理并播放以进行现场演出

VOICE_RECOGNITION 用于获取语音进行语音识别

但在通话时,类型会变成 VOICE_CALL 。这种状态下录音,需要申请android.permission.CAPTURE_AUDIO_OUTPUT 权限,UID改成 “android.uid.system” ,只能是系统应用使用。

系统中可以通过修改源码,放开此限制,代码路径frameworks\av\services\audiopolicy\service\AudioPolicyInterfaceImpl.cpp

// 这里判断是否有普通录制权限 if (!recordingAllowed(opPackageName, pid, uid)) {ALOGE("%s permission denied: recording not allowed for uid %d pid %d",__func__, uid, pid);return PERMISSION_DENIED;}// 是否是以下三种类型且未申请 CAPTURE_AUDIO_OUTPUT 权限if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||attr->source == AUDIO_SOURCE_VOICE_CALL) &&!captureAudioOutputAllowed(pid, uid)) {return PERMISSION_DENIED;}if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {return BAD_VALUE;}sp<AudioPolicyEffects>audioPolicyEffects;{status_t status;AudioPolicyInterface::input_type_t inputType;Mutex::Autolock _l(mLock);{AutoCallerClear acc;// the audio_in_acoustics_t parameter is ignored by get_input()status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,config,flags, selectedDeviceId,&inputType, portId);}audioPolicyEffects = mAudioPolicyEffects;if (status == NO_ERROR) {// enforce permission (if any) required for each type of inputswitch (inputType) {case AudioPolicyInterface::API_INPUT_LEGACY:break;case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:// FIXME: use the same permission as for remote submix for now.case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:if (!captureAudioOutputAllowed(pid, uid)) {ALOGE("getInputForAttr() permission allowed: capture allowed");//cczheng annotation for don't check android.Manifest.permission.CAPTURE_AUDIO_OUTPUT/*ALOGE("getInputForAttr() permission denied: capture not allowed");status = PERMISSION_DENIED;*/}break;case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:if (!modifyAudioRoutingAllowed()) {ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");status = PERMISSION_DENIED;}break;case AudioPolicyInterface::API_INPUT_INVALID:default:LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d",(int)inputType);}}

权限检查代码frameworks/av/media/utils/ServiceUtilities.cpp

// 判断是否AUDIO服务、root应用、已申请权限 bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {if (isAudioServerOrRootUid(uid)) return true;static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);if (!ok) ALOGV("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");return ok;}// 判断是否系统服务或者root进程,申请普通录音权限static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,uid_t uid, bool start) {// Okay to not track in app ops as audio server or media server is us and if// device is rooted security model is considered compromised.// system_server loses its RECORD_AUDIO permission when a secondary// user is active, but it is a core system service so let it through.// TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true;// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)// may open a record track on behalf of a client. Note that pid may be a tid.// IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.PermissionController permissionController;const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid);if (!ok) {ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str());return false;}String16 resolvedOpPackageName = resolveCallingPackage(permissionController, opPackageName, uid);if (resolvedOpPackageName.size() == 0) {return false;}AppOpsManager appOps;const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio);if (start) {if (appOps.startOpNoThrow(op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false)!= AppOpsManager::MODE_ALLOWED) {ALOGE("Request denied by app op: %d", op);return false;}} else {if (appOps.checkOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) {ALOGE("Request denied by app op: %d", op);return false;}}return true;}

解决以上权限问题后,正常录音即可,否则即使录音了,文件也是无声的。录音最好采用 amr 编码格式,体积小音质还行,综合相对比较适合录音。

private MediaRecorder mMediaRecorder;private boolean isRecording;private void initMediaRecorder() {mMediaRecorder = new MediaRecorder();// 设置音频来源 MIC == 麦克mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置默认音频输出格式 .amr 格式mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);// 设置默认音频编码方式 .amr 编码mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 指定音频输出文件路径SimpleDateFormat simpleDateFormat = null;// HH:mm:ssif (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");Date date = new Date(System.currentTimeMillis());String fileName = simpleDateFormat.format(date);File file = new File(Environment.getExternalStorageDirectory(), fileName + ".amr");mMediaRecorder.setOutputFile(file);}}// 开始录音public void start() {new Thread() {@Overridepublic void run() {super.run();if (mMediaRecorder == null) {initMediaRecorder();}try {sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}if (!isRecording) {try {isRecording = true;mMediaRecorder.prepare();mMediaRecorder.start();//开始录制} catch (IOException e) {e.printStackTrace();isRecording = false;}}}}.start();}// 停止录音public void stop() {if (mMediaRecorder != null && isRecording) {isRecording = false;mMediaRecorder.stop();mMediaRecorder.release();mMediaRecorder = null;}}

如果觉得《Android 通话录音功能》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。