• 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
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