CVE-2026-0023 AI-generated analysis
platform/frameworks/base, PackageManager / PackageInstallerService (install session creation)
In PackageInstallerService.java, the session creation path now always clears PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE from the caller-supplied params.installFlags, then adds it back only when DevicePolicyManagerInternal.isUserOrganizationManaged(userId) is true.
Root cause
When creating an install session, PackageInstallerService took params.installFlags as supplied by the caller. It only ORed in INSTALL_FROM_MANAGED_USER_OR_PROFILE when dpmi.isUserOrganizationManaged(userId) was true and never cleared the bit otherwise. A caller that pre-set the flag in its SessionParams therefore had it carried into the PackageInstallerSession even when the install was not from a managed user or profile. The commit message says this could lead to unexpected behaviour.
How the fix works
- services/core/java/com/android/server/pm/PackageInstallerService.java, session creation path (the block after `final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);`): adds `params.installFlags &= ~PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE;`, so the flag is always removed from caller input first.
- The existing conditional `if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) { params.installFlags |= PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE; }` is unchanged. At this point the flag is now set only from the device policy state of the target userId, not by the caller.
- A regression test is referenced: PackageManagerShellCommandInstallTest#testSessionCreationWithManagedUserOrProfileFlag_notFromManagedProfile. The test file is not included in the diff.
Completeness
- The sanitized params object is the one passed to the new PackageInstallerSession constructor in the same method.
- Only INSTALL_FROM_MANAGED_USER_OR_PROFILE is addressed. The same block already strips INSTALL_REQUEST_UPDATE_OWNERSHIP when ENFORCE_UPDATE_OWNERSHIP is denied (or for APEX), and resets forceQueryableOverride for callers that are not root or shell. No other caller-controlled flag is newly sanitized by this change.
- The diff is a two-line change (one comment, one statement). No other files or paths, such as the code that consumes the flag, are modified. The material does not show whether the flag can reach a session by any route other than this creation path.
Behavior changes
- If a caller sets INSTALL_FROM_MANAGED_USER_OR_PROFILE in SessionParams.installFlags, the flag is now silently dropped. No error path is added.
- The flag is present on a session only when DevicePolicyManagerInternal is available and reports the target userId as organization managed.
Per major
- Android 14: same fix: commit a9550d0a75d0 in android-security-14.0.0_r25 (Change-Id I21bbbf628e97244d469eb23ce0558dbf560b7618); 3/3 lines of the patched region present at android-security-14.0.0_r29.
- Android 15: same fix: commit 221ac26392cd in android-security-15.0.0_r13, identical two-line hunk at a different offset (hunk at line 1016); 3/3 lines present at android-security-15.0.0_r17.
- Android 16: same fix: commit 09055276288a in android-security-16.0.0_r4, identical two-line hunk (hunk at line 1027); 3/3 lines present at android-security-16.0.0_r8.
- Android 17: not affected (not listed by the bulletin); the patched region (3/3 lines) is present at android-security-17.0.0_r1.
Unknowns
- The name of the enclosing method is not shown in the hunk header or the provided context.
- The material does not show which code that consumes INSTALL_FROM_MANAGED_USER_OR_PROFILE produces the 'unexpected behaviour' named in the commit message, or what concrete privilege results. The bulletin only rates the issue High EoP.
- The material does not show whether the flag can enter a session through any path other than session creation.
- The contents of the referenced regression test (PackageManagerShellCommandInstallTest) are not included.