You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的后台访问施加了更严格的限制:

  1. 后台Activity无法直接弹出系统权限请求对话框,导致权限请求被静默忽略
  2. 即使调用了requestPermissions,如果Activity处于后台状态,权限请求不会触发,代码会直接走到Start_Calling()尝试打开相机,最终因权限不足崩溃
  3. 原代码没有等待权限请求的回调结果就执行相机操作,也是问题的触发点

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:29:50