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.model.livedatatypes.v31
18 
19 import android.app.AppOpsManager.AttributedHistoricalOps
20 import android.app.AppOpsManager.AttributedOpEntry
21 import android.app.AppOpsManager.HistoricalOp
22 import android.app.AppOpsManager.HistoricalPackageOps
23 import android.app.AppOpsManager.OP_FLAG_SELF
24 import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED
25 import android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY
26 import android.app.AppOpsManager.OpEventProxyInfo
27 import android.os.UserHandle
28 import com.android.permissioncontroller.permission.utils.PermissionMapping.getPlatformPermissionGroupForOp
29 
30 /**
31  * Light version of [HistoricalPackageOps] class, tracking the last permission access for system
32  * permission groups.
33  */
34 data class LightHistoricalPackageOps(
35     /** Name of the package. */
36     val packageName: String,
37     /** [UserHandle] running the package. */
38     val userHandle: UserHandle,
39     /**
40      * Data about permission accesses, one [AppPermissionDiscreteAccesses] for each permission
41      * group.
42      */
43     // TODO(b/262042582): Consider removing this field and using attributed accesses aggregated over
44     // attribution tags instead.
45     val appPermissionDiscreteAccesses: List<AppPermissionDiscreteAccesses>,
46     /**
47      * Attributed data about permission accesses, one [AttributedAppPermissionDiscreteAccesses] for
48      * each permission group.
49      */
50     val attributedAppPermissionDiscreteAccesses: List<AttributedAppPermissionDiscreteAccesses>
51 ) {
52     constructor(
53         historicalPackageOps: HistoricalPackageOps,
54         userHandle: UserHandle,
55         opNames: Set<String>
56     ) : this(
57         historicalPackageOps.packageName,
58         userHandle,
59         historicalPackageOps.getAppPermissionDiscreteAccesses(userHandle, opNames),
60         historicalPackageOps.getAttributedAppPermissionDiscreteAccesses(userHandle, opNames),
61     )
62 
63     /** Companion object for [LightHistoricalPackageOps]. */
64     companion object {
65         /** String to represent the absence of an attribution tag. */
66         const val NO_ATTRIBUTION_TAG = "no_attribution_tag"
67         /** String to represent the absence of a permission group. */
68         private const val NO_PERM_GROUP = "no_perm_group"
69         private const val DISCRETE_ACCESS_OP_FLAGS =
70             OP_FLAG_SELF or OP_FLAG_TRUSTED_PROXIED or OP_FLAG_TRUSTED_PROXY
71 
72         /**
73          * Creates a list of [AppPermissionDiscreteAccesses] for the provided package, user and ops.
74          */
75         private fun HistoricalPackageOps.getAppPermissionDiscreteAccesses(
76             userHandle: UserHandle,
77             opNames: Set<String>
78         ): List<AppPermissionDiscreteAccesses> {
79             val permissionsToOpNames = partitionOpsByPermission(opNames)
80             val appPermissionDiscreteAccesses = mutableListOf<AppPermissionDiscreteAccesses>()
81             for (permissionToOpNames in permissionsToOpNames.entries) {
82                 this.getDiscreteAccesses(permissionToOpNames.value)?.let {
83                     appPermissionDiscreteAccesses.add(
84                         AppPermissionDiscreteAccesses(
85                             AppPermissionId(packageName, userHandle, permissionToOpNames.key),
86                             it
87                         )
88                     )
89                 }
90             }
91 
92             return appPermissionDiscreteAccesses
93         }
94 
95         /**
96          * Creates a list of [AttributedAppPermissionDiscreteAccesses] for the provided package,
97          * user and ops.
98          */
99         private fun HistoricalPackageOps.getAttributedAppPermissionDiscreteAccesses(
100             userHandle: UserHandle,
101             opNames: Set<String>
102         ): List<AttributedAppPermissionDiscreteAccesses> {
103             val permissionsToOpNames = partitionOpsByPermission(opNames)
104             val attributedAppPermissionDiscreteAccesses =
105                 mutableMapOf<AppPermissionId, MutableMap<String, List<DiscreteAccess>>>()
106 
107             val attributedHistoricalOpsList = mutableListOf<AttributedHistoricalOps>()
108             for (i in 0 until attributedOpsCount) {
109                 attributedHistoricalOpsList.add(getAttributedOpsAt(i))
110             }
111 
112             for (permissionToOpNames in permissionsToOpNames.entries) {
113                 attributedHistoricalOpsList.forEach { attributedHistoricalOps ->
114                     attributedHistoricalOps.getDiscreteAccesses(permissionToOpNames.value)?.let {
115                         discAccessData ->
116                         val appPermissionId =
117                             AppPermissionId(packageName, userHandle, permissionToOpNames.key)
118                         if (!attributedAppPermissionDiscreteAccesses.containsKey(appPermissionId)) {
119                             attributedAppPermissionDiscreteAccesses[appPermissionId] =
120                                 mutableMapOf()
121                         }
122                         attributedAppPermissionDiscreteAccesses[appPermissionId]?.put(
123                             attributedHistoricalOps.tag ?: NO_ATTRIBUTION_TAG,
124                             discAccessData
125                         )
126                     }
127                 }
128             }
129 
130             return attributedAppPermissionDiscreteAccesses.map {
131                 AttributedAppPermissionDiscreteAccesses(it.key, it.value)
132             }
133         }
134 
135         /**
136          * Retrieves all discrete accesses for the provided op names, if any.
137          *
138          * Returns null if there are no accesses.
139          */
140         private fun HistoricalPackageOps.getDiscreteAccesses(
141             opNames: List<String>
142         ): List<DiscreteAccess>? {
143             if (opCount == 0) {
144                 return null
145             }
146 
147             val historicalOps = mutableListOf<HistoricalOp>()
148             for (opName in opNames) {
149                 getOp(opName)?.let { historicalOps.add(it) }
150             }
151 
152             val discreteAccessList = mutableListOf<DiscreteAccess>()
153             historicalOps.forEach {
154                 for (i in 0 until it.discreteAccessCount) {
155                     val opEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)
156                     discreteAccessList.add(
157                         DiscreteAccess(
158                             opEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
159                             opEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
160                             opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
161                         )
162                     )
163                 }
164             }
165 
166             if (discreteAccessList.isEmpty()) {
167                 return null
168             }
169             return discreteAccessList.sortedWith(compareBy { -it.accessTimeMs })
170         }
171 
172         /**
173          * Retrieves all discrete accesses for the provided op names, if any.
174          *
175          * Returns null if there are no accesses.
176          */
177         private fun AttributedHistoricalOps.getDiscreteAccesses(
178             opNames: List<String>
179         ): List<DiscreteAccess>? {
180             if (opCount == 0) {
181                 return null
182             }
183 
184             val historicalOps = mutableListOf<HistoricalOp>()
185             for (opName in opNames) {
186                 getOp(opName)?.let { historicalOps.add(it) }
187             }
188 
189             val discreteAccessList = mutableListOf<DiscreteAccess>()
190             historicalOps.forEach {
191                 for (i in 0 until it.discreteAccessCount) {
192                     val attributedOpEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)
193                     discreteAccessList.add(
194                         DiscreteAccess(
195                             attributedOpEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
196                             attributedOpEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
197                             attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
198                         )
199                     )
200                 }
201             }
202 
203             if (discreteAccessList.isEmpty()) {
204                 return null
205             }
206             return discreteAccessList.sortedWith(compareBy { -it.accessTimeMs })
207         }
208 
209         private fun partitionOpsByPermission(ops: Set<String>): Map<String, List<String>> =
210             ops.groupBy { getPlatformPermissionGroupForOp(it) ?: NO_PERM_GROUP }
211                 .filter { it.key != NO_PERM_GROUP }
212     }
213 
214     /**
215      * Data class representing permissions accesses for a particular permission group by a
216      * particular package and user.
217      */
218     data class AppPermissionDiscreteAccesses(
219         val appPermissionId: AppPermissionId,
220         val discreteAccesses: List<DiscreteAccess>
221     )
222 
223     /**
224      * Data class representing permissions accesses for a particular permission group by a
225      * particular package and user, partitioned by attribution tag.
226      */
227     data class AttributedAppPermissionDiscreteAccesses(
228         val appPermissionId: AppPermissionId,
229         val attributedDiscreteAccesses: Map<String, List<DiscreteAccess>>
230     )
231 
232     /** Data class representing a discrete permission access. */
233     data class DiscreteAccess(
234         val accessTimeMs: Long,
235         val accessDurationMs: Long,
236         val proxy: OpEventProxyInfo?
237     )
238 }
239