1 /* 2 * Copyright (C) 2019 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.data 18 19 import android.app.Application 20 import android.app.role.RoleManager 21 import android.os.UserHandle 22 import androidx.lifecycle.LiveData 23 import com.android.permissioncontroller.PermissionControllerApplication 24 import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo 25 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo 26 import com.android.permissioncontroller.permission.utils.Utils 27 28 /** 29 * A LiveData which tracks all app permission groups for a set of permission groups, either platform 30 * or custom, as well as the UI information related to each app permission group, and the permission 31 * group as a whole. 32 * 33 * @param app The current application 34 */ 35 class PermGroupsPackagesUiInfoLiveData( 36 private val app: Application, 37 groupNamesLiveData: LiveData<List<String>> 38 ) : SmartUpdateMediatorLiveData< 39 @kotlin.jvm.JvmSuppressWildcards Map<String, PermGroupPackagesUiInfo?>>() { 40 private val SYSTEM_SHELL = "android.app.role.SYSTEM_SHELL" 41 42 /** 43 * Map<permission group name, PermGroupUiLiveDatas> 44 */ 45 private val permGroupPackagesLiveDatas = mutableMapOf<String, 46 SinglePermGroupPackagesUiInfoLiveData>() 47 private val allPackageData = mutableMapOf<String, PermGroupPackagesUiInfo?>() 48 49 private lateinit var groupNames: List<String> 50 51 init { 52 addSource(groupNamesLiveData) { 53 groupNames = it ?: emptyList() 54 update() 55 getPermGroupPackageLiveDatas() 56 } 57 } 58 59 private fun getPermGroupPackageLiveDatas() { 60 val getLiveData = { groupName: String -> SinglePermGroupPackagesUiInfoLiveData[groupName] } 61 setSourcesToDifference(groupNames, permGroupPackagesLiveDatas, getLiveData) 62 } 63 64 private fun isGranted(grantState: AppPermGroupUiInfo.PermGrantState): Boolean { 65 return grantState != AppPermGroupUiInfo.PermGrantState.PERMS_DENIED && 66 grantState != AppPermGroupUiInfo.PermGrantState.PERMS_ASK 67 } 68 69 private fun createPermGroupPackageUiInfo( 70 groupName: String, 71 appPermGroups: Map<Pair<String, UserHandle>, AppPermGroupUiInfo> 72 ): PermGroupPackagesUiInfo { 73 var nonSystem = 0 74 var grantedNonSystem = 0 75 var userInteractedNonSystem = 0 76 var grantedSystem = 0 77 var userInteractedSystem = 0 78 var firstGrantedSystemPackageName: String? = null 79 80 for ((packageUserPair, appPermGroup) in appPermGroups) { 81 if (!appPermGroup.shouldShow) { 82 continue 83 } 84 85 if (appPermGroup.isSystem) { 86 if (isGranted(appPermGroup.permGrantState)) { 87 if (grantedSystem == 0) { 88 firstGrantedSystemPackageName = packageUserPair.first 89 } 90 grantedSystem++ 91 userInteractedSystem++ 92 } else if (appPermGroup.isUserSet) { 93 userInteractedSystem++ 94 } 95 } else { 96 nonSystem++ 97 98 if (isGranted(appPermGroup.permGrantState)) { 99 grantedNonSystem++ 100 userInteractedNonSystem++ 101 } else if (appPermGroup.isUserSet) { 102 userInteractedNonSystem++ 103 } 104 } 105 } 106 val onlyShellGranted = grantedNonSystem == 0 && grantedSystem == 1 && 107 isPackageShell(firstGrantedSystemPackageName) 108 return PermGroupPackagesUiInfo(groupName, nonSystem, grantedNonSystem, 109 userInteractedNonSystem, grantedSystem, userInteractedSystem, onlyShellGranted) 110 } 111 112 private fun isPackageShell(packageName: String?): Boolean { 113 if (packageName == null) { 114 return false 115 } 116 117 // This method is only called at most once per permission group, so no need to cache value 118 val roleManager = Utils.getSystemServiceSafe(PermissionControllerApplication.get(), 119 RoleManager::class.java) 120 return roleManager.getRoleHolders(SYSTEM_SHELL).contains(packageName) 121 } 122 123 override fun onUpdate() { 124 /** 125 * Only update when either- 126 * We have a list of groups, and none have loaded their data, or 127 * All packages have loaded their data 128 */ 129 val haveAllLiveDatas = groupNames.all { permGroupPackagesLiveDatas.contains(it) } 130 val allInitialized = permGroupPackagesLiveDatas.all { it.value.isInitialized } 131 for (groupName in groupNames) { 132 allPackageData[groupName] = if (haveAllLiveDatas && allInitialized) { 133 permGroupPackagesLiveDatas[groupName]?.value?.let { uiInfo -> 134 createPermGroupPackageUiInfo(groupName, uiInfo) 135 } 136 } else { 137 null 138 } 139 } 140 value = allPackageData.toMap() 141 } 142 } 143