1 /*
<lambda>null2  * 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.Manifest
20 import android.app.Application
21 import android.os.Build
22 import android.os.UserHandle
23 import androidx.lifecycle.LiveData
24 import com.android.permissioncontroller.PermissionControllerApplication
25 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroup
26 import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
27 
28 /**
29  * A LiveData which tracks either all platform permission groups, or all custom permission groups,
30  * and the packages which contain runtime permissions in one of those group.
31  *
32  * @param app The current application
33  */
34 class PermGroupsPackagesLiveData
35 private constructor(private val app: Application, groupNamesLiveData: LiveData<List<String>>) :
36     SmartUpdateMediatorLiveData<Map<String, Set<Pair<String, UserHandle>>>>() {
37 
38     private val packagesLiveData = AllPackageInfosLiveData
39     private val permGroupLiveDatas = mutableMapOf<String, PermGroupLiveData>()
40 
41     private var groupNames = emptyList<String>()
42 
43     init {
44         addSource(groupNamesLiveData) {
45             groupNames = it
46 
47             val getLiveData = { groupName: String -> PermGroupLiveData[groupName] }
48             setSourcesToDifference(groupNames, permGroupLiveDatas, getLiveData) {
49                 if (
50                     packagesLiveData.isInitialized &&
51                         permGroupLiveDatas.all { it.value.isInitialized }
52                 ) {
53                     update()
54                 }
55             }
56         }
57 
58         addSource(packagesLiveData) {
59             if (permGroupLiveDatas.all { it.value.isInitialized }) {
60                 update()
61             }
62         }
63     }
64 
65     /**
66      * Using the current list of permission groups, go through all packages in the system, and
67      * figure out which permission groups they have permissions for. If applicable, remove any
68      * lone-permission permission that are not requested by any packages.
69      */
70     override fun onUpdate() {
71         if (groupNames.isEmpty()) {
72             return
73         }
74 
75         val groupApps = mutableMapOf<String, MutableSet<Pair<String, UserHandle>>>()
76         val permGroups = mutableListOf<PermGroup>()
77         for (groupName in groupNames) {
78             val permGroup = permGroupLiveDatas[groupName]?.value
79             if (permGroup == null || !permGroup.hasRuntimePermissions) {
80                 continue
81             }
82             permGroups.add(permGroup)
83             groupApps[groupName] = mutableSetOf()
84         }
85 
86         val allPackages = packagesLiveData.value ?: return
87         for ((userHandle, packageInfos) in allPackages) {
88             for (packageInfo in packageInfos) {
89                 val isPreMApp = packageInfo.targetSdkVersion < Build.VERSION_CODES.M
90 
91                 for ((groupInfo, permissionInfos) in permGroups) {
92                     // Do not allow toggling non-platform permission groups for legacy apps via app
93                     // ops.
94                     if (isPreMApp && groupInfo.packageName != OS_PKG) {
95                         continue
96                     }
97                     // Categorize all requested permissions of this package
98                     for (permissionName in packageInfo.requestedPermissions) {
99                         if (permissionInfos.containsKey(permissionName)) {
100                             groupApps[groupInfo.name]?.add(packageInfo.packageName to userHandle)
101                         }
102                     }
103                 }
104             }
105         }
106 
107         /*
108          * Remove any lone permission groups that are not used by any package, and the UNDEFINED
109          * group, if also empty.
110          */
111         for (permGroup in permGroups) {
112             if (
113                 permGroup.groupInfo.isSinglePermGroup ||
114                     permGroup.name == Manifest.permission_group.UNDEFINED
115             ) {
116                 val groupPackages = groupApps[permGroup.name] ?: continue
117                 if (groupPackages.isEmpty()) {
118                     groupApps.remove(permGroup.name)
119                 }
120             }
121         }
122 
123         value = groupApps
124     }
125 
126     companion object {
127         private val customInstance =
128             PermGroupsPackagesLiveData(
129                 PermissionControllerApplication.get(),
130                 CustomPermGroupNamesLiveData
131             )
132         private val standardInstance =
133             PermGroupsPackagesLiveData(
134                 PermissionControllerApplication.get(),
135                 StandardPermGroupNamesLiveData
136             )
137 
138         /**
139          * Get either the PermGroupsPackageLiveData instance corresponding either to the custom
140          * permission groups, or the standard permission group.
141          *
142          * @param customGroups Whether to get the custom groups instance, or the standard
143          * @return The specified PermGroupsPackageLiveData
144          */
145         @JvmStatic
146         fun get(customGroups: Boolean = false): PermGroupsPackagesLiveData {
147             return if (customGroups) {
148                 customInstance
149             } else {
150                 standardInstance
151             }
152         }
153     }
154 }
155