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