CVE-2026-28607 AI-generated analysis
platform/frameworks/base, packages/PackageInstaller (app archiving / unarchive UI)
Adds caller permission checks (REQUEST_INSTALL_PACKAGES requested or INSTALL_PACKAGES granted) plus the hide-non-system-overlays window flag to UnarchiveErrorActivity.onCreate, factors the UID-to-package and requested-permission helpers into PackageUtil, and on Android 17 moves the PIA v2 UnarchiveLaunch forwarding behind those checks.
Root cause
UnarchiveErrorActivity.onCreate read intent extras (EXTRA_UNARCHIVE_STATUS, EXTRA_REQUIRED_BYTES; the class also defines installer package name/title extras) from any launcher. It did not verify that the calling UID held INSTALL_PACKAGES or requested REQUEST_INSTALL_PACKAGES, and it did not set SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS on its window. The sibling UnarchiveActivity already performed these caller checks, so the error dialog path was the unguarded one. On Android 17 (shown in the 17 diff), UnarchiveActivity also forwarded the incoming intent to the v2 UnarchiveLaunch activity (when PackageUtil.isVersionTwoEnabled) before running its own caller checks, so the v2 path was reached without the caller being validated.
How the fix works
- PackageUtil.java: adds two static helpers, moved out of UnarchiveActivity so both activities share them. getPackageNameForUid(PackageManager, int) returns pm.getPackagesForUid(uid)[0], or null with a log message when no package matches. getRequestedPermissions(PackageManager, String) returns requestedPermissions from getPackageInfo(pkg, GET_PERMISSIONS), or an empty array on any Exception.
- UnarchiveActivity.java:onCreate: switches to the PackageUtil helpers and a single PackageManager local. On 15 and 16 the existing check logic is unchanged: INVALID_UID aborts, a null package aborts, and neither REQUEST_INSTALL_PACKAGES requested nor INSTALL_PACKAGES granted gives RESULT_FIRST_USER + finish. The private getRequestedPermissions and getPackageNameForUid methods are deleted.
- UnarchiveErrorActivity.java:onCreate: before reading any extras, the method now does the following, mirroring the UnarchiveActivity gate. It calls getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS). It gets callingUid via getLaunchedFromUid() and aborts with RESULT_FIRST_USER if that is INVALID_UID. It aborts if PackageUtil.getPackageNameForUid returns null. It aborts if the caller neither lists REQUEST_INSTALL_PACKAGES in its requested permissions nor passes getBaseContext().checkPermission(INSTALL_PACKAGES, 0, callingUid).
- Android 17 only, UnarchiveActivity.java:onCreate: the PackageUtil.isVersionTwoEnabled block that builds the piaV2 intent (EXTRA_CALLING_PKG_NAME/EXTRA_CALLING_PKG_UID, setClass UnarchiveLaunch, FLAG_ACTIVITY_FORWARD_RESULT) moves from right after super.onCreate to after the permission checks. The unused androidx NonNull import is dropped. In UnarchiveErrorActivity on 17, the existing isVersionTwoEnabled forwarding block now sits after the newly inserted checks.
Completeness
- Covers both v1 unarchive activities (UnarchiveActivity, UnarchiveErrorActivity) with the same caller gate. On 17 it also makes sure the v2 UnarchiveLaunch forwarding in both activities happens only after the gate.
- The REQUEST_INSTALL_PACKAGES branch only checks that the calling package declares the permission (requestedPermissions), not that the app op is granted. The in-code comment in UnarchiveActivity says the AppOpsManager is not checked because the requester is not the source of the installation. The INSTALL_PACKAGES branch is a real checkPermission against the UID.
- For shared-UID callers, PackageUtil.getPackageNameForUid uses only packagesForUid[0], so the requested-permission check is evaluated against the first package of the UID (same as the existing UnarchiveActivity behavior).
- The rest of UnarchiveErrorActivity.onCreate (extras parsing and dialog handling) is not changed and not shown. Any validation of those extras is outside this fix.
- Whether the v2 UnarchiveLaunch activity performs its own caller validation is not shown in the material.
Behavior changes
- UnarchiveErrorActivity now logs an error and finishes immediately with Activity.RESULT_FIRST_USER when the launching UID cannot be resolved, maps to no package, or neither requests REQUEST_INSTALL_PACKAGES nor holds INSTALL_PACKAGES.
- The UnarchiveErrorActivity window now sets SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, which hides non-system overlays while it is shown.
- PackageUtil.getRequestedPermissions catches Exception (broader than the previous NameNotFoundException), and getPackageNameForUid now logs when no package matches the UID.
- On Android 17, UnarchiveActivity forwards to the v2 UnarchiveLaunch only after the permission gate passes, so launches by callers that fail the gate are no longer forwarded to UnarchiveLaunch.
Per major
- Android 14: not affected per the bulletin (Android 14 not listed). The per-major facts show 7/8 lines of the patched region present at android-security-14.0.0_r29, so some form of this code exists there, but no fix is recorded for 14.
- Android 15: same fix: a4b48c54049c in android-security-15.0.0_r17 (cherry-pick, Change-Id I8f1265acae162cd752e76259f4ffaf39ab76b7e5). Adds the gate to UnarchiveErrorActivity and moves the helpers into PackageUtil. The 15 diff and code context show no v2 UnarchiveLaunch forwarding in these activities. 8/8 patched lines present.
- Android 16: same fix: 2e158f2d9a81 in android-security-16.0.0_r8. The activity changes are textually identical to 15 (same before/after blob hashes); only the PackageUtil hunk offset differs. No v2 forwarding shown in these activities. 8/8 patched lines present.
- Android 17: adapted fix: 743a34f05af0 in android-security-17.0.0_r1. Same PackageUtil helpers and the same UnarchiveErrorActivity overlay flag and caller gate (the LOG_TAG field is not added in this diff). In addition, UnarchiveActivity's PackageUtil.isVersionTwoEnabled forwarding to UnarchiveLaunch moves after the permission checks, and the androidx NonNull import is removed. The existing v2 block in UnarchiveErrorActivity now follows the new gate. 8/8 patched lines present.
Unknowns
- Whether UnarchiveErrorActivity is exported or otherwise reachable by third-party apps (manifest not in the material).
- What a caller could achieve by launching UnarchiveErrorActivity with crafted extras before the fix (the extras handling body, including any use of the installer extras or a PendingIntent, is not shown).
- Whether UnarchiveLaunch (v2) has independent caller checks.
- Why Android 14 is not listed even though the per-major facts show most of the patched region present there.