CVE-2025-26440 AI-generated analysis
platform/frameworks/av (libcameraservice, CameraService::BasicClient AppOps handling) and platform/frameworks/base (AppOps, AppOpsUidStateTrackerImpl)
CameraService now registers its AppOps watcher with WATCH_FOREGROUND_CHANGES and blocks untrusted MODE_IGNORED clients that are not user-visible (proc state above PROCESS_STATE_BOUND_TOP). Before, it decided between mute and block based on whether the uid was "active". AppOpsUidStateTrackerImpl now delays committing a uid-state change that loses a capability instead of committing it immediately.
Root cause
In services/camera/libcameraservice/CameraService.cpp, BasicClient::startCameraOps() called mAppOpsManager->startWatchingMode(OP_CAMERA, pkg, cb) without the WATCH_FOREGROUND_CHANGES flag, so the client was not notified of foreground-related changes such as the app losing its camera capability. BasicClient::opChanged() handled MODE_IGNORED for untrusted uids using mUidPolicy->isUidActive(). It muted only if the uid was active, camera privacy was enabled and mute was supported. It blocked only if the uid was not active, or if camera privacy was enabled and mute was not supported. An active but not visible uid (such as a foreground service) with camera privacy disabled matched neither branch, so the client was neither muted nor blocked. The commit message states that an app that should not have camera access outside the foreground could otherwise keep it.
How the fix works
- frameworks/av CameraService.cpp:CameraService::BasicClient::startCameraOps(): startWatchingMode(OP_CAMERA, ...) now passes AppOpsManager::WATCH_FOREGROUND_CHANGES, so the OpsCallback receives the notifications AppOps sends when the app loses its camera capability (per the commit message).
- frameworks/av CameraService.cpp:CameraService::BasicClient::opChanged(): on MODE_IGNORED, it computes procState via sCameraService->mUidPolicy->getProcState(mClientUid) and sets isUidVisible = (procState <= ActivityManager::PROCESS_STATE_BOUND_TOP). For !mUidIsTrusted, it calls setCameraMute(true) only if isUidVisible && isCameraPrivacyEnabled && supportsCameraMute(). In every other case it calls block().
- frameworks/base AppOpsUidStateTrackerImpl.java:updateUidProcState(): introduces hasLostCapability = (prevCapability & ~capability) != 0. The state is now committed immediately in only three cases: moving to a more important state (uidState < prevUidState), the state is unchanged and no capability was lost, or the new state is at or below UID_STATE_MAX_LAST_NON_RESTRICTED and no capability was lost. A transition that loses a capability (for example UID_STATE_TOP -> UID_STATE_FOREGROUND, as in the new test) falls through to the settle-time delay path. The clause (uidState <= MAX_LAST_NON_RESTRICTED && prevUidState > MAX_LAST_NON_RESTRICTED) was removed; it is already covered by uidState < prevUidState.
- frameworks/base AppOpsUidStateTrackerTest.java: adds assertions that MODE_FOREGROUND ops stay MODE_ALLOWED until the settle time elapses after capability loss, and the new test testProcStateChangesAndStaysUnrestrictedAndCapabilityRemoved.
Completeness
- The av fix covers CameraService::BasicClient (startCameraOps and opChanged) only. Per index search (android-14.0.0_r75), the sibling CameraOfflineSessionClient::startCameraOps in api2/CameraOfflineSessionClient.cpp calls startWatchingMode(OP_CAMERA, pkg, cb) without WATCH_FOREGROUND_CHANGES. This diff does not touch it.
- The visibility test is a single threshold (procState <= PROCESS_STATE_BOUND_TOP) and applies only inside the MODE_IGNORED branch for untrusted uids. MODE_ERRORED handling and trusted-uid handling are unchanged.
- handleAppOpMode(), which startCameraOps calls at open time, still uses isUidActive rather than visibility, as the r29 context shows. The fix changes only the runtime opChanged path.
- The frameworks/base change defers commits of capability-losing transitions by the settle time; it does not add a new check of its own.
Behavior changes
- Camera clients registered via BasicClient::startCameraOps now receive opChanged callbacks for foreground changes (WATCH_FOREGROUND_CHANGES).
- Untrusted camera clients evaluated as MODE_IGNORED are now blocked unless they are visible, camera privacy is enabled and mute is supported, in which case they are muted. Active but not visible clients that were previously muted or left alone are now blocked.
- AppOps uid-state commits that lose a capability without moving to a more important state are deferred by the applicable settle time (TOP_STATE_SETTLE_TIME, FG_SERVICE_STATE_SETTLE_TIME or BG_STATE_SETTLE_TIME). During that window MODE_FOREGROUND ops still evaluate to MODE_ALLOWED, as the new tests assert.
Per major
- Android 14: Same fix. frameworks/av b9a047c94deb and frameworks/base b90d4d01e1bf landed in android-security-14.0.0_r20 (linked by bug 290086710). At android-security-14.0.0_r29, the CameraService.cpp context shows WATCH_FOREGROUND_CHANGES and AppOpsUidStateTrackerImpl shows the hasLostCapability logic; 6/8 lines of the patched region are present.
- Android 15: Not affected per the bulletin. At android-security-15.0.0_r17, 7/8 lines of the patched region are present. The material does not show whether the change itself is there.
- Android 16: Not affected per the bulletin. At android-security-16.0.0_r8, 4/8 lines of the patched region are present. The code is not shown in the material.
- Android 17: Not affected per the bulletin. At android-security-17.0.0_r1, 3/8 lines of the patched region are present. The code is not shown in the material.
Unknowns
- Whether the frameworks/av commit b9a047c94deb, or an equivalent, is present on Android 15+ branches. The per-major facts give only line-match counts.
- The exact AppOps flow that produced MODE_IGNORED for the affected app (bug 290086710 details are not in the material).
- How the two commits depend on each other, beyond sharing a bug ID.
- Whether CameraOfflineSessionClient was addressed by a separate change or needs this one.