CVE-2026-0026 AI-generated analysis
platform/frameworks/base, services/core PermissionManagerService (permission registry)
Adds an early return in PermissionManagerServiceImpl.removePermission() so that a non-dynamic permission is no longer removed from the permission registry after the "not allowed to modify non-dynamic permission" condition is detected.
Root cause
In services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java, removePermission() looks up the Permission and, if !bp.isDynamic(), only emits Slog.wtf("Not allowed to modify non-dynamic permission ...") (a TODO says to switch this back to SecurityException). Control then fell through to mRegistry.removePermission(permName) and mPackageManagerInt.writeSettings(false). So a caller that passed enforcePermissionTree() could remove a non-dynamic permission from the registry, although the commit states that only dynamic permissions may be removed.
How the fix works
- PermissionManagerServiceImpl.java:removePermission(): a single `return;` is added inside the `if (!bp.isDynamic())` block, right after the Slog.wtf call. For non-dynamic permissions the method now returns from within the synchronized(mLock) block, before mRegistry.removePermission(permName) and before mPackageManagerInt.writeSettings(false).
- The Slog.wtf logging is kept and the TODO to restore a SecurityException is left in place. The caller gets no error, but the removal no longer happens.
Completeness
- Covers the only fall-through in removePermission(): the bp == null branch already returned, and the new return closes the non-dynamic branch, so mRegistry.removePermission() is now reached only for dynamic permissions.
- The sibling addPermission() path (lines 665-670 in the context) already throws SecurityException when modifying an existing non-dynamic permission and is not changed by this fix.
- The fix does not restore the SecurityException noted in the TODO; a caller attempting to remove a non-dynamic permission receives no error, only a Slog.wtf is logged.
- The instant-app check and mRegistry.enforcePermissionTree() gating are unchanged.
Behavior changes
- removePermission() on an existing non-dynamic permission now returns without modifying the registry and without calling writeSettings(false); previously the permission was removed and settings were written.
- No new exception or error code is introduced; the only observable signal is the pre-existing Slog.wtf log line.
Per major
- Android 14: same fix: commit 6c22d6c1e696 in android-security-14.0.0_r9 (cherry-pick of 528a87e90ff9, Change-Id Ie2f43663bc71a06ffadb868d2d0eea5ee78f76e5); at android-security-14.0.0_r29 all 3/3 lines of the patched region are present.
- Android 15: not affected per the bulletin; at android-security-15.0.0_r17 the patched region is present (3/3 lines).
- Android 16: not affected per the bulletin (the bulletin does list 16-qpr2-next); at android-security-16.0.0_r8 the patched region is present (3/3 lines).
- Android 17: not affected per the bulletin; the patched file services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java is absent at android-security-17.0.0_r1.
Unknowns
- Why the non-dynamic check was previously downgraded from SecurityException to Slog.wtf (the TODO only hints at it).
- Where the equivalent logic lives in Android 17, since the patched file is absent at android-security-17.0.0_r1.
- Why the bulletin lists 16-qpr2-next as affected while the patched region is already present at android-security-16.0.0_r8.
- The concrete impact on the device of removing a non-dynamic permission from the registry (bulletin only rates it High EoP).