Android 14+(SDK34)使用MediaProjection启动前台服务遇SecurityException
崩溃错误信息
由java.lang.SecurityException引起:启动类型为microphone的前台服务(FGS),调用应用ProcessRecord{6d0f21f 24588:com.app.freeboomshare/u0a271},targetSDK=34,需要满足以下权限要求:必须拥有所有allOf=true的权限[android.permission.FOREGROUND_SERVICE_MICROPHONE],且拥有任意一个allOf=false的权限[android.permission.CAPTURE_AUDIO_HOTWORD, android.permission.CAPTURE_AUDIO_OUTPUT, android.permission.CAPTURE_MEDIA_OUTPUT, android.permission.CAPTURE_TUNER_AUDIO_INPUT, android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT, android.permission.RECORD_AUDIO],同时应用必须处于符合条件的状态/豁免范围才能访问前台专属权限
背景信息
应用已上线Google Play,通过MediaProjection + 前台服务实现屏幕和音频录制功能,targetSdkVersion设为34。崩溃发生在启动前台服务后、屏幕录制开始前。
AndroidManifest.xml权限配置
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
已尝试方案
- 确认
RECORD_AUDIO和FOREGROUND_SERVICE权限已通过运行时授权 - 添加Android 15(UpsideDownCake)适配的前台服务启动逻辑:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // 同时录制屏幕、音频和摄像头 val serviceTypes = ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE or ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA startForeground(1, notification, serviceTypes) } else { startForeground(1, notification) }
Fragment核心代码
private fun startRecording() { val mediaProjectionManager = requireActivity().getSystemService(AppCompatActivity.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE) { if (resultCode == AppCompatActivity.RESULT_OK && data != null) { RecordingUtils.startCountdown(viewBinding.countdownText) { viewBinding.btStopRecording.visibility = View.VISIBLE val serviceIntent = Intent( requireActivity(), ScreenCameraReordingService::class.java ) serviceIntent.action = ACTION_START_RECORDING serviceIntent.putExtra(EXTRA_RESULT_CODE, resultCode) serviceIntent.putExtra(EXTRA_RESULT_DATA, data) serviceIntent.putExtra("isAudioRecordingON", isAudioRecordingON) serviceIntent.putExtra("isCameraRecordingON", isCameraRecordingON) ContextCompat.startForegroundService(requireActivity(), serviceIntent) disableToggles() } } else { viewBinding.btStartRecording.visibility = View.VISIBLE viewBinding.btStopRecording.visibility = View.GONE viewModel.startScreenRecordingClickLiveData.value = false Toast.makeText(requireActivity(), "Screen capture permission denied", Toast.LENGTH_SHORT).show() } } /*else if (requestCode == REQUEST_CODE_OVERLAY && resultCode == AppCompatActivity.RESULT_OK){ val intent = Intent(requireContext(), ScreenCameraReordingService::class.java).apply { action = ScreenCameraReordingService.ACTION_START_OVERLAY } requireContext().startForegroundService(intent) }*/ }
问题分析与修复方案
核心问题
Android 15对前台服务的权限验证逻辑更严格:启动包含MICROPHONE类型的前台服务时,除了声明FOREGROUND_SERVICE_MICROPHONE权限,还需满足两个关键条件:
- 必须持有
RECORD_AUDIO(或其他指定音频权限)的运行时授权 - 服务启动必须由即时用户交互触发(比如直接点击按钮,而非延迟操作)
另外你的Manifest中缺少RECORD_AUDIO权限声明,这是运行时授权的前提。
具体修复步骤
补全Manifest权限声明
在Manifest中添加RECORD_AUDIO权限:<uses-permission android:name="android.permission.RECORD_AUDIO" />提前检查并请求必要权限
在调用startRecording()前,先检查并请求录音、摄像头权限,确保授权完成后再进入流程:private fun checkPermissionsAndStart() { val requiredPermissions = mutableListOf<String>() // 检查录音权限 if (isAudioRecordingON && ContextCompat.checkSelfPermission(requireActivity(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { requiredPermissions.add(Manifest.permission.RECORD_AUDIO) } // 检查摄像头权限 if (isCameraRecordingON && ContextCompat.checkSelfPermission(requireActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { requiredPermissions.add(Manifest.permission.CAMERA) } if (requiredPermissions.isNotEmpty()) { ActivityCompat.requestPermissions(requireActivity(), requiredPermissions.toTypedArray(), REQUEST_PERMISSION_CODE) } else { startRecording() } }在
onRequestPermissionsResult中处理授权结果,通过后再执行startRecording()。按需配置前台服务类型
根据实际录制需求动态设置serviceTypes,避免添加不需要的类型触发额外权限检查:val serviceTypes = buildList { add(ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION) if (isAudioRecordingON) add(ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE) if (isCameraRecordingON) add(ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA) }.reduce { acc, type -> acc or type } startForeground(1, notification, serviceTypes)确保服务启动时机符合要求
Android 15要求前台服务必须由即时用户交互触发,建议将前台服务启动逻辑调整到用户点击"开始录制"的直接回调中,避免通过倒计时等延迟操作触发。如果必须保留倒计时,需确保倒计时的触发紧接用户操作,且倒计时时长不宜过长。
内容的提问来源于stack exchange,提问作者Mansi Shah

