Android 9(Pie)下Sinch视频通话相机权限崩溃问题求助
Android 9 (Camera2 API) 下Sinch Payload跳转来电Activity相机权限请求失效导致崩溃
问题描述
我正在开发一款需要相机权限的小型应用,已经实现了运行时权限处理逻辑,但遇到一个奇怪的问题:当通过Sinch payload跳转至来电Activity时,本该弹出相机等3项权限的请求对话框,结果直接因为SecurityException崩溃。
这个问题有几个关键特征:
- 仅在请求相机权限时出现
- 只发生在采用Camera2 API的Android 9设备上,Android 8.1及以下设备完全正常
- 甚至Sinch官方的推送示例也存在同样问题
- 如果通过常规Intent启动该Activity,权限对话框能正常弹出并工作
复现环境
- Android 9(API 28)设备,依赖Camera2 API
- 通过Sinch推送Payload触发来电Activity启动
- 应用未预先授予相机权限
错误日志
-------- beginning of crash 2019-02-10 22:11:55.390 2113-2681/com.example.myapp.app E/AndroidRuntime: FATAL EXCEPTION: VideoCapturerThread Process: com.example.myapp.app, PID: 2113 java.lang.SecurityException: validateClientPermissionsLocked:1054: Caller "com.example.myapp.app" (PID 10319, UID 2113) cannot open camera "1" without camera permission at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:747) at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:405) at android.hardware.camera2.CameraManager.openCameraForUid(CameraManager.java:567) at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:495) at org.webrtc.Camera2Session.openCamera(Unknown Source:44) at org.webrtc.Camera2Session.start(Unknown Source:60) at org.webrtc.Camera2Session.<init>(Unknown Source:73) at org.webrtc.Camera2Session.create(Unknown Source:17) at org.webrtc.Camera2Capturer.createCameraSession(Unknown Source:17) at org.webrtc.CameraCapturer$5.run(Unknown Source:52) at android.os.Handler.handleCallback(Handler.java:891) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:207) at android.os.HandlerThread.run(HandlerThread.java:65) Caused by: android.os.ServiceSpecificException: validateClientPermissionsLocked:1054: Caller "com.example.myapp.app" (PID 10319, UID 2113) cannot open camera "1" without camera permission (code 1) at android.os.Parcel.createException(Parcel.java:1967) at android.os.Parcel.readException(Parcel.java:1921) at android.os.Parcel.readException(Parcel.java:1871) at android.hardware.ICameraService$Stub$Proxy.connectDevice(ICameraService.java:343) at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:369) at android.hardware.camera2.CameraManager.openCameraForUid(CameraManager.java:567) at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:495) at org.webrtc.Camera2Session.openCamera(Unknown Source:44) at org.webrtc.Camera2Session.start(Unknown Source:60) at org.webrtc.Camera2Session.<init>(Unknown Source:73) at org.webrtc.Camera2Session.create(Unknown Source:17) at org.webrtc.Camera2Capturer.createCameraSession(Unknown Source:17) at org.webrtc.CameraCapturer$5.run(Unknown Source:52) at android.os.Handler.handleCallback(Handler.java:891) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:207) at android.os.HandlerThread.run(HandlerThread.java:65)
当前权限请求代码
if (Build.VERSION.SDK_INT>=23) { if (GlobalConstants.isAllPermissionsGrantedBefore_Video_Call(this)==false) ActivityCompat.requestPermissions( Video_incomming_call.this, GlobalConstants.mPermissions_Video_Chat, GlobalConstants.VIDEO_RECIEVE_CODE); else { Start_Calling(); } } else Start_Calling();
分析与解决方案
核心原因
Android 9开始对后台启动的Activity和Camera2 API的后台访问施加了更严格的限制:
- 后台Activity无法直接弹出系统权限请求对话框,导致权限请求被静默忽略
- 即使调用了
requestPermissions,如果Activity处于后台状态,权限请求不会触发,代码会直接走到Start_Calling()尝试打开相机,最终因权限不足崩溃 - 原代码没有等待权限请求的回调结果就执行相机操作,也是问题的触发点
方案1:确保权限请求完成后再初始化相机
修改代码逻辑,将Start_Calling()的执行移到权限请求的回调方法中,确保只有在用户授予权限后才访问相机:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_incoming_call); // 处理权限请求 if (Build.VERSION.SDK_INT >= 23) { if (!hasAllVideoCallPermissions()) { ActivityCompat.requestPermissions( Video_incomming_call.this, GlobalConstants.mPermissions_Video_Chat, GlobalConstants.VIDEO_RECIEVE_CODE); } else { // 已预先授权,直接启动通话 Start_Calling(); } } else { // Android 6以下无需运行时权限 Start_Calling(); } } // 重写权限请求回调,处理用户的权限选择 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == GlobalConstants.VIDEO_RECIEVE_CODE) { boolean allPermissionsGranted = true; // 检查所有权限是否都被授予 for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allPermissionsGranted = false; break; } } if (allPermissionsGranted) { // 权限全部授予,启动通话 Start_Calling(); } else { // 权限被拒绝,提示用户并关闭页面 Toast.makeText(this, "需要相机等权限才能进行视频通话,请前往设置开启", Toast.LENGTH_LONG).show(); finish(); } } } // 替换原GlobalConstants的权限检查方法,确保判断准确 private boolean hasAllVideoCallPermissions() { if (Build.VERSION.SDK_INT >= 23) { for (String permission : GlobalConstants.mPermissions_Video_Chat) { if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { return false; } } } return true; }
方案2:确保Activity在前台状态下处理权限请求
由于Android 9限制后台Activity弹出系统对话框,我们需要先将应用切换到前台,再处理权限请求和通话启动:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_incoming_call); if (Build.VERSION.SDK_INT >= 23) { if (!isAppInForeground()) { // 将应用切换到前台 Intent foregroundIntent = new Intent(this, MainActivity.class); foregroundIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(foregroundIntent); // 延迟处理来电逻辑,确保应用完全进入前台 new Handler().postDelayed(() -> handleIncomingCallPermissions(), 1000); } else { handleIncomingCallPermissions(); } } else { Start_Calling(); } } // 封装权限处理逻辑 private void handleIncomingCallPermissions() { if (!hasAllVideoCallPermissions()) { ActivityCompat.requestPermissions( Video_incomming_call.this, GlobalConstants.mPermissions_Video_Chat, GlobalConstants.VIDEO_RECIEVE_CODE); } else { Start_Calling(); } } // 判断应用是否处于前台状态 private boolean isAppInForeground() { ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses(); if (runningProcesses == null) return false; String packageName = getPackageName(); for (ActivityManager.RunningAppProcessInfo process : runningProcesses) { if (process.processName.equals(packageName) && process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { return true; } } return false; } // 权限回调方法同方案1 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // ... 同方案1的回调逻辑 ... }
方案3:验证Sinch推送的启动方式
如果上述方案仍未解决问题,可以检查Sinch推送的配置:
- 确保Sinch的payload启动Activity时使用
FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_BROUGHT_TO_FRONT标志,强制将Activity带到前台 - 避免在后台服务中直接初始化相机相关逻辑,必须等待Activity完全可见并获取权限后再执行
总结
这个问题本质是Android 9的后台权限限制与Sinch启动Activity的方式共同导致的。通过等待权限请求回调结果再初始化相机,并确保Activity在前台状态下处理权限请求,就能解决崩溃问题。
内容的提问来源于stack exchange,提问作者Trần Quốc Trung
相关产品推荐
相关产品推荐

