• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.v31
18 
19 import android.Manifest
20 import android.app.LoaderManager
21 import android.app.role.RoleManager
22 import android.content.Context
23 import android.os.Build
24 import android.util.ArrayMap
25 import android.util.ArraySet
26 import android.util.Log
27 import androidx.annotation.RequiresApi
28 import androidx.lifecycle.ViewModel
29 import androidx.lifecycle.ViewModelProvider
30 import com.android.permissioncontroller.permission.model.AppPermissionGroup
31 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
32 import com.android.permissioncontroller.permission.model.v31.PermissionUsages
33 import com.android.permissioncontroller.permission.model.legacy.PermissionApps.PermissionApp
34 import com.android.permissioncontroller.permission.ui.handheld.v31.is7DayToggleEnabled
35 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
36 import com.android.permissioncontroller.permission.utils.Utils
37 import java.time.Instant
38 import java.util.concurrent.TimeUnit
39 import kotlin.math.max
40 
41 @RequiresApi(Build.VERSION_CODES.S)
42 class PermissionUsageViewModel(val roleManager: RoleManager) : ViewModel() {
43 
44     companion object {
45         private const val LOG_TAG = "PermissionUsageViewModel"
46 
47         /** TODO(ewol): Use the config setting to determine amount of time to show. */
48         private val TIME_FILTER_MILLIS = TimeUnit.DAYS.toMillis(7)
49         private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7)
50         private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1)
51 
52         @JvmStatic
53         val PERMISSION_GROUP_ORDER: Map<String, Int> = mapOf(
54             Manifest.permission_group.LOCATION to 0,
55             Manifest.permission_group.CAMERA to 1,
56             Manifest.permission_group.MICROPHONE to 2
57         )
58         private const val DEFAULT_ORDER = 3
59     }
60 
61     fun loadPermissionUsages(
62         loaderManager: LoaderManager,
63         permissionUsages: PermissionUsages,
64         callback: PermissionUsages.PermissionsUsagesChangeCallback
65     ) {
66         val filterTimeBeginMillis = max(System.currentTimeMillis() - TIME_FILTER_MILLIS,
67             Instant.EPOCH.toEpochMilli())
68         permissionUsages.load(null /*filterPackageName*/, null /*filterPermissionGroups*/,
69             filterTimeBeginMillis, Long.MAX_VALUE, PermissionUsages.USAGE_FLAG_LAST
70                 or PermissionUsages.USAGE_FLAG_HISTORICAL, loaderManager,
71             false /*getUiInfo*/, false /*getNonPlatformPermissions*/, callback /*callback*/,
72             false /*sync*/)
73     }
74 
75     fun extractUsages(
76         permissionUsages: List<AppPermissionUsage>,
77         show7Days: Boolean,
78         showSystem: Boolean
79     ): Triple<MutableMap<String, Int>, ArrayList<PermissionApp>, Boolean> {
80         val curTime = System.currentTimeMillis()
81         val showPermissionUsagesDuration = if (is7DayToggleEnabled() && show7Days) {
82             TIME_7_DAYS_DURATION
83         } else {
84             TIME_24_HOURS_DURATION
85         }
86         val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli())
87 
88         // Permission group to count mapping.
89         val usages: MutableMap<String, Int> = HashMap()
90         val permissionGroups: List<AppPermissionGroup> = getOSPermissionGroups(permissionUsages)
91         for (i in permissionGroups.indices) {
92             usages[permissionGroups[i].name] = 0
93         }
94         val permApps = ArrayList<PermissionApp>()
95 
96         val exemptedPackages = Utils.getExemptedPackages(roleManager)
97 
98         val seenSystemApp: Boolean = extractPermissionUsage(exemptedPackages,
99             usages, permApps, startTime, permissionUsages, showSystem)
100 
101         return Triple(usages, permApps, seenSystemApp)
102     }
103 
104     fun createGroupUsagesList(
105         context: Context,
106         usages: Map<String, Int>
107     ): List<Map.Entry<String, Int>> {
108         val groupUsageNameToLabel: MutableMap<String, CharSequence> = HashMap()
109         val groupUsagesList: MutableList<Map.Entry<String, Int>> = ArrayList(usages.entries)
110         val usagesEntryCount = groupUsagesList.size
111         for (usageEntryIndex in 0 until usagesEntryCount) {
112             val (key) = groupUsagesList[usageEntryIndex]
113             groupUsageNameToLabel[key] = getPermGroupLabel(context, key)
114         }
115 
116         groupUsagesList.sortWith { e1: Map.Entry<String, Int>, e2: Map.Entry<String, Int> ->
117             comparePermissionGroupUsage(e1, e2, groupUsageNameToLabel)
118         }
119 
120         return groupUsagesList
121     }
122 
123     private fun comparePermissionGroupUsage(
124         first: Map.Entry<String, Int>,
125         second: Map.Entry<String, Int>,
126         groupUsageNameToLabelMapping: Map<String, CharSequence>
127     ): Int {
128         val firstPermissionOrder = PERMISSION_GROUP_ORDER
129             .getOrDefault(first.key, DEFAULT_ORDER)
130         val secondPermissionOrder = PERMISSION_GROUP_ORDER
131             .getOrDefault(second.key, DEFAULT_ORDER)
132         return if (firstPermissionOrder != secondPermissionOrder) {
133             firstPermissionOrder - secondPermissionOrder
134         } else groupUsageNameToLabelMapping[first.key].toString()
135             .compareTo(groupUsageNameToLabelMapping[second.key].toString())
136     }
137 
138     /**
139      * Get the permission groups declared by the OS.
140      *
141      * @return a list of the permission groups declared by the OS.
142      */
143     private fun getOSPermissionGroups(
144         permissionUsages: List<AppPermissionUsage>
145     ): List<AppPermissionGroup> {
146         val groups: MutableList<AppPermissionGroup> = java.util.ArrayList()
147         val seenGroups: MutableSet<String> = ArraySet()
148         val numGroups: Int = permissionUsages.size
149         for (i in 0 until numGroups) {
150             val appUsage: AppPermissionUsage = permissionUsages.get(i)
151             val groupUsages = appUsage.groupUsages
152             val groupUsageCount = groupUsages.size
153             for (j in 0 until groupUsageCount) {
154                 val groupUsage = groupUsages[j]
155                 if (Utils.isModernPermissionGroup(groupUsage.group.name)) {
156                     if (seenGroups.add(groupUsage.group.name)) {
157                         groups.add(groupUsage.group)
158                     }
159                 }
160             }
161         }
162         return groups
163     }
164 
165     /**
166      * Extract the permission usages from mAppPermissionUsages and put the extracted usages
167      * into usages and permApps. Returns whether we have seen a system app during the process.
168      *
169      * TODO: theianchen
170      * It's doing two things at the same method which is violating the SOLID principle.
171      * We should fix this.
172      *
173      * @param exemptedPackages packages that are the role holders for exempted roles
174      * @param usages an empty List that will be filled with permission usages.
175      * @param permApps an empty List that will be filled with permission apps.
176      * @return whether we have seen a system app.
177      */
178     private fun extractPermissionUsage(
179         exemptedPackages: Set<String>,
180         usages: MutableMap<String, Int>,
181         permApps: java.util.ArrayList<PermissionApp>,
182         startTime: Long,
183         permissionUsages: List<AppPermissionUsage>,
184         showSystem: Boolean
185     ): Boolean {
186 
187         val mGroupAppCounts: ArrayMap<String?, Int> = ArrayMap()
188         var seenSystemApp = false
189         val numApps: Int = permissionUsages.size
190         for (appNum in 0 until numApps) {
191             val appUsage: AppPermissionUsage = permissionUsages.get(appNum)
192             if (exemptedPackages.contains(appUsage.packageName)) {
193                 continue
194             }
195             var used = false
196             val appGroups = appUsage.groupUsages
197             val numGroups = appGroups.size
198             for (groupNum in 0 until numGroups) {
199                 val groupUsage = appGroups[groupNum]
200                 val groupName = groupUsage.group.name
201                 val lastAccessTime = groupUsage.lastAccessTime
202                 if (lastAccessTime == 0L) {
203                     Log.w(
204                         LOG_TAG,
205                         "Unexpected access time of 0 for ${appUsage.app.key} " +
206                             groupUsage.group.name)
207                     continue
208                 }
209                 if (lastAccessTime < startTime) {
210                     continue
211                 }
212                 val isSystemApp = !Utils.isGroupOrBgGroupUserSensitive(
213                     groupUsage.group)
214                 seenSystemApp = seenSystemApp || isSystemApp
215 
216                 // If not showing system apps, skip.
217                 if (!showSystem && isSystemApp) {
218                     continue
219                 }
220                 used = true
221                 addGroupUser(mGroupAppCounts, groupName)
222                 usages[groupName] = usages.getOrDefault(groupName, 0) + 1
223             }
224             if (used) {
225                 permApps.add(appUsage.app)
226                 addGroupUser(mGroupAppCounts, null)
227             }
228         }
229         return seenSystemApp
230     }
231 
232     private fun addGroupUser(groupAppCounts: ArrayMap<String?, Int>, app: String?) {
233         val count: Int? = groupAppCounts[app]
234         if (count == null) {
235             groupAppCounts[app] = 1
236         } else {
237             groupAppCounts[app] = count + 1
238         }
239     }
240 }
241 
242 /**
243  * Factory for an PermissionUsageViewModel
244  */
245 @RequiresApi(Build.VERSION_CODES.S)
246 class PermissionUsageViewModelFactory(
247     private val roleManager: RoleManager
248 ) : ViewModelProvider.Factory {
249 
createnull250     override fun <T : ViewModel> create(modelClass: Class<T>): T {
251         @Suppress("UNCHECKED_CAST")
252         return PermissionUsageViewModel(roleManager) as T
253     }
254 }
255