Chapter 126: Camera Privacy and Resource Arbitration
相机在移动系统中同时属于硬件资源、隐私入口和实时媒体管线。读者读完本章后,应能追踪一次相机使用请求从 App 发起到系统服务仲裁的完整路径,判断失败来自权限状态、前台状态、设备占用、系统策略、硬件断开还是平台私有实现。
本章贯穿一个场景:视频会议 App 已经获得相机授权,正在显示预览并录制本地视频;用户把它切到后台,又打开系统相机或另一个会议 App。这个场景会触发三个问题:原 App 是否仍有权访问相机,系统是否允许另一个客户端接管硬件,用户是否能看到相机正在被占用。相机隐私模型的关键就在这三个问题的交汇处。
相机权限授予只说明用户允许某个应用在合适场景中请求相机能力。真正决定画面是否进入应用的,是后续的系统服务检查、session owner 记录、前后台状态、设备策略、全局开关、资源优先级和错误回调。Android 与 Apple 平台的公开 API 不同,但共同结构一致:App 只能请求能力,系统服务持有硬件所有权,用户界面呈现占用状态,底层 driver、ISP 和 sensor 只接受系统授权后的控制路径。
分析相机隐私时要把“相机是否可以打开”拆成一组连续判断:应用身份是否有效,权限是否授予,当前生命周期是否匹配,设备是否被策略禁用,camera service 是否能分配设备,会话是否成为 owner,其他客户端是否拥有更高优先级,隐私指示器是否把占用状态反馈给用户。这组判断比单独记住 CAMERA 权限或 AVCaptureDevice.requestAccess 更稳定。
126.1 Camera Permission 作为硬件访问入口控制
Camera permission 是相机硬件访问的入口闸门。它把一个敏感硬件能力转成平台可记录、可授权、可撤销、可审计的系统能力。App 看到的是 CameraManager.openCamera、CameraX、AVCaptureSession 或 AVCaptureDevice 这类 framework API;系统服务看到的是调用方 UID、包名、进程状态、权限记录、AppOps 或 entitlement/sandbox 上下文;硬件层看到的是经过系统服务分配后的设备控制请求。
在 Android 中,公开文档把运行时权限放在 limited-access sandbox 之外的资源访问流程中:应用需要在 manifest 声明权限,并在使用功能时请求运行时授权;每次执行需要权限的操作时,都应检查权限状态。Android runtime permissions 文档明确把权限请求拆成声明、用户动作、权限检查、系统弹窗、用户响应和降级处理。相机属于典型敏感硬件资源,因此 android.permission.CAMERA 只构成进入 camera service 的第一层条件。
在 Apple 平台中,公开入口通常是 AVFoundation。应用通过 NSCameraUsageDescription 说明用途,通过 AVCaptureDevice.authorizationStatus(for: .video) 查询状态,再通过 AVCaptureDevice.requestAccess(for: .video) 触发授权流程。Apple 的 requesting authorization to capture and save media 页面提供了公开 API 入口;AVFoundation 之后的 daemon、driver、ISP 细节属于平台私有实现,本书只把它们作为系统服务与硬件边界来讨论。
相机权限的核心价值在于把用户授权绑定到应用身份。Android 使用安装包、UID、权限记录、AppOps 和进程状态组合判断调用方;Apple 使用 bundle identity、usage description、authorization status、sandbox 和 entitlement 组合判断调用方。不同平台名称不同,责任链相同:用户授权记录回答“这个应用曾被允许请求相机”,系统服务检查回答“当前这一次请求是否可以进入硬件分配”。
这个分层解释了视频会议 App 的第一步:它打开预览前先通过 framework 查询授权状态;授权缺失时触发系统弹窗;授权存在时向系统服务申请设备。此时 App 仍未直接控制 sensor。App 获得的是一个 session 或 device handle,真正的硬件所有权保留在系统服务与 driver 边界内。
这张图限定在 App 到系统服务的授权与分配路径内,未展开 ISP、图像算法和编码链路。关键点是权限弹窗发生在用户授权层,资源分配发生在系统服务层,隐私指示发生在用户可见 UI 层。把三者合成一个“打开相机成功”会遮蔽故障来源。
126.2 Permission Prompt、Runtime Grant、System Service Check
Permission prompt、runtime grant 和 system service check 分别解决不同问题。Prompt 让用户确认某个应用请求访问敏感硬件;grant state 保存用户选择;service check 在每一次真实访问时重新评估调用方是否满足当前策略。相机路径中这三个阶段连续发生,但失败表现不同。
Android 的权限请求流程强调“在用户触发相关功能时请求权限”。文档建议把具体动作与具体权限关联起来,系统权限弹窗只说明应用请求哪类权限,应用自己的说明负责解释用途。用户授予后,App 才能继续访问被权限保护的数据;用户拒绝后,App 要提供缺失功能下的可用路径。Android permission denial 的处理建议说明,拒绝权限后应清楚标出受影响功能,并尊重用户选择。
Runtime grant 的状态并非永久稳定。Android 11 起,位置、麦克风、相机权限弹窗包含 one-time 选项;用户选择后,应用在活动可见、短暂后台或特定 foreground service 条件下获得临时访问,权限撤销后进程可能被终止。Android 13 起,应用还可以主动撤销自身不再需要的运行时权限。这个演进说明相机授权更接近“当前会话的能力租约”,权限记录和生命周期状态共同决定能否继续使用。
System service check 是更靠近硬件的检查点。Android CameraService 会在打开设备、创建 session、提交请求、处理死亡回调和资源冲突时重新检查调用方状态。公开 API 层能观察到的结果包括 CameraAccessException、CameraDevice.StateCallback.onError、onDisconnected、availability callback 和空预览。Apple 平台在公开 API 上表现为授权状态失败、session 无法启动、AVCaptureSessionRuntimeErrorNotification、session interruption 或 input/output 断开;具体 daemon 和 driver 行为只能依据公开 API、系统行为和日志进行边界推断。
同一个视频会议 App 因相机失败而出现黑屏时,排查顺序应按责任链推进。先看用户是否授予相机权限,再看 App 是否在当前生命周期中仍属于可使用状态,再看系统级相机开关或设备策略,再看是否有另一个客户端占用设备,再看 camera service 或硬件是否返回 fatal error。跳过前两步直接怀疑 driver,通常会把用户授权问题误判为硬件问题。
以下表格把三类检查分开,便于定位责任边界。
| 阶段 | 系统问题 | Android 可观察对象 | Apple 可观察对象 | App 应处理结果 |
|---|---|---|---|---|
| Prompt | 用户是否同意当前应用访问相机 | requestPermissions / Activity Result | requestAccess(for: .video) | 授权后进入 session 配置,拒绝后显示无视频路径 |
| Grant state | 权限记录当前是否有效 | checkSelfPermission、one-time grant、auto reset | authorizationStatus(for: .video) | 每次进入相机功能前重新读取状态 |
| Service check | 本次硬件访问是否被系统接受 | CameraService、AppOps、foreground state、policy | AVFoundation session、系统 daemon、sandbox | 处理打开失败、断开、抢占和重试 |
| Resource check | 设备是否可分配给当前客户端 | ERROR_CAMERA_IN_USE、ERROR_MAX_CAMERAS_IN_USE | session interruption、device unavailable | 释放资源、更新 UI、等待用户重新进入 |
这张表的用途是把“权限通过但相机仍不可用”解释为正常系统现象。权限允许访问意图,service check 执行当前策略,resource check 执行硬件仲裁。三层任一失败,都能让预览停止。
126.3 Privacy Indicator 与用户可见硬件占用状态
Privacy indicator 是相机隐私模型的用户可见出口。它向用户表达“某个应用或系统组件正在使用相机或麦克风”。这个提示并不描述画面内容,也不保证 App 已经消费到每一帧;它表达的是敏感硬件处于活跃访问状态,帮助用户把授权记录和当前占用状态联系起来。
Android 12 之后,支持设备在应用访问麦克风或相机时会在状态栏显示图标,系统还提供 camera 和 microphone 的全局开关。Android 12 behavior changes 文档说明,用户可以通过 Quick Settings 或系统隐私设置启用、停用 camera/microphone access;当应用访问这些资源时,状态栏会出现图标。对 App 来说,这说明相机访问已经进入系统级透明度范围,应用自己的 UI 提示要和系统提示保持一致。
Apple 在 iOS 14 之后使用橙色和绿色状态指示。Apple Support 的 orange and green indicators 页面说明,橙色表示麦克风正在被使用,绿色表示相机或相机与麦克风正在被使用。Apple 的说明是用户层语义,开发者从中得到的系统结论是:相机访问会被系统 UI 显式呈现,应用无法把相机采集完全隐藏在普通界面状态之后。
Privacy indicator 解决的是信任闭环中的“用户感知”问题。权限弹窗发生在请求前,授权设置记录过去选择,indicator 呈现当前访问。三者组合后,用户可以把“我曾授权过这个 App”与“此刻它正在使用相机”联系起来。Android 的隐私面板、状态栏图标、Quick Settings toggles,Apple 的状态栏指示、控制中心提示和隐私设置,都属于同一类用户可见证据。
对视频会议 App,正确的 UI 行为是让应用内相机状态、系统隐私指示和实际 session 状态对齐。应用显示“摄像头已关闭”时,应释放或停止向系统请求相机;应用仍在后台维持视频时,系统应能通过前台服务、后台模式或平台规定的可见状态让用户感知占用。若 App UI 说已关闭,但系统 indicator 仍显示相机活跃,排查焦点应转向 session 未释放、系统级录制、画中画状态或另一个进程占用。
Privacy indicator 还有一个边界:它是平台信任界面的一部分,权限判断仍由系统服务和授权记录完成。用户看到 indicator 时,可以确认存在活跃访问;具体是哪一个 session、哪一个进程、是否属于系统组件、是否与当前前台界面相关,需要结合控制中心、隐私面板、App UI、系统日志或平台提供的诊断入口判断。
126.4 Foreground Requirement 与后台访问限制
Foreground requirement 把相机访问和用户可见状态绑定起来。移动系统对相机的限制强于普通文件读写,因为相机持续采集环境图像,既有隐私风险,又有功耗、温控和资源独占成本。系统通常要求应用在可见界面、用户明确交互、foreground service、画中画、视频通话或平台批准的后台模式中使用相机。
Android 的“while-in-use permission”模型把相机、麦克风、位置等权限与当前前台状态关联。Android 14 对 foreground service 的限制更严格:应用在后台创建需要 while-in-use 权限的 foreground service 时,系统会在创建阶段检查当前是否拥有对应权限;后台状态下的 while-in-use 权限会导致 SecurityException。Foreground service restrictions 文档明确把 camera、microphone、location 放在这类限制中。
这条规则对视频会议场景影响明显。用户从会议界面按 Home 键后,如果应用仍以可见通知、PiP 或系统认可的视频通话状态运行,平台可以继续允许相机;如果应用退到普通后台状态并试图新建相机采集链路,系统会把它视为后台敏感硬件访问。Android 不同版本和 OEM 策略会影响具体异常时机,稳定判断是:相机访问要与用户可见状态或系统认可的后台能力绑定。
Apple 平台的公开模型更强调 AVFoundation session 与应用生命周期的配合。普通 iPhone 应用进入后台后,系统会收缩其执行机会;相机采集通常需要前台交互、视频通话、画中画、特定多任务模式或系统认可的持续媒体场景。由于 Apple 的相机服务和优先级实现大多是私有路径,正文只使用公开 API 与用户可见行为推断:session 是否被中断、是否出现 runtime error、控制中心是否显示相机使用、应用是否仍处于系统认可的活跃形态。
后台访问限制的工程意义是把“权限存在”改写为“权限在当前状态下可用”。一个 App 可以拥有相机授权,但在后台启动时没有当前可用的 while-in-use 访问条件;一个 App 可以已经打开 session,但在电话、系统相机、屏幕锁定、设备折叠、多窗口焦点变化或另一个高优先级客户端出现后被中断。权限记录、前台状态和 session owner 共同决定结果。
排查后台相机问题时,应使用同一组问题:当前应用是否有可见 Activity、是否处于 PiP、是否有用户可见通知、是否属于视频通话/录制的系统认可场景、是否从后台新建 foreground service、是否在系统设置中被关闭相机权限、是否有另一个 App 或系统组件持有相机。这个顺序能把策略限制和资源冲突区分开。
126.5 多 App Camera Resource Arbitration
Camera resource arbitration 是系统在多个客户端之间分配相机设备的过程。相机设备常常具有独占性:同一物理 sensor、ISP pipeline、buffer queue 和同步时钟无法随意同时服务多个 App。即使设备支持并发摄像头或逻辑多摄,系统也需要判断哪些组合合法、哪些 stream 配置受支持、哪个客户端拥有更高优先级。
Android 的公开 API 直接暴露了仲裁结果。CameraDevice.StateCallback 的 ERROR_CAMERA_IN_USE 表示设备已经被使用,且可能由更高优先级 camera API client 导致;ERROR_MAX_CAMERAS_IN_USE 表示系统范围内已经达到打开摄像头数量上限;ERROR_CAMERA_DISABLED 表示设备策略禁用相机;ERROR_CAMERA_SERVICE 表示 camera service fatal error。相关错误码在 CameraDevice.StateCallback 中定义。
CameraManager.AvailabilityCallback 提供了另一类仲裁信号:相机从 available 变为 unavailable,说明某个 cameraId 已经无法被当前客户端使用。Android 文档还特别提到,在多个应用同时处于 resumed 状态、用户在它们之间切换焦点、当前相机应用在全屏和 PiP 之间移动时,多个应用可能同时收到可用性变化,实际打开成功取决于优先级和时序。CameraManager.AvailabilityCallback 因此属于资源仲裁的观察入口。
Android 11 之后,Camera2 提供查询和打开并发摄像头的能力。AOSP 的 concurrent camera streaming 文档说明,设备可以支持前后摄同时工作,应用可以查询支持的 camera combination 和 stream configuration。这个能力改变的是“哪些组合可以并发”,并未取消系统服务对每个组合的资源检查。硬件支持、HAL 报告、framework 查询和 session 创建都需要一致。
Apple 平台公开 API 通常用 session interruption、runtime error、device unavailable、input/output 失败来表达仲裁结果。开发者能通过 AVCaptureSessionWasInterruptedNotification、AVCaptureSessionRuntimeErrorNotification 和 AVCaptureSession.InterruptionReason 一类入口处理电话、系统服务、另一个客户端或设备变化带来的中断。Apple 的 AVCaptureSession.InterruptionReason 文档属于可回溯的公开入口;具体优先级队列、daemon 决策和 ISP 分配仍属于私有实现边界。
在贯穿场景中,用户打开第二个相机 App 时,系统可能产生四类结果。第一类是第二个 App 打开失败,原会议 App 保持 owner;第二类是原会议 App 被抢占并收到 disconnect/interruption,第二个 App 成为 owner;第三类是设备支持特定并发组合,两个 session 以受限 stream 规格运行;第四类是系统策略或全局开关让两个 App 都无法获得画面。判断哪一类发生,要看错误码、availability callback、session interruption、系统 UI 指示和 App 生命周期。
多 App 仲裁的稳定原则是:用户意图越直接、界面越可见、系统角色越高、当前任务越紧急,越可能获得高优先级。系统相机、电话、FaceTime/视频通话、二维码扫描、权限弹窗相关组件和设备管理策略可能拥有不同特权。第三方 App 不应假设已打开的相机永远保持 owner;它必须把被抢占和断开作为正常路径处理。
126.6 Camera Session Ownership、Disconnect、Preemption、Error Recovery
Camera session ownership 表示某个客户端在当前时刻持有相机设备或 stream 组合的使用权。这个 owner 表达系统服务授予的会话状态,硬件产权仍归平台控制面持有。它可以因为 App 主动关闭、进程死亡、权限撤销、前后台变化、设备策略、系统相机打开、电话进入、硬件断开或 service error 而变化。
Android 的 onDisconnected 是理解 session owner 变化的关键回调。文档说明,当 camera device 不再可用时会调用该方法;原因可能包括安全策略或权限变化、可移除相机物理断开、相机被更高优先级客户端需要。回调发生后,对该 CameraDevice 的方法调用会抛出 CameraAccessException。同一文档还说明,在断开或错误之后,仍可能有后续 capture callback 或 image buffer 到达,这要求应用按状态机处理资源释放。
错误恢复应按“停止提交新请求 → 关闭 session → 释放 output surface / image reader → 更新 UI 状态 → 等待可用性或用户重试”的顺序。直接在错误回调中立即循环重开相机,容易和系统仲裁冲突,造成连续失败、发热或电量开销。应用应把 ERROR_CAMERA_IN_USE、ERROR_MAX_CAMERAS_IN_USE、ERROR_CAMERA_DISABLED、ERROR_CAMERA_SERVICE 分别映射到不同用户提示和恢复路径。
Apple 平台的恢复模型同样需要状态机。session interrupted 时,应用应暂停 UI 中依赖相机的功能,保留用户上下文;interruption ended 时再恢复或重新配置 session;runtime error 时根据错误类型判断是否重建 session。由于 Apple 的底层实现不可完全公开验证,工程上应只依赖公开通知、AVFoundation 状态、授权状态和用户可见现象来做判断。
一个可靠的相机会话状态机可以压缩为六个状态:Idle、Authorizing、Opening、Running、Interrupted、Recovering。每个状态只允许有限动作。Idle 可以请求权限;Authorizing 等待用户响应;Opening 等待系统服务分配;Running 提交 repeating request 或输出 sample buffer;Interrupted 停止假定画面可用;Recovering 清理旧 session 并等待条件恢复。
这张状态图的重点是把抢占和错误恢复视为常规状态。移动相机系统面对的硬件、隐私和生命周期变化很频繁,App 的健壮性来自清晰状态迁移,而非假设 startRunning 或 openCamera 之后一路成功。
126.7 Camera 隐私模型与系统信任边界
Camera 隐私模型由五个边界组成:应用身份边界、用户授权边界、系统服务边界、硬件所有权边界、用户可见 UI 边界。任何一个边界缺失,都会让相机成为不可审计的环境传感器;五个边界同时工作,系统才能把相机封装成可授权、可调度、可恢复的能力。
应用身份边界回答“谁在请求相机”。Android 使用 UID、package、签名、AppOps、SELinux domain 等信息约束调用方;Apple 使用 bundle identifier、签名、sandbox、usage description 和 entitlement 上下文约束调用方。用户授权边界回答“用户是否允许这个身份请求相机”。系统服务边界回答“当前状态下是否把设备交给这个请求”。硬件所有权边界回答“sensor、ISP、buffer queue、driver handle 当前归哪个 session 使用”。用户可见 UI 边界回答“用户是否能看到相机正在被占用”。
这个模型能解释多个常见现象。App 有权限但打不开相机,通常是 service check 或 resource arbitration 失败;App 在后台使用相机被中断,通常是 foreground requirement 失效;系统相机打开后会议 App 黑屏,通常是 session owner 被抢占;用户看到绿色点但当前 App 没显示预览,可能是另一个 App、系统组件、PiP、未释放 session 或延迟回调造成的占用信号。
Android 和 Apple 的差异主要在开放程度。Android 的 Camera2、CameraService 行为、AOSP 文档、错误码、availability callback 和 concurrent camera API 给了开发者更多公开观察点;Apple 的 AVFoundation 给了稳定的高层模型,但 daemon、driver、ISP、优先级和部分后台策略属于私有系统路径。写跨平台相机代码时,应把共同模型放在前面:权限查询、session 创建、运行中断、资源释放、用户提示、重试节流。平台差异放在后面:错误类型、后台模式、并发能力、日志入口和可见 UI。
相机安全闭环可以归纳为一条责任链:App 表达意图,framework 校验 API 入口,系统服务检查身份和策略,资源仲裁决定 owner,driver 和 ISP 执行采集,隐私指示器呈现占用,错误回调把失败返回给 App。读者以后看到“相机黑屏”“权限已开但无法录像”“切后台后摄像头断开”“另一个 App 打开相机导致会议画面停止”这类问题,应优先把现象放回这条链路定位。
本章最终结论是:相机隐私是一条跨 App、framework、system service、driver、hardware 和 system UI 的控制链。权限负责入口,前台状态负责用户可见性,资源仲裁负责硬件所有权,错误回调负责恢复路径,隐私指示器负责让占用状态回到用户视野。
最小自检任务
你正在排查一个视频会议 App:用户已经授予相机权限,会议中预览正常;用户按 Home 键让 App 进入后台后,又打开系统相机。会议 App 返回前台时显示黑屏,系统状态栏曾出现相机指示器。请写出你会如何沿系统路径定位问题,并说明哪些结果分别指向权限问题、后台策略问题、资源抢占问题和硬件/service 问题。
答案要点
先确认权限状态,再确认当前生命周期和后台访问条件。Android 上检查 CAMERA 是否仍为 granted、one-time permission 是否已失效、App 是否从后台创建需要 camera while-in-use 权限的 foreground service;Apple 上检查 authorizationStatus(for: .video) 和 session 是否因生命周期或系统策略进入 interruption。权限缺失或一次性授权失效时,问题属于用户授权边界。
接着确认系统服务和资源仲裁结果。Android 上观察 CameraDevice.StateCallback.onDisconnected、onError、ERROR_CAMERA_IN_USE、ERROR_MAX_CAMERAS_IN_USE、ERROR_CAMERA_DISABLED 和 CameraManager.AvailabilityCallback;Apple 上观察 session interruption、runtime error、device unavailable 和恢复通知。若系统相机打开后会议 App 收到 disconnect/interruption,问题属于 session owner 被更高优先级客户端抢占。
再检查用户可见信号。状态栏相机指示器说明系统认为某个客户端正在访问相机;它需要结合控制中心、隐私面板、App UI 和 session 状态判断具体 owner。若会议 App UI 显示已关闭但 indicator 仍存在,应检查 session 释放、PiP、系统相机、后台录制或其他进程占用。
最后制定恢复路径。App 应停止提交新 capture request,关闭旧 session,释放 output buffer,更新 UI 为“摄像头暂不可用”,等待 availability 或 interruption ended 后再重建 session。若返回 ERROR_CAMERA_SERVICE 或硬件 fatal error,应提示用户重试或重启相关功能;若返回 device policy disabled,应引导到系统设置或设备管理说明。
本章知识点总结
- 权限入口:Camera permission 把相机硬件访问转成平台可授权、可撤销、可审计的系统能力。
- 身份绑定:Android 使用 UID、package、AppOps 和进程状态判断调用方,Apple 使用 bundle identity、sandbox 和授权状态判断调用方。
- 运行检查:Prompt 保存用户选择,system service check 在每一次真实访问时重新评估当前策略。
- 授权租约:相机授权在现代移动系统中更接近受生命周期约束的能力租约。
- 指示器:Privacy indicator 把当前相机占用状态反馈给用户,形成授权之外的实时可见证据。
- 前台条件:相机访问通常需要可见界面、foreground service、PiP、视频通话或平台认可的持续媒体场景。
- 资源所有权:Camera session owner 是系统服务授予的当前使用权,可以因策略、抢占、断开或错误而变化。
- 多端竞争:多个 App 请求相机时,系统按优先级、时序、硬件能力和 stream 组合进行仲裁。
- 并发摄像:设备支持 concurrent camera streaming 时,系统仍要检查合法组合和 stream 配置。
- 错误信号:
ERROR_CAMERA_IN_USE、ERROR_MAX_CAMERAS_IN_USE、onDisconnected和 session interruption 都是资源仲裁结果的可观察出口。 - 恢复顺序:可靠恢复应先停止新请求,再关闭 session、释放 buffer、更新 UI,最后等待条件恢复后重试。
- 私有边界:Apple 相机 daemon、driver、ISP 和优先级实现大多属于私有路径,应依据公开 API 和可见行为推断。
- 信任闭环:相机安全闭环由应用身份、用户授权、系统服务、硬件所有权和用户可见 UI 共同组成。
- 排查顺序:相机黑屏应先查权限和生命周期,再查系统策略、资源抢占、service error 和硬件状态。