1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.permissioncontroller.permission.ui.model.v33 18 19 import android.app.Application 20 import android.content.Context 21 import android.content.pm.PackageInfo 22 import android.os.Bundle 23 import androidx.fragment.app.Fragment 24 import androidx.lifecycle.ViewModel 25 import androidx.lifecycle.ViewModelProvider 26 import androidx.navigation.NavController 27 import androidx.navigation.fragment.NavHostFragment 28 import com.android.permissioncontroller.R 29 import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData 30 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData 31 import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS 32 import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData 33 import com.android.permissioncontroller.permission.data.get 34 import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup 35 import com.android.permissioncontroller.permission.utils.Utils 36 import com.android.permissioncontroller.permission.utils.navigateSafe 37 import com.android.settingslib.RestrictedLockUtils 38 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin 39 import java.util.stream.Collectors 40 41 /** 42 * View model for legacy {@link ReviewPermissionsFragment}. 43 */ 44 class ReviewPermissionsViewModel( 45 val app: Application, 46 val packageInfo: PackageInfo 47 ) : ViewModel() { 48 49 private val mUser = android.os.Process.myUserHandle() 50 51 /** 52 * Holds permission groups for a package or an empty map in case no user review is required. 53 */ 54 val permissionGroupsLiveData = 55 object : SmartUpdateMediatorLiveData<Map<String, LightAppPermGroup>>() { 56 val packagePermsLiveData = PackagePermissionsLiveData[packageInfo.packageName, mUser] 57 58 init { 59 addSource(packagePermsLiveData) { 60 update() 61 } 62 } 63 64 val permissionGroups = mutableMapOf<String, LightAppPermGroupLiveData>() 65 66 override fun onUpdate() { 67 val permissionGroupsMap = packagePermsLiveData.value ?: return 68 val filteredGroups = permissionGroupsMap.keys.stream() 69 .filter { it -> !it.equals(NON_RUNTIME_NORMAL_PERMS) } 70 .collect(Collectors.toList()) 71 72 val getPermGroupLiveData = { permGroupName: String -> 73 LightAppPermGroupLiveData[packageInfo.packageName, permGroupName, mUser] 74 } 75 setSourcesToDifference(filteredGroups, permissionGroups, getPermGroupLiveData) 76 if (permissionGroups.values.all { it.isInitialized } && 77 permissionGroups.values.all { !it.isStale }) { 78 val permGroups: List<LightAppPermGroup?> = permissionGroups.values.map { 79 it.value } 80 val reviewGroups = permGroups.filterNotNull().filter { 81 shouldShowPermission(it) && 82 Utils.OS_PKG == it.permGroupInfo.packageName 83 }.associateBy { 84 it.permGroupName 85 } 86 value = if (reviewGroups.any { it.value.isReviewRequired }) reviewGroups 87 else emptyMap() 88 } 89 } 90 } 91 92 fun isInitialized(): Boolean { 93 return permissionGroupsLiveData.isInitialized 94 } 95 96 private fun shouldShowPermission(group: LightAppPermGroup): Boolean { 97 if (!(group.foreground.isGrantable || group.background.isGrantable)) { 98 return false 99 } 100 val isPlatformPermission = group.packageName == Utils.OS_PKG 101 // Show legacy permissions only if the user chose that. 102 return !(isPlatformPermission && !Utils.isModernPermissionGroup(group.permGroupName)) 103 } 104 105 fun isPackageUpdated(): Boolean { 106 val permGroupsMap: Map<String, LightAppPermGroup> = permissionGroupsLiveData.value!! 107 return permGroupsMap.any { !it.value.isReviewRequired } 108 } 109 110 /** 111 * Update the summary of a permission group that has background permission. 112 * This does not apply to permission groups that are fixed by policy 113 */ 114 fun getSummaryForPermGroupWithBackgroundPermission( 115 state: PermissionTarget 116 ): PermissionSummary { 117 if (state != PermissionTarget.PERMISSION_NONE) { 118 if (state.and(PermissionTarget.PERMISSION_BACKGROUND) 119 != PermissionTarget.PERMISSION_NONE.value) { 120 return SummaryMessage.ACCESS_ALWAYS.toPermSummary() 121 } else { 122 return SummaryMessage.ACCESS_ONLY_FOREGROUND.toPermSummary() 123 } 124 } else { 125 return SummaryMessage.ACCESS_NEVER.toPermSummary() 126 } 127 } 128 129 fun getSummaryForIndividuallyControlledPermGroup( 130 permGroup: LightAppPermGroup 131 ): PermissionSummary { 132 var revokedCount = 0 133 val lightPerms = permGroup.allPermissions.values.toList() 134 val permissionCount = lightPerms.size 135 for (i in 0 until permissionCount) { 136 if (!lightPerms[i].isGrantedIncludingAppOp) { 137 revokedCount++ 138 } 139 } 140 return when (revokedCount) { 141 0 -> { 142 SummaryMessage.REVOKED_NONE.toPermSummary() 143 } 144 permissionCount -> { 145 SummaryMessage.REVOKED_ALL.toPermSummary() 146 } 147 else -> { 148 PermissionSummary(SummaryMessage.REVOKED_COUNT, false, revokedCount) 149 } 150 } 151 } 152 153 /** 154 * Show all individual permissions in this group in a new fragment. 155 */ 156 fun showAllPermissions(fragment: Fragment, args: Bundle) { 157 val navController: NavController = NavHostFragment.findNavController(fragment) 158 navController.navigateSafe(R.id.app_to_all_perms, args) 159 } 160 161 enum class SummaryMessage { 162 NO_SUMMARY, 163 DISABLED_BY_ADMIN, 164 ENABLED_BY_ADMIN, 165 ENABLED_SYSTEM_FIXED, 166 ENFORCED_BY_POLICY, 167 ENABLED_BY_ADMIN_FOREGROUND_ONLY, 168 ENABLED_BY_POLICY_FOREGROUND_ONLY, 169 ENABLED_BY_ADMIN_BACKGROUND_ONLY, 170 ENABLED_BY_POLICY_BACKGROUND_ONLY, 171 DISABLED_BY_ADMIN_BACKGROUND_ONLY, 172 DISABLED_BY_POLICY_BACKGROUND_ONLY, 173 REVOKED_NONE, 174 REVOKED_ALL, 175 REVOKED_COUNT, 176 ACCESS_ALWAYS, 177 ACCESS_ONLY_FOREGROUND, 178 ACCESS_NEVER; 179 180 fun toPermSummary(): PermissionSummary { 181 return PermissionSummary(this, false) 182 } 183 184 fun toPermSummary(isEnterprise: Boolean): PermissionSummary { 185 return PermissionSummary(this, isEnterprise) 186 } 187 } 188 189 data class PermissionSummary( 190 val msg: SummaryMessage, 191 val isEnterprise: Boolean = false, 192 val revokeCount: Int = 0 193 ) 194 195 fun getSummaryForFixedByPolicyPermissionGroup( 196 mState: PermissionTarget, 197 permGroup: LightAppPermGroup, 198 context: Context 199 ): PermissionSummary { 200 val admin = getAdmin(context, permGroup) 201 val hasAdmin = admin != null 202 if (permGroup.isSystemFixed) { 203 // Permission is fully controlled by the system and cannot be switched 204 return SummaryMessage.ENABLED_SYSTEM_FIXED.toPermSummary() 205 } else if (isForegroundDisabledByPolicy(permGroup)) { 206 // Permission is fully controlled by policy and cannot be switched 207 return if (hasAdmin) { 208 SummaryMessage.DISABLED_BY_ADMIN.toPermSummary() 209 } else { 210 // Disabled state will be displayed by switch, so no need to add text for that 211 SummaryMessage.ENFORCED_BY_POLICY.toPermSummary() 212 } 213 } else if (permGroup.isPolicyFullyFixed) { 214 // Permission is fully controlled by policy and cannot be switched 215 if (!permGroup.hasBackgroundGroup) { 216 return if (hasAdmin) { 217 SummaryMessage.ENABLED_BY_ADMIN.toPermSummary() 218 } else { 219 // Enabled state will be displayed by switch, so no need to add text for that 220 SummaryMessage.ENFORCED_BY_POLICY.toPermSummary() 221 } 222 } else { 223 if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) != 224 PermissionTarget.PERMISSION_NONE.value) { 225 return if (hasAdmin) { 226 SummaryMessage.ENABLED_BY_ADMIN.toPermSummary() 227 } else { 228 // Enabled state will be displayed by switch, so no need to add text for 229 // that 230 SummaryMessage.ENFORCED_BY_POLICY.toPermSummary() 231 } 232 } else { 233 return if (hasAdmin) { 234 SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary() 235 } else { 236 SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary() 237 } 238 } 239 } 240 } else { 241 // Part of the permission group can still be switched 242 if (permGroup.background.isPolicyFixed) { 243 return if (mState.and(PermissionTarget.PERMISSION_BACKGROUND) != 244 PermissionTarget.PERMISSION_NONE.value) { 245 if (hasAdmin) { 246 SummaryMessage.ENABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true) 247 } else { 248 SummaryMessage.ENABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary() 249 } 250 } else { 251 if (hasAdmin) { 252 SummaryMessage.DISABLED_BY_ADMIN_BACKGROUND_ONLY.toPermSummary(true) 253 } else { 254 SummaryMessage.DISABLED_BY_POLICY_BACKGROUND_ONLY.toPermSummary() 255 } 256 } 257 } else if (permGroup.foreground.isPolicyFixed) { 258 return if (hasAdmin) { 259 SummaryMessage.ENABLED_BY_ADMIN_FOREGROUND_ONLY.toPermSummary(true) 260 } else { 261 SummaryMessage.ENABLED_BY_POLICY_FOREGROUND_ONLY.toPermSummary() 262 } 263 } 264 } 265 return SummaryMessage.NO_SUMMARY.toPermSummary() 266 } 267 268 /** 269 * Is the foreground part of this group disabled. If the foreground is disabled, there is no 270 * need to possible grant background access. 271 * 272 * @return `true` iff the permissions of this group are fixed 273 */ 274 private fun isForegroundDisabledByPolicy(mGroup: LightAppPermGroup): Boolean { 275 return mGroup.foreground.isPolicyFixed && !mGroup.isGranted 276 } 277 278 /** 279 * Whether policy is system fixed or fully fixed or foreground disabled 280 */ 281 fun isFixedOrForegroundDisabled(mGroup: LightAppPermGroup): Boolean { 282 return mGroup.isSystemFixed || mGroup.isPolicyFullyFixed || 283 isForegroundDisabledByPolicy(mGroup) 284 } 285 286 /** 287 * Get the app that acts as admin for this profile. 288 * 289 * @return The admin or `null` if there is no admin. 290 */ 291 fun getAdmin(context: Context, mGroup: LightAppPermGroup): EnforcedAdmin? { 292 return RestrictedLockUtils.getProfileOrDeviceOwner(context, mGroup.userHandle) 293 } 294 295 enum class PermissionTarget(val value: Int) { 296 PERMISSION_NONE(0), 297 PERMISSION_FOREGROUND(1), 298 PERMISSION_BACKGROUND(2), 299 PERMISSION_BOTH(3); 300 301 infix fun and(other: PermissionTarget): Int { 302 return value and other.value 303 } 304 305 infix fun and(other: Int): Int { 306 return value and other 307 } 308 309 infix fun or(other: PermissionTarget): Int { 310 return value or other.value 311 } 312 313 companion object { 314 fun fromInt(value: Int) = values().first { it.value == value } 315 } 316 } 317 } 318 319 class ReviewPermissionViewModelFactory( 320 private val app: Application, 321 private val packageInfo: PackageInfo 322 ) : ViewModelProvider.Factory { createnull323 override fun <T : ViewModel> create(modelClass: Class<T>): T { 324 @Suppress("UNCHECKED_CAST") 325 return ReviewPermissionsViewModel(app, packageInfo = packageInfo) as T 326 } 327 }