CVE-2024-43095 AI-generated analysis
platform/packages/modules/Permission, PermissionController (runtime permission grant flow and the LightAppPermGroup/LightPermission data model)
PermissionController now treats a platform permission group as granted (and eligible for whole-group auto-grant) only when a permission in that group defined by the platform or a system app is granted, or when all permissions in the group were auto-granted (REVOKE_WHEN_REQUESTED). Grants of permissions defined by other apps that sit in a platform group no longer count.
Root cause
In LightAppPermGroup.AppPermSubGroup, isGranted and isGrantedExcludingRWROrAllRWR were computed over every LightPermission in the subgroup, regardless of which package defined the permission. When a permission defined by a non-platform, non-system package was in a platform permission group (permGroupInfo.packageName == Utils.OS_PKG) and was granted, the subgroup reported itself as granted. GrantPermissionsViewModel used group.foreground/background.isGrantedExcludingRWROrAllRWR together with canAutoGrantWholeGroup to call grantForegroundRuntimePermissions/grantBackgroundRuntimePermissions for other requested permissions in the same group. After a user grant, it also set groupState.state = STATE_ALLOWED unconditionally, without checking whether the affected permissions were platform or system ones. LightPermInfo carried no information about whether the defining package was a system app, so no such check could be made.
How the fix works
- LightPermInfo.kt: adds a nullable isSystem field (whether a system app defines the permission) and changes the constructor to LightPermInfo(permInfo: PermissionInfo, isSystem: Boolean?).
- LightPackageInfo.kt: the constructor now builds LightPermInfo(perm, pI.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM != 0) for the permissions the package defines.
- PermGroupLiveData.kt:onUpdate: collects PermissionInfo objects into permInfos and registers LightPackageInfoLiveData[packageName, UserHandle.SYSTEM] sources for every defining package. It returns early until all of them are initialized, then builds LightPermInfo(permInfo, isSystem) with isSystem derived from the defining package's appFlags and FLAG_SYSTEM, and only then posts the PermGroup value. Previously the value was posted before the package live datas were set up.
- LightPermInfoLiveData.kt:loadDataAndPostValue: passes null for isSystem.
- LightPermission.kt: renames isGrantedIncludingAppOp to isGranted. Adds isPlatformOrSystem = permInfo.packageName == Utils.OS_PKG || permInfo.isSystem == true, and allowFullGroupGrant = isGranted && !isRevokeWhenRequested.
- LightAppPermGroup.kt: adds isPlatformPermissionGroup = permGroupInfo.packageName == Utils.OS_PKG and passes it to both AppPermSubGroup instances. For platform groups, AppPermSubGroup.isGranted now also requires the granted permission to be isPlatformOrSystem. isGrantedExcludingRWROrAllRWR is renamed allowFullGroupGrant. It is true only if (a) some permission has allowFullGroupGrant and either the group is not a platform group or the permission isPlatformOrSystem, or (b) all permissions are granted and REVOKE_WHEN_REQUESTED. specialLocationGrant still overrides both.
- GrantPermissionsViewModel.kt: the check that background access requires granted foreground access, and the auto-grant-whole-group check, now use group.foreground/background.allowFullGroupGrant.
- GrantPermissionsViewModel.kt (user-grant result path): tracks the affectedPermissions actually passed to the grant call and computes shouldDenyFullGroupGrant = group.isPlatformPermissionGroup && affectedPermissions.none { isPlatformOrSystem }. When that is true, groupState.state is set to STATE_UNKNOWN instead of STATE_ALLOWED.
- Remaining files (AutoRevokePermissions.kt, RuntimePermissionsUpgradeController.kt, ReviewPermissionsFragment.java, AppPermissionViewModel.kt, ReviewPermissionsViewModel.kt, KotlinUtils.kt, SafetyNetLogger.java, tests): rename of isGrantedIncludingAppOp to isGranted and updates to LightPermInfo constructor calls. No logic change.
Completeness
- Covers the group-level grant signals (AppPermSubGroup.isGranted, allowFullGroupGrant) and both GrantPermissionsViewModel paths that auto-grant or mark a group as allowed, for platform permission groups only.
- Non-platform permission groups are explicitly exempt through !isPlatformPermissionGroup. Their any-granted semantics are unchanged.
- isSystem is only populated through PermGroupLiveData and LightPackageInfo. LightPermInfoLiveData passes null, so a permission obtained through that path counts as platform-or-system only if its package is Utils.OS_PKG. If the defining package's LightPackageInfoLiveData value is null (the TODO about packages not installed for the system user remains), isSystem is null and the permission is treated as non-system.
- The onUpdate path in GrantPermissionsViewModel that sets STATE_ALLOWED when all affectedPermissions are isGranted and not isRevokeWhenRequested is unchanged apart from the rename.
- The REVOKE_WHEN_REQUESTED clause (permissions.all { isGranted && isRevokeWhenRequested }) still grants whole-group status without checking isPlatformOrSystem, as the commit message describes.
Behavior changes
- PermGroupLiveData posts no new value until LightPackageInfoLiveData is initialized for every package that defines a permission in the group, plus the group's own package. Previously the value was posted immediately.
- For platform permission groups, consumers of LightAppPermGroup.foreground/background.isGranted no longer report the group as granted when the only granted permissions in it are defined by non-platform, non-system packages.
- After a user grant in GrantPermissionsViewModel in a platform group where none of the affected permissions are platform or system ones, the group state becomes STATE_UNKNOWN instead of STATE_ALLOWED.
- Internal API changes inside PermissionController: LightPermission.isGrantedIncludingAppOp is renamed isGranted; AppPermSubGroup.isGrantedExcludingRWROrAllRWR is renamed allowFullGroupGrant; the LightPermInfo constructor gains a required isSystem parameter; the AppPermSubGroup constructor gains isPlatformPermissionGroup.
Per major
- Android 14: Same fix: ea7b26fe98c1 in android-security-14.0.0_r15, with the grant-flow changes made directly in GrantPermissionsViewModel. At android-security-14.0.0_r29, 7/8 lines of the patched region are present.
- Android 15: Adapted fix: f677d33568d9 in android-security-15.0.0_r3. It makes the same data-model changes, but the group-grant checks also land in the refactored grantPermissions/BackgroundGrantBehavior.kt, GrantBehavior.kt, HealthGrantBehavior.kt, LocationGrantBehavior.kt and StorageGrantBehavior.kt. The grant-result path uses STATE_GRANTED/STATE_UNKNOWN. At android-security-15.0.0_r17, 7/8 lines are present.
- Android 16: Not affected (not listed by the bulletin). At android-security-16.0.0_r8, 6/8 lines of the patched region are present.
- Android 17: Not affected (not listed by the bulletin). At android-security-17.0.0_r1, 6/8 lines of the patched region are present.
Unknowns
- The material does not show how the non-system permission in a platform group initially becomes granted in the reported scenario.
- The exact downstream effect of setting groupState.state to STATE_UNKNOWN after a grant is not fully visible in the provided context.
- The material does not show whether the referenced DynamicPermissionsTest is present on the security branch (it is not in the diff).
- The exact names of the GrantPermissionsViewModel functions changed on Android 14 are not shown in the diff hunk headers.